diff --git a/packages/@weex/plugins/debug/.gitignore b/packages/@weex/plugins/debug/.gitignore
index bb987ae..87ae600 100644
--- a/packages/@weex/plugins/debug/.gitignore
+++ b/packages/@weex/plugins/debug/.gitignore
@@ -3,6 +3,7 @@ node_modules/
# coverage
coverage/
+!frontend/src/assets/inspector/coverage/
package-lock.json
diff --git a/packages/@weex/plugins/debug/.npmignore b/packages/@weex/plugins/debug/.npmignore
index 1257691..a16c9c2 100644
--- a/packages/@weex/plugins/debug/.npmignore
+++ b/packages/@weex/plugins/debug/.npmignore
@@ -1,5 +1,6 @@
node_modules
coverage/
+!frontend/public/assets/inspector/coverage/
.git/
-.vscode/
+.vscode/
\ No newline at end of file
diff --git a/packages/@weex/plugins/debug/frontend/config/webpack.config.base.js b/packages/@weex/plugins/debug/frontend/config/webpack.config.base.js
index f215df7..1f4ca68 100644
--- a/packages/@weex/plugins/debug/frontend/config/webpack.config.base.js
+++ b/packages/@weex/plugins/debug/frontend/config/webpack.config.base.js
@@ -54,10 +54,7 @@ let config = {
from: 'src/assets',
to: './assets'
},{
- from: 'src/runtime.html',
- to: './',
- },{
- from: 'src/runtime',
+ from: 'runtime',
to: './runtime',
},{
from: 'node_modules/monaco-editor/min/vs',
diff --git a/packages/@weex/plugins/debug/frontend/package.json b/packages/@weex/plugins/debug/frontend/package.json
index 632bc84..eb9f7c9 100644
--- a/packages/@weex/plugins/debug/frontend/package.json
+++ b/packages/@weex/plugins/debug/frontend/package.json
@@ -89,7 +89,7 @@
"mocha": "~5.0.0",
"monaco-editor": "^0.14.3",
"ncp": "~2.0.0",
- "node-sass": "~4.7.2",
+ "node-sass": "^4.7.2",
"opn-cli": "~3.1.0",
"optimize-css-assets-webpack-plugin": "~3.2.0",
"postcss-loader": "~2.0.10",
@@ -110,6 +110,6 @@
"vue-loader": "^15.4.2",
"vue-template-compiler": "^2.5.17",
"webpack": "~3.10.0",
- "webpack-dev-server": "^3.1.11"
+ "webpack-dev-server": "^2.11.1"
}
}
diff --git a/packages/@weex/plugins/debug/frontend/runtime/EventEmitter.js b/packages/@weex/plugins/debug/frontend/runtime/EventEmitter.js
new file mode 100644
index 0000000..bc55fbb
--- /dev/null
+++ b/packages/@weex/plugins/debug/frontend/runtime/EventEmitter.js
@@ -0,0 +1,64 @@
+function EventEmitter() {
+ this._handlers = {};
+}
+EventEmitter.prototype = {
+ constructor: EventEmitter,
+ off: function (method, handler) {
+ if (handler) {
+ for (var i = 0; i < this._handlers[method].length; i++) {
+ if (this._handlers[method][i] === handler) {
+ this._handlers[method].splice(i, 1);
+ i--;
+ }
+ }
+ }
+ else {
+ this._handlers[method] = [];
+ }
+ },
+ once: function (method, handler) {
+ var self = this;
+ var fired = false;
+
+ function g() {
+ self.off(method, g);
+ if (!fired) {
+ fired = true;
+ handler.apply(self, Array.prototype.slice.call(arguments));
+ }
+ }
+
+ this.on(method, g);
+ },
+ on: function (method, handler) {
+ if (this._handlers[method]) {
+ this._handlers[method].push(handler);
+ }
+ else {
+ this._handlers[method] = [handler];
+ }
+ },
+
+ _emit: function (method, args, context) {
+ var handlers = this._handlers[method];
+ if (handlers && handlers.length > 0) {
+ handlers.forEach(function (handler) {
+ handler.apply(context, args)
+ });
+ return true;
+ }
+ else {
+ return false;
+ }
+ },
+
+ emit: function (method) {
+ var context = {};
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (!this._emit(method, args, context)) {
+ this._emit('*', args, context)
+ }
+ this._emit('$finally', args, context);
+ return context;
+ }
+};
\ No newline at end of file
diff --git a/packages/@weex/plugins/debug/frontend/runtime/WebsocketClient.js b/packages/@weex/plugins/debug/frontend/runtime/WebsocketClient.js
new file mode 100644
index 0000000..41cd4d5
--- /dev/null
+++ b/packages/@weex/plugins/debug/frontend/runtime/WebsocketClient.js
@@ -0,0 +1,52 @@
+function WebsocketClient(url) {
+ this.connect(url);
+}
+WebsocketClient.prototype = {
+ constructor: WebsocketClient,
+ connect: function (url) {
+ var self = this;
+ self.isSocketReady = false;
+ self._sended = [];
+ self._received = [];
+ if (self.ws) {
+ self.ws.onopen = null;
+ self.ws.onmessage = null;
+ self.ws.onclose = null;
+ if (self.ws.readyState == WebSocket.OPEN) {
+ self.ws.close();
+ }
+ }
+ var ws = new WebSocket(url);
+ self.ws = ws;
+ ws.onopen = function () {
+ self.isSocketReady = true;
+ self.emit('socketOpened');
+ };
+ ws.onmessage = function (e) {
+ var message = JSON.parse(e.data);
+ if (message.method) {
+ self.emit(message.method, message);
+ }
+ };
+ ws.onclose = function () {
+ self.isSocketReady = false;
+ self.emit('socketClose');
+ };
+
+ },
+ send: function (data) {
+ var self = this;
+ if (self.isSocketReady) {
+ self.ws.send(JSON.stringify(data));
+ }
+ else {
+ self.once('socketOpened', function () {
+ self.ws.send(JSON.stringify(data))
+ });
+ }
+ },
+ close: function () {
+ this.ws && this.ws.close();
+ }
+};
+WebsocketClient.prototype.__proto__ = new EventEmitter();
diff --git a/packages/@weex/plugins/debug/frontend/runtime/index.js b/packages/@weex/plugins/debug/frontend/runtime/index.js
new file mode 100644
index 0000000..ab8e001
--- /dev/null
+++ b/packages/@weex/plugins/debug/frontend/runtime/index.js
@@ -0,0 +1,152 @@
+var workers = {};
+var instanceMaps = {};
+var RuntimeSocket
+var BrowserChannelId
+var cacheWeexEnv;
+var cacheJsbundleImportMessage;
+var cacheRegisterLoop = [];
+var cacheSyncList = [];
+var activeWorkerId;
+var EntrySocket = new WebsocketClient('ws://' + location.host + '/page/entry');
+
+EntrySocket.on('WxDebug.startDebugger', function (message) {
+ if (!RuntimeSocket) {
+ location.href = `http://${location.host}/runtime/runtime.html?channelId=${message.params}`
+ }
+ else if(RuntimeSocket && BrowserChannelId!==message.params){
+ location.href = `http://${location.host}/runtime/runtime.html?channelId=${message.params}`
+ }
+})
+
+BrowserChannelId = new URLSearchParams(location.search).get('channelId');
+
+if (BrowserChannelId) {
+ connect(BrowserChannelId)
+}
+
+function connect(channelId) {
+ RuntimeSocket = new WebsocketClient('ws://' + window.location.host + '/debugProxy/runtime/' + channelId);
+
+ RuntimeSocket.on('*', function (message) {
+ if (!message) return;
+ var domain = message.method.split('.')[0];
+ if (domain === 'WxDebug') {
+ var instanceId;
+ if (message && message.params) {
+ instanceId = message.params && message.params.args && message.params.args[0];
+ }
+ else {
+ instanceId = activeWorkerId
+ }
+ if (workers[instanceId]) {
+ workers[instanceId].postMessage(message);
+ }
+ }
+ });
+
+ RuntimeSocket.on('WxDebug.deviceDisconnect', function () {
+ location.href = `http://${location.host}/runtime/runtime.html`
+ })
+
+ RuntimeSocket.on('WxDebug.refresh', function () {
+ location.reload();
+ });
+
+ RuntimeSocket.on('WxDebug.callJS', function (message) {
+ var instanceId = message.params.args[0];
+ if (message.params.method === 'createInstanceContext') {
+ destroyJSRuntime(message)
+ message.channelId = BrowserChannelId;
+ message.method = 'WxDebug.initSandboxWorker';
+ message.params.env = cacheWeexEnv;
+ message.params.syncList = cacheSyncList.splice(0, cacheSyncList.length);
+ initJSRuntime(message)
+ }
+ else if(message.params.method === 'createInstance') {
+ destroyJSRuntime(message)
+ message.channelId = BrowserChannelId;
+ message.method = 'WxDebug.initWorker';
+ message.params.env = cacheWeexEnv;
+ initJSRuntime(message)
+ }
+ else if(message.params.method === 'importScript') {
+ if (workers[instanceId]) {
+ workers[instanceId].postMessage(message)
+ }
+ else {
+ cacheJsbundleImportMessage = message;
+ }
+ }
+ else if(message.params.method === 'destroyInstance') {
+ destroyJSRuntime(message);
+ }
+ else if (message.params.args && (message.params.method === 'registerComponents' || message.params.method === 'registerModules' || message.params.method === 'getJSFMVersion' || message.params.method === 'getJSFMVersion')) {
+ cacheRegisterLoop.push(message);
+ }
+ else {
+ if (message.params && message.params.args && message.params.args[0] && workers[message.params.args[0]]) {
+ workers[message.params.args[0]].postMessage(message);
+ }
+ else if (activeWorkerId && workers[activeWorkerId]) {
+ workers[activeWorkerId].postMessage(message);
+ }
+ }
+ });
+
+ RuntimeSocket.on('WxDebug.initJSRuntime', function (message) {
+ var logLevel = localStorage.getItem('logLevel');
+ if (logLevel) {
+ message.params.env.WXEnvironment.logLevel = logLevel;
+ }
+ cacheWeexEnv = message.params.env;
+ cacheRegisterLoop = [];
+ });
+}
+
+function destroyJSRuntime(message) {
+ var instanceId = message.params.args[0];
+ var workerjs = message.params.workerjs;
+ if (workerjs) {
+ instanceId = instanceMaps[workerjs]
+ }
+ if (workers[instanceId]) {
+ if (workers[instanceId].prev) {
+ activeWorkerId = workers[instanceId].prev;
+ }
+ else {
+ activeWorkerId = null;
+ }
+ workers[instanceId].terminate();
+ delete workers[instanceId];
+ }
+}
+
+function initJSRuntime(message) {
+ var instanceId = activeWorkerId = message.params.args[0];
+ instanceMaps[message.params.workerjs] = instanceId;
+ workers[instanceId] = new Worker(message.params.workerjs);
+ workers[instanceId]['prev'] = getPrevWorker(workers);
+ workers[instanceId].onmessage = function (message) {
+ message = message.data;
+ RuntimeSocket.send(message);
+ };
+ cacheRegisterLoop.forEach(function(message) {
+ workers[instanceId].postMessage(message)
+ })
+ if (cacheJsbundleImportMessage) {
+ workers[instanceId].postMessage(message);
+ }
+ workers[instanceId].postMessage(message);
+}
+
+function getPrevWorker(workers) {
+ var lists = Object.keys(workers);
+ if (lists.length === 0) return null;
+ for(var i = lists.length - 2; i >=0; i--) {
+ if (workers[lists[lists.length - 2]]) {
+ return lists[lists.length - 2];
+ }
+ }
+ return null;
+}
+
diff --git a/packages/@weex/plugins/debug/frontend/src/runtime.html b/packages/@weex/plugins/debug/frontend/runtime/runtime.html
similarity index 50%
rename from packages/@weex/plugins/debug/frontend/src/runtime.html
rename to packages/@weex/plugins/debug/frontend/runtime/runtime.html
index c5af4c0..60f43e5 100644
--- a/packages/@weex/plugins/debug/frontend/src/runtime.html
+++ b/packages/@weex/plugins/debug/frontend/runtime/runtime.html
@@ -3,9 +3,9 @@
Weex Devtool - JS Debugger
-
-
-
+
+
+
diff --git a/packages/@weex/plugins/debug/frontend/src/assets/inspector/Runtime.js b/packages/@weex/plugins/debug/frontend/src/assets/inspector/Runtime.js
new file mode 100644
index 0000000..f48f9e4
--- /dev/null
+++ b/packages/@weex/plugins/debug/frontend/src/assets/inspector/Runtime.js
@@ -0,0 +1,1128 @@
+/*
+ * Copyright (C) 2014 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// This gets all concatenated module descriptors in the release mode.
+const allDescriptors = [];
+let applicationDescriptor;
+const _loadedScripts = {};
+
+// FIXME: This is a workaround to force Closure compiler provide
+// the standard ES6 runtime for all modules. This should be removed
+// once Closure provides standard externs for Map et al.
+for (const k of []) { // eslint-disable-line
+}
+
+(function() {
+const baseUrl = self.location ? self.location.origin + self.location.pathname : '';
+self._importScriptPathPrefix = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);
+})();
+
+const REMOTE_MODULE_FALLBACK_REVISION = '@010ddcfda246975d194964ccf20038ebbdec6084';
+
+/**
+ * @unrestricted
+ */
+var Runtime = class { // eslint-disable-line
+ /**
+ * @param {!Array.} descriptors
+ */
+ constructor(descriptors) {
+ /** @type {!Array} */
+ this._modules = [];
+ /** @type {!Object} */
+ this._modulesMap = {};
+ /** @type {!Array} */
+ this._extensions = [];
+ /** @type {!Object} */
+ this._cachedTypeClasses = {};
+ /** @type {!Object} */
+ this._descriptorsMap = {};
+
+ for (let i = 0; i < descriptors.length; ++i)
+ this._registerModule(descriptors[i]);
+
+ Runtime._runtimeReadyPromiseCallback();
+ }
+
+ /**
+ * @param {string} url
+ * @return {!Promise.}
+ */
+ static loadResourcePromise(url) {
+ return new Promise(load);
+
+ /**
+ * @param {function(?)} fulfill
+ * @param {function(*)} reject
+ */
+ function load(fulfill, reject) {
+ const xhr = new XMLHttpRequest();
+ xhr.open('GET', url, true);
+ xhr.onreadystatechange = onreadystatechange;
+
+ /**
+ * @param {Event} e
+ */
+ function onreadystatechange(e) {
+ if (xhr.readyState !== XMLHttpRequest.DONE)
+ return;
+
+ // DevTools Proxy server can mask 404s as 200s, check the body to be sure
+ const status = /^HTTP\/1.1 404/.test(e.target.response) ? 404 : xhr.status;
+
+ if ([0, 200, 304].indexOf(status) === -1) // Testing harness file:/// results in 0.
+ reject(new Error('While loading from url ' + url + ' server responded with a status of ' + status));
+ else
+ fulfill(e.target.response);
+ }
+ xhr.send(null);
+ }
+ }
+
+ /**
+ * @param {string} url
+ * @return {!Promise.}
+ */
+ static loadResourcePromiseWithFallback(url) {
+ return Runtime.loadResourcePromise(url).catch(err => {
+ const urlWithFallbackVersion = url.replace(/@[0-9a-f]{40}/, REMOTE_MODULE_FALLBACK_REVISION);
+ // TODO(phulce): mark fallbacks in module.json and modify build script instead
+ if (urlWithFallbackVersion === url || !url.includes('audits2_worker_module'))
+ throw err;
+ return Runtime.loadResourcePromise(urlWithFallbackVersion);
+ });
+ }
+
+ /**
+ * http://tools.ietf.org/html/rfc3986#section-5.2.4
+ * @param {string} path
+ * @return {string}
+ */
+ static normalizePath(path) {
+ if (path.indexOf('..') === -1 && path.indexOf('.') === -1)
+ return path;
+
+ const normalizedSegments = [];
+ const segments = path.split('/');
+ for (let i = 0; i < segments.length; i++) {
+ const segment = segments[i];
+ if (segment === '.')
+ continue;
+ else if (segment === '..')
+ normalizedSegments.pop();
+ else if (segment)
+ normalizedSegments.push(segment);
+ }
+ let normalizedPath = normalizedSegments.join('/');
+ if (normalizedPath[normalizedPath.length - 1] === '/')
+ return normalizedPath;
+ if (path[0] === '/' && normalizedPath)
+ normalizedPath = '/' + normalizedPath;
+ if ((path[path.length - 1] === '/') || (segments[segments.length - 1] === '.') ||
+ (segments[segments.length - 1] === '..'))
+ normalizedPath = normalizedPath + '/';
+
+ return normalizedPath;
+ }
+
+ /**
+ * @param {!Array.} scriptNames
+ * @param {string=} base
+ * @return {!Promise.}
+ */
+ static _loadScriptsPromise(scriptNames, base) {
+ /** @type {!Array>} */
+ const promises = [];
+ /** @type {!Array} */
+ const urls = [];
+ const sources = new Array(scriptNames.length);
+ let scriptToEval = 0;
+ for (let i = 0; i < scriptNames.length; ++i) {
+ const scriptName = scriptNames[i];
+ let sourceURL = (base || self._importScriptPathPrefix) + scriptName;
+
+ const schemaIndex = sourceURL.indexOf('://') + 3;
+ let pathIndex = sourceURL.indexOf('/', schemaIndex);
+ if (pathIndex === -1)
+ pathIndex = sourceURL.length;
+ sourceURL = sourceURL.substring(0, pathIndex) + Runtime.normalizePath(sourceURL.substring(pathIndex));
+
+ if (_loadedScripts[sourceURL])
+ continue;
+ urls.push(sourceURL);
+ const loadResourcePromise =
+ base ? Runtime.loadResourcePromiseWithFallback(sourceURL) : Runtime.loadResourcePromise(sourceURL);
+ promises.push(
+ loadResourcePromise.then(scriptSourceLoaded.bind(null, i), scriptSourceLoaded.bind(null, i, undefined)));
+ }
+ return Promise.all(promises).then(undefined);
+
+ /**
+ * @param {number} scriptNumber
+ * @param {string=} scriptSource
+ */
+ function scriptSourceLoaded(scriptNumber, scriptSource) {
+ sources[scriptNumber] = scriptSource || '';
+ // Eval scripts as fast as possible.
+ while (typeof sources[scriptToEval] !== 'undefined') {
+ evaluateScript(urls[scriptToEval], sources[scriptToEval]);
+ ++scriptToEval;
+ }
+ }
+
+ /**
+ * @param {string} sourceURL
+ * @param {string=} scriptSource
+ */
+ function evaluateScript(sourceURL, scriptSource) {
+ _loadedScripts[sourceURL] = true;
+ if (!scriptSource) {
+ // Do not reject, as this is normal in the hosted mode.
+ console.error('Empty response arrived for script \'' + sourceURL + '\'');
+ return;
+ }
+ self.eval(scriptSource + '\n//# sourceURL=' + sourceURL);
+ }
+ }
+
+ /**
+ * @param {string} url
+ * @param {boolean} appendSourceURL
+ * @return {!Promise}
+ */
+ static _loadResourceIntoCache(url, appendSourceURL) {
+ return Runtime.loadResourcePromise(url).then(
+ cacheResource.bind(this, url), cacheResource.bind(this, url, undefined));
+
+ /**
+ * @param {string} path
+ * @param {string=} content
+ */
+ function cacheResource(path, content) {
+ if (!content) {
+ console.error('Failed to load resource: ' + path);
+ return;
+ }
+ const sourceURL = appendSourceURL ? Runtime.resolveSourceURL(path) : '';
+ Runtime.cachedResources[path] = content + sourceURL;
+ }
+ }
+
+ /**
+ * @return {!Promise}
+ */
+ static async runtimeReady() {
+ return Runtime._runtimeReadyPromise;
+ }
+
+ /**
+ * @param {string} appName
+ * @return {!Promise.}
+ */
+ static async startApplication(appName) {
+ console.timeStamp('Runtime.startApplication');
+
+ const allDescriptorsByName = {};
+ for (let i = 0; i < allDescriptors.length; ++i) {
+ const d = allDescriptors[i];
+ allDescriptorsByName[d['name']] = d;
+ }
+
+ if (!applicationDescriptor) {
+ let data = await Runtime.loadResourcePromise(appName + '.json');
+ applicationDescriptor = JSON.parse(data);
+ let descriptor = applicationDescriptor;
+ while (descriptor.extends) {
+ data = await Runtime.loadResourcePromise(descriptor.extends + '.json');
+ descriptor = JSON.parse(data);
+ applicationDescriptor.modules = descriptor.modules.concat(applicationDescriptor.modules);
+ }
+ }
+
+ const configuration = applicationDescriptor.modules;
+ const moduleJSONPromises = [];
+ const coreModuleNames = [];
+ for (let i = 0; i < configuration.length; ++i) {
+ const descriptor = configuration[i];
+ const name = descriptor['name'];
+ const moduleJSON = allDescriptorsByName[name];
+ if (moduleJSON)
+ moduleJSONPromises.push(Promise.resolve(moduleJSON));
+ else
+ moduleJSONPromises.push(Runtime.loadResourcePromise(name + '/module.json').then(JSON.parse.bind(JSON)));
+ if (descriptor['type'] === 'autostart')
+ coreModuleNames.push(name);
+ }
+
+ const moduleDescriptors = await Promise.all(moduleJSONPromises);
+
+ for (let i = 0; i < moduleDescriptors.length; ++i) {
+ moduleDescriptors[i].name = configuration[i]['name'];
+ moduleDescriptors[i].condition = configuration[i]['condition'];
+ moduleDescriptors[i].remote = configuration[i]['type'] === 'remote';
+ }
+ self.runtime = new Runtime(moduleDescriptors);
+ if (coreModuleNames)
+ return /** @type {!Promise} */ (self.runtime._loadAutoStartModules(coreModuleNames));
+ }
+
+ /**
+ * @param {string} appName
+ * @return {!Promise.}
+ */
+ static startWorker(appName) {
+ return Runtime.startApplication(appName).then(sendWorkerReady);
+
+ function sendWorkerReady() {
+ self.postMessage('workerReady');
+ }
+ }
+
+ /**
+ * @param {string} name
+ * @return {?string}
+ */
+ static queryParam(name) {
+ return Runtime._queryParamsObject[name] || null;
+ }
+
+ /**
+ * @return {string}
+ */
+ static queryParamsString() {
+ return location.search;
+ }
+
+ /**
+ * @return {!Object}
+ */
+ static _experimentsSetting() {
+ try {
+ return /** @type {!Object} */ (
+ JSON.parse(self.localStorage && self.localStorage['experiments'] ? self.localStorage['experiments'] : '{}'));
+ } catch (e) {
+ console.error('Failed to parse localStorage[\'experiments\']');
+ return {};
+ }
+ }
+
+ static _assert(value, message) {
+ if (value)
+ return;
+ Runtime._originalAssert.call(Runtime._console, value, message + ' ' + new Error().stack);
+ }
+
+ /**
+ * @param {string} platform
+ */
+ static setPlatform(platform) {
+ Runtime._platform = platform;
+ }
+
+ /**
+ * @param {!Object} descriptor
+ * @return {boolean}
+ */
+ static _isDescriptorEnabled(descriptor) {
+ const activatorExperiment = descriptor['experiment'];
+ if (activatorExperiment === '*')
+ return Runtime.experiments.supportEnabled();
+ if (activatorExperiment && activatorExperiment.startsWith('!') &&
+ Runtime.experiments.isEnabled(activatorExperiment.substring(1)))
+ return false;
+ if (activatorExperiment && !activatorExperiment.startsWith('!') &&
+ !Runtime.experiments.isEnabled(activatorExperiment))
+ return false;
+ const condition = descriptor['condition'];
+ if (condition && !condition.startsWith('!') && !Runtime.queryParam(condition))
+ return false;
+ if (condition && condition.startsWith('!') && Runtime.queryParam(condition.substring(1)))
+ return false;
+ return true;
+ }
+
+ /**
+ * @param {string} path
+ * @return {string}
+ */
+ static resolveSourceURL(path) {
+ let sourceURL = self.location.href;
+ if (self.location.search)
+ sourceURL = sourceURL.replace(self.location.search, '');
+ sourceURL = sourceURL.substring(0, sourceURL.lastIndexOf('/') + 1) + path;
+ return '\n/*# sourceURL=' + sourceURL + ' */';
+ }
+
+ useTestBase() {
+ Runtime._remoteBase = 'http://localhost:8000/inspector-sources/';
+ if (Runtime.queryParam('debugFrontend'))
+ Runtime._remoteBase += 'debug/';
+ }
+
+ /**
+ * @param {!Runtime.ModuleDescriptor} descriptor
+ */
+ _registerModule(descriptor) {
+ const module = new Runtime.Module(this, descriptor);
+ this._modules.push(module);
+ this._modulesMap[descriptor['name']] = module;
+ }
+
+ /**
+ * @param {string} moduleName
+ * @return {!Promise.}
+ */
+ loadModulePromise(moduleName) {
+ return this._modulesMap[moduleName]._loadPromise();
+ }
+
+ /**
+ * @param {!Array.} moduleNames
+ * @return {!Promise.>}
+ */
+ _loadAutoStartModules(moduleNames) {
+ const promises = [];
+ for (let i = 0; i < moduleNames.length; ++i)
+ promises.push(this.loadModulePromise(moduleNames[i]));
+ return Promise.all(promises);
+ }
+
+ /**
+ * @param {!Runtime.Extension} extension
+ * @param {?function(function(new:Object)):boolean} predicate
+ * @return {boolean}
+ */
+ _checkExtensionApplicability(extension, predicate) {
+ if (!predicate)
+ return false;
+ const contextTypes = extension.descriptor().contextTypes;
+ if (!contextTypes)
+ return true;
+ for (let i = 0; i < contextTypes.length; ++i) {
+ const contextType = this._resolve(contextTypes[i]);
+ const isMatching = !!contextType && predicate(contextType);
+ if (isMatching)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param {!Runtime.Extension} extension
+ * @param {?Object} context
+ * @return {boolean}
+ */
+ isExtensionApplicableToContext(extension, context) {
+ if (!context)
+ return true;
+ return this._checkExtensionApplicability(extension, isInstanceOf);
+
+ /**
+ * @param {!Function} targetType
+ * @return {boolean}
+ */
+ function isInstanceOf(targetType) {
+ return context instanceof targetType;
+ }
+ }
+
+ /**
+ * @param {!Runtime.Extension} extension
+ * @param {!Set.=} currentContextTypes
+ * @return {boolean}
+ */
+ isExtensionApplicableToContextTypes(extension, currentContextTypes) {
+ if (!extension.descriptor().contextTypes)
+ return true;
+
+ return this._checkExtensionApplicability(extension, currentContextTypes ? isContextTypeKnown : null);
+
+ /**
+ * @param {!Function} targetType
+ * @return {boolean}
+ */
+ function isContextTypeKnown(targetType) {
+ return currentContextTypes.has(targetType);
+ }
+ }
+
+ /**
+ * @param {*} type
+ * @param {?Object=} context
+ * @param {boolean=} sortByTitle
+ * @return {!Array.}
+ */
+ extensions(type, context, sortByTitle) {
+ return this._extensions.filter(filter).sort(sortByTitle ? titleComparator : orderComparator);
+
+ /**
+ * @param {!Runtime.Extension} extension
+ * @return {boolean}
+ */
+ function filter(extension) {
+ if (extension._type !== type && extension._typeClass() !== type)
+ return false;
+ if (!extension.enabled())
+ return false;
+ return !context || extension.isApplicable(context);
+ }
+
+ /**
+ * @param {!Runtime.Extension} extension1
+ * @param {!Runtime.Extension} extension2
+ * @return {number}
+ */
+ function orderComparator(extension1, extension2) {
+ const order1 = extension1.descriptor()['order'] || 0;
+ const order2 = extension2.descriptor()['order'] || 0;
+ return order1 - order2;
+ }
+
+ /**
+ * @param {!Runtime.Extension} extension1
+ * @param {!Runtime.Extension} extension2
+ * @return {number}
+ */
+ function titleComparator(extension1, extension2) {
+ const title1 = extension1.title() || '';
+ const title2 = extension2.title() || '';
+ return title1.localeCompare(title2);
+ }
+ }
+
+ /**
+ * @param {*} type
+ * @param {?Object=} context
+ * @return {?Runtime.Extension}
+ */
+ extension(type, context) {
+ return this.extensions(type, context)[0] || null;
+ }
+
+ /**
+ * @param {*} type
+ * @param {?Object=} context
+ * @return {!Promise.>}
+ */
+ allInstances(type, context) {
+ return Promise.all(this.extensions(type, context).map(extension => extension.instance()));
+ }
+
+ /**
+ * @return {?function(new:Object)}
+ */
+ _resolve(typeName) {
+ if (!this._cachedTypeClasses[typeName]) {
+ const path = typeName.split('.');
+ let object = self;
+ for (let i = 0; object && (i < path.length); ++i)
+ object = object[path[i]];
+ if (object)
+ this._cachedTypeClasses[typeName] = /** @type function(new:Object) */ (object);
+ }
+ return this._cachedTypeClasses[typeName] || null;
+ }
+
+ /**
+ * @param {!Function} constructorFunction
+ * @return {!Object}
+ */
+ sharedInstance(constructorFunction) {
+ if (Runtime._instanceSymbol in constructorFunction &&
+ Object.getOwnPropertySymbols(constructorFunction).includes(Runtime._instanceSymbol))
+ return constructorFunction[Runtime._instanceSymbol];
+
+ const instance = new constructorFunction();
+ constructorFunction[Runtime._instanceSymbol] = instance;
+ return instance;
+ }
+};
+
+/**
+ * @type {!Object.}
+ */
+Runtime._queryParamsObject = {
+ __proto__: null
+};
+
+Runtime._instanceSymbol = Symbol('instance');
+
+/**
+ * @type {!Object.}
+ */
+Runtime.cachedResources = {
+ __proto__: null
+};
+
+
+Runtime._console = console;
+Runtime._originalAssert = console.assert;
+
+
+Runtime._platform = '';
+
+
+/**
+ * @unrestricted
+ */
+Runtime.ModuleDescriptor = class {
+ constructor() {
+ /**
+ * @type {string}
+ */
+ this.name;
+
+ /**
+ * @type {!Array.}
+ */
+ this.extensions;
+
+ /**
+ * @type {!Array.|undefined}
+ */
+ this.dependencies;
+
+ /**
+ * @type {!Array.}
+ */
+ this.scripts;
+
+ /**
+ * @type {string|undefined}
+ */
+ this.condition;
+
+ /**
+ * @type {boolean|undefined}
+ */
+ this.remote;
+ }
+};
+
+/**
+ * @unrestricted
+ */
+Runtime.ExtensionDescriptor = class {
+ constructor() {
+ /**
+ * @type {string}
+ */
+ this.type;
+
+ /**
+ * @type {string|undefined}
+ */
+ this.className;
+
+ /**
+ * @type {string|undefined}
+ */
+ this.factoryName;
+
+ /**
+ * @type {!Array.|undefined}
+ */
+ this.contextTypes;
+ }
+};
+
+/**
+ * @unrestricted
+ */
+Runtime.Module = class {
+ /**
+ * @param {!Runtime} manager
+ * @param {!Runtime.ModuleDescriptor} descriptor
+ */
+ constructor(manager, descriptor) {
+ this._manager = manager;
+ this._descriptor = descriptor;
+ this._name = descriptor.name;
+ /** @type {!Array} */
+ this._extensions = [];
+
+ /** @type {!Map>} */
+ this._extensionsByClassName = new Map();
+ const extensions = /** @type {?Array.} */ (descriptor.extensions);
+ for (let i = 0; extensions && i < extensions.length; ++i) {
+ const extension = new Runtime.Extension(this, extensions[i]);
+ this._manager._extensions.push(extension);
+ this._extensions.push(extension);
+ }
+ this._loadedForTest = false;
+ }
+
+ /**
+ * @return {string}
+ */
+ name() {
+ return this._name;
+ }
+
+ /**
+ * @return {boolean}
+ */
+ enabled() {
+ return Runtime._isDescriptorEnabled(this._descriptor);
+ }
+
+ /**
+ * @param {string} name
+ * @return {string}
+ */
+ resource(name) {
+ const fullName = this._name + '/' + name;
+ const content = Runtime.cachedResources[fullName];
+ if (!content)
+ throw new Error(fullName + ' not preloaded. Check module.json');
+ return content;
+ }
+
+ /**
+ * @return {!Promise.}
+ */
+ _loadPromise() {
+ if (!this.enabled())
+ return Promise.reject(new Error('Module ' + this._name + ' is not enabled'));
+
+ if (this._pendingLoadPromise)
+ return this._pendingLoadPromise;
+
+ const dependencies = this._descriptor.dependencies;
+ const dependencyPromises = [];
+ for (let i = 0; dependencies && i < dependencies.length; ++i)
+ dependencyPromises.push(this._manager._modulesMap[dependencies[i]]._loadPromise());
+
+ this._pendingLoadPromise = Promise.all(dependencyPromises)
+ .then(this._loadResources.bind(this))
+ .then(this._loadScripts.bind(this))
+ .then(() => this._loadedForTest = true);
+
+ return this._pendingLoadPromise;
+ }
+
+ /**
+ * @return {!Promise.}
+ * @this {Runtime.Module}
+ */
+ _loadResources() {
+ const resources = this._descriptor['resources'];
+ if (!resources || !resources.length)
+ return Promise.resolve();
+ const promises = [];
+ for (let i = 0; i < resources.length; ++i) {
+ const url = this._modularizeURL(resources[i]);
+ promises.push(Runtime._loadResourceIntoCache(url, true));
+ }
+ return Promise.all(promises).then(undefined);
+ }
+
+ /**
+ * @return {!Promise.}
+ */
+ _loadScripts() {
+ if (!this._descriptor.scripts || !this._descriptor.scripts.length)
+ return Promise.resolve();
+
+ // Module namespaces.
+ // NOTE: Update scripts/special_case_namespaces.json if you add a special cased namespace.
+ // The namespace keyword confuses clang-format.
+ // clang-format off
+ const specialCases = {
+ 'sdk': 'SDK',
+ 'js_sdk': 'JSSDK',
+ 'browser_sdk': 'BrowserSDK',
+ 'ui': 'UI',
+ 'object_ui': 'ObjectUI',
+ 'javascript_metadata': 'JavaScriptMetadata',
+ 'perf_ui': 'PerfUI',
+ 'har_importer': 'HARImporter',
+ 'sdk_test_runner': 'SDKTestRunner',
+ 'cpu_profiler_test_runner': 'CPUProfilerTestRunner'
+ };
+ const namespace = specialCases[this._name] || this._name.split('_').map(a => a.substring(0, 1).toUpperCase() + a.substring(1)).join('');
+ self[namespace] = self[namespace] || {};
+ // clang-format on
+ return Runtime._loadScriptsPromise(this._descriptor.scripts.map(this._modularizeURL, this), this._remoteBase());
+ }
+
+ /**
+ * @param {string} resourceName
+ */
+ _modularizeURL(resourceName) {
+ return Runtime.normalizePath(this._name + '/' + resourceName);
+ }
+
+ /**
+ * @return {string|undefined}
+ */
+ _remoteBase() {
+ return !Runtime.queryParam('debugFrontend') && this._descriptor.remote && Runtime._remoteBase || undefined;
+ }
+
+ /**
+ * @param {string} value
+ * @return {string}
+ */
+ substituteURL(value) {
+ const base = this._remoteBase() || '';
+ return value.replace(/@url\(([^\)]*?)\)/g, convertURL.bind(this));
+
+ function convertURL(match, url) {
+ return base + this._modularizeURL(url);
+ }
+ }
+};
+
+
+/**
+ * @unrestricted
+ */
+Runtime.Extension = class {
+ /**
+ * @param {!Runtime.Module} module
+ * @param {!Runtime.ExtensionDescriptor} descriptor
+ */
+ constructor(module, descriptor) {
+ this._module = module;
+ this._descriptor = descriptor;
+
+ this._type = descriptor.type;
+ this._hasTypeClass = this._type.charAt(0) === '@';
+
+ /**
+ * @type {?string}
+ */
+ this._className = descriptor.className || null;
+ this._factoryName = descriptor.factoryName || null;
+ }
+
+ /**
+ * @return {!Object}
+ */
+ descriptor() {
+ return this._descriptor;
+ }
+
+ /**
+ * @return {!Runtime.Module}
+ */
+ module() {
+ return this._module;
+ }
+
+ /**
+ * @return {boolean}
+ */
+ enabled() {
+ return this._module.enabled() && Runtime._isDescriptorEnabled(this.descriptor());
+ }
+
+ /**
+ * @return {?function(new:Object)}
+ */
+ _typeClass() {
+ if (!this._hasTypeClass)
+ return null;
+ return this._module._manager._resolve(this._type.substring(1));
+ }
+
+ /**
+ * @param {?Object} context
+ * @return {boolean}
+ */
+ isApplicable(context) {
+ return this._module._manager.isExtensionApplicableToContext(this, context);
+ }
+
+ /**
+ * @return {!Promise.}
+ */
+ instance() {
+ return this._module._loadPromise().then(this._createInstance.bind(this));
+ }
+
+ /**
+ * @return {boolean}
+ */
+ canInstantiate() {
+ return !!(this._className || this._factoryName);
+ }
+
+ /**
+ * @return {!Object}
+ */
+ _createInstance() {
+ const className = this._className || this._factoryName;
+ if (!className)
+ throw new Error('Could not instantiate extension with no class');
+ const constructorFunction = self.eval(/** @type {string} */ (className));
+ if (!(constructorFunction instanceof Function))
+ throw new Error('Could not instantiate: ' + className);
+ if (this._className)
+ return this._module._manager.sharedInstance(constructorFunction);
+ return new constructorFunction(this);
+ }
+
+ /**
+ * @return {string}
+ */
+ title() {
+ // FIXME: should be Common.UIString() but runtime is not l10n aware yet.
+ return this._descriptor['title-' + Runtime._platform] || this._descriptor['title'];
+ }
+
+ /**
+ * @param {function(new:Object)} contextType
+ * @return {boolean}
+ */
+ hasContextType(contextType) {
+ const contextTypes = this.descriptor().contextTypes;
+ if (!contextTypes)
+ return false;
+ for (let i = 0; i < contextTypes.length; ++i) {
+ if (contextType === this._module._manager._resolve(contextTypes[i]))
+ return true;
+ }
+ return false;
+ }
+};
+
+/**
+ * @unrestricted
+ */
+Runtime.ExperimentsSupport = class {
+ constructor() {
+ this._supportEnabled = Runtime.queryParam('experiments') !== null;
+ this._experiments = [];
+ this._experimentNames = {};
+ this._enabledTransiently = {};
+ }
+
+ /**
+ * @return {!Array.}
+ */
+ allConfigurableExperiments() {
+ const result = [];
+ for (let i = 0; i < this._experiments.length; i++) {
+ const experiment = this._experiments[i];
+ if (!this._enabledTransiently[experiment.name])
+ result.push(experiment);
+ }
+ return result;
+ }
+
+ /**
+ * @return {boolean}
+ */
+ supportEnabled() {
+ return this._supportEnabled;
+ }
+
+ /**
+ * @param {!Object} value
+ */
+ _setExperimentsSetting(value) {
+ if (!self.localStorage)
+ return;
+ self.localStorage['experiments'] = JSON.stringify(value);
+ }
+
+ /**
+ * @param {string} experimentName
+ * @param {string} experimentTitle
+ * @param {boolean=} hidden
+ */
+ register(experimentName, experimentTitle, hidden) {
+ Runtime._assert(!this._experimentNames[experimentName], 'Duplicate registration of experiment ' + experimentName);
+ this._experimentNames[experimentName] = true;
+ this._experiments.push(new Runtime.Experiment(this, experimentName, experimentTitle, !!hidden));
+ }
+
+ /**
+ * @param {string} experimentName
+ * @return {boolean}
+ */
+ isEnabled(experimentName) {
+ this._checkExperiment(experimentName);
+
+ if (this._enabledTransiently[experimentName])
+ return true;
+ if (!this.supportEnabled())
+ return false;
+
+ return !!Runtime._experimentsSetting()[experimentName];
+ }
+
+ /**
+ * @param {string} experimentName
+ * @param {boolean} enabled
+ */
+ setEnabled(experimentName, enabled) {
+ this._checkExperiment(experimentName);
+ const experimentsSetting = Runtime._experimentsSetting();
+ experimentsSetting[experimentName] = enabled;
+ this._setExperimentsSetting(experimentsSetting);
+ }
+
+ /**
+ * @param {!Array.} experimentNames
+ */
+ setDefaultExperiments(experimentNames) {
+ for (let i = 0; i < experimentNames.length; ++i) {
+ this._checkExperiment(experimentNames[i]);
+ this._enabledTransiently[experimentNames[i]] = true;
+ }
+ }
+
+ /**
+ * @param {string} experimentName
+ */
+ enableForTest(experimentName) {
+ this._checkExperiment(experimentName);
+ this._enabledTransiently[experimentName] = true;
+ }
+
+ clearForTest() {
+ this._experiments = [];
+ this._experimentNames = {};
+ this._enabledTransiently = {};
+ }
+
+ cleanUpStaleExperiments() {
+ const experimentsSetting = Runtime._experimentsSetting();
+ const cleanedUpExperimentSetting = {};
+ for (let i = 0; i < this._experiments.length; ++i) {
+ const experimentName = this._experiments[i].name;
+ if (experimentsSetting[experimentName])
+ cleanedUpExperimentSetting[experimentName] = true;
+ }
+ this._setExperimentsSetting(cleanedUpExperimentSetting);
+ }
+
+ /**
+ * @param {string} experimentName
+ */
+ _checkExperiment(experimentName) {
+ Runtime._assert(this._experimentNames[experimentName], 'Unknown experiment ' + experimentName);
+ }
+};
+
+/**
+ * @unrestricted
+ */
+Runtime.Experiment = class {
+ /**
+ * @param {!Runtime.ExperimentsSupport} experiments
+ * @param {string} name
+ * @param {string} title
+ * @param {boolean} hidden
+ */
+ constructor(experiments, name, title, hidden) {
+ this.name = name;
+ this.title = title;
+ this.hidden = hidden;
+ this._experiments = experiments;
+ }
+
+ /**
+ * @return {boolean}
+ */
+ isEnabled() {
+ return this._experiments.isEnabled(this.name);
+ }
+
+ /**
+ * @param {boolean} enabled
+ */
+ setEnabled(enabled) {
+ this._experiments.setEnabled(this.name, enabled);
+ }
+};
+
+{
+ (function parseQueryParameters() {
+ const queryParams = Runtime.queryParamsString();
+ if (!queryParams)
+ return;
+ const params = queryParams.substring(1).split('&');
+ for (let i = 0; i < params.length; ++i) {
+ const pair = params[i].split('=');
+ const name = pair.shift();
+ Runtime._queryParamsObject[name] = pair.join('=');
+ }
+ })();
+}
+
+// This must be constructed after the query parameters have been parsed.
+Runtime.experiments = new Runtime.ExperimentsSupport();
+
+/** @type {Function} */
+Runtime._runtimeReadyPromiseCallback;
+Runtime._runtimeReadyPromise = new Promise(fulfil => Runtime._runtimeReadyPromiseCallback = fulfil);
+/**
+ * @type {?string}
+ */
+Runtime._remoteBase;
+(function validateRemoteBase() {
+ if (location.href.startsWith('chrome-devtools://devtools/bundled/') && Runtime.queryParam('remoteBase')) {
+ const versionMatch = /\/serve_file\/(@[0-9a-zA-Z]+)\/?$/.exec(Runtime.queryParam('remoteBase'));
+ if (versionMatch)
+ Runtime._remoteBase = `${location.origin}/remote/serve_file/${versionMatch[1]}/`;
+ }
+})();
+
+
+/**
+ * @interface
+ */
+function ServicePort() {
+}
+
+ServicePort.prototype = {
+ /**
+ * @param {function(string)} messageHandler
+ * @param {function(string)} closeHandler
+ */
+ setHandlers(messageHandler, closeHandler) {},
+
+ /**
+ * @param {string} message
+ * @return {!Promise}
+ */
+ send(message) {},
+
+ /**
+ * @return {!Promise}
+ */
+ close() {}
+};
+
+/** @type {!Runtime} */
+var runtime; // eslint-disable-line
diff --git a/packages/@weex/plugins/debug/frontend/src/assets/inspector/coverage/coverageListView.css b/packages/@weex/plugins/debug/frontend/src/assets/inspector/coverage/coverageListView.css
new file mode 100644
index 0000000..3b915e2
--- /dev/null
+++ b/packages/@weex/plugins/debug/frontend/src/assets/inspector/coverage/coverageListView.css
@@ -0,0 +1,61 @@
+.data-grid {
+ border: none;
+}
+
+.data-grid td .url-outer {
+ width: 100%;
+ display: inline-flex;
+ justify-content: flex-start;
+}
+
+.data-grid td .url-outer .filter-highlight {
+ font-weight: bold;
+}
+
+.data-grid td .url-prefix {
+ overflow-x: hidden;
+ text-overflow: ellipsis;
+}
+
+.data-grid td .url-suffix {
+ flex: none;
+}
+
+.data-grid td .bar {
+ display: inline-block;
+ height: 8px;
+}
+
+.data-grid .selected td .bar {
+ border-top: 1px white solid;
+ border-bottom: 1px white solid;
+}
+
+.data-grid .selected td .bar:last-child {
+ border-right: 1px white solid;
+}
+
+.data-grid .selected td .bar:first-child {
+ border-left: 1px white solid;
+}
+
+.data-grid td .bar-container {
+}
+
+.data-grid td .bar-unused-size {
+ background-color: #E57373;
+}
+
+.data-grid td .bar-used-size {
+ background-color: #81C784;
+}
+
+.data-grid td .percent-value {
+ color: #888;
+ width: 45px;
+ display: inline-block;
+}
+
+.data-grid:focus tr.selected span.percent-value {
+ color: #eee;
+}
diff --git a/packages/@weex/plugins/debug/frontend/src/assets/inspector/coverage/coverageView.css b/packages/@weex/plugins/debug/frontend/src/assets/inspector/coverage/coverageView.css
new file mode 100644
index 0000000..816ef2a
--- /dev/null
+++ b/packages/@weex/plugins/debug/frontend/src/assets/inspector/coverage/coverageView.css
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+:host {
+ overflow: hidden;
+}
+
+.coverage-toolbar-container {
+ display: flex;
+ border-bottom: 1px solid #ccc;
+ flex: 0 0;
+}
+
+.coverage-toolbar {
+ display: inline-block;
+}
+
+.coverage-toolbar-summary {
+ background-color: #eee;
+ border-top: 1px solid #ccc;
+ padding-left: 5px;
+ flex: 0 0 19px;
+ display: flex;
+ padding-right: 5px;
+}
+
+.coverage-toolbar-summary .coverage-message {
+ padding-top: 2px;
+ padding-left: 1ex;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.coverage-results {
+ overflow-y: auto;
+ display: flex;
+ flex: auto;
+}
+
+.landing-page {
+ justify-content: center;
+ align-items: center;
+ padding: 20px;
+}
+
+.landing-page .message {
+ white-space: pre-line;
+}
diff --git a/packages/@weex/plugins/debug/frontend/src/assets/inspector/coverage/module.json b/packages/@weex/plugins/debug/frontend/src/assets/inspector/coverage/module.json
new file mode 100644
index 0000000..ca5239f
--- /dev/null
+++ b/packages/@weex/plugins/debug/frontend/src/assets/inspector/coverage/module.json
@@ -0,0 +1,62 @@
+{
+ "extensions": [
+ {
+ "type": "view",
+ "location": "drawer-view",
+ "id": "coverage",
+ "title": "Coverage",
+ "persistence": "closeable",
+ "className": "Coverage.CoverageView",
+ "order": 100
+ },
+ {
+ "type": "@SourceFrame.LineDecorator",
+ "className": "Coverage.CoverageView.LineDecorator",
+ "decoratorType": "coverage"
+ },
+ {
+ "type": "action",
+ "actionId": "coverage.toggle-recording",
+ "iconClass": "largeicon-start-recording",
+ "toggledIconClass": "largeicon-stop-recording",
+ "toggleWithRedColor": true,
+ "className": "Coverage.CoverageView.ActionDelegate",
+ "category": "Performance",
+ "options": [
+ {
+ "value": true,
+ "title": "Instrument coverage"
+ },
+ {
+ "value": false,
+ "title": "Stop instrumenting coverage and show results"
+ }
+ ]
+ },
+ {
+ "type": "action",
+ "actionId": "coverage.start-with-reload",
+ "iconClass": "largeicon-refresh",
+ "className": "Coverage.CoverageView.ActionDelegate",
+ "category": "Performance",
+ "title": "Start instrumenting coverage and reload page"
+ }
+ ],
+ "dependencies": [
+ "sdk",
+ "ui",
+ "source_frame",
+ "sources",
+ "data_grid"
+ ],
+ "scripts": [
+ "CoverageModel.js",
+ "CoverageListView.js",
+ "CoverageView.js",
+ "CoverageDecorationManager.js"
+ ],
+ "resources": [
+ "coverageListView.css",
+ "coverageView.css"
+ ]
+}
diff --git a/packages/@weex/plugins/debug/frontend/src/assets/inspector/inspector.js b/packages/@weex/plugins/debug/frontend/src/assets/inspector/inspector.js
new file mode 100644
index 0000000..c690b32
--- /dev/null
+++ b/packages/@weex/plugins/debug/frontend/src/assets/inspector/inspector.js
@@ -0,0 +1,4 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+Runtime.startApplication('inspector');
diff --git a/packages/@weex/plugins/debug/frontend/src/views/home/home.ts b/packages/@weex/plugins/debug/frontend/src/views/home/home.ts
index 2d64ff4..3b61767 100644
--- a/packages/@weex/plugins/debug/frontend/src/views/home/home.ts
+++ b/packages/@weex/plugins/debug/frontend/src/views/home/home.ts
@@ -113,7 +113,6 @@ export class HomeComponent extends Vue {
this.updateVersion(data.params.version)
} else if (data.method === 'WxDebug.startDebugger') {
this.$router.push({ path: `/client/weex/${data.params}?type=weex` })
- this.disconnect()
}
})
this.socket.on('close', (data) => {
@@ -138,7 +137,7 @@ export class HomeComponent extends Vue {
}
disconnect () {
- this.socket && this.socket.close()
+ this.socket && this.socket.connected && this.socket.close()
this.cleanQRCode()
this.$snotify.clear()
}
diff --git a/packages/@weex/plugins/debug/frontend/src/views/weex/weex.scss b/packages/@weex/plugins/debug/frontend/src/views/weex/weex.scss
index b7420d5..9997e5d 100644
--- a/packages/@weex/plugins/debug/frontend/src/views/weex/weex.scss
+++ b/packages/@weex/plugins/debug/frontend/src/views/weex/weex.scss
@@ -188,7 +188,7 @@
&.navigation {
flex: 1;
input {
- width: calc(100% - 40px);
+ width: calc(100% - 90px);
height: 32px;
border-radius: 3px;
border: 1px solid #ced4da;
@@ -213,7 +213,7 @@
&.navigation-button {
flex: 1;
.navigation-selection {
- width: calc(100% - 80px);
+ width: calc(100% - 90px);
margin-right: 10px;
height: 30px;
border: 1px solid #ddd;
diff --git a/packages/@weex/plugins/debug/src/link/managers/runtime_manager.js b/packages/@weex/plugins/debug/src/link/managers/runtime_manager.js
index 8d515b5..347cb9f 100644
--- a/packages/@weex/plugins/debug/src/link/managers/runtime_manager.js
+++ b/packages/@weex/plugins/debug/src/link/managers/runtime_manager.js
@@ -20,7 +20,7 @@ class RuntimeManager {
for (const target of list) {
const urlObj = URL.parse(target.url)
if (
- urlObj.pathname === '/runtime.html' &&
+ urlObj.pathname === '/runtime/runtime.html' &&
urlObj.port === config.port + ''
) {
found = target
diff --git a/packages/@weex/plugins/debug/src/server/headless.js b/packages/@weex/plugins/debug/src/server/headless.js
index fcccb4b..0eeb835 100644
--- a/packages/@weex/plugins/debug/src/server/headless.js
+++ b/packages/@weex/plugins/debug/src/server/headless.js
@@ -12,8 +12,8 @@ exports.launchHeadless = async (host, remotePort) => {
await page.setUserAgent(
'5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
)
- await page.goto(`http://${host}/runtime.html`)
- logger.verbose(`Headless page goto http://${host}/runtime.html`)
+ await page.goto(`http://${host}/runtime/runtime.html`)
+ logger.verbose(`Headless page goto http://${host}/runtime/runtime.html`)
}
exports.closeHeadless = async () => {