diff --git a/BUILD.gn b/BUILD.gn index 88e6375e70..7b22252a1a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -76,7 +76,7 @@ visibility = [ "//third_party/WebKit/*" ] group("devtools_frontend_resources") { public_deps = [ ":build_applications", - ":copy_compatibility_scripts", + ":copy_embedder_scripts", ":copy_emulated_devices_images", ":copy_inspector_images", ":devtools_extension_api", @@ -85,8 +85,8 @@ group("devtools_frontend_resources") { ] } -copy("copy_compatibility_scripts") { - sources = gypi_values.devtools_compatibility_scripts +copy("copy_embedder_scripts") { + sources = gypi_values.devtools_embedder_scripts outputs = [ resources_out_dir + "{{source_file_part}}", ] @@ -113,7 +113,7 @@ action("generate_devtools_grd") { ":devtools_frontend_resources", ] inputs = gypi_values.devtools_image_files + all_devtools_files - inputs += gypi_values.devtools_compatibility_scripts + inputs += gypi_values.devtools_embedder_scripts if (debug_devtools) { # Debug: all files are picked as-is. @@ -181,18 +181,17 @@ action("generate_devtools_grd") { "front_end", ] - args = - rebase_path(generated_files, root_build_dir) + - rebase_path(generated_files, root_build_dir) + - rebase_path(gypi_values.devtools_compatibility_scripts, root_build_dir) + - static_files_args + [ "--relative_path_dirs" ] + - rebase_path(relative_path_dirs, root_build_dir) + - [ - "--images", - rebase_path(images_path, root_build_dir), - "--output", - rebase_path(outfile, root_build_dir), - ] + args = rebase_path(generated_files, root_build_dir) + + rebase_path(generated_files, root_build_dir) + + rebase_path(gypi_values.devtools_embedder_scripts, root_build_dir) + + static_files_args + [ "--relative_path_dirs" ] + + rebase_path(relative_path_dirs, root_build_dir) + + [ + "--images", + rebase_path(images_path, root_build_dir), + "--output", + rebase_path(outfile, root_build_dir), + ] } action("devtools_extension_api") { diff --git a/devtools.gyp b/devtools.gyp index 7e9472a88f..ab14a9aa8f 100644 --- a/devtools.gyp +++ b/devtools.gyp @@ -57,7 +57,7 @@ { 'destination': '<(PRODUCT_DIR)/resources/inspector/', 'files': [ - '<@(devtools_compatibility_scripts)', + '<@(devtools_embedder_scripts)', ], }, ], @@ -132,7 +132,7 @@ 'inputs': [ '<@(_script_name)', '<@(_static_files)', - '<@(devtools_compatibility_scripts)', + '<@(devtools_embedder_scripts)', '<@(_generated_files)', '<@(devtools_image_files)', '<(_devtools_static_files_list)', @@ -141,7 +141,7 @@ 'front_end/Images', ], 'outputs': ['<(SHARED_INTERMEDIATE_DIR)/devtools/devtools_resources.grd'], - 'action': ['python', '<@(_script_name)', '<@(_generated_files)', '<@(devtools_compatibility_scripts)', '--static_files_list', '<(_devtools_static_files_list)', '--relative_path_dirs', '<@(_relative_path_dirs)', '--images', '<@(_images_path)', '--output', '<@(_outputs)'], + 'action': ['python', '<@(_script_name)', '<@(_generated_files)', '<@(devtools_embedder_scripts)', '--static_files_list', '<(_devtools_static_files_list)', '--relative_path_dirs', '<@(_relative_path_dirs)', '--images', '<@(_images_path)', '--output', '<@(_outputs)'], }], }, { @@ -170,7 +170,7 @@ 'inputs': [ '<@(_script_name)', '<@(_static_files)', - '<@(devtools_compatibility_scripts)', + '<@(devtools_embedder_scripts)', '<@(_generated_files)', '<@(devtools_image_files)', '<(_devtools_static_files_list)', @@ -180,7 +180,7 @@ ], # Note that other files are put under /devtools directory, together with declared devtools_resources.grd 'outputs': ['<(SHARED_INTERMEDIATE_DIR)/devtools/devtools_resources.grd'], - 'action': ['python', '<@(_script_name)', '<@(_generated_files)', '<@(devtools_compatibility_scripts)', '--static_files_list', '<(_devtools_static_files_list)', '--relative_path_dirs', '<@(_relative_path_dirs)', '--images', '<@(_images_path)', '--output', '<@(_outputs)'], + 'action': ['python', '<@(_script_name)', '<@(_generated_files)', '<@(devtools_embedder_scripts)', '--static_files_list', '<(_devtools_static_files_list)', '--relative_path_dirs', '<@(_relative_path_dirs)', '--images', '<@(_images_path)', '--output', '<@(_outputs)'], }], }], ], diff --git a/devtools.gypi b/devtools.gypi index b8f071fc1c..27a3c6cb7c 100644 --- a/devtools.gypi +++ b/devtools.gypi @@ -48,8 +48,9 @@ '<@(devtools_ui_js_files)', '<@(devtools_workspace_js_files)', ], - 'devtools_compatibility_scripts': [ + 'devtools_embedder_scripts': [ 'front_end/devtools.js', + 'front_end/Tests.js', ], 'devtools_core_base_files': [ 'front_end/inspector.js', @@ -73,7 +74,6 @@ 'front_end/common/StaticContentProvider.js', 'front_end/common/OutputStream.js', 'front_end/common/SegmentedRange.js', - 'front_end/common/TestBase.js', 'front_end/common/Text.js', 'front_end/common/TextDictionary.js', 'front_end/common/TextRange.js', @@ -122,6 +122,7 @@ 'front_end/sass/ASTSourceMap.js', 'front_end/sass/SASSProcessor.js', 'front_end/sass/SASSSupport.js', + 'front_end/sass/SASSSourceMapFactory.js', ], 'devtools_screencast_js_files': [ 'front_end/screencast/screencastView.css', @@ -307,12 +308,10 @@ 'front_end/main/renderingOptions.css', 'front_end/main/targetCrashedScreen.css', 'front_end/main/Connections.js', - 'front_end/main/FrontendWebSocketAPI.js', 'front_end/main/Main.js', 'front_end/main/OverlayController.js', 'front_end/main/RenderingOptions.js', 'front_end/main/SimpleApp.js', - 'front_end/main/Tests.js', ], 'devtools_module_json_files': [ 'front_end/accessibility/module.json', @@ -598,7 +597,6 @@ 'front_end/acorn/acorn.js', 'front_end/cm/css.js', 'front_end/cm/headlesscodemirror.js', - 'front_end/cm/htmlmixed.js', 'front_end/cm/xml.js', 'front_end/es_tree/AcornTokenizer.js', 'front_end/es_tree/ESTreeWalker.js', @@ -606,6 +604,10 @@ 'front_end/formatter_worker/CSSFormatter.js', 'front_end/formatter_worker/FormattedContentBuilder.js', 'front_end/formatter_worker/JavaScriptFormatter.js', + 'front_end/formatter_worker/CSSRuleParser.js', + 'front_end/formatter_worker/HTMLFormatter.js', + 'front_end/formatter_worker/IdentityFormatter.js', + 'front_end/formatter_worker/JavaScriptOutline.js', 'front_end/formatter_worker/FormatterWorker.js', ], 'devtools_settings_js_files': [ diff --git a/front_end/main/Tests.js b/front_end/Tests.js similarity index 84% rename from front_end/main/Tests.js rename to front_end/Tests.js index d7cebcf033..81e0557550 100644 --- a/front_end/main/Tests.js +++ b/front_end/Tests.js @@ -37,23 +37,210 @@ * FIXME: change field naming style to use trailing underscore. */ -function createTestSuite(domAutomationController) +(function createTestSuite(window) { /** * Test suite for interactive UI tests. * @constructor + * @param {Object} domAutomationController DomAutomationController instance. */ -function TestSuite() +function TestSuite(domAutomationController) { - WebInspector.TestBase.call(this, domAutomationController); + this.domAutomationController_ = domAutomationController; + this.controlTaken_ = false; + this.timerId_ = -1; this._asyncInvocationId = 0; +} + +/** + * Reports test failure. + * @param {string} message Failure description. + */ +TestSuite.prototype.fail = function(message) +{ + if (this.controlTaken_) + this.reportFailure_(message); + else + throw message; +}; + + +/** + * Equals assertion tests that expected === actual. + * @param {!Object|boolean} expected Expected object. + * @param {!Object|boolean} actual Actual object. + * @param {string} opt_message User message to print if the test fails. + */ +TestSuite.prototype.assertEquals = function(expected, actual, opt_message) +{ + if (expected !== actual) { + var message = "Expected: '" + expected + "', but was '" + actual + "'"; + if (opt_message) + message = opt_message + "(" + message + ")"; + this.fail(message); + } +}; + + +/** + * True assertion tests that value == true. + * @param {!Object} value Actual object. + * @param {string} opt_message User message to print if the test fails. + */ +TestSuite.prototype.assertTrue = function(value, opt_message) +{ + this.assertEquals(true, !!value, opt_message); +}; + + +/** + * Takes control over execution. + */ +TestSuite.prototype.takeControl = function() +{ + this.controlTaken_ = true; + // Set up guard timer. + var self = this; + this.timerId_ = setTimeout(function() { + self.reportFailure_("Timeout exceeded: 20 sec"); + }, 20000); +}; + + +/** + * Releases control over execution. + */ +TestSuite.prototype.releaseControl = function() +{ + if (this.timerId_ !== -1) { + clearTimeout(this.timerId_); + this.timerId_ = -1; + } + this.controlTaken_ = false; + this.reportOk_(); +}; + + +/** + * Async tests use this one to report that they are completed. + */ +TestSuite.prototype.reportOk_ = function() +{ + this.domAutomationController_.send("[OK]"); +}; + + +/** + * Async tests use this one to report failures. + */ +TestSuite.prototype.reportFailure_ = function(error) +{ + if (this.timerId_ !== -1) { + clearTimeout(this.timerId_); + this.timerId_ = -1; + } + this.domAutomationController_.send("[FAILED] " + error); +}; + + +/** + * Run specified test on a fresh instance of the test suite. + * @param {Array} args method name followed by its parameters. + */ +TestSuite.prototype.dispatchOnTestSuite = function(args) +{ + var methodName = args.shift(); + try { + this[methodName].apply(this, args); + if (!this.controlTaken_) + this.reportOk_(); + } catch (e) { + this.reportFailure_(e); + } }; -TestSuite.prototype = { - __proto__: WebInspector.TestBase.prototype + +/** + * Wrap an async method with TestSuite.{takeControl(), releaseControl()} + * and invoke TestSuite.reportOk_ upon completion. + * @param {Array} args method name followed by its parameters. + */ +TestSuite.prototype.waitForAsync = function(var_args) +{ + var args = Array.prototype.slice.call(arguments); + this.takeControl(); + args.push(this.releaseControl.bind(this)); + this.dispatchOnTestSuite(args); +}; + +/** + * Overrides the method with specified name until it's called first time. + * @param {!Object} receiver An object whose method to override. + * @param {string} methodName Name of the method to override. + * @param {!Function} override A function that should be called right after the + * overridden method returns. + * @param {?boolean} opt_sticky Whether restore original method after first run + * or not. + */ +TestSuite.prototype.addSniffer = function(receiver, methodName, override, opt_sticky) +{ + var orig = receiver[methodName]; + if (typeof orig !== "function") + this.fail("Cannot find method to override: " + methodName); + var test = this; + receiver[methodName] = function(var_args) { + try { + var result = orig.apply(this, arguments); + } finally { + if (!opt_sticky) + receiver[methodName] = orig; + } + // In case of exception the override won't be called. + try { + override.apply(this, arguments); + } catch (e) { + test.fail("Exception in overriden method '" + methodName + "': " + e); + } + return result; + }; }; +/** + * Waits for current throttler invocations, if any. + * @param {!WebInspector.Throttler} throttler + * @param {function()} callback + */ +TestSuite.prototype.waitForThrottler = function(throttler, callback) +{ + var test = this; + var scheduleShouldFail = true; + test.addSniffer(throttler, "schedule", onSchedule); + + function hasSomethingScheduled() + { + return throttler._isRunningProcess || throttler._process; + } + + function checkState() + { + if (!hasSomethingScheduled()) { + scheduleShouldFail = false; + callback(); + return; + } + + test.addSniffer(throttler, "_processCompletedForTests", checkState); + } + + function onSchedule() + { + if (scheduleShouldFail) + test.fail("Unexpected Throttler.schedule"); + } + + checkState(); +}; /** * @param {string} panelName Name of the panel to show. @@ -888,11 +1075,5 @@ TestSuite.createKeyEvent = function(keyIdentifier) return evt; }; -return new TestSuite(); - -} - -if (window.uiTests) { - WebInspector.notifications.addEventListener(WebInspector.NotificationService.Events.InspectorAgentEnabledForTests, - window.uiTests.testSuiteReady.bind(null, createTestSuite)); -} +window.uiTests = new TestSuite(window.domAutomationController); +})(window); diff --git a/front_end/bindings/BlackboxManager.js b/front_end/bindings/BlackboxManager.js index 98fc9c29ee..9e16e76f24 100644 --- a/front_end/bindings/BlackboxManager.js +++ b/front_end/bindings/BlackboxManager.js @@ -100,7 +100,7 @@ WebInspector.BlackboxManager.prototype = { /** * @param {!WebInspector.Script} script - * @param {?WebInspector.SourceMap} sourceMap + * @param {?WebInspector.TextSourceMap} sourceMap * @return {!Promise} */ sourceMapLoaded: function(script, sourceMap) @@ -136,8 +136,8 @@ WebInspector.BlackboxManager.prototype = { } return this._setScriptState(script, !isBlackboxed ? [] : positions); /** - * @param {!WebInspector.SourceMap.Entry} a - * @param {!WebInspector.SourceMap.Entry} b + * @param {!WebInspector.SourceMapEntry} a + * @param {!WebInspector.SourceMapEntry} b * @return {number} */ function mappingComparator(a, b) diff --git a/front_end/bindings/CompilerScriptMapping.js b/front_end/bindings/CompilerScriptMapping.js index 24cfc9e861..ecd99fe233 100644 --- a/front_end/bindings/CompilerScriptMapping.js +++ b/front_end/bindings/CompilerScriptMapping.js @@ -47,13 +47,13 @@ WebInspector.CompilerScriptMapping = function(debuggerModel, workspace, networkM this._networkProject = networkProject; this._debuggerWorkspaceBinding = debuggerWorkspaceBinding; - /** @type {!Map>} */ + /** @type {!Map>} */ this._sourceMapLoadingPromises = new Map(); - /** @type {!Map} */ + /** @type {!Map} */ this._sourceMapForScriptId = new Map(); - /** @type {!Map.} */ + /** @type {!Map.} */ this._scriptForSourceMap = new Map(); - /** @type {!Map.} */ + /** @type {!Map.} */ this._sourceMapForURL = new Map(); /** @type {!Map.} */ this._stubUISourceCodes = new Map(); @@ -156,7 +156,7 @@ WebInspector.CompilerScriptMapping.prototype = { /** * @param {!WebInspector.Script} script - * @return {?WebInspector.SourceMap} + * @return {?WebInspector.TextSourceMap} */ sourceMapForScript: function(script) { @@ -206,7 +206,7 @@ WebInspector.CompilerScriptMapping.prototype = { /** * @param {!WebInspector.Script} script * @param {string} uiSourceCodePath - * @param {?WebInspector.SourceMap} sourceMap + * @param {?WebInspector.TextSourceMap} sourceMap */ _sourceMapLoaded: function(script, uiSourceCodePath, sourceMap) { @@ -230,7 +230,7 @@ WebInspector.CompilerScriptMapping.prototype = { this._scriptForSourceMap.set(sourceMap, script); // Report sources. - var sourceURLs = sourceMap.sources(); + var sourceURLs = sourceMap.sourceURLs(); var missingSources = []; for (var i = 0; i < sourceURLs.length; ++i) { var sourceURL = sourceURLs[i]; @@ -317,7 +317,7 @@ WebInspector.CompilerScriptMapping.prototype = { /** * @param {!WebInspector.Script} script - * @return {!Promise} + * @return {!Promise} */ _loadSourceMapForScript: function(script) { @@ -325,25 +325,25 @@ WebInspector.CompilerScriptMapping.prototype = { // relative links. var scriptURL = WebInspector.ParsedURL.completeURL(this._target.resourceTreeModel.inspectedPageURL(), script.sourceURL); if (!scriptURL) - return Promise.resolve(/** @type {?WebInspector.SourceMap} */(null)); + return Promise.resolve(/** @type {?WebInspector.TextSourceMap} */(null)); console.assert(script.sourceMapURL); var scriptSourceMapURL = /** @type {string} */ (script.sourceMapURL); var sourceMapURL = WebInspector.ParsedURL.completeURL(scriptURL, scriptSourceMapURL); if (!sourceMapURL) - return Promise.resolve(/** @type {?WebInspector.SourceMap} */(null)); + return Promise.resolve(/** @type {?WebInspector.TextSourceMap} */(null)); var loadingPromise = this._sourceMapLoadingPromises.get(sourceMapURL); if (!loadingPromise) { - loadingPromise = WebInspector.SourceMap.load(sourceMapURL, scriptURL).then(sourceMapLoaded.bind(this, sourceMapURL)); + loadingPromise = WebInspector.TextSourceMap.load(sourceMapURL, scriptURL).then(sourceMapLoaded.bind(this, sourceMapURL)); this._sourceMapLoadingPromises.set(sourceMapURL, loadingPromise); } return loadingPromise; /** * @param {string} url - * @param {?WebInspector.SourceMap} sourceMap + * @param {?WebInspector.TextSourceMap} sourceMap * @this {WebInspector.CompilerScriptMapping} */ function sourceMapLoaded(url, sourceMap) @@ -360,7 +360,7 @@ WebInspector.CompilerScriptMapping.prototype = { _debuggerReset: function() { /** - * @param {!WebInspector.SourceMap} sourceMap + * @param {!WebInspector.TextSourceMap} sourceMap * @this {WebInspector.CompilerScriptMapping} */ function unbindSourceMapSources(sourceMap) @@ -368,7 +368,7 @@ WebInspector.CompilerScriptMapping.prototype = { var script = this._scriptForSourceMap.get(sourceMap); if (!script) return; - var sourceURLs = sourceMap.sources(); + var sourceURLs = sourceMap.sourceURLs(); for (var i = 0; i < sourceURLs.length; ++i) { var uiSourceCode = this._networkMapping.uiSourceCodeForScriptURL(sourceURLs[i], script); if (uiSourceCode) diff --git a/front_end/bindings/DebuggerWorkspaceBinding.js b/front_end/bindings/DebuggerWorkspaceBinding.js index 844698aa55..313b8a4d5f 100644 --- a/front_end/bindings/DebuggerWorkspaceBinding.js +++ b/front_end/bindings/DebuggerWorkspaceBinding.js @@ -245,7 +245,7 @@ WebInspector.DebuggerWorkspaceBinding.prototype = { /** * @param {!WebInspector.Script} script - * @return {?WebInspector.SourceMap} + * @return {?WebInspector.TextSourceMap} */ sourceMapForScript: function(script) { diff --git a/front_end/bindings/SASSSourceMapping.js b/front_end/bindings/SASSSourceMapping.js index e188e8d139..6041cebdea 100644 --- a/front_end/bindings/SASSSourceMapping.js +++ b/front_end/bindings/SASSSourceMapping.js @@ -52,8 +52,7 @@ WebInspector.SASSSourceMapping.prototype = { { var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data); var sourceMap = this._cssModel.sourceMapForHeader(header); - var sources = sourceMap.sources(); - for (var sassURL of sourceMap.sources()) { + for (var sassURL of sourceMap.sourceURLs()) { if (!this._networkMapping.hasMappingForNetworkURL(sassURL)) { var contentProvider = sourceMap.sourceContentProvider(sassURL, WebInspector.resourceTypes.SourceMapStyleSheet); this._networkProject.addFileForURL(sassURL, contentProvider, WebInspector.ResourceTreeFrame.fromStyleSheet(header)); diff --git a/front_end/common/NotificationService.js b/front_end/common/NotificationService.js index ea2e8c5536..a6292fe260 100644 --- a/front_end/common/NotificationService.js +++ b/front_end/common/NotificationService.js @@ -15,7 +15,6 @@ WebInspector.NotificationService.prototype = { } WebInspector.NotificationService.Events = { - InspectorAgentEnabledForTests: "InspectorAgentEnabledForTests", SelectedNodeChanged: "SelectedNodeChanged" } diff --git a/front_end/common/TestBase.js b/front_end/common/TestBase.js deleted file mode 100644 index 48fecf7b69..0000000000 --- a/front_end/common/TestBase.js +++ /dev/null @@ -1,205 +0,0 @@ -// 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. - -/** - * Test suite for interactive UI tests. - * @constructor - * @param {Object} domAutomationController DomAutomationController instance. - */ -WebInspector.TestBase = function(domAutomationController) -{ - this.domAutomationController_ = domAutomationController; - this.controlTaken_ = false; - this.timerId_ = -1; -}; - - -/** - * Reports test failure. - * @param {string} message Failure description. - */ -WebInspector.TestBase.prototype.fail = function(message) -{ - if (this.controlTaken_) - this.reportFailure_(message); - else - throw message; -}; - - -/** - * Equals assertion tests that expected === actual. - * @param {!Object|boolean} expected Expected object. - * @param {!Object|boolean} actual Actual object. - * @param {string} opt_message User message to print if the test fails. - */ -WebInspector.TestBase.prototype.assertEquals = function(expected, actual, opt_message) -{ - if (expected !== actual) { - var message = "Expected: '" + expected + "', but was '" + actual + "'"; - if (opt_message) - message = opt_message + "(" + message + ")"; - this.fail(message); - } -}; - - -/** - * True assertion tests that value == true. - * @param {!Object} value Actual object. - * @param {string} opt_message User message to print if the test fails. - */ -WebInspector.TestBase.prototype.assertTrue = function(value, opt_message) -{ - this.assertEquals(true, !!value, opt_message); -}; - - -/** - * Takes control over execution. - */ -WebInspector.TestBase.prototype.takeControl = function() -{ - this.controlTaken_ = true; - // Set up guard timer. - var self = this; - this.timerId_ = setTimeout(function() { - self.reportFailure_("Timeout exceeded: 20 sec"); - }, 20000); -}; - - -/** - * Releases control over execution. - */ -WebInspector.TestBase.prototype.releaseControl = function() -{ - if (this.timerId_ !== -1) { - clearTimeout(this.timerId_); - this.timerId_ = -1; - } - this.controlTaken_ = false; - this.reportOk_(); -}; - - -/** - * Async tests use this one to report that they are completed. - */ -WebInspector.TestBase.prototype.reportOk_ = function() -{ - this.domAutomationController_.send("[OK]"); -}; - - -/** - * Async tests use this one to report failures. - */ -WebInspector.TestBase.prototype.reportFailure_ = function(error) -{ - if (this.timerId_ !== -1) { - clearTimeout(this.timerId_); - this.timerId_ = -1; - } - this.domAutomationController_.send("[FAILED] " + error); -}; - - -/** - * Run specified test on a fresh instance of the test suite. - * @param {Array} args method name followed by its parameters. - */ -WebInspector.TestBase.prototype.dispatch = function(args) -{ - var methodName = args.shift(); - try { - this[methodName].apply(this, args); - if (!this.controlTaken_) - this.reportOk_(); - } catch (e) { - this.reportFailure_(e); - } -}; - - -/** - * Wrap an async method with TestBase.{takeControl(), releaseControl()} - * and invoke TestBase.reportOk_ upon completion. - * @param {Array} args method name followed by its parameters. - */ -WebInspector.TestBase.prototype.waitForAsync = function(var_args) -{ - var args = Array.prototype.slice.call(arguments); - this.takeControl(); - args.push(this.releaseControl.bind(this)); - this.dispatch(args); -}; - -/** - * Overrides the method with specified name until it's called first time. - * @param {!Object} receiver An object whose method to override. - * @param {string} methodName Name of the method to override. - * @param {!Function} override A function that should be called right after the - * overridden method returns. - * @param {?boolean} opt_sticky Whether restore original method after first run - * or not. - */ -WebInspector.TestBase.prototype.addSniffer = function(receiver, methodName, override, opt_sticky) -{ - var orig = receiver[methodName]; - if (typeof orig !== "function") - this.fail("Cannot find method to override: " + methodName); - var test = this; - receiver[methodName] = function(var_args) { - try { - var result = orig.apply(this, arguments); - } finally { - if (!opt_sticky) - receiver[methodName] = orig; - } - // In case of exception the override won't be called. - try { - override.apply(this, arguments); - } catch (e) { - test.fail("Exception in overriden method '" + methodName + "': " + e); - } - return result; - }; -}; - -/** - * Waits for current throttler invocations, if any. - * @param {!WebInspector.Throttler} throttler - * @param {function()} callback - */ -WebInspector.TestBase.prototype.waitForThrottler = function(throttler, callback) -{ - var test = this; - var scheduleShouldFail = true; - test.addSniffer(throttler, "schedule", onSchedule); - - function hasSomethingScheduled() - { - return throttler._isRunningProcess || throttler._process; - } - - function checkState() - { - if (!hasSomethingScheduled()) { - scheduleShouldFail = false; - callback(); - return; - } - - test.addSniffer(throttler, "_processCompletedForTests", checkState); - } - - function onSchedule() - { - if (scheduleShouldFail) - test.fail("Unexpected Throttler.schedule"); - } - - checkState(); -}; diff --git a/front_end/common/module.json b/front_end/common/module.json index fc9ec62dce..2b04f326b5 100644 --- a/front_end/common/module.json +++ b/front_end/common/module.json @@ -19,15 +19,11 @@ "StaticContentProvider.js", "OutputStream.js", "SegmentedRange.js", - "TestBase.js", "Text.js", "TextRange.js", "TextUtils.js", "Throttler.js", "UIString.js", "ModuleExtensionInterfaces.js" - ], - "skip_compilation": [ - "TestBase.js" ] } diff --git a/front_end/components/ShortcutsScreen.js b/front_end/components/ShortcutsScreen.js index c919b8718f..45ca577ae3 100644 --- a/front_end/components/ShortcutsScreen.js +++ b/front_end/components/ShortcutsScreen.js @@ -418,7 +418,7 @@ WebInspector.ShortcutsScreen.SourcesPanelShortcuts = { ], GoToMember: [ - WebInspector.KeyboardShortcut.makeDescriptor("p", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta | WebInspector.KeyboardShortcut.Modifiers.Shift) + WebInspector.KeyboardShortcut.makeDescriptor("o", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta | WebInspector.KeyboardShortcut.Modifiers.Shift) ], GoToLine: [ diff --git a/front_end/devtools.js b/front_end/devtools.js index 9da8325688..84325f7a37 100644 --- a/front_end/devtools.js +++ b/front_end/devtools.js @@ -160,6 +160,15 @@ DevToolsAPIImpl.prototype = { this._dispatchOnInspectorFrontendAPI("enterInspectElementMode", []); }, + /** + * @param {number} callId + * @param {string} script + */ + evaluateForTestInFrontend: function(callId, script) + { + this._dispatchOnInspectorFrontendAPI("evaluateForTestInFrontend", [callId, script]); + }, + /** * @param {!Array.} fileSystems */ @@ -297,24 +306,6 @@ DevToolsAPIImpl.prototype = { streamWrite: function(id, chunk) { this._dispatchOnInspectorFrontendAPI("streamWrite", [id, chunk]); - }, - - frontendAPIAttached: function() - { - this._dispatchOnInspectorFrontendAPI("frontendAPIAttached", []); - }, - - frontendAPIDetached: function() - { - this._dispatchOnInspectorFrontendAPI("frontendAPIDetached", []); - }, - - /** - * @param {string} command - */ - dispatchFrontendAPIMessage: function(command) - { - this._dispatchOnInspectorFrontendAPI("dispatchFrontendAPIMessage", [command]); } } @@ -537,15 +528,6 @@ InspectorFrontendHostImpl.prototype = { DevToolsAPI.sendMessageToEmbedder("recordEnumeratedHistogram", [actionName, actionCode, bucketSize], null); }, - /** - * @override - * @param {string} message - */ - sendFrontendAPINotification: function(message) - { - DevToolsAPI.sendMessageToEmbedder("sendFrontendAPINotification", [message], null); - }, - /** * @override */ @@ -673,6 +655,14 @@ InspectorFrontendHostImpl.prototype = { return DevToolsHost.isUnderTest(); }, + /** + * @override + */ + readyForTest: function() + { + DevToolsAPI.sendMessageToEmbedder("readyForTest", [], null); + }, + /** * @override * @param {boolean} discoverUsbDevices @@ -736,6 +726,14 @@ InspectorFrontendHostImpl.prototype = { // Backward-compatible methods below this line -------------------------------------------- + /** + * Support for legacy front-ends ( ol { position: relative; margin: 0; @@ -355,6 +369,13 @@ li.hovered:not(.always-parent) + ol.children, .elements-tree-outline ol.shadow-r border-left-style: solid; } +.elements-reduced-indentation li.hovered:not(.always-parent) + ol.children, +.elements-reduced-indentation .elements-tree-outline ol.shadow-root, +.elements-reduced-indentation li.selected:not(.always-parent) + ol.children { + margin-left: 6px; + -webkit-padding-start: 1px; +} + li.hovered:not(.always-parent) + ol.children:not(.shadow-root) { border-color: hsla(0,0%,0%,0.1); } diff --git a/front_end/formatter_worker/CSSFormatter.js b/front_end/formatter_worker/CSSFormatter.js index 22c474950c..5743e0deb5 100644 --- a/front_end/formatter_worker/CSSFormatter.js +++ b/front_end/formatter_worker/CSSFormatter.js @@ -30,23 +30,29 @@ /** * @constructor - * @param {string} content - * @param {!FormatterWorker.FormattedContentBuilder} builder + * @param {!WebInspector.FormattedContentBuilder} builder */ -FormatterWorker.CSSFormatter = function(content, builder) +WebInspector.CSSFormatter = function(builder) { - this._content = content; - this._lineEndings = this._content.computeLineEndings(); this._builder = builder; - this._lastLine = -1; - this._state = {}; } -FormatterWorker.CSSFormatter.prototype = { - format: function() +WebInspector.CSSFormatter.prototype = { + /** + * @param {string} text + * @param {!Array.} lineEndings + * @param {number} fromOffset + * @param {number} toOffset + */ + format: function(text, lineEndings, fromOffset, toOffset) { - var tokenize = FormatterWorker.createTokenizer("text/css"); - tokenize(this._content, this._tokenCallback.bind(this)); + this._lineEndings = lineEndings; + this._fromOffset = fromOffset; + this._toOffset = toOffset; + this._lastLine = -1; + this._state = {}; + var tokenize = WebInspector.createTokenizer("text/css"); + tokenize(text.substring(this._fromOffset, this._toOffset), this._tokenCallback.bind(this)); }, /** @@ -57,6 +63,8 @@ FormatterWorker.CSSFormatter.prototype = { */ _tokenCallback: function(token, type, startPosition, endPosition) { + startPosition += this._fromOffset; + endPosition += this._fromOffset; var startLine = this._lineEndings.lowerBound(startPosition); var endLine = this._lineEndings.lowerBound(endPosition); if (startLine !== this._lastLine) diff --git a/front_end/formatter_worker/CSSRuleParser.js b/front_end/formatter_worker/CSSRuleParser.js new file mode 100644 index 0000000000..7240a11f02 --- /dev/null +++ b/front_end/formatter_worker/CSSRuleParser.js @@ -0,0 +1,194 @@ +// Copyright 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. + +WebInspector.CSSParserStates = { + Initial: "Initial", + Selector: "Selector", + Style: "Style", + PropertyName: "PropertyName", + PropertyValue: "PropertyValue", + AtRule: "AtRule" +}; + +/** + * @param {string} text + */ +WebInspector.parseCSS = function(text) +{ + WebInspector._innerParseCSS(text, postMessage); +} + +/** + * @param {string} text + * @param {function(*)} chunkCallback + */ +WebInspector._innerParseCSS = function(text, chunkCallback) +{ + var chunkSize = 100000; // characters per data chunk + var lines = text.split("\n"); + var rules = []; + var processedChunkCharacters = 0; + + var state = WebInspector.CSSParserStates.Initial; + var rule; + var property; + var UndefTokenType = {}; + + var disabledRules = []; + function disabledRulesCallback(chunk) + { + disabledRules = disabledRules.concat(chunk.chunk); + } + + /** + * @param {string} tokenValue + * @param {?string} tokenTypes + * @param {number} column + * @param {number} newColumn + */ + function processToken(tokenValue, tokenTypes, column, newColumn) + { + var tokenType = tokenTypes ? tokenTypes.split(" ").keySet() : UndefTokenType; + switch (state) { + case WebInspector.CSSParserStates.Initial: + if (tokenType["qualifier"] || tokenType["builtin"] || tokenType["tag"]) { + rule = { + selectorText: tokenValue, + lineNumber: lineNumber, + columnNumber: column, + properties: [], + }; + state = WebInspector.CSSParserStates.Selector; + } else if (tokenType["def"]) { + rule = { + atRule: tokenValue, + lineNumber: lineNumber, + columnNumber: column, + }; + state = WebInspector.CSSParserStates.AtRule; + } + break; + case WebInspector.CSSParserStates.Selector: + if (tokenValue === "{" && tokenType === UndefTokenType) { + rule.selectorText = rule.selectorText.trim(); + rule.styleRange = createRange(lineNumber, newColumn); + state = WebInspector.CSSParserStates.Style; + } else { + rule.selectorText += tokenValue; + } + break; + case WebInspector.CSSParserStates.AtRule: + if ((tokenValue === ";" || tokenValue === "{") && tokenType === UndefTokenType) { + rule.atRule = rule.atRule.trim(); + rules.push(rule); + state = WebInspector.CSSParserStates.Initial; + } else { + rule.atRule += tokenValue; + } + break; + case WebInspector.CSSParserStates.Style: + if (tokenType["meta"] || tokenType["property"]) { + property = { + name: tokenValue, + value: "", + range: createRange(lineNumber, column), + nameRange: createRange(lineNumber, column) + }; + state = WebInspector.CSSParserStates.PropertyName; + } else if (tokenValue === "}" && tokenType === UndefTokenType) { + rule.styleRange.endLine = lineNumber; + rule.styleRange.endColumn = column; + rules.push(rule); + state = WebInspector.CSSParserStates.Initial; + } else if (tokenType["comment"]) { + // The |processToken| is called per-line, so no token spans more then one line. + // Support only a one-line comments. + if (tokenValue.substring(0, 2) !== "/*" || tokenValue.substring(tokenValue.length - 2) !== "*/") + break; + var uncommentedText = tokenValue.substring(2, tokenValue.length - 2); + var fakeRule = "a{\n" + uncommentedText + "}"; + disabledRules = []; + WebInspector._innerParseCSS(fakeRule, disabledRulesCallback); + if (disabledRules.length === 1 && disabledRules[0].properties.length === 1) { + var disabledProperty = disabledRules[0].properties[0]; + disabledProperty.disabled = true; + disabledProperty.range = createRange(lineNumber, column); + disabledProperty.range.endColumn = newColumn; + var lineOffset = lineNumber - 1; + var columnOffset = column + 2; + disabledProperty.nameRange.startLine += lineOffset; + disabledProperty.nameRange.startColumn += columnOffset; + disabledProperty.nameRange.endLine += lineOffset; + disabledProperty.nameRange.endColumn += columnOffset; + disabledProperty.valueRange.startLine += lineOffset; + disabledProperty.valueRange.startColumn += columnOffset; + disabledProperty.valueRange.endLine += lineOffset; + disabledProperty.valueRange.endColumn += columnOffset; + rule.properties.push(disabledProperty); + } + } + break; + case WebInspector.CSSParserStates.PropertyName: + if (tokenValue === ":" && tokenType === UndefTokenType) { + property.name = property.name; + property.nameRange.endLine = lineNumber; + property.nameRange.endColumn = column; + property.valueRange = createRange(lineNumber, newColumn); + state = WebInspector.CSSParserStates.PropertyValue; + } else if (tokenType["property"]) { + property.name += tokenValue; + } + break; + case WebInspector.CSSParserStates.PropertyValue: + if ((tokenValue === ";" || tokenValue === "}") && tokenType === UndefTokenType) { + property.value = property.value; + property.valueRange.endLine = lineNumber; + property.valueRange.endColumn = column; + property.range.endLine = lineNumber; + property.range.endColumn = tokenValue === ";" ? newColumn : column; + rule.properties.push(property); + if (tokenValue === "}") { + rule.styleRange.endLine = lineNumber; + rule.styleRange.endColumn = column; + rules.push(rule); + state = WebInspector.CSSParserStates.Initial; + } else { + state = WebInspector.CSSParserStates.Style; + } + } else if (!tokenType["comment"]) { + property.value += tokenValue; + } + break; + default: + console.assert(false, "Unknown CSS parser state."); + } + processedChunkCharacters += newColumn - column; + if (processedChunkCharacters > chunkSize) { + chunkCallback({ chunk: rules, isLastChunk: false }); + rules = []; + processedChunkCharacters = 0; + } + } + var tokenizer = WebInspector.createTokenizer("text/css"); + var lineNumber; + for (lineNumber = 0; lineNumber < lines.length; ++lineNumber) { + var line = lines[lineNumber]; + tokenizer(line, processToken); + processToken("\n", null, line.length, line.length + 1); + } + chunkCallback({ chunk: rules, isLastChunk: true }); + + /** + * @return {!{startLine: number, startColumn: number, endLine: number, endColumn: number}} + */ + function createRange(lineNumber, columnNumber) + { + return { + startLine: lineNumber, + startColumn: columnNumber, + endLine: lineNumber, + endColumn: columnNumber + }; + } +} diff --git a/front_end/formatter_worker/FormattedContentBuilder.js b/front_end/formatter_worker/FormattedContentBuilder.js index 98fb0634c4..4a5428be4f 100644 --- a/front_end/formatter_worker/FormattedContentBuilder.js +++ b/front_end/formatter_worker/FormattedContentBuilder.js @@ -4,22 +4,18 @@ /** * @constructor - * @param {!{original: !Array., formatted: !Array.}} mapping - * @param {number} originalOffset - * @param {number} formattedOffset * @param {string} indentString */ -FormatterWorker.FormattedContentBuilder = function(mapping, originalOffset, formattedOffset, indentString) +WebInspector.FormattedContentBuilder = function(indentString) { - this._originalOffset = originalOffset; this._lastOriginalPosition = 0; this._formattedContent = []; this._formattedContentLength = 0; - this._formattedOffset = formattedOffset; this._lastFormattedPosition = 0; - this._mapping = mapping; + /** @type {!{original: !Array., formatted: !Array.}} */ + this._mapping = { original: [0], formatted: [0] }; this._lineNumber = 0; this._nestingLevel = 0; @@ -32,7 +28,7 @@ FormatterWorker.FormattedContentBuilder = function(mapping, originalOffset, form this._hardSpaces = 0; } -FormatterWorker.FormattedContentBuilder.prototype = { +WebInspector.FormattedContentBuilder.prototype = { /** * @param {string} token * @param {number} startPosition @@ -116,6 +112,14 @@ FormatterWorker.FormattedContentBuilder.prototype = { return this._formattedContent.join("") + (this._newLines ? "\n" : ""); }, + /** + * @return {!{original: !Array., formatted: !Array.}} + */ + mapping: function() + { + return this._mapping; + }, + /** * @return {string} */ @@ -151,9 +155,9 @@ FormatterWorker.FormattedContentBuilder.prototype = { { if (originalPosition - this._lastOriginalPosition === this._formattedContentLength - this._lastFormattedPosition) return; - this._mapping.original.push(this._originalOffset + originalPosition); + this._mapping.original.push(originalPosition); this._lastOriginalPosition = originalPosition; - this._mapping.formatted.push(this._formattedOffset + this._formattedContentLength); + this._mapping.formatted.push(this._formattedContentLength); this._lastFormattedPosition = this._formattedContentLength; } } diff --git a/front_end/formatter_worker/FormatterWorker.js b/front_end/formatter_worker/FormatterWorker.js index 9b2afc85f5..4d22f1b252 100644 --- a/front_end/formatter_worker/FormatterWorker.js +++ b/front_end/formatter_worker/FormatterWorker.js @@ -27,509 +27,123 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var FormatterWorker = { + +/** + * @param {string} mimeType + * @return {function(string, function(string, ?string, number, number):(!Object|undefined))} + */ +WebInspector.createTokenizer = function(mimeType) +{ + var mode = CodeMirror.getMode({indentUnit: 2}, mimeType); + var state = CodeMirror.startState(mode); /** - * @param {string} mimeType - * @return {function(string, function(string, ?string, number, number))} + * @param {string} line + * @param {function(string, ?string, number, number):?} callback */ - createTokenizer: function(mimeType) + function tokenize(line, callback) { - var mode = CodeMirror.getMode({indentUnit: 2}, mimeType); - var state = CodeMirror.startState(mode); - function tokenize(line, callback) - { - var stream = new CodeMirror.StringStream(line); - while (!stream.eol()) { - var style = mode.token(stream, state); - var value = stream.current(); - callback(value, style, stream.start, stream.start + value.length); - stream.start = stream.pos; - } + var stream = new CodeMirror.StringStream(line); + while (!stream.eol()) { + var style = mode.token(stream, state); + var value = stream.current(); + if (callback(value, style, stream.start, stream.start + value.length) === WebInspector.AbortTokenization) + return; + stream.start = stream.pos; } - return tokenize; } -}; + return tokenize; +} -/** - * @typedef {{indentString: string, content: string, mimeType: string}} - */ -var FormatterParameters; +WebInspector.AbortTokenization = {}; self.onmessage = function(event) { - var data = /** @type !{method: string, params: !FormatterParameters} */ (event.data); - if (!data.method) + var method = /** @type {string} */(event.data.method); + var params = /** @type !{indentString: string, content: string, mimeType: string} */ (event.data.params); + if (!method) return; - FormatterWorker[data.method](data.params); + switch (method) { + case "format": + WebInspector.format(params.mimeType, params.content, params.indentString); + break; + case "parseCSS": + WebInspector.parseCSS(params.content); + break; + case "javaScriptOutline": + WebInspector.javaScriptOutline(params.content); + break; + default: + console.error("Unsupport method name: " + method); + } }; /** - * @param {!FormatterParameters} params + * @param {string} mimeType + * @param {string} text + * @param {string=} indentString */ -FormatterWorker.format = function(params) +WebInspector.format = function(mimeType, text, indentString) { // Default to a 4-space indent. - var indentString = params.indentString || " "; + indentString = indentString || " "; var result = {}; - - if (params.mimeType === "text/html") { - var formatter = new FormatterWorker.HTMLFormatter(indentString); - result = formatter.format(params.content); - } else if (params.mimeType === "text/css") { - result.mapping = { original: [0], formatted: [0] }; - result.content = FormatterWorker._formatCSS(params.content, result.mapping, 0, 0, indentString); - } else { - result.mapping = { original: [0], formatted: [0] }; - result.content = FormatterWorker._formatScript(params.content, result.mapping, 0, 0, indentString); - } - postMessage(result); -} - -/** - * @param {!Object} params - */ -FormatterWorker.javaScriptOutline = function(params) -{ - var chunkSize = 100000; // characters per data chunk - var outlineChunk = []; - var previousIdentifier = null; - var previousToken = null; - var processedChunkCharacters = 0; - var addedFunction = false; - var isReadingArguments = false; - var argumentsText = ""; - var currentFunction = null; - var tokenizer = new WebInspector.AcornTokenizer(params.content); - var AT = WebInspector.AcornTokenizer; - - while (tokenizer.peekToken()) { - var token = /** @type {!Acorn.TokenOrComment} */(tokenizer.nextToken()); - if (AT.lineComment(token) || AT.blockComment(token)) - continue; - - var tokenValue = params.content.substring(token.start, token.end); - - if (AT.identifier(token) && previousToken && (AT.identifier(previousToken, "get") || AT.identifier(previousToken, "set"))) { - currentFunction = { - line: tokenizer.tokenLineStart(), - column: tokenizer.tokenColumnStart(), - name : previousToken.value + " " + tokenValue - }; - addedFunction = true; - previousIdentifier = null; - } else if (AT.identifier(token)) { - previousIdentifier = tokenValue; - if (tokenValue && previousToken && AT.keyword(previousToken, "function")) { - // A named function: "function f...". - currentFunction = { - line: tokenizer.tokenLineStart(), - column: tokenizer.tokenColumnStart(), - name: tokenValue - }; - addedFunction = true; - previousIdentifier = null; - } - } else if (AT.keyword(token, "function") && previousIdentifier && previousToken && AT.punctuator(previousToken, ":=")) { - // Anonymous function assigned to an identifier: "...f = function..." - // or "funcName: function...". - currentFunction = { - line: tokenizer.tokenLineStart(), - column: tokenizer.tokenColumnStart(), - name: previousIdentifier - }; - addedFunction = true; - previousIdentifier = null; - } else if (AT.punctuator(token, ".") && previousToken && AT.identifier(previousToken)) - previousIdentifier += "."; - else if (AT.punctuator(token, "(") && addedFunction) - isReadingArguments = true; - if (isReadingArguments && tokenValue) - argumentsText += tokenValue; - - if (AT.punctuator(token, ")") && isReadingArguments) { - addedFunction = false; - isReadingArguments = false; - currentFunction.arguments = argumentsText.replace(/,[\r\n\s]*/g, ", ").replace(/([^,])[\r\n\s]+/g, "$1"); - argumentsText = ""; - outlineChunk.push(currentFunction); - } - - previousToken = token; - processedChunkCharacters += token.end - token.start; - - if (processedChunkCharacters >= chunkSize) { - postMessage({ chunk: outlineChunk, isLastChunk: false }); - outlineChunk = []; - processedChunkCharacters = 0; - } - } - - postMessage({ chunk: outlineChunk, isLastChunk: true }); -} - -FormatterWorker.CSSParserStates = { - Initial: "Initial", - Selector: "Selector", - Style: "Style", - PropertyName: "PropertyName", - PropertyValue: "PropertyValue", - AtRule: "AtRule" -}; - -FormatterWorker.parseCSS = function(params) -{ - FormatterWorker._innerParseCSS(params.content, postMessage); -} - -FormatterWorker._innerParseCSS = function(text, chunkCallback) -{ - var chunkSize = 100000; // characters per data chunk - var lines = text.split("\n"); - var rules = []; - var processedChunkCharacters = 0; - - var state = FormatterWorker.CSSParserStates.Initial; - var rule; - var property; - var UndefTokenType = {}; - - var disabledRules = []; - function disabledRulesCallback(chunk) - { - disabledRules = disabledRules.concat(chunk.chunk); - } - - /** - * @param {string} tokenValue - * @param {?string} tokenTypes - * @param {number} column - * @param {number} newColumn - */ - function processToken(tokenValue, tokenTypes, column, newColumn) - { - var tokenType = tokenTypes ? tokenTypes.split(" ").keySet() : UndefTokenType; - switch (state) { - case FormatterWorker.CSSParserStates.Initial: - if (tokenType["qualifier"] || tokenType["builtin"] || tokenType["tag"]) { - rule = { - selectorText: tokenValue, - lineNumber: lineNumber, - columnNumber: column, - properties: [], - }; - state = FormatterWorker.CSSParserStates.Selector; - } else if (tokenType["def"]) { - rule = { - atRule: tokenValue, - lineNumber: lineNumber, - columnNumber: column, - }; - state = FormatterWorker.CSSParserStates.AtRule; - } - break; - case FormatterWorker.CSSParserStates.Selector: - if (tokenValue === "{" && tokenType === UndefTokenType) { - rule.selectorText = rule.selectorText.trim(); - rule.styleRange = createRange(lineNumber, newColumn); - state = FormatterWorker.CSSParserStates.Style; - } else { - rule.selectorText += tokenValue; - } - break; - case FormatterWorker.CSSParserStates.AtRule: - if ((tokenValue === ";" || tokenValue === "{") && tokenType === UndefTokenType) { - rule.atRule = rule.atRule.trim(); - rules.push(rule); - state = FormatterWorker.CSSParserStates.Initial; - } else { - rule.atRule += tokenValue; - } - break; - case FormatterWorker.CSSParserStates.Style: - if (tokenType["meta"] || tokenType["property"]) { - property = { - name: tokenValue, - value: "", - range: createRange(lineNumber, column), - nameRange: createRange(lineNumber, column) - }; - state = FormatterWorker.CSSParserStates.PropertyName; - } else if (tokenValue === "}" && tokenType === UndefTokenType) { - rule.styleRange.endLine = lineNumber; - rule.styleRange.endColumn = column; - rules.push(rule); - state = FormatterWorker.CSSParserStates.Initial; - } else if (tokenType["comment"]) { - // The |processToken| is called per-line, so no token spans more then one line. - // Support only a one-line comments. - if (tokenValue.substring(0, 2) !== "/*" || tokenValue.substring(tokenValue.length - 2) !== "*/") - break; - var uncommentedText = tokenValue.substring(2, tokenValue.length - 2); - var fakeRule = "a{\n" + uncommentedText + "}"; - disabledRules = []; - FormatterWorker._innerParseCSS(fakeRule, disabledRulesCallback); - if (disabledRules.length === 1 && disabledRules[0].properties.length === 1) { - var disabledProperty = disabledRules[0].properties[0]; - disabledProperty.disabled = true; - disabledProperty.range = createRange(lineNumber, column); - disabledProperty.range.endColumn = newColumn; - var lineOffset = lineNumber - 1; - var columnOffset = column + 2; - disabledProperty.nameRange.startLine += lineOffset; - disabledProperty.nameRange.startColumn += columnOffset; - disabledProperty.nameRange.endLine += lineOffset; - disabledProperty.nameRange.endColumn += columnOffset; - disabledProperty.valueRange.startLine += lineOffset; - disabledProperty.valueRange.startColumn += columnOffset; - disabledProperty.valueRange.endLine += lineOffset; - disabledProperty.valueRange.endColumn += columnOffset; - rule.properties.push(disabledProperty); - } - } + var builder = new WebInspector.FormattedContentBuilder(indentString); + var lineEndings = text.computeLineEndings(); + try { + switch (mimeType) { + case "text/html": + formatMixedHTML(builder, text, lineEndings); break; - case FormatterWorker.CSSParserStates.PropertyName: - if (tokenValue === ":" && tokenType === UndefTokenType) { - property.name = property.name; - property.nameRange.endLine = lineNumber; - property.nameRange.endColumn = column; - property.valueRange = createRange(lineNumber, newColumn); - state = FormatterWorker.CSSParserStates.PropertyValue; - } else if (tokenType["property"]) { - property.name += tokenValue; - } + case "text/css": + var formatter = new WebInspector.CSSFormatter(builder); + formatter.format(text, lineEndings, 0, text.length); break; - case FormatterWorker.CSSParserStates.PropertyValue: - if ((tokenValue === ";" || tokenValue === "}") && tokenType === UndefTokenType) { - property.value = property.value; - property.valueRange.endLine = lineNumber; - property.valueRange.endColumn = column; - property.range.endLine = lineNumber; - property.range.endColumn = tokenValue === ";" ? newColumn : column; - rule.properties.push(property); - if (tokenValue === "}") { - rule.styleRange.endLine = lineNumber; - rule.styleRange.endColumn = column; - rules.push(rule); - state = FormatterWorker.CSSParserStates.Initial; - } else { - state = FormatterWorker.CSSParserStates.Style; - } - } else if (!tokenType["comment"]) { - property.value += tokenValue; - } + case "text/javascript": + var formatter = new WebInspector.JavaScriptFormatter(builder); + formatter.format(text, lineEndings, 0, text.length); break; default: - console.assert(false, "Unknown CSS parser state."); - } - processedChunkCharacters += newColumn - column; - if (processedChunkCharacters > chunkSize) { - chunkCallback({ chunk: rules, isLastChunk: false }); - rules = []; - processedChunkCharacters = 0; + var formatter = new WebInspector.IdentityFormatter(builder); + formatter.format(text, lineEndings, 0, text.length); } - } - var tokenizer = FormatterWorker.createTokenizer("text/css"); - var lineNumber; - for (lineNumber = 0; lineNumber < lines.length; ++lineNumber) { - var line = lines[lineNumber]; - tokenizer(line, processToken); - processToken("\n", null, line.length, line.length + 1); - } - chunkCallback({ chunk: rules, isLastChunk: true }); - - /** - * @return {!{startLine: number, startColumn: number, endLine: number, endColumn: number}} - */ - function createRange(lineNumber, columnNumber) - { - return { - startLine: lineNumber, - startColumn: columnNumber, - endLine: lineNumber, - endColumn: columnNumber - }; - } -} - -/** - * @param {string} content - * @param {!{original: !Array., formatted: !Array.}} mapping - * @param {number} offset - * @param {number} formattedOffset - * @param {string} indentString - * @return {string} - */ -FormatterWorker._formatScript = function(content, mapping, offset, formattedOffset, indentString) -{ - var formattedContent; - try { - var builder = new FormatterWorker.FormattedContentBuilder(mapping, offset, formattedOffset, indentString); - var formatter = new FormatterWorker.JavaScriptFormatter(content, builder); - formatter.format(); - formattedContent = builder.content(); + result.mapping = builder.mapping(); + result.content = builder.content(); } catch (e) { console.error(e); - formattedContent = content; - } - return formattedContent; -} - -/** - * @param {string} content - * @param {!{original: !Array., formatted: !Array.}} mapping - * @param {number} offset - * @param {number} formattedOffset - * @param {string} indentString - * @return {string} - */ -FormatterWorker._formatCSS = function(content, mapping, offset, formattedOffset, indentString) -{ - var formattedContent; - try { - var builder = new FormatterWorker.FormattedContentBuilder(mapping, offset, formattedOffset, indentString); - var formatter = new FormatterWorker.CSSFormatter(content, builder); - formatter.format(); - formattedContent = builder.content(); - } catch (e) { - formattedContent = content; + result.mapping = { original: [0], formatted: [0] }; + result.content = text; } - return formattedContent; + postMessage(result); } /** - * @constructor - * @param {string} indentString + * @param {!WebInspector.FormattedContentBuilder} builder + * @param {string} text + * @param {!Array} lineEndings */ -FormatterWorker.HTMLFormatter = function(indentString) +function formatMixedHTML(builder, text, lineEndings) { - this._indentString = indentString; -} - -FormatterWorker.HTMLFormatter.prototype = { - /** - * @param {string} content - * @return {!{content: string, mapping: {original: !Array., formatted: !Array.}}} - */ - format: function(content) - { - this.line = content; - this._content = content; - this._formattedContent = ""; - this._mapping = { original: [0], formatted: [0] }; - this._position = 0; - - var scriptOpened = false; - var styleOpened = false; - var tokenizer = FormatterWorker.createTokenizer("text/html"); - var accumulatedTokenValue = ""; - var accumulatedTokenStart = 0; - - /** - * @this {FormatterWorker.HTMLFormatter} - */ - function processToken(tokenValue, tokenType, tokenStart, tokenEnd) { - if (!tokenType) - return; - tokenType = tokenType.split(" ").keySet(); - if (!tokenType["tag"]) - return; - if (tokenType["bracket"] && (tokenValue === "<" || tokenValue === "") { - scriptOpened = false; - this._scriptStarted(tokenEnd); - } else if (accumulatedTokenValue === "") { - styleOpened = false; - this._styleStarted(tokenEnd); - } else if (accumulatedTokenValue === ", original: !Array.}, number, number, string)} formatFunction - * @param {number} cursor - */ - _handleSubFormatterEnd: function(formatFunction, cursor) - { - if (cursor === this._position) - return; - - var scriptContent = this._content.substring(this._position, cursor); - this._mapping.original.push(this._position); - this._mapping.formatted.push(this._formattedContent.length); - var formattedScriptContent = formatFunction(scriptContent, this._mapping, this._position, this._formattedContent.length, this._indentString); - - this._formattedContent += formattedScriptContent; - this._position = cursor; + var htmlFormatter = new WebInspector.HTMLFormatter(builder); + var jsFormatter = new WebInspector.JavaScriptFormatter(builder); + var cssFormatter = new WebInspector.CSSFormatter(builder); + var identityFormatter = new WebInspector.IdentityFormatter(builder); + + var offset = 0; + while (offset < text.length) { + var result = htmlFormatter.format(text, lineEndings, offset); + if (result.offset >= text.length) + break; + builder.addNewLine(); + var closeTag = " is found. -CodeMirror.defineMode("javascript", function(config, parserConfig) { - return { - token: function(stream, state) - { - return stream.next(); - } - } -}); diff --git a/front_end/formatter_worker/HTMLFormatter.js b/front_end/formatter_worker/HTMLFormatter.js new file mode 100644 index 0000000000..ab7f46a5c2 --- /dev/null +++ b/front_end/formatter_worker/HTMLFormatter.js @@ -0,0 +1,79 @@ +// Copyright 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. + +/** + * @constructor + * @param {!WebInspector.FormattedContentBuilder} builder + */ +WebInspector.HTMLFormatter = function(builder) +{ + this._builder = builder; +} + +/** + * @constructor + * @param {string} tagName + * @param {number} offset + */ +WebInspector.HTMLFormatter.Result = function(tagName, offset) +{ + this.tagName = tagName; + this.offset = offset; +} + +WebInspector.HTMLFormatter.prototype = { + /** + * @param {string} text + * @param {!Array} lineEndings + * @param {number} fromOffset + * @return {!WebInspector.HTMLFormatter.Result} + */ + format: function(text, lineEndings, fromOffset) + { + var content = text.substring(fromOffset); + var tagName = ""; + var accumulatedTokenValue = ""; + var lastOffset = fromOffset; + + /** + * @param {string} tokenValue + * @param {?string} type + * @param {number} tokenStart + * @param {number} tokenEnd + * @return {(!Object|undefined)} + * @this {WebInspector.HTMLFormatter} + */ + function processToken(tokenValue, type, tokenStart, tokenEnd) + { + tokenStart += fromOffset; + tokenEnd += fromOffset; + lastOffset = tokenEnd; + var startLine = lineEndings.lowerBound(tokenStart); + var endLine = lineEndings.lowerBound(tokenEnd); + this._builder.addToken(tokenValue, tokenStart, startLine, endLine); + + if (!type) + return; + var tokenType = type.split(" ").keySet(); + if (!tokenType["tag"]) + return; + + if (tokenType["bracket"] && (tokenValue === "<" || tokenValue === "") + return WebInspector.AbortTokenization; + + accumulatedTokenValue = accumulatedTokenValue + tokenValue.toLowerCase(); + if (accumulatedTokenValue === "} lineEndings + * @param {number} fromOffset + * @param {number} toOffset + */ + format: function(text, lineEndings, fromOffset, toOffset) + { + var content = text.substring(fromOffset, toOffset); + var startLine = lineEndings.lowerBound(fromOffset); + var endLine = lineEndings.lowerBound(toOffset); + this._builder.addToken(content, fromOffset, startLine, endLine); + } +} + diff --git a/front_end/formatter_worker/JavaScriptFormatter.js b/front_end/formatter_worker/JavaScriptFormatter.js index 2977cd69f7..7a8c611aa5 100644 --- a/front_end/formatter_worker/JavaScriptFormatter.js +++ b/front_end/formatter_worker/JavaScriptFormatter.js @@ -30,19 +30,27 @@ /** * @constructor - * @param {string} content - * @param {!FormatterWorker.FormattedContentBuilder} builder + * @param {!WebInspector.FormattedContentBuilder} builder */ -FormatterWorker.JavaScriptFormatter = function(content, builder) +WebInspector.JavaScriptFormatter = function(builder) { - this._content = content; this._builder = builder; - this._tokenizer = new WebInspector.AcornTokenizer(this._content); } -FormatterWorker.JavaScriptFormatter.prototype = { - format: function() +WebInspector.JavaScriptFormatter.prototype = { + /** + * @param {string} text + * @param {!Array} lineEndings + * @param {number} fromOffset + * @param {number} toOffset + */ + format: function(text, lineEndings, fromOffset, toOffset) { + this._lineOffset = lineEndings.lowerBound(fromOffset); + this._fromOffset = fromOffset; + this._toOffset = toOffset; + this._content = text.substring(this._fromOffset, this._toOffset); + this._tokenizer = new WebInspector.AcornTokenizer(this._content); var ast = acorn.parse(this._content, { ranges: false, ecmaVersion: 6 }); var walker = new WebInspector.ESTreeWalker(this._beforeVisit.bind(this), this._afterVisit.bind(this)); walker.walk(ast); @@ -66,7 +74,7 @@ FormatterWorker.JavaScriptFormatter.prototype = { else if (format[i] === "<") this._builder.decreaseNestingLevel(); else if (format[i] === "t") - this._builder.addToken(this._content.substring(token.start, token.end), token.start, this._tokenizer.tokenLineStart(), this._tokenizer.tokenLineEnd()); + this._builder.addToken(this._content.substring(token.start, token.end), this._fromOffset + token.start, this._lineOffset + this._tokenizer.tokenLineStart(), this._lineOffset + this._tokenizer.tokenLineEnd()); } }, diff --git a/front_end/formatter_worker/JavaScriptOutline.js b/front_end/formatter_worker/JavaScriptOutline.js new file mode 100644 index 0000000000..b709232c52 --- /dev/null +++ b/front_end/formatter_worker/JavaScriptOutline.js @@ -0,0 +1,86 @@ +// Copyright 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. + +/** + * @param {string} content + */ +WebInspector.javaScriptOutline = function(content) +{ + var chunkSize = 100000; // characters per data chunk + var outlineChunk = []; + var previousIdentifier = null; + var previousToken = null; + var processedChunkCharacters = 0; + var addedFunction = false; + var isReadingArguments = false; + var argumentsText = ""; + var currentFunction = null; + var tokenizer = new WebInspector.AcornTokenizer(content); + var AT = WebInspector.AcornTokenizer; + + while (tokenizer.peekToken()) { + var token = /** @type {!Acorn.TokenOrComment} */(tokenizer.nextToken()); + if (AT.lineComment(token) || AT.blockComment(token)) + continue; + + var tokenValue = content.substring(token.start, token.end); + + if (AT.identifier(token) && previousToken && (AT.identifier(previousToken, "get") || AT.identifier(previousToken, "set"))) { + currentFunction = { + line: tokenizer.tokenLineStart(), + column: tokenizer.tokenColumnStart(), + name : previousToken.value + " " + tokenValue + }; + addedFunction = true; + previousIdentifier = null; + } else if (AT.identifier(token)) { + previousIdentifier = tokenValue; + if (tokenValue && previousToken && AT.keyword(previousToken, "function")) { + // A named function: "function f...". + currentFunction = { + line: tokenizer.tokenLineStart(), + column: tokenizer.tokenColumnStart(), + name: tokenValue + }; + addedFunction = true; + previousIdentifier = null; + } + } else if (AT.keyword(token, "function") && previousIdentifier && previousToken && AT.punctuator(previousToken, ":=")) { + // Anonymous function assigned to an identifier: "...f = function..." + // or "funcName: function...". + currentFunction = { + line: tokenizer.tokenLineStart(), + column: tokenizer.tokenColumnStart(), + name: previousIdentifier + }; + addedFunction = true; + previousIdentifier = null; + } else if (AT.punctuator(token, ".") && previousToken && AT.identifier(previousToken)) + previousIdentifier += "."; + else if (AT.punctuator(token, "(") && addedFunction) + isReadingArguments = true; + if (isReadingArguments && tokenValue) + argumentsText += tokenValue; + + if (AT.punctuator(token, ")") && isReadingArguments) { + addedFunction = false; + isReadingArguments = false; + currentFunction.arguments = argumentsText.replace(/,[\r\n\s]*/g, ", ").replace(/([^,])[\r\n\s]+/g, "$1"); + argumentsText = ""; + outlineChunk.push(currentFunction); + } + + previousToken = token; + processedChunkCharacters += token.end - token.start; + + if (processedChunkCharacters >= chunkSize) { + postMessage({ chunk: outlineChunk, isLastChunk: false }); + outlineChunk = []; + processedChunkCharacters = 0; + } + } + + postMessage({ chunk: outlineChunk, isLastChunk: true }); +} + diff --git a/front_end/formatter_worker/module.json b/front_end/formatter_worker/module.json index 760d0e6027..0a2036a023 100644 --- a/front_end/formatter_worker/module.json +++ b/front_end/formatter_worker/module.json @@ -4,7 +4,6 @@ "../cm/headlesscodemirror.js", "../cm/css.js", "../cm/xml.js", - "../cm/htmlmixed.js", "../common/WebInspector.js", "../es_tree/ESTreeWalker.js", "FormatterWorker.js", @@ -12,13 +11,16 @@ "CSSFormatter.js", "../es_tree/AcornTokenizer.js", "JavaScriptFormatter.js", - "FormattedContentBuilder.js" + "FormattedContentBuilder.js", + "CSSRuleParser.js", + "HTMLFormatter.js", + "IdentityFormatter.js", + "JavaScriptOutline.js" ], "skip_compilation": [ "../acorn/acorn.js", "../cm/headlesscodemirror.js", "../cm/css.js", - "../cm/xml.js", - "../cm/htmlmixed.js" + "../cm/xml.js" ] } diff --git a/front_end/host/InspectorFrontendHost.js b/front_end/host/InspectorFrontendHost.js index 5fc21beba9..7cb296f9df 100644 --- a/front_end/host/InspectorFrontendHost.js +++ b/front_end/host/InspectorFrontendHost.js @@ -381,6 +381,13 @@ WebInspector.InspectorFrontendHostStub.prototype = { return false; }, + /** + * @override + */ + readyForTest: function() + { + }, + /** * @override * @param {boolean} discoverUsbDevices @@ -436,14 +443,6 @@ WebInspector.InspectorFrontendHostStub.prototype = { isHostedMode: function() { return true; - }, - - /** - * @override - * @param {string} message - */ - sendFrontendAPINotification: function(message) - { } }; diff --git a/front_end/host/InspectorFrontendHostAPI.js b/front_end/host/InspectorFrontendHostAPI.js index 738c61b677..98f2717648 100644 --- a/front_end/host/InspectorFrontendHostAPI.js +++ b/front_end/host/InspectorFrontendHostAPI.js @@ -38,14 +38,12 @@ InspectorFrontendHostAPI.Events = { DevicesUpdated: "devicesUpdated", DispatchMessage: "dispatchMessage", DispatchMessageChunk: "dispatchMessageChunk", - DispatchFrontendAPIMessage: "dispatchFrontendAPIMessage", EnterInspectElementMode: "enterInspectElementMode", + EvaluateForTestInFrontend: "evaluateForTestInFrontend", FileSystemsLoaded: "fileSystemsLoaded", FileSystemRemoved: "fileSystemRemoved", FileSystemAdded: "fileSystemAdded", FileSystemFilesChanged: "fileSystemFilesChanged", - FrontendAPIAttached: "frontendAPIAttached", - FrontendAPIDetached: "frontendAPIDetached", IndexingTotalWorkCalculated: "indexingTotalWorkCalculated", IndexingWorked: "indexingWorked", IndexingDone: "indexingDone", @@ -71,14 +69,12 @@ InspectorFrontendHostAPI.EventDescriptors = [ [InspectorFrontendHostAPI.Events.DevicesUpdated, ["devices"]], [InspectorFrontendHostAPI.Events.DispatchMessage, ["messageObject"]], [InspectorFrontendHostAPI.Events.DispatchMessageChunk, ["messageChunk", "messageSize"]], - [InspectorFrontendHostAPI.Events.DispatchFrontendAPIMessage, ["messageObject"]], [InspectorFrontendHostAPI.Events.EnterInspectElementMode, []], + [InspectorFrontendHostAPI.Events.EvaluateForTestInFrontend, ["callId", "script"]], [InspectorFrontendHostAPI.Events.FileSystemsLoaded, ["fileSystems"]], [InspectorFrontendHostAPI.Events.FileSystemRemoved, ["fileSystemPath"]], [InspectorFrontendHostAPI.Events.FileSystemAdded, ["errorMessage", "fileSystem"]], [InspectorFrontendHostAPI.Events.FileSystemFilesChanged, ["paths"]], - [InspectorFrontendHostAPI.Events.FrontendAPIAttached, ["frontendAPIAttached"]], - [InspectorFrontendHostAPI.Events.FrontendAPIDetached, ["frontendAPIDetached"]], [InspectorFrontendHostAPI.Events.IndexingTotalWorkCalculated, ["requestId", "fileSystemPath", "totalWork"]], [InspectorFrontendHostAPI.Events.IndexingWorked, ["requestId", "fileSystemPath", "worked"]], [InspectorFrontendHostAPI.Events.IndexingDone, ["requestId", "fileSystemPath"]], @@ -292,13 +288,10 @@ InspectorFrontendHostAPI.prototype = { */ isUnderTest: function() { }, - /** - * @return {boolean} - */ - isHostedMode: function() { }, + readyForTest: function() { }, /** - * @param {string} message + * @return {boolean} */ - sendFrontendAPINotification: function(message) { } + isHostedMode: function() { } } diff --git a/front_end/main/FrontendWebSocketAPI.js b/front_end/main/FrontendWebSocketAPI.js deleted file mode 100644 index e67bcf237c..0000000000 --- a/front_end/main/FrontendWebSocketAPI.js +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2015 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. - -/** - * @constructor - * @implements {WebInspector.Linkifier.LinkHandler} - */ -WebInspector.FrontendWebSocketAPI = function() -{ - InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DispatchFrontendAPIMessage, this._onFrontendAPIMessage, this); - InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.FrontendAPIAttached, this._onAttach, this); - InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.FrontendAPIDetached, this._onDetach, this); -} - -WebInspector.FrontendWebSocketAPI.prototype = { - _onAttach: function() - { - WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.WorkingCopyCommittedByUser, this._workingCopyCommitted, this); - WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.WorkingCopyChanged, this._workingCopyChanged, this); - WebInspector.Linkifier.setLinkHandler(this); - }, - - _onDetach: function() - { - WebInspector.workspace.removeEventListener(WebInspector.Workspace.Events.WorkingCopyCommittedByUser, this._workingCopyCommitted, this); - WebInspector.workspace.removeEventListener(WebInspector.Workspace.Events.WorkingCopyChanged, this._workingCopyChanged, this); - WebInspector.Linkifier.setLinkHandler(null); - }, - - /** - * @override - * @param {string} url - * @param {number=} lineNumber - * @return {boolean} - */ - handleLink: function(url, lineNumber) - { - var uiSourceCode = WebInspector.networkMapping.uiSourceCodeForURLForAnyTarget(url); - if (uiSourceCode) - url = uiSourceCode.url(); - if (url.startsWith("file://")) { - var file = url.substring(7); - this._issueFrontendAPINotification("Frontend.revealLocation", { file: file, line: lineNumber }); - return true; - } - return false; - }, - - /** - * @param {!WebInspector.Event} event - */ - _onFrontendAPIMessage: function(event) - { - var message = JSON.parse(/** @type {string} */ (event.data)); - this._dispatchFrontendAPIMessage(message["id"], message["method"], message["params"] || null); - }, - - /** - * @param {number} id - * @param {string} method - * @param {?Object} params - */ - _dispatchFrontendAPIMessage: function(id, method, params) - { - this._dispatchingFrontendMessage = true; - switch (method) { - case "Frontend.updateBuffer": - var file = params["file"]; - var buffer = params["buffer"]; - var saved = params["saved"]; - var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL("file://" + file); - if (uiSourceCode) { - if (buffer !== uiSourceCode.workingCopy()) - uiSourceCode.setWorkingCopy(buffer); - if (saved) - uiSourceCode.checkContentUpdated(true); - } - break; - default: - WebInspector.console.log("Unhandled API message: " + method); - } - this._issueResponse(id); - this._dispatchingFrontendMessage = false; - }, - - /** - * @param {!WebInspector.Event} event - * @param {boolean=} saved - */ - _workingCopyChanged: function(event, saved) - { - if (this._dispatchingFrontendMessage) - return; - var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data["uiSourceCode"]); - var url = uiSourceCode.url(); - if (url.startsWith("file://")) - url = url.substring(7); - var params = { file: url, buffer: uiSourceCode.workingCopy() }; - if (saved) - params.saved = true; - this._issueFrontendAPINotification("Frontend.bufferUpdated", params); - }, - - /** - * @param {!WebInspector.Event} event - */ - _workingCopyCommitted: function(event) - { - this._workingCopyChanged(event, true); - }, - - /** - * @param {number} id - * @param {!Object=} params - */ - _issueResponse: function(id, params) - { - var object = {id: id}; - if (params) - object.params = params; - InspectorFrontendHost.sendFrontendAPINotification(JSON.stringify(object)); - }, - - /** - * @param {string} method - * @param {?Object} params - */ - _issueFrontendAPINotification: function(method, params) - { - InspectorFrontendHost.sendFrontendAPINotification(JSON.stringify({ method: method, params: params })); - } -} diff --git a/front_end/main/Main.js b/front_end/main/Main.js index 8a767d5032..b67f01d7f5 100644 --- a/front_end/main/Main.js +++ b/front_end/main/Main.js @@ -58,7 +58,6 @@ WebInspector.Main.prototype = { if (InspectorFrontendHost.isUnderTest()) self.runtime.useTestBase(); InspectorFrontendHost.getPreferences(this._gotPreferences.bind(this)); - new WebInspector.FrontendWebSocketAPI(); }, /** @@ -118,6 +117,7 @@ WebInspector.Main.prototype = { Runtime.experiments.register("networkRequestsOnTimeline", "Network requests on Timeline", true); Runtime.experiments.register("privateScriptInspection", "Private script inspection"); Runtime.experiments.register("promiseTracker", "Promise inspector"); + Runtime.experiments.register("reducedIndentation", "Reduced indentation in Elements DOM tree"); Runtime.experiments.register("requestBlocking", "Request blocking", true); Runtime.experiments.register("resolveVariableNames", "Resolve variable names", true); Runtime.experiments.register("timelineShowAllEvents", "Show all events on Timeline", true); @@ -318,22 +318,20 @@ WebInspector.Main.prototype = { this._mainTarget.registerInspectorDispatcher(this); InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.ReloadInspectedPage, this._reloadInspectedPage, this); + InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.EvaluateForTestInFrontend, this._evaluateForTestInFrontend, this); if (this._mainTarget.isServiceWorker() || this._mainTarget.isPage()) this._mainTarget.runtimeAgent().run(); - this._mainTarget.inspectorAgent().enable(inspectorAgentEnableCallback); + this._mainTarget.inspectorAgent().enable(); + InspectorFrontendHost.readyForTest(); - function inspectorAgentEnableCallback() - { - console.timeStamp("Main.inspectorAgentEnableCallback"); - WebInspector.notifications.dispatchEventToListeners(WebInspector.NotificationService.Events.InspectorAgentEnabledForTests); - // Asynchronously run the extensions. - setTimeout(lateInitialization, 0); - } + // Asynchronously run the extensions. + setTimeout(lateInitialization, 0); function lateInitialization() { + console.timeStamp("Main.lateInitialization"); WebInspector.extensionServer.initializeExtensions(); } }, @@ -611,15 +609,16 @@ WebInspector.Main.prototype = { }, /** - * @override - * @param {number} callId - * @param {string} script + * @param {!WebInspector.Event} event */ - evaluateForTestInFrontend: function(callId, script) + _evaluateForTestInFrontend: function(event) { if (!InspectorFrontendHost.isUnderTest()) return; + var callId = /** @type {number} */ (event.data["callId"]); + var script = /** @type {number} */ (event.data["script"]); + /** * @suppressGlobalPropertiesCheck */ diff --git a/front_end/main/module.json b/front_end/main/module.json index 3244c6a9a5..dfccf28766 100644 --- a/front_end/main/module.json +++ b/front_end/main/module.json @@ -352,17 +352,12 @@ "sdk" ], "scripts": [ - "FrontendWebSocketAPI.js", "RenderingOptions.js", "SimpleApp.js", - "Tests.js", "OverlayController.js", "Connections.js", "Main.js" ], - "skip_compilation": [ - "Tests.js" - ], "resources": [ "errorWarningCounter.css", "remoteDebuggingTerminatedScreen.css", diff --git a/front_end/sass/ASTSourceMap.js b/front_end/sass/ASTSourceMap.js index b76948921d..fdfe2fd18b 100644 --- a/front_end/sass/ASTSourceMap.js +++ b/front_end/sass/ASTSourceMap.js @@ -4,121 +4,120 @@ /** * @constructor - * @param {string} cssURL + * @implements {WebInspector.SourceMap} + * @param {string} compiledURL + * @param {string} sourceMapURL * @param {!Map} models + * @param {?function(!WebInspector.ASTSourceMap, !Array, !Array):!Promise} editCallback */ -WebInspector.ASTSourceMap = function(cssURL, models) +WebInspector.ASTSourceMap = function(compiledURL, sourceMapURL, models, editCallback) { - this._cssURL = cssURL; + this._editCallback = editCallback; + this._compiledURL = compiledURL; + this._sourceMapURL = sourceMapURL; /** @type {!Map} */ this._models = models; /** @type {!Map} */ - this._cssToSass = new Map(); + this._compiledToSource = new Map(); /** @type {!Multimap} */ - this._sassToCss = new Multimap(); + this._sourceToCompiled = new Multimap(); } -/** - * @param {!WebInspector.ASTService} astService - * @param {!WebInspector.CSSModel} cssModel - * @param {!WebInspector.SourceMap} sourceMap - * @return {!Promise} - */ -WebInspector.ASTSourceMap.fromSourceMap = function(astService, cssModel, sourceMap) -{ - var headerIds = cssModel.styleSheetIdsForURL(sourceMap.compiledURL()); - if (!headerIds || !headerIds.length) - return Promise.resolve(/** @type {?WebInspector.ASTSourceMap} */(null)); - var header = cssModel.styleSheetHeaderForId(headerIds[0]); +WebInspector.ASTSourceMap.prototype = { + /** + * @override + * @return {string} + */ + compiledURL: function() + { + return this._compiledURL; + }, - /** @type {!Map} */ - var models = new Map(); - var promises = []; - for (var url of sourceMap.sources()) { - var contentProvider = sourceMap.sourceContentProvider(url, WebInspector.resourceTypes.SourceMapStyleSheet); - var sassPromise = contentProvider.requestContent() - .then(onSCSSText.bind(null, url)) - .then(ast => models.set(ast.document.url, ast)); - promises.push(sassPromise); - } - var cssURL = sourceMap.compiledURL(); - var cssPromise = header.requestContent() - .then(text => astService.parseCSS(cssURL, text || "")) - .then(ast => models.set(ast.document.url, ast)); - promises.push(cssPromise); + /** + * @override + * @return {string} + */ + url: function() + { + return this._sourceMapURL; + }, - return Promise.all(promises) - .then(() => onParsed(cssURL, models, sourceMap)) - .catchException(/** @type {?WebInspector.ASTSourceMap} */(null)); + /** + * @override + * @return {!Array} + */ + sourceURLs: function() + { + return this._models.keysArray().filter(url => url !== this._compiledURL); + }, /** - * @param {string} url - * @param {?string} text - * @return {!Promise} + * @override + * @param {string} sourceURL + * @param {!WebInspector.ResourceType} contentType + * @return {!WebInspector.ContentProvider} */ - function onSCSSText(url, text) + sourceContentProvider: function(sourceURL, contentType) { - return astService.parseSCSS(url, text || ""); - } + var model = this.modelForURL(sourceURL); + var sourceContent = model ? model.document.text.value() : ""; + return new WebInspector.StaticContentProvider(contentType, sourceContent); + }, /** - * @param {string} cssURL - * @param {!Map} models - * @param {!WebInspector.SourceMap} sourceMap - * @return {!WebInspector.ASTSourceMap} - */ - function onParsed(cssURL, models, sourceMap) - { - var map = new WebInspector.ASTSourceMap(cssURL, models); - //FIXME: this works O(N^2). - map.cssAST().visit(onNode); - return map; - - /** - * @param {!WebInspector.SASSSupport.Node} cssNode - */ - function onNode(cssNode) - { - if (!(cssNode instanceof WebInspector.SASSSupport.TextNode)) - return; - var entry = sourceMap.findEntry(cssNode.range.endLine, cssNode.range.endColumn); - if (!entry || !entry.sourceURL || typeof entry.sourceLineNumber === "undefined" || typeof entry.sourceColumnNumber === "undefined") - return; - var sassAST = models.get(entry.sourceURL); - if (!sassAST) - return; - var sassNode = sassAST.findNodeForPosition(entry.sourceLineNumber, entry.sourceColumnNumber); - if (sassNode) - map.mapCssToSass(cssNode, sassNode); - } - } -} + * @override + * @param {number} lineNumber + * @param {number=} columnNumber + * @return {?WebInspector.SourceMapEntry} + */ + findEntry: function(lineNumber, columnNumber) + { + columnNumber = columnNumber || 0; + var compiledNode = this.compiledModel().findNodeForPosition(lineNumber, columnNumber); + if (!compiledNode) + return null; + var sourceNode = this.toSourceNode(compiledNode); + if (!sourceNode) + return null; + return new WebInspector.SourceMapEntry(lineNumber, columnNumber, sourceNode.document.url, sourceNode.range.startLine, sourceNode.range.startColumn); + }, -WebInspector.ASTSourceMap.prototype = { /** - * @return {string} + * @override + * @return {boolean} */ - cssURL: function() + editable: function() { - return this._cssURL; + return !!this._editCallback; + }, + + /** + * @override + * @param {!Array} ranges + * @param {!Array} texts + * @return {!Promise} + */ + editCompiled: function(ranges, texts) + { + return this._editCallback.call(null, this, ranges, texts); }, /** * @return {!WebInspector.SASSSupport.AST} */ - cssAST: function() + compiledModel: function() { - return /** @type {!WebInspector.SASSSupport.AST} */(this._models.get(this._cssURL)); + return /** @type {!WebInspector.SASSSupport.AST} */(this._models.get(this._compiledURL)); }, /** * @return {!Map} */ - sassModels: function() + sourceModels: function() { - var sassModels = /** @type {!Map} */(new Map(this._models)); - sassModels.delete(this._cssURL); - return sassModels; + var sourceModels = /** @type {!Map} */(new Map(this._models)); + sourceModels.delete(this._compiledURL); + return sourceModels; }, /** @@ -139,61 +138,42 @@ WebInspector.ASTSourceMap.prototype = { }, /** - * @param {!WebInspector.SASSSupport.TextNode} css - * @param {!WebInspector.SASSSupport.TextNode} sass + * @param {!WebInspector.SASSSupport.TextNode} compiled + * @param {!WebInspector.SASSSupport.TextNode} source */ - mapCssToSass: function(css, sass) + addMapping: function(compiled, source) { - this._cssToSass.set(css, sass); - this._sassToCss.set(sass, css); + this._compiledToSource.set(compiled, source); + this._sourceToCompiled.set(source, compiled); }, /** - * @param {!WebInspector.SASSSupport.TextNode} css - * @param {!WebInspector.SASSSupport.TextNode} sass + * @param {!WebInspector.SASSSupport.TextNode} compiled + * @param {!WebInspector.SASSSupport.TextNode} source */ - unmapCssFromSass: function(css, sass) + removeMapping: function(compiled, source) { - this._cssToSass.delete(css); - this._sassToCss.remove(sass, css); + this._compiledToSource.delete(compiled); + this._sourceToCompiled.remove(source, compiled); }, /** - * @param {!WebInspector.SASSSupport.TextNode} css + * @param {!WebInspector.SASSSupport.TextNode} compiled * @return {?WebInspector.SASSSupport.TextNode} */ - toSASSNode: function(css) + toSourceNode: function(compiled) { - return this._cssToSass.get(css) || null; + return this._compiledToSource.get(compiled) || null; }, /** - * @param {!WebInspector.SASSSupport.TextNode} sass + * @param {!WebInspector.SASSSupport.TextNode} source * @return {!Array} */ - toCSSNodes: function(sass) - { - var cssNodes = this._sassToCss.get(sass); - return cssNodes ? cssNodes.valuesArray() : []; - }, - - /** - * @param {!WebInspector.SASSSupport.Property} cssProperty - * @return {?WebInspector.SASSSupport.Property} - */ - toSASSProperty: function(cssProperty) - { - var sassName = this._cssToSass.get(cssProperty.name); - return sassName ? sassName.parent : null; - }, - - /** - * @param {!WebInspector.SASSSupport.Property} sassProperty - * @return {!Array} - */ - toCSSProperties: function(sassProperty) + toCompiledNodes: function(source) { - return this.toCSSNodes(sassProperty.name).map(name => name.parent); + var compiledNodes = this._sourceToCompiled.get(source); + return compiledNodes ? compiledNodes.valuesArray() : []; }, /** @@ -214,34 +194,15 @@ WebInspector.ASTSourceMap.prototype = { models.set(newAST.document.url, newAST); } - var newMap = new WebInspector.ASTSourceMap(this._cssURL, models); - var cssNodes = this._cssToSass.keysArray(); - for (var i = 0; i < cssNodes.length; ++i) { - var cssNode = cssNodes[i]; - var sassNode = /** @type {!WebInspector.SASSSupport.TextNode} */(this._cssToSass.get(cssNode)); - var mappedCSSNode = /** @type {!WebInspector.SASSSupport.TextNode} */(outNodeMapping.get(cssNode) || cssNode); - var mappedSASSNode = /** @type {!WebInspector.SASSSupport.TextNode} */(outNodeMapping.get(sassNode) || sassNode); - newMap.mapCssToSass(mappedCSSNode, mappedSASSNode); + var newMap = new WebInspector.ASTSourceMap(this._compiledURL, this._sourceMapURL, models, this._editCallback); + var compiledNodes = this._compiledToSource.keysArray(); + for (var i = 0; i < compiledNodes.length; ++i) { + var compiledNode = compiledNodes[i]; + var sourceNode = /** @type {!WebInspector.SASSSupport.TextNode} */(this._compiledToSource.get(compiledNode)); + var mappedCompiledNode = /** @type {!WebInspector.SASSSupport.TextNode} */(outNodeMapping.get(compiledNode) || compiledNode); + var mappedSourceNode = /** @type {!WebInspector.SASSSupport.TextNode} */(outNodeMapping.get(sourceNode) || sourceNode); + newMap.addMapping(mappedCompiledNode, mappedSourceNode); } return newMap; - }, - - /** - * @return {boolean} - */ - isValid: function() - { - var cssNodes = this._cssToSass.keysArray(); - for (var i = 0; i < cssNodes.length; ++i) { - var cssNode = cssNodes[i]; - if (!cssNode.parent || !(cssNode.parent instanceof WebInspector.SASSSupport.Property)) - continue; - if (cssNode !== cssNode.parent.name) - continue; - var sassNode = this._cssToSass.get(cssNode); - if (sassNode && cssNode.text.trim() !== sassNode.text.trim()) - return false; - } - return true; } } diff --git a/front_end/sass/SASSProcessor.js b/front_end/sass/SASSProcessor.js index 02671e7cce..a747f0c431 100644 --- a/front_end/sass/SASSProcessor.js +++ b/front_end/sass/SASSProcessor.js @@ -17,7 +17,7 @@ WebInspector.SASSProcessor = function(astService, map, editOperations) WebInspector.SASSProcessor.prototype = { /** - * @return {!Promise} + * @return {!Promise} */ _mutate: function() { @@ -34,7 +34,7 @@ WebInspector.SASSProcessor.prototype = { if (!ast.document.hasChanged()) continue; var promise; - if (ast.document.url === this._map.cssURL()) + if (ast.document.url === this._map.compiledURL()) promise = this._astService.parseCSS(ast.document.url, ast.document.newText().value()); else promise = this._astService.parseSCSS(ast.document.url, ast.document.newText().value()); @@ -48,7 +48,7 @@ WebInspector.SASSProcessor.prototype = { /** * @param {!Set} changedCSSRules * @param {!Array} changedModels - * @return {?WebInspector.SASSProcessor.Result} + * @return {?WebInspector.SourceMap.EditResult} */ _onFinished: function(changedCSSRules, changedModels) { @@ -68,25 +68,33 @@ WebInspector.SASSProcessor.prototype = { /** @type {!Map} */ var newSASSSources = new Map(); for (var model of changedModels) { - if (model.document.url === map.cssURL()) + if (model.document.url === map.compiledURL()) continue; newSASSSources.set(model.document.url, model.document.text.value()); } - return new WebInspector.SASSProcessor.Result(map, cssEdits, newSASSSources); + return new WebInspector.SourceMap.EditResult(map, cssEdits, newSASSSources); } } /** - * @constructor * @param {!WebInspector.ASTSourceMap} map - * @param {!Array} cssEdits - * @param {!Map} newSASSSources + * @param {!WebInspector.SASSSupport.Property} cssProperty + * @return {?WebInspector.SASSSupport.Property} */ -WebInspector.SASSProcessor.Result = function(map, cssEdits, newSASSSources) +WebInspector.SASSProcessor._toSASSProperty = function(map, cssProperty) { - this.map = map; - this.cssEdits = cssEdits; - this.newSASSSources = newSASSSources; + var sassName = map.toSourceNode(cssProperty.name); + return sassName ? sassName.parent : null; +} + +/** + * @param {!WebInspector.ASTSourceMap} map + * @param {!WebInspector.SASSSupport.Property} sassProperty + * @return {!Array} + */ +WebInspector.SASSProcessor._toCSSProperties = function(map, sassProperty) +{ + return map.toCompiledNodes(sassProperty.name).map(name => name.parent); } /** @@ -94,13 +102,13 @@ WebInspector.SASSProcessor.Result = function(map, cssEdits, newSASSSources) * @param {!WebInspector.ASTSourceMap} map * @param {!Array} ranges * @param {!Array} newTexts - * @return {!Promise} + * @return {!Promise} */ WebInspector.SASSProcessor.processCSSEdits = function(astService, map, ranges, newTexts) { console.assert(ranges.length === newTexts.length); - var cssURL = map.cssURL(); - var cssText = map.cssAST().document.text; + var cssURL = map.compiledURL(); + var cssText = map.compiledModel().document.text; for (var i = 0; i < ranges.length; ++i) cssText = new WebInspector.Text(cssText.replaceRange(ranges[i], newTexts[i])); return astService.parseCSS(cssURL, cssText.value()) @@ -108,17 +116,17 @@ WebInspector.SASSProcessor.processCSSEdits = function(astService, map, ranges, n /** * @param {!WebInspector.SASSSupport.AST} newCSSAST - * @return {!Promise} + * @return {!Promise} */ function onCSSParsed(newCSSAST) { //TODO(lushnikov): only diff changed styles. - var cssDiff = WebInspector.SASSSupport.diffModels(map.cssAST(), newCSSAST); + var cssDiff = WebInspector.SASSSupport.diffModels(map.compiledModel(), newCSSAST); var edits = WebInspector.SASSProcessor._editsFromCSSDiff(cssDiff, map); // Determine AST trees which will change and clone them. var changedURLs = new Set(edits.map(edit => edit.sassURL)); - changedURLs.add(map.cssURL()); + changedURLs.add(map.compiledURL()); var clonedModels = []; for (var url of changedURLs) clonedModels.push(map.modelForURL(url).clone()); @@ -235,10 +243,10 @@ WebInspector.SASSProcessor.SetTextOperation.fromCSSChange = function(change, map var sassNode = null; if (change.type === WebInspector.SASSSupport.PropertyChangeType.NameChanged) { newValue = newProperty.name.text; - sassNode = map.toSASSNode(oldProperty.name); + sassNode = map.toSourceNode(oldProperty.name); } else { newValue = newProperty.value.text; - sassNode = map.toSASSNode(oldProperty.value); + sassNode = map.toSourceNode(oldProperty.value); } if (!sassNode) return null; @@ -265,7 +273,7 @@ WebInspector.SASSProcessor.SetTextOperation.prototype = { perform: function() { this._sassNode.setText(this._newText); - var nodes = this.map.toCSSNodes(this._sassNode); + var nodes = this.map.toCompiledNodes(this._sassNode); for (var node of nodes) node.setText(this._newText); @@ -311,7 +319,7 @@ WebInspector.SASSProcessor.TogglePropertyOperation.fromCSSChange = function(chan { var oldCSSProperty = /** @type {!WebInspector.SASSSupport.Property} */(change.oldProperty()); console.assert(oldCSSProperty, "TogglePropertyOperation must have old CSS property"); - var sassProperty = map.toSASSProperty(oldCSSProperty); + var sassProperty = WebInspector.SASSProcessor._toSASSProperty(map, oldCSSProperty); if (!sassProperty) return null; var newDisabled = change.newProperty().disabled; @@ -338,7 +346,7 @@ WebInspector.SASSProcessor.TogglePropertyOperation.prototype = { perform: function() { this._sassProperty.setDisabled(this._newDisabled); - var cssProperties = this.map.toCSSProperties(this._sassProperty); + var cssProperties = WebInspector.SASSProcessor._toCSSProperties(this.map, this._sassProperty); for (var property of cssProperties) property.setDisabled(this._newDisabled); @@ -382,7 +390,7 @@ WebInspector.SASSProcessor.RemovePropertyOperation.fromCSSChange = function(chan { var removedProperty = /** @type {!WebInspector.SASSSupport.Property} */(change.oldProperty()); console.assert(removedProperty, "RemovePropertyOperation must have removed CSS property"); - var sassProperty = map.toSASSProperty(removedProperty); + var sassProperty = WebInspector.SASSProcessor._toSASSProperty(map, removedProperty); if (!sassProperty) return null; return new WebInspector.SASSProcessor.RemovePropertyOperation(map, sassProperty); @@ -407,13 +415,13 @@ WebInspector.SASSProcessor.RemovePropertyOperation.prototype = { */ perform: function() { - var cssProperties = this.map.toCSSProperties(this._sassProperty); + var cssProperties = WebInspector.SASSProcessor._toCSSProperties(this.map, this._sassProperty); var cssRules = cssProperties.map(property => property.parent); this._sassProperty.remove(); for (var cssProperty of cssProperties) { cssProperty.remove(); - this.map.unmapCssFromSass(cssProperty.name, this._sassProperty.name); - this.map.unmapCssFromSass(cssProperty.value, this._sassProperty.value); + this.map.removeMapping(cssProperty.name, this._sassProperty.name); + this.map.removeMapping(cssProperty.value, this._sassProperty.value); } return cssRules; @@ -467,11 +475,11 @@ WebInspector.SASSProcessor.InsertPropertiesOperation.fromCSSChange = function(ch var sassAnchor = null; if (change.oldPropertyIndex) { cssAnchor = change.oldRule.properties[change.oldPropertyIndex - 1].name; - sassAnchor = map.toSASSNode(cssAnchor); + sassAnchor = map.toSourceNode(cssAnchor); } else { insertBefore = true; cssAnchor = change.oldRule.properties[0].name; - sassAnchor = map.toSASSNode(cssAnchor); + sassAnchor = map.toSourceNode(cssAnchor); } if (!sassAnchor) return null; @@ -516,14 +524,14 @@ WebInspector.SASSProcessor.InsertPropertiesOperation.prototype = { var cssRules = []; var sassRule = this._sassAnchor.parent; var newSASSProperties = sassRule.insertProperties(this._nameTexts, this._valueTexts, this._disabledStates, this._sassAnchor, this._insertBefore); - var cssAnchors = this.map.toCSSProperties(this._sassAnchor); + var cssAnchors = WebInspector.SASSProcessor._toCSSProperties(this.map, this._sassAnchor); for (var cssAnchor of cssAnchors) { var cssRule = cssAnchor.parent; cssRules.push(cssRule); var newCSSProperties = cssRule.insertProperties(this._nameTexts, this._valueTexts, this._disabledStates, cssAnchor, this._insertBefore); for (var i = 0; i < newCSSProperties.length; ++i) { - this.map.mapCssToSass(newCSSProperties[i].name, newSASSProperties[i].name); - this.map.mapCssToSass(newCSSProperties[i].value, newSASSProperties[i].value); + this.map.addMapping(newCSSProperties[i].name, newSASSProperties[i].name); + this.map.addMapping(newCSSProperties[i].value, newSASSProperties[i].value); } } return cssRules; diff --git a/front_end/sass/SASSSourceMapFactory.js b/front_end/sass/SASSSourceMapFactory.js new file mode 100644 index 0000000000..32f74761f2 --- /dev/null +++ b/front_end/sass/SASSSourceMapFactory.js @@ -0,0 +1,89 @@ +// Copyright 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. + +/** + * @constructor + * @implements {WebInspector.SourceMapFactory} + */ +WebInspector.SASSSourceMapFactory = function() +{ + this._astService = new WebInspector.ASTService(); +} + +WebInspector.SASSSourceMapFactory.prototype = { + /** + * @override + * @param {!WebInspector.Target} target + * @param {!WebInspector.SourceMap} sourceMap + * @return {!Promise} + */ + editableSourceMap: function(target, sourceMap) + { + var cssModel = WebInspector.CSSModel.fromTarget(target); + if (!cssModel) + return Promise.resolve(/** @type {?WebInspector.SourceMap} */(null)); + + var headerIds = cssModel.styleSheetIdsForURL(sourceMap.compiledURL()); + if (!headerIds || !headerIds.length) + return Promise.resolve(/** @type {?WebInspector.SourceMap} */(null)); + var header = cssModel.styleSheetHeaderForId(headerIds[0]); + + /** @type {!Map} */ + var models = new Map(); + var promises = []; + for (let url of sourceMap.sourceURLs()) { + var contentProvider = sourceMap.sourceContentProvider(url, WebInspector.resourceTypes.SourceMapStyleSheet); + var sassPromise = contentProvider.requestContent() + .then(text => this._astService.parseSCSS(url, text || "")) + .then(ast => models.set(ast.document.url, ast)); + promises.push(sassPromise); + } + var cssURL = sourceMap.compiledURL(); + var cssPromise = header.requestContent() + .then(text => this._astService.parseCSS(cssURL, text || "")) + .then(ast => models.set(ast.document.url, ast)); + promises.push(cssPromise); + + return Promise.all(promises) + .then(this._onSourcesParsed.bind(this, sourceMap, models)) + .catchException(/** @type {?WebInspector.SourceMap} */(null)); + }, + + /** + * @param {!WebInspector.SourceMap} sourceMap + * @param {!Map} models + * @return {?WebInspector.SourceMap} + */ + _onSourcesParsed: function(sourceMap, models) + { + var editCallback = WebInspector.SASSProcessor.processCSSEdits.bind(WebInspector.SASSProcessor, this._astService); + var map = new WebInspector.ASTSourceMap(sourceMap.compiledURL(), sourceMap.url(), models, editCallback); + //FIXME: this works O(N^2). + var valid = true; + map.compiledModel().visit(onNode); + return valid ? map : null; + + /** + * @param {!WebInspector.SASSSupport.Node} cssNode + */ + function onNode(cssNode) + { + if (!(cssNode instanceof WebInspector.SASSSupport.TextNode)) + return; + var entry = sourceMap.findEntry(cssNode.range.endLine, cssNode.range.endColumn); + if (!entry || !entry.sourceURL || typeof entry.sourceLineNumber === "undefined" || typeof entry.sourceColumnNumber === "undefined") + return; + var sassAST = models.get(entry.sourceURL); + if (!sassAST) + return; + var sassNode = sassAST.findNodeForPosition(entry.sourceLineNumber, entry.sourceColumnNumber); + if (!sassNode) + return; + if (cssNode.parent && (cssNode.parent instanceof WebInspector.SASSSupport.Property) && cssNode === cssNode.parent.name) + valid = valid && cssNode.text.trim() === sassNode.text.trim(); + map.addMapping(cssNode, sassNode); + } + }, +} + diff --git a/front_end/sass/module.json b/front_end/sass/module.json index 317d348fab..dc9deb47ad 100644 --- a/front_end/sass/module.json +++ b/front_end/sass/module.json @@ -4,6 +4,7 @@ "SASSSupport.js", "ASTService.js", "SASSProcessor.js", - "ASTSourceMap.js" + "ASTSourceMap.js", + "SASSSourceMapFactory.js" ] } diff --git a/front_end/sdk/CSSModel.js b/front_end/sdk/CSSModel.js index 01a115330c..f1860be784 100644 --- a/front_end/sdk/CSSModel.js +++ b/front_end/sdk/CSSModel.js @@ -127,13 +127,13 @@ WebInspector.CSSModel.prototype = { } if (this._sourceMapLoadingPromises.has(sourceMapURL)) return; - var loadingPromise = WebInspector.SourceMap.load(sourceMapURL, header.sourceURL) + var loadingPromise = WebInspector.TextSourceMap.load(sourceMapURL, header.sourceURL) .then(onSourceMapLoaded.bind(this, sourceMapURL)); this._sourceMapLoadingPromises.set(sourceMapURL, loadingPromise); /** * @param {string} sourceMapURL - * @param {?WebInspector.SourceMap} sourceMap + * @param {?WebInspector.TextSourceMap} sourceMap * @this {WebInspector.CSSModel} */ function onSourceMapLoaded(sourceMapURL, sourceMap) @@ -455,7 +455,9 @@ WebInspector.CSSModel.prototype = { if (pseudoClasses.indexOf(pseudoClass) < 0) return false; pseudoClasses.remove(pseudoClass); - if (!pseudoClasses.length) + if (pseudoClasses.length) + node.setMarker(WebInspector.CSSModel.PseudoStateMarker, pseudoClasses); + else node.setMarker(WebInspector.CSSModel.PseudoStateMarker, null); } diff --git a/front_end/sdk/DOMModel.js b/front_end/sdk/DOMModel.js index ba8394e8b9..840766e560 100644 --- a/front_end/sdk/DOMModel.js +++ b/front_end/sdk/DOMModel.js @@ -63,7 +63,8 @@ WebInspector.DOMNode = function(domModel, doc, isInShadowTree, payload) if (payload.attributes) this._setAttributesPayload(payload.attributes); - this._markers = {}; + /** @type {!Map} */ + this._markers = new Map(); this._subtreeMarkerCount = 0; this._childNodeCount = payload.childNodeCount || 0; @@ -795,10 +796,10 @@ WebInspector.DOMNode.prototype = { setMarker: function(name, value) { if (value === null) { - if (!this._markers.hasOwnProperty(name)) + if (!this._markers.has(name)) return; - delete this._markers[name]; + this._markers.delete(name); for (var node = this; node; node = node.parentNode) --node._subtreeMarkerCount; for (var node = this; node; node = node.parentNode) @@ -806,11 +807,11 @@ WebInspector.DOMNode.prototype = { return; } - if (this.parentNode && !this._markers.hasOwnProperty(name)) { + if (this.parentNode && !this._markers.has(name)) { for (var node = this; node; node = node.parentNode) ++node._subtreeMarkerCount; } - this._markers[name] = value; + this._markers.set(name, value); for (var node = this; node; node = node.parentNode) this._domModel.dispatchEventToListeners(WebInspector.DOMModel.Events.MarkersChanged, node); }, @@ -822,15 +823,7 @@ WebInspector.DOMNode.prototype = { */ marker: function(name) { - return this._markers[name] || null; - }, - - /** - * @return {!Array} - */ - markers: function() - { - return Object.values(this._markers); + return this._markers.get(name) || null; }, /** @@ -845,7 +838,7 @@ WebInspector.DOMNode.prototype = { { if (!node._subtreeMarkerCount) return; - for (var marker in node._markers) + for (var marker of node._markers.keys()) visitor(node, marker); if (!node._children) return; diff --git a/front_end/sdk/NetworkManager.js b/front_end/sdk/NetworkManager.js index 42503ecedf..45986d2318 100644 --- a/front_end/sdk/NetworkManager.js +++ b/front_end/sdk/NetworkManager.js @@ -44,7 +44,12 @@ WebInspector.NetworkManager = function(target) this._networkAgent.setCacheDisabled(true); if (WebInspector.moduleSetting("monitoringXHREnabled").get()) this._networkAgent.setMonitoringXHREnabled(true); - this._networkAgent.enable(); + + // Limit buffer when talking to a remote device. + if (Runtime.queryParam("remoteFrontend") || Runtime.queryParam("ws")) + this._networkAgent.enable(10000000, 5000000); + else + this._networkAgent.enable(); /** @type {!Map>} */ this._certificateDetailsCache = new Map(); diff --git a/front_end/sdk/SourceMap.js b/front_end/sdk/SourceMap.js index 92041e5151..64d823919b 100644 --- a/front_end/sdk/SourceMap.js +++ b/front_end/sdk/SourceMap.js @@ -60,21 +60,116 @@ SourceMapV3.Offset = function() /** @type {number} */ this.column; } +/** + * @constructor + * @param {number} lineNumber + * @param {number} columnNumber + * @param {string=} sourceURL + * @param {number=} sourceLineNumber + * @param {number=} sourceColumnNumber + * @param {string=} name + */ +WebInspector.SourceMapEntry = function(lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber, name) +{ + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + this.sourceURL = sourceURL; + this.sourceLineNumber = sourceLineNumber; + this.sourceColumnNumber = sourceColumnNumber; + this.name = name; +} + +/** + * @interface + */ +WebInspector.SourceMap = function() { } + +WebInspector.SourceMap.prototype = { + /** + * @return {string} + */ + compiledURL: function() { }, + + /** + * @return {string} + */ + url: function() { }, + + /** + * @return {!Array} + */ + sourceURLs: function() { }, + + /** + * @param {string} sourceURL + * @param {!WebInspector.ResourceType} contentType + * @return {!WebInspector.ContentProvider} + */ + sourceContentProvider: function(sourceURL, contentType) { }, + + /** + * @param {number} lineNumber in compiled resource + * @param {number} columnNumber in compiled resource + * @return {?WebInspector.SourceMapEntry} + */ + findEntry: function(lineNumber, columnNumber) { }, + + /** + * @return {boolean} + */ + editable: function() { }, + + /** + * @param {!Array} ranges + * @param {!Array} texts + * @return {!Promise} + */ + editCompiled: function(ranges, texts) { }, +} + +/** + * @constructor + * @param {!WebInspector.SourceMap} map + * @param {!Array} compiledEdits + * @param {!Map} newSources + */ +WebInspector.SourceMap.EditResult = function(map, compiledEdits, newSources) +{ + this.map = map; + this.compiledEdits = compiledEdits; + this.newSources = newSources; +} + +/** + * @interface + */ +WebInspector.SourceMapFactory = function() { } + +WebInspector.SourceMapFactory.prototype = { + /** + * @param {!WebInspector.Target} target + * @param {!WebInspector.SourceMap} sourceMap + * @return {!Promise} + */ + editableSourceMap: function(target, sourceMap) { }, +} + /** * Implements Source Map V3 model. See https://github.com/google/closure-compiler/wiki/Source-Maps * for format description. * @constructor + * @implements {WebInspector.SourceMap} * @param {string} compiledURL * @param {string} sourceMappingURL * @param {!SourceMapV3} payload */ -WebInspector.SourceMap = function(compiledURL, sourceMappingURL, payload) +WebInspector.TextSourceMap = function(compiledURL, sourceMappingURL, payload) { - if (!WebInspector.SourceMap.prototype._base64Map) { + if (!WebInspector.TextSourceMap.prototype._base64Map) { const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - WebInspector.SourceMap.prototype._base64Map = {}; + WebInspector.TextSourceMap.prototype._base64Map = {}; for (var i = 0; i < base64Digits.length; ++i) - WebInspector.SourceMap.prototype._base64Map[base64Digits.charAt(i)] = i; + WebInspector.TextSourceMap.prototype._base64Map[base64Digits.charAt(i)] = i; } this._compiledURL = compiledURL; @@ -89,10 +184,10 @@ WebInspector.SourceMap = function(compiledURL, sourceMappingURL, payload) /** * @param {string} sourceMapURL * @param {string} compiledURL - * @return {!Promise} - * @this {WebInspector.SourceMap} + * @return {!Promise} + * @this {WebInspector.TextSourceMap} */ -WebInspector.SourceMap.load = function(sourceMapURL, compiledURL) +WebInspector.TextSourceMap.load = function(sourceMapURL, compiledURL) { var callback; var promise = new Promise(fulfill => callback = fulfill); @@ -117,7 +212,7 @@ WebInspector.SourceMap.load = function(sourceMapURL, compiledURL) var payload = /** @type {!SourceMapV3} */ (JSON.parse(content)); var baseURL = sourceMapURL.startsWith("data:") ? compiledURL : sourceMapURL; - callback(new WebInspector.SourceMap(compiledURL, baseURL, payload)); + callback(new WebInspector.TextSourceMap(compiledURL, baseURL, payload)); } catch(e) { console.error(e); WebInspector.console.error("Failed to parse SourceMap: " + sourceMapURL); @@ -126,8 +221,9 @@ WebInspector.SourceMap.load = function(sourceMapURL, compiledURL) } } -WebInspector.SourceMap.prototype = { +WebInspector.TextSourceMap.prototype = { /** + * @override * @return {string} */ compiledURL: function() @@ -136,6 +232,7 @@ WebInspector.SourceMap.prototype = { }, /** + * @override * @return {string} */ url: function() @@ -144,35 +241,48 @@ WebInspector.SourceMap.prototype = { }, /** + * @override * @return {!Array.} */ - sources: function() + sourceURLs: function() { return Object.keys(this._sources); }, /** - * @param {string} sourceURL - * @return {string|undefined} - */ - sourceContent: function(sourceURL) - { - return this._sourceContentByURL[sourceURL]; - }, - - /** + * @override * @param {string} sourceURL * @param {!WebInspector.ResourceType} contentType * @return {!WebInspector.ContentProvider} */ sourceContentProvider: function(sourceURL, contentType) { - var sourceContent = this.sourceContent(sourceURL); + var sourceContent = this._sourceContentByURL[sourceURL]; if (sourceContent) return new WebInspector.StaticContentProvider(contentType, sourceContent); return new WebInspector.CompilerSourceMappingContentProvider(sourceURL, contentType); }, + /** + * @override + * @return {boolean} + */ + editable: function() + { + return false; + }, + + /** + * @override + * @param {!Array} ranges + * @param {!Array} texts + * @return {!Promise} + */ + editCompiled: function(ranges, texts) + { + return Promise.resolve(/** @type {?WebInspector.SourceMap.EditResult} */(null)); + }, + /** * @param {!SourceMapV3} mappingPayload */ @@ -196,9 +306,10 @@ WebInspector.SourceMap.prototype = { }, /** + * @override * @param {number} lineNumber in compiled resource * @param {number} columnNumber in compiled resource - * @return {?WebInspector.SourceMap.Entry} + * @return {?WebInspector.SourceMapEntry} */ findEntry: function(lineNumber, columnNumber) { @@ -224,7 +335,7 @@ WebInspector.SourceMap.prototype = { /** * @param {string} sourceURL * @param {number} lineNumber - * @return {?WebInspector.SourceMap.Entry} + * @return {?WebInspector.SourceMapEntry} */ firstSourceLineMapping: function(sourceURL, lineNumber) { @@ -236,7 +347,7 @@ WebInspector.SourceMap.prototype = { /** * @param {number} lineNumber - * @param {!WebInspector.SourceMap.Entry} mapping + * @param {!WebInspector.SourceMapEntry} mapping * @return {number} */ function lineComparator(lineNumber, mapping) @@ -246,7 +357,7 @@ WebInspector.SourceMap.prototype = { }, /** - * @return {!Array} + * @return {!Array} */ mappings: function() { @@ -255,7 +366,7 @@ WebInspector.SourceMap.prototype = { /** * @param {string} sourceURL - * @return {!Array.} + * @return {!Array.} */ _reversedMappings: function(sourceURL) { @@ -269,8 +380,8 @@ WebInspector.SourceMap.prototype = { return mappings; /** - * @param {!WebInspector.SourceMap.Entry} a - * @param {!WebInspector.SourceMap.Entry} b + * @param {!WebInspector.SourceMapEntry} a + * @param {!WebInspector.SourceMapEntry} b * @return {number} */ function sourceMappingComparator(a, b) @@ -317,7 +428,7 @@ WebInspector.SourceMap.prototype = { this._sourceContentByURL[url] = map.sourcesContent[i]; } - var stringCharIterator = new WebInspector.SourceMap.StringCharIterator(map.mappings); + var stringCharIterator = new WebInspector.TextSourceMap.StringCharIterator(map.mappings); var sourceURL = sources[sourceIndex]; while (true) { @@ -335,7 +446,7 @@ WebInspector.SourceMap.prototype = { columnNumber += this._decodeVLQ(stringCharIterator); if (!stringCharIterator.hasNext() || this._isSeparator(stringCharIterator.peek())) { - this._mappings.push(new WebInspector.SourceMap.Entry(lineNumber, columnNumber)); + this._mappings.push(new WebInspector.SourceMapEntry(lineNumber, columnNumber)); continue; } @@ -349,7 +460,7 @@ WebInspector.SourceMap.prototype = { if (!this._isSeparator(stringCharIterator.peek())) nameIndex += this._decodeVLQ(stringCharIterator); - this._mappings.push(new WebInspector.SourceMap.Entry(lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber, names[nameIndex])); + this._mappings.push(new WebInspector.SourceMapEntry(lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber, names[nameIndex])); } for (var i = 0; i < this._mappings.length; ++i) { @@ -374,7 +485,7 @@ WebInspector.SourceMap.prototype = { }, /** - * @param {!WebInspector.SourceMap.StringCharIterator} stringCharIterator + * @param {!WebInspector.TextSourceMap.StringCharIterator} stringCharIterator * @return {number} */ _decodeVLQ: function(stringCharIterator) @@ -403,7 +514,7 @@ WebInspector.SourceMap.prototype = { { /** * @param {!{lineNumber: number, columnNumber: number}} position - * @param {!WebInspector.SourceMap.Entry} mapping + * @param {!WebInspector.SourceMapEntry} mapping * @return {number} */ function comparator(position, mapping) @@ -432,13 +543,13 @@ WebInspector.SourceMap.prototype = { * @constructor * @param {string} string */ -WebInspector.SourceMap.StringCharIterator = function(string) +WebInspector.TextSourceMap.StringCharIterator = function(string) { this._string = string; this._position = 0; } -WebInspector.SourceMap.StringCharIterator.prototype = { +WebInspector.TextSourceMap.StringCharIterator.prototype = { /** * @return {string} */ @@ -463,22 +574,3 @@ WebInspector.SourceMap.StringCharIterator.prototype = { return this._position < this._string.length; } } - -/** - * @constructor - * @param {number} lineNumber - * @param {number} columnNumber - * @param {string=} sourceURL - * @param {number=} sourceLineNumber - * @param {number=} sourceColumnNumber - * @param {string=} name - */ -WebInspector.SourceMap.Entry = function(lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber, name) -{ - this.lineNumber = lineNumber; - this.columnNumber = columnNumber; - this.sourceURL = sourceURL; - this.sourceLineNumber = sourceLineNumber; - this.sourceColumnNumber = sourceColumnNumber; - this.name = name; -} diff --git a/front_end/source_frame/cmdevtools.css b/front_end/source_frame/cmdevtools.css index a03ddad525..ef520c6f86 100644 --- a/front_end/source_frame/cmdevtools.css +++ b/front_end/source_frame/cmdevtools.css @@ -80,6 +80,7 @@ padding-left: 1px; height: 11px; line-height: 12px !important; + border-style: solid; } .cm-line-without-source-mapping { diff --git a/front_end/sources/JavaScriptSourceFrame.js b/front_end/sources/JavaScriptSourceFrame.js index 141eb0a9ef..ddd650ea27 100644 --- a/front_end/sources/JavaScriptSourceFrame.js +++ b/front_end/sources/JavaScriptSourceFrame.js @@ -765,7 +765,7 @@ WebInspector.JavaScriptSourceFrame.prototype = { for (var line of this._valueWidgets.keys()) toLine = Math.max(toLine, line + 1); } - if (fromLine >= toLine || toLine - fromLine > 500) { + if (fromLine >= toLine || toLine - fromLine > 500 || fromLine < 0 || toLine >= this.textEditor.linesCount) { this._clearValueWidgets(); return; } diff --git a/front_end/sources/SourcesView.js b/front_end/sources/SourcesView.js index 3ae09ab2f0..337ba81e46 100644 --- a/front_end/sources/SourcesView.js +++ b/front_end/sources/SourcesView.js @@ -107,7 +107,6 @@ WebInspector.SourcesView.prototype = { registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.CloseEditorTab, this._onCloseEditorTab.bind(this)); registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.GoToLine, this._showGoToLineDialog.bind(this)); registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.GoToMember, this._showOutlineDialog.bind(this)); - registerShortcut.call(this, [WebInspector.KeyboardShortcut.makeDescriptor("o", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta | WebInspector.KeyboardShortcut.Modifiers.Shift)], this._showOutlineDialog.bind(this)); registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.ToggleBreakpoint, this._toggleBreakpoint.bind(this)); registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.Save, this._save.bind(this)); registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.SaveAll, this._saveAll.bind(this)); diff --git a/front_end/timeline/TimelineModel.js b/front_end/timeline/TimelineModel.js index 1f67829867..243d057d79 100644 --- a/front_end/timeline/TimelineModel.js +++ b/front_end/timeline/TimelineModel.js @@ -1761,7 +1761,7 @@ WebInspector.TimelineModel.LineLevelProfile.prototype = { } for (var j = 0; j < node.positionTicks.length; ++j) { var lineInfo = node.positionTicks[j]; - var line = lineInfo.line - 1; + var line = lineInfo.line; var time = lineInfo.ticks * sampleDuration; fileInfo.set(line, (fileInfo.get(line) || 0) + time); } diff --git a/front_end/timeline/TimelinePanel.js b/front_end/timeline/TimelinePanel.js index b4d110908d..a048529b60 100644 --- a/front_end/timeline/TimelinePanel.js +++ b/front_end/timeline/TimelinePanel.js @@ -1269,14 +1269,20 @@ WebInspector.TimelinePanel.prototype = { */ _setLineLevelCPUProfile: function(profile) { + var debuggerModel = WebInspector.DebuggerModel.fromTarget(WebInspector.targetManager.mainTarget()); + if (!debuggerModel) + return; for (var fileInfo of profile.files()) { - var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(/** @type {string} */ (fileInfo[0])); - if (!uiSourceCode) - continue; + var url = /** @type {string} */ (fileInfo[0]); + var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(url); for (var lineInfo of fileInfo[1]) { - var line = lineInfo[0]; + var line = lineInfo[0] - 1; var time = lineInfo[1]; - uiSourceCode.addLineDecoration(line, WebInspector.TimelineUIUtils.PerformanceLineDecorator.type, time); + var rawLocation = debuggerModel.createRawLocationByURL(url, line, 0); + if (rawLocation) + new WebInspector.TimelineUIUtils.LineLevelProfilePresentation(rawLocation, time); + else if (uiSourceCode) + uiSourceCode.addLineDecoration(line, WebInspector.TimelineUIUtils.PerformanceLineDecorator.type, time); } } }, diff --git a/front_end/timeline/TimelineUIUtils.js b/front_end/timeline/TimelineUIUtils.js index 8cc41a4eb0..9e77d90fc3 100644 --- a/front_end/timeline/TimelineUIUtils.js +++ b/front_end/timeline/TimelineUIUtils.js @@ -694,6 +694,8 @@ WebInspector.TimelineUIUtils._buildTraceEventDetailsSynchronously = function(eve contentHelper.appendTextRow(WebInspector.UIString("Callback ID"), eventData["id"]); break; case recordTypes.FunctionCall: + if (typeof eventData["functionName"] === "string") + contentHelper.appendTextRow(WebInspector.UIString("Function"), WebInspector.beautifyFunctionName(eventData["functionName"])); if (eventData["scriptName"]) contentHelper.appendLocationRow(WebInspector.UIString("Location"), eventData["scriptName"], eventData["scriptLine"]); break; @@ -2108,6 +2110,31 @@ WebInspector.TimelineUIUtils.eventWarning = function(event, warningType) return span; } +/** + * @constructor + * @param {!WebInspector.DebuggerModel.Location} rawLocation + * @param {number} time + */ +WebInspector.TimelineUIUtils.LineLevelProfilePresentation = function(rawLocation, time) +{ + this._time = time; + WebInspector.debuggerWorkspaceBinding.createLiveLocation(rawLocation, this.updateLocation.bind(this)); +} + +WebInspector.TimelineUIUtils.LineLevelProfilePresentation.prototype = { + /** + * @param {!WebInspector.LiveLocation} liveLocation + */ + updateLocation: function(liveLocation) + { + if (this._uiLocation) + this._uiLocation.uiSourceCode.removeLineDecoration(this._uiLocation.lineNumber, WebInspector.TimelineUIUtils.PerformanceLineDecorator.type); + this._uiLocation = liveLocation.uiLocation(); + if (this._uiLocation) + this._uiLocation.uiSourceCode.addLineDecoration(this._uiLocation.lineNumber, WebInspector.TimelineUIUtils.PerformanceLineDecorator.type, this._time); + } +} + /** * @constructor * @implements {WebInspector.UISourceCodeFrame.LineDecorator} @@ -2127,9 +2154,11 @@ WebInspector.TimelineUIUtils.PerformanceLineDecorator.prototype = { decorate: function(uiSourceCode, textEditor) { var type = WebInspector.TimelineUIUtils.PerformanceLineDecorator.type; - var decorations = uiSourceCode.lineDecorations(type) || []; + var decorations = uiSourceCode.lineDecorations(type); textEditor.resetGutterDecorations(type); - for (var decoration of decorations) { + if (!decorations) + return; + for (var decoration of decorations.values()) { var time = /** @type {number} */ (decoration.data()); var text = WebInspector.UIString("%.1f\xa0ms", time); var intensity = Number.constrain(Math.log10(1 + 2 * time) / 5, 0.02, 1); diff --git a/front_end/ui/Popover.js b/front_end/ui/Popover.js index cccb6642a2..c17ef693ca 100644 --- a/front_end/ui/Popover.js +++ b/front_end/ui/Popover.js @@ -186,12 +186,14 @@ WebInspector.Popover.prototype = { var verticalAlignment; var roomAbove = anchorBox.y; var roomBelow = totalHeight - anchorBox.y - anchorBox.height; + this._popupArrowElement.hidden = false; if ((roomAbove > roomBelow) || (arrowDirection === WebInspector.Popover.Orientation.Bottom)) { // Positioning above the anchor. if ((anchorBox.y > newElementPosition.height + arrowHeight + borderRadius) || (arrowDirection === WebInspector.Popover.Orientation.Bottom)) newElementPosition.y = anchorBox.y - newElementPosition.height - arrowHeight; else { + this._popupArrowElement.hidden = true; newElementPosition.y = borderRadius; newElementPosition.height = anchorBox.y - borderRadius * 2 - arrowHeight; if (this._hasFixedHeight && newElementPosition.height < preferredHeight) { @@ -204,6 +206,7 @@ WebInspector.Popover.prototype = { // Positioning below the anchor. newElementPosition.y = anchorBox.y + anchorBox.height + arrowHeight; if ((newElementPosition.y + newElementPosition.height + borderRadius >= totalHeight) && (arrowDirection !== WebInspector.Popover.Orientation.Top)) { + this._popupArrowElement.hidden = true; newElementPosition.height = totalHeight - borderRadius - newElementPosition.y; if (this._hasFixedHeight && newElementPosition.height < preferredHeight) { newElementPosition.y = totalHeight - preferredHeight - borderRadius; diff --git a/front_end/ui_lazy/CommandMenu.js b/front_end/ui_lazy/CommandMenu.js index e619b3185d..4446daf3d4 100644 --- a/front_end/ui_lazy/CommandMenu.js +++ b/front_end/ui_lazy/CommandMenu.js @@ -113,7 +113,7 @@ WebInspector.CommandMenuDelegate.prototype = { itemScoreAt: function(itemIndex, query) { var command = this._commands[itemIndex]; - var opcodes = WebInspector.Diff.charDiff(query.toLowerCase(), command.title.toLowerCase()); + var opcodes = WebInspector.Diff.charDiff(query.toLowerCase(), command.title().toLowerCase()); var score = 0; // Score longer sequences higher. for (var i = 0; i < opcodes.length; ++i) { @@ -122,9 +122,9 @@ WebInspector.CommandMenuDelegate.prototype = { } // Score panel/drawer reveals above regular actions. - if (command.title.startsWith("Panel")) + if (command.title().startsWith("Panel")) score += 2; - else if (command.title.startsWith("Drawer")) + else if (command.title().startsWith("Drawer")) score += 1; return score; diff --git a/front_end/ui_lazy/FlameChart.js b/front_end/ui_lazy/FlameChart.js index 096d801416..da1c28a86d 100644 --- a/front_end/ui_lazy/FlameChart.js +++ b/front_end/ui_lazy/FlameChart.js @@ -96,6 +96,8 @@ WebInspector.FlameChart = function(dataProvider, flameChartDelegate, groupExpans this._windowRight = 1.0; this._timeWindowLeft = 0; this._timeWindowRight = Infinity; + this._rangeSelectionStart = 0; + this._rangeSelectionEnd = 0; this._barHeight = dataProvider.barHeight(); this._paddingLeft = this._dataProvider.paddingLeft(); var markerPadding = 2; diff --git a/front_end/ui_lazy/ViewportDataGrid.js b/front_end/ui_lazy/ViewportDataGrid.js index 222d7762ab..c1ca48c7a4 100644 --- a/front_end/ui_lazy/ViewportDataGrid.js +++ b/front_end/ui_lazy/ViewportDataGrid.js @@ -237,11 +237,14 @@ WebInspector.ViewportDataGrid.prototype = { } this.setVerticalPadding(viewportState.topPadding, viewportState.bottomPadding); - var contentFits = viewportState.contentHeight <= clientHeight; - this.element.classList.toggle("data-grid-fits-viewport", contentFits && viewportState.topPadding + viewportState.bottomPadding === 0); this._lastScrollTop = scrollTop; if (scrollTop !== currentScrollTop) this._scrollContainer.scrollTop = scrollTop; + var contentFits = viewportState.contentHeight <= clientHeight && viewportState.topPadding + viewportState.bottomPadding === 0; + if (contentFits !== this.element.classList.contains("data-grid-fits-viewport")) { + this.element.classList.toggle("data-grid-fits-viewport", contentFits); + this.updateWidths(); + } this._visibleNodes = visibleNodes; }, diff --git a/front_end/workspace/UISourceCode.js b/front_end/workspace/UISourceCode.js index 5e78d0a368..e37d5c28ec 100644 --- a/front_end/workspace/UISourceCode.js +++ b/front_end/workspace/UISourceCode.js @@ -52,7 +52,7 @@ WebInspector.UISourceCode = function(project, url, contentType) this._requestContentCallback = null; /** @type {?Promise} */ this._requestContentPromise = null; - /** @type {!Map>} */ + /** @type {!Map>} */ this._lineDecorations = new Map(); /** @type {!Array.} */ @@ -657,27 +657,49 @@ WebInspector.UISourceCode.prototype = { { var markers = this._lineDecorations.get(type); if (!markers) { - markers = []; + markers = new Map(); this._lineDecorations.set(type, markers); } var marker = new WebInspector.UISourceCode.LineMarker(lineNumber, type, data); - markers.push(marker); + markers.set(lineNumber, marker); this.dispatchEventToListeners(WebInspector.UISourceCode.Events.LineDecorationAdded, marker); }, + /** + * @param {number} lineNumber + * @param {string} type + */ + removeLineDecoration: function(lineNumber, type) + { + var markers = this._lineDecorations.get(type); + if (!markers) + return; + var marker = markers.get(lineNumber); + if (!marker) + return; + markers.delete(lineNumber); + this.dispatchEventToListeners(WebInspector.UISourceCode.Events.LineDecorationRemoved, marker); + if (!markers.size) + this._lineDecorations.delete(type); + }, + /** * @param {string} type */ removeAllLineDecorations: function(type) { - var markers = this._lineDecorations.get(type) || []; - markers.forEach(this.dispatchEventToListeners.bind(this, WebInspector.UISourceCode.Events.LineDecorationRemoved)); + var markers = this._lineDecorations.get(type); + if (!markers) + return; this._lineDecorations.delete(type); + markers.forEach(marker => { + this.dispatchEventToListeners(WebInspector.UISourceCode.Events.LineDecorationRemoved, marker); + }); }, /** * @param {string} type - * @return {?Array} + * @return {?Map} */ lineDecorations: function(type) { diff --git a/protocol.json b/protocol.json index dd4e5d8b1f..9462985bd0 100644 --- a/protocol.json +++ b/protocol.json @@ -16,13 +16,6 @@ } ], "events": [ - { - "name": "evaluateForTestInFrontend", - "parameters": [ - { "name": "testCallId", "type": "integer" }, - { "name": "script", "type": "string" } - ] - }, { "name": "inspect", "parameters": [ @@ -1412,7 +1405,11 @@ "commands": [ { "name": "enable", - "description": "Enables network tracking, network events will now be delivered to the client." + "description": "Enables network tracking, network events will now be delivered to the client.", + "parameters": [ + { "name": "maxTotalBufferSize", "type": "integer", "optional": true, "hidden": true, "description": "Buffer size in bytes to use when preserving network payloads (XHRs, etc)." }, + { "name": "maxResourceBufferSize", "type": "integer", "optional": true, "hidden": true, "description": "Per-resource buffer size in bytes to use when preserving network payloads (XHRs, etc)." } + ] }, { "name": "disable",