diff --git a/Cargo.lock b/Cargo.lock index 18050ff24421..5f3303065fb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -630,21 +630,21 @@ name = "encoding_c" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "encoding_rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "encoding_glue" version = "0.1.0" dependencies = [ - "encoding_rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "nserror 0.1.0", "nsstring 0.1.0", ] [[package]] name = "encoding_rs" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2551,7 +2551,7 @@ dependencies = [ "checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a" "checksum ena 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe5a5078ac8c506d3e4430763b1ba9b609b1286913e7d08e581d1c2de9b7e5" "checksum encoding_c 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "769ecb8b33323998e482b218c0d13cd64c267609023b4b7ec3ee740714c318ee" -"checksum encoding_rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6848cbd169668c2338be9940ac8968179edcd8704248e1e0c885a306c42772e" +"checksum encoding_rs 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "22b758dc5e2c2b9e1dc3aa7a8be71035eed9742c907b7567627527af4c03324e" "checksum env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0561146661ae44c579e993456bc76d11ce1e0c7d745e57b2fa7146b6e49fa2ad" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" "checksum euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c95fd0d455f114291a3109286bd387bd423770058474a2d3f38b712cd661df60" diff --git a/browser/base/content/test/urlbar/browser_pasteAndGo.js b/browser/base/content/test/urlbar/browser_pasteAndGo.js index 6d5d580cbda5..9ff67e2f5f23 100644 --- a/browser/base/content/test/urlbar/browser_pasteAndGo.js +++ b/browser/base/content/test/urlbar/browser_pasteAndGo.js @@ -36,3 +36,32 @@ add_task(async function() { }); } }); + +add_task(async function() { + const url = "http://example.com/4\u2028"; + await BrowserTestUtils.withNewTab("about:blank", async function(browser) { + gURLBar.focus(); + await new Promise((resolve, reject) => { + waitForClipboard(url, function() { + clipboardHelper.copyString(url); + }, resolve, + () => reject(new Error(`Failed to copy string '${url}' to clipboard`)) + ); + }); + let textBox = document.getAnonymousElementByAttribute(gURLBar, + "anonid", "textbox-input-box"); + let cxmenu = document.getAnonymousElementByAttribute(textBox, + "anonid", "input-box-contextmenu"); + let cxmenuPromise = BrowserTestUtils.waitForEvent(cxmenu, "popupshown"); + EventUtils.synthesizeMouseAtCenter(gURLBar, {type: "contextmenu", button: 2}); + await cxmenuPromise; + let menuitem = document.getAnonymousElementByAttribute(textBox, + "anonid", "paste-and-go"); + let browserLoadedPromise = BrowserTestUtils.browserLoaded(browser, false, url.replace(/\u2028/g, "")); + EventUtils.synthesizeMouseAtCenter(menuitem, {}); + // Using toSource in order to get the newlines escaped: + info("Paste and go, loading " + url.toSource()); + await browserLoadedPromise; + ok(true, "Successfully loaded " + url); + }); +}); diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index cdea7a09fa77..46169bc1032a 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -791,6 +791,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. // set value and try to confirm it. UnifiedComplete should always // resolve to a valid url. try { + url = url.trim(); new URL(url); } catch (ex) { let lastLocationChange = browser.lastLocationChange; diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index e99b39801161..72c2032e3d03 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -626,7 +626,7 @@ bin/libfreebl_32int64_3.so ; NOTE: This must match the config checks in ; /toolkit/components/backgroundhangmonitor/moz.build. -#if defined(NIGHTLY_BUILD) && !defined(MOZ_DEBUG) && !defined(MOZ_TSAN) +#if defined(NIGHTLY_BUILD) && !defined(MOZ_DEBUG) && !defined(MOZ_TSAN) && !defined(MOZ_ASAN) @RESPATH@/components/BHRTelemetryService.js @RESPATH@/components/BHRTelemetryService.manifest #endif diff --git a/browser/themes/shared/customizableui/panelUI.inc.css b/browser/themes/shared/customizableui/panelUI.inc.css index 7d615371197d..c205b15abcd0 100644 --- a/browser/themes/shared/customizableui/panelUI.inc.css +++ b/browser/themes/shared/customizableui/panelUI.inc.css @@ -88,26 +88,22 @@ list-style-image: url(chrome://branding/content/icon16.png); } +#PanelUI-menu-button[badge-status="addon-alert"] > .toolbarbutton-badge-stack > .toolbarbutton-badge, #PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge { + height: 13px; + background: url(chrome://browser/skin/warning.svg) center / contain no-repeat transparent; box-shadow: none; - filter: drop-shadow(0 1px 0 hsla(206, 50%, 10%, .15)); + border-radius: 0; + /* Use the included fallbacks defined in the SVG file instead of inheriting from .toolbarbutton-1. */ + -moz-context-properties: none; } -#PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge { - height: 13px; - background: transparent url(chrome://browser/skin/warning.svg) no-repeat center; +#PanelUI-menu-button[badge-status] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive { + filter: grayscale(100%); } -#PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive { - filter: none; -} - -#PanelUI-menu-button[badge-status="addon-alert"] > .toolbarbutton-badge-stack > .toolbarbutton-badge { - height: 13px; - background: url(chrome://browser/skin/warning.svg) no-repeat center; -} - -:root[lwt-popup-brighttext] #PanelUI-menu-button[badge-status="addon-alert"] > .toolbarbutton-badge-stack > .toolbarbutton-badge { +#nav-bar[brighttext] #PanelUI-menu-button[badge-status="addon-alert"] > .toolbarbutton-badge-stack > .toolbarbutton-badge, +#nav-bar[brighttext] #PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge { -moz-context-properties: fill, stroke; fill: #FFE900; stroke: transparent; diff --git a/devtools/client/accessibility/components/Accessible.js b/devtools/client/accessibility/components/Accessible.js index 21acd4b3ee96..ebab85eb293c 100644 --- a/devtools/client/accessibility/components/Accessible.js +++ b/devtools/client/accessibility/components/Accessible.js @@ -24,6 +24,8 @@ const Tree = createFactory(require("devtools/client/shared/components/Virtualize const { REPS, MODE } = require("devtools/client/shared/components/reps/reps"); const { Rep, ElementNode } = REPS; +loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true); + const TELEMETRY_NODE_INSPECTED_COUNT = "devtools.accessibility.node_inspected_count"; class AccessiblePropertyClass extends Component { @@ -175,23 +177,7 @@ class Accessible extends Component { } openLink(link, e) { - if (!gToolbox) { - return; - } - - // Avoid using Services.appinfo.OS in order to keep accessible pane's frontend free of - // priveleged code. - const os = window.navigator.userAgent; - const isOSX = os && os.includes("Mac"); - let where = "tab"; - if (e && (e.button === 1 || (e.button === 0 && (isOSX ? e.metaKey : e.ctrlKey)))) { - where = "tabshifted"; - } else if (e && e.shiftKey) { - where = "window"; - } - - const win = gToolbox.doc.defaultView.top; - win.openWebLinkIn(link, where); + openContentLink(link); } renderItem(item, depth, focused, arrow, expanded) { diff --git a/devtools/client/accessibility/test/mochitest/test_accessible_openLink.html b/devtools/client/accessibility/test/mochitest/test_accessible_openLink.html index c7427252963c..5ee2e2b66ab7 100644 --- a/devtools/client/accessibility/test/mochitest/test_accessible_openLink.html +++ b/devtools/client/accessibility/test/mochitest/test_accessible_openLink.html @@ -22,6 +22,7 @@ Test that openLink function is called if accessible object property is rendered window.onload = async function() { try { + const { gDevTools } = require("devtools/client/framework/devtools"); const Services = browserRequire("Services"); const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom"); const { createFactory, createElement } = @@ -34,28 +35,22 @@ window.onload = async function() { browserRequire("devtools/client/accessibility/components/Accessible")); function testLinkClicked(link, event, expectedUrl, expectedWhere) { + const browserWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); + const defaultOpenWebLinkIn = browserWindow.openWebLinkIn; + const checker = Symbol(); let onClickArgs = checker; - const mockToolbox = { - doc: { - defaultView: { - top: { - openWebLinkIn: (url, where) => { - onClickArgs = { url, where }; - } - } - } - } + browserWindow.openWebLinkIn = (url, where) => { + onClickArgs = { url, where }; }; - window.gToolbox = mockToolbox; Simulate.click(link, event); ok(onClickArgs !== checker, "Link was clicked"); is(onClickArgs.url, expectedUrl, "Correct URL is opened"); is(onClickArgs.where, expectedWhere, "URL was opened correctly"); - window.gToolbox = null; + browserWindow.openWebLinkIn = defaultOpenWebLinkIn; } const a = Accessible({ labelledby: "Test Accessible" }); @@ -72,13 +67,6 @@ window.onload = async function() { let link = document.querySelector(".url"); testLinkClicked(link, null, URL, "tab"); - let event = { button: 0 }; - event[Services.appinfo.OS == "Darwin" ? "metaKey" : "ctrlKey"] = true; - testLinkClicked(link, event, URL, "tabshifted"); - - event = { shiftKey: true }; - testLinkClicked(link, event, URL, "window"); - URL = "non-URL"; await mockStore.dispatch( { type: "update", details: { DOMNode: {}, accessible: { value: URL } } }); diff --git a/devtools/client/application/src/components/WorkerListEmpty.js b/devtools/client/application/src/components/WorkerListEmpty.js index 8fdffc6a9343..4209ee478512 100644 --- a/devtools/client/application/src/components/WorkerListEmpty.js +++ b/devtools/client/application/src/components/WorkerListEmpty.js @@ -4,7 +4,7 @@ "use strict"; -const { openWebLink, openTrustedLink } = require("devtools/client/shared/link"); +const { openDocLink, openTrustedLink } = require("devtools/client/shared/link"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const { createFactory, Component } = require("devtools/client/shared/vendor/react"); const { a, article, h1, li, p, ul } = require("devtools/client/shared/vendor/react-dom-factories"); @@ -39,7 +39,7 @@ class WorkerListEmpty extends Component { } openDocumentation() { - openWebLink(DOC_URL); + openDocLink(DOC_URL); } render() { diff --git a/devtools/client/debugger/content/views/sources-view.js b/devtools/client/debugger/content/views/sources-view.js index e6900a06d86f..c73d42bfea31 100644 --- a/devtools/client/debugger/content/views/sources-view.js +++ b/devtools/client/debugger/content/views/sources-view.js @@ -28,7 +28,8 @@ const { const { Task } = require("devtools/shared/task"); const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm"); const { gDevTools } = require("devtools/client/framework/devtools"); -const {KeyCodes} = require("devtools/client/shared/keycodes"); +const { KeyCodes } = require("devtools/client/shared/keycodes"); +loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true); const NEW_SOURCE_DISPLAY_DELAY = 200; // ms const FUNCTION_SEARCH_POPUP_POSITION = "topcenter bottomleft"; @@ -898,10 +899,8 @@ SourcesView.prototype = extend(WidgetMethods, { * Opens selected item source in a new tab. */ _onNewTabCommand: function () { - let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); let selected = this.selectedItem.attachment; - win.openWebLinkIn(selected.source.url, "tab", { - triggeringPrincipal: win.document.nodePrincipal, + openContentLink(selected.source.url, { relatedToCurrent: true, }); }, diff --git a/devtools/client/debugger/new/panel.js b/devtools/client/debugger/new/panel.js index 834a2ee78d83..efb715ce4f80 100644 --- a/devtools/client/debugger/new/panel.js +++ b/devtools/client/debugger/new/panel.js @@ -8,6 +8,7 @@ const { LocalizationHelper } = require("devtools/shared/l10n"); const { gDevTools } = require("devtools/client/framework/devtools"); const { TargetFactory } = require("devtools/client/framework/target"); const { Toolbox } = require("devtools/client/framework/toolbox"); +loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true); const DBG_STRINGS_URI = "devtools/client/locales/debugger.properties"; const L10N = new LocalizationHelper(DBG_STRINGS_URI); @@ -63,24 +64,7 @@ DebuggerPanel.prototype = { }, openLink: function(url) { - const parentDoc = this.toolbox.doc; - if (!parentDoc) { - return; - } - - const win = parentDoc.querySelector("window"); - if (!win) { - return; - } - - const top = win.ownerDocument.defaultView.top; - if (!top || typeof top.openWebLink !== "function") { - return; - } - - top.openWebLinkIn(url, "tab", { - triggeringPrincipal: win.document.nodePrincipal - }); + openContentLink(url); }, openWorkerToolbox: async function(worker) { diff --git a/devtools/client/dom/dom-panel.js b/devtools/client/dom/dom-panel.js index 6dd2a1ce30b2..79e9e2538a6e 100644 --- a/devtools/client/dom/dom-panel.js +++ b/devtools/client/dom/dom-panel.js @@ -10,6 +10,7 @@ const ObjectClient = require("devtools/shared/client/object-client"); const defer = require("devtools/shared/defer"); const EventEmitter = require("devtools/shared/event-emitter"); +loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true); /** * This object represents DOM panel. It's responsibility is to @@ -179,10 +180,7 @@ DomPanel.prototype = { }, openLink: function(url) { - const parentDoc = this._toolbox.doc; - const iframe = parentDoc.getElementById("this._toolbox"); - const top = iframe.ownerDocument.defaultView.top; - top.openWebLinkIn(url, "tab"); + openContentLink(url); }, getRootGrip: function() { diff --git a/devtools/client/framework/components/ToolboxToolbar.js b/devtools/client/framework/components/ToolboxToolbar.js index 4fc1b5a369ea..973e89955407 100644 --- a/devtools/client/framework/components/ToolboxToolbar.js +++ b/devtools/client/framework/components/ToolboxToolbar.js @@ -7,7 +7,7 @@ const { Component, createFactory } = require("devtools/client/shared/vendor/reac const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const {div, button} = dom; -const {openWebLink} = require("devtools/client/shared/link"); +const {openDocLink} = require("devtools/client/shared/link"); const Menu = require("devtools/client/framework/menu"); const MenuItem = require("devtools/client/framework/menu-item"); @@ -432,7 +432,7 @@ function showMeatballMenu( id: "toolbox-meatball-menu-documentation", label: L10N.getStr("toolbox.meatballMenu.documentation.label"), click: () => { - openWebLink( + openDocLink( "https://developer.mozilla.org/docs/Tools?utm_source=devtools&utm_medium=tabbar-menu"); }, })); @@ -442,7 +442,7 @@ function showMeatballMenu( id: "toolbox-meatball-menu-community", label: L10N.getStr("toolbox.meatballMenu.community.label"), click: () => { - openWebLink( + openDocLink( "https://discourse.mozilla.org/c/devtools?utm_source=devtools&utm_medium=tabbar-menu"); }, })); diff --git a/devtools/client/framework/target.js b/devtools/client/framework/target.js index 5fc14c2ddbd6..a5e8ababa181 100644 --- a/devtools/client/framework/target.js +++ b/devtools/client/framework/target.js @@ -389,6 +389,19 @@ TabTarget.prototype = { } }, + /** + * For local tabs, returns the tab's contentPrincipal, which can be used as a + * `triggeringPrincipal` when opening links. However, this is a hack as it is not + * correct for subdocuments and it won't work for remote debugging. Bug 1467945 hopes + * to devise a better approach. + */ + get contentPrincipal() { + if (!this.isLocalTab) { + return null; + } + return this.tab.linkedBrowser.contentPrincipal; + }, + /** * Adds remote protocol capabilities to the target, so that it can be used * for tools that support the Remote Debugging Protocol even for local diff --git a/devtools/client/inspector/computed/computed.js b/devtools/client/inspector/computed/computed.js index a083eb705580..4ea826f4fbe4 100644 --- a/devtools/client/inspector/computed/computed.js +++ b/devtools/client/inspector/computed/computed.js @@ -27,6 +27,7 @@ const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overl loader.lazyRequireGetter(this, "StyleInspectorMenu", "devtools/client/inspector/shared/style-inspector-menu"); loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts"); loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard"); +loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true); const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties"; const {LocalizationHelper} = require("devtools/shared/l10n"); @@ -698,8 +699,7 @@ CssComputedView.prototype = { if (target.nodeName === "a") { event.stopPropagation(); event.preventDefault(); - const browserWin = this.inspector.target.tab.ownerDocument.defaultView; - browserWin.openWebLinkIn(target.href, "tab"); + openContentLink(target.href); } }, @@ -1190,12 +1190,7 @@ PropertyView.prototype = { * The action when a user clicks on the MDN help link for a property. */ mdnLinkClick: function(event) { - const inspector = this.tree.inspector; - - if (inspector.target.tab) { - const browserWin = inspector.target.tab.ownerDocument.defaultView; - browserWin.openWebLinkIn(this.link, "tab"); - } + openContentLink(this.link); }, /** diff --git a/devtools/client/inspector/inspector.js b/devtools/client/inspector/inspector.js index cdf33470b4cc..55d1d5cbe63b 100644 --- a/devtools/client/inspector/inspector.js +++ b/devtools/client/inspector/inspector.js @@ -35,6 +35,7 @@ loader.lazyRequireGetter(this, "MenuItem", "devtools/client/framework/menu-item" loader.lazyRequireGetter(this, "ExtensionSidebar", "devtools/client/inspector/extensions/extension-sidebar"); loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true); loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard"); +loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true); const {LocalizationHelper, localizeMarkup} = require("devtools/shared/l10n"); const INSPECTOR_L10N = @@ -2369,8 +2370,7 @@ Inspector.prototype = { this.inspector.resolveRelativeURL( link, this.selection.nodeFront).then(url => { if (type === "uri") { - const browserWin = this.target.tab.ownerDocument.defaultView; - browserWin.openWebLinkIn(url, "tab"); + openContentLink(url); } else if (type === "cssresource") { return this.toolbox.viewSourceInStyleEditor(url); } else if (type === "jsresource") { diff --git a/devtools/client/inspector/rules/views/text-property-editor.js b/devtools/client/inspector/rules/views/text-property-editor.js index a80b9b12e187..f940c0ca2002 100644 --- a/devtools/client/inspector/rules/views/text-property-editor.js +++ b/devtools/client/inspector/rules/views/text-property-editor.js @@ -4,7 +4,9 @@ "use strict"; -const {l10n} = require("devtools/shared/inspector/css-logic"); +const Services = require("Services"); + +const { l10n } = require("devtools/shared/inspector/css-logic"); const {getCssProperties} = require("devtools/shared/fronts/css-properties"); const {InplaceEditor, editableField} = require("devtools/client/shared/inplace-editor"); @@ -18,7 +20,8 @@ const { parseDeclarations, parseSingleValue, } = require("devtools/shared/css/parsing-utils"); -const Services = require("Services"); + +loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true); const HTML_NS = "http://www.w3.org/1999/xhtml"; @@ -284,8 +287,7 @@ TextPropertyEditor.prototype = { if (target.nodeName === "a") { event.stopPropagation(); event.preventDefault(); - const browserWin = this.ruleView.inspector.target.tab.ownerDocument.defaultView; - browserWin.openWebLinkIn(target.href, "tab"); + openContentLink(target.href); } }); diff --git a/devtools/client/inspector/shared/three-pane-onboarding-tooltip.js b/devtools/client/inspector/shared/three-pane-onboarding-tooltip.js index 76fe3c1313be..5f6319986bbd 100644 --- a/devtools/client/inspector/shared/three-pane-onboarding-tooltip.js +++ b/devtools/client/inspector/shared/three-pane-onboarding-tooltip.js @@ -5,7 +5,7 @@ "use strict"; const Services = require("Services"); -const { openWebLink } = require("devtools/client/shared/link"); +const { openDocLink } = require("devtools/client/shared/link"); const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip"); const { LocalizationHelper } = require("devtools/shared/l10n"); @@ -103,7 +103,7 @@ class ThreePaneOnboardingTooltip { onLearnMoreLinkClick() { Services.prefs.setBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF, false); this.tooltip.hide(); - openWebLink(LEARN_MORE_LINK); + openDocLink(LEARN_MORE_LINK); } } diff --git a/devtools/client/menus.js b/devtools/client/menus.js index 6b52e293cf58..209feb20e47e 100644 --- a/devtools/client/menus.js +++ b/devtools/client/menus.js @@ -34,6 +34,7 @@ loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/de loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true); loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true); loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true); +loader.lazyRequireGetter(this, "openDocLink", "devtools/client/shared/link", true); loader.lazyImporter(this, "BrowserToolboxProcess", "resource://devtools/client/framework/ToolboxProcess.jsm"); loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm"); @@ -132,8 +133,7 @@ exports.menuitems = [ { id: "getMoreDevtools", l10nKey: "getMoreDevtoolsCmd", oncommand(event) { - const window = event.target.ownerDocument.defaultView; - window.openWebLinkIn("https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/", "tab"); + openDocLink("https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/"); } }, ]; diff --git a/devtools/client/netmonitor/src/utils/open-request-in-tab.js b/devtools/client/netmonitor/src/utils/open-request-in-tab.js index 0db20945a4c3..c4468abd478e 100644 --- a/devtools/client/netmonitor/src/utils/open-request-in-tab.js +++ b/devtools/client/netmonitor/src/utils/open-request-in-tab.js @@ -14,16 +14,14 @@ "use strict"; -const Services = require("Services"); -const { gDevTools } = require("devtools/client/framework/devtools"); +loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true); /** * Opens given request in a new tab. */ function openRequestInTab(url, requestPostData) { - const win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); if (!requestPostData) { - win.openWebLinkIn(url, "tab", {relatedToCurrent: true}); + openContentLink(url, {relatedToCurrent: true}); } else { openPostRequestInTabHelper({ url, diff --git a/devtools/client/performance-new/components/Description.js b/devtools/client/performance-new/components/Description.js index 4e71acd4d3f6..8741898938da 100644 --- a/devtools/client/performance-new/components/Description.js +++ b/devtools/client/performance-new/components/Description.js @@ -5,7 +5,7 @@ const { PureComponent } = require("devtools/client/shared/vendor/react"); const { div, button, p } = require("devtools/client/shared/vendor/react-dom-factories"); -const { openWebLink } = require("devtools/client/shared/link"); +const { openDocLink } = require("devtools/client/shared/link"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const { connect } = require("devtools/client/shared/vendor/react-redux"); const selectors = require("devtools/client/performance-new/store/selectors"); @@ -28,7 +28,7 @@ class Description extends PureComponent { } handleLinkClick(event) { - openWebLink(event.target.value); + openDocLink(event.target.value); } /** diff --git a/devtools/client/scratchpad/scratchpad.js b/devtools/client/scratchpad/scratchpad.js index a2f074165d05..4cbb8b5e99ed 100644 --- a/devtools/client/scratchpad/scratchpad.js +++ b/devtools/client/scratchpad/scratchpad.js @@ -88,6 +88,7 @@ loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugge loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/environment-client"); loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client"); loader.lazyRequireGetter(this, "HUDService", "devtools/client/webconsole/hudservice", true); +loader.lazyRequireGetter(this, "openDocLink", "devtools/client/shared/link", true); XPCOMUtils.defineLazyGetter(this, "REMOTE_TIMEOUT", () => Services.prefs.getIntPref("devtools.debugger.remote-timeout")); @@ -1987,8 +1988,7 @@ var Scratchpad = { * Opens the MDN documentation page for Scratchpad. */ openDocumentationPage: function SP_openDocumentationPage() { - const url = this.strings.GetStringFromName("help.openDocumentationPage"); - this.browserWindow.openWebLinkIn(url, "tab"); + openDocLink(this.strings.GetStringFromName("help.openDocumentationPage")); this.browserWindow.focus(); }, }; diff --git a/devtools/client/shared/components/MdnLink.js b/devtools/client/shared/components/MdnLink.js index b516246b6a0e..3a396a467b64 100644 --- a/devtools/client/shared/components/MdnLink.js +++ b/devtools/client/shared/components/MdnLink.js @@ -4,13 +4,12 @@ "use strict"; -const Services = require("Services"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); -const { gDevTools } = require("devtools/client/framework/devtools"); - const { a } = dom; +loader.lazyRequireGetter(this, "openDocLink", "devtools/client/shared/link", true); + function MDNLink({ url, title }) { return ( a({ @@ -30,15 +29,7 @@ MDNLink.propTypes = { function onLearnMoreClick(e, url) { e.stopPropagation(); e.preventDefault(); - - const win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); - const { button, ctrlKey, metaKey } = e; - const isOSX = Services.appinfo.OS == "Darwin"; - let where = "tab"; - if (button === 1 || (button === 0 && (isOSX ? metaKey : ctrlKey))) { - where = "tabshifted"; - } - win.openWebLinkIn(url, where, {triggeringPrincipal: win.document.nodePrincipal}); + openDocLink(url); } module.exports = MDNLink; diff --git a/devtools/client/shared/components/tree/TreeView.js b/devtools/client/shared/components/tree/TreeView.js index 6a744cf51438..35e8079930db 100644 --- a/devtools/client/shared/components/tree/TreeView.js +++ b/devtools/client/shared/components/tree/TreeView.js @@ -291,10 +291,17 @@ define(function(require, exports, module) { break; case "Home": const firstRow = this.rows[0]; + if (firstRow) { - this.selectRow(firstRow); + // Due to the styling, the first row is sometimes overlapped by + // the table head. So we want to force the tree to scroll to the very top. + this.selectRow(firstRow, { + block: "end", + inline: "nearest" + }); } break; + case "End": const lastRow = this.rows[this.rows.length - 1]; if (lastRow) { @@ -341,16 +348,19 @@ define(function(require, exports, module) { return this.rows.indexOf(row); } - selectRow(row) { + selectRow(row, scrollOptions = {block: "nearest"}) { row = findDOMNode(row); + if (this.state.selected === row.id) { + row.scrollIntoView(scrollOptions); return; } this.setState(Object.assign({}, this.state, { selected: row.id })); - row.scrollIntoView({block: "nearest"}); + + row.scrollIntoView(scrollOptions); } isSelected(nodePath) { diff --git a/devtools/client/shared/link.js b/devtools/client/shared/link.js index 89316def4d7f..092bd1f57290 100644 --- a/devtools/client/shared/link.js +++ b/devtools/client/shared/link.js @@ -6,27 +6,67 @@ const Services = require("Services"); const { gDevTools } = require("devtools/client/framework/devtools"); +const { TargetFactory } = require("devtools/client/framework/target"); /** * Retrieve the most recent chrome window. */ function _getTopWindow() { - return Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); + // Try the main application window, such as a browser window. + let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); + if (win && win.openWebLinkIn && win.openTrustedLinkIn) { + return win; + } + // For non-browser cases like Browser Toolbox, try any chrome window. + win = Services.wm.getMostRecentWindow(null); + if (win && win.openWebLinkIn && win.openTrustedLinkIn) { + return win; + } + return null; } /** - * Opens a |url| controlled by webcontent in a new tab. + * Opens a |url| that does not require trusted access, such as a documentation page, in a + * new tab. * * @param {String} url * The url to open. * @param {Object} options * Optional parameters, see documentation for openUILinkIn in utilityOverlay.js */ -exports.openWebLink = async function(url, options) { +exports.openDocLink = async function(url, options) { const top = _getTopWindow(); - if (top && typeof top.openWebLinkIn === "function") { - top.openWebLinkIn(url, "tab", options); + if (!top) { + return; } + top.openWebLinkIn(url, "tab", options); +}; + +/** + * Opens a |url| controlled by web content in a new tab. + * + * If the current tab has an open toolbox, this will attempt to refine the + * `triggeringPrincipal` of the link using the tab's `contentPrincipal`. This is only an + * approximation, so bug 1467945 hopes to improve this. + * + * @param {String} url + * The url to open. + * @param {Object} options + * Optional parameters, see documentation for openUILinkIn in utilityOverlay.js + */ +exports.openContentLink = async function(url, options = {}) { + const top = _getTopWindow(); + if (!top) { + return; + } + if (!options.triggeringPrincipal && top.gBrowser) { + const tab = top.gBrowser.selectedTab; + if (TargetFactory.isKnownTab(tab)) { + const target = TargetFactory.forTab(tab); + options.triggeringPrincipal = target.contentPrincipal; + } + } + top.openWebLinkIn(url, "tab", options); }; /** @@ -39,7 +79,8 @@ exports.openWebLink = async function(url, options) { */ exports.openTrustedLink = async function(url, options) { const top = _getTopWindow(); - if (top && typeof top.openTrustedLinkIn === "function") { - top.openTrustedLinkIn(url, "tab", options); + if (!top) { + return; } + top.openTrustedLinkIn(url, "tab", options); }; diff --git a/devtools/client/shared/test/browser_filter-editor-01.js b/devtools/client/shared/test/browser_filter-editor-01.js index d6b5caa81549..16e7822ccbe7 100644 --- a/devtools/client/shared/test/browser_filter-editor-01.js +++ b/devtools/client/shared/test/browser_filter-editor-01.js @@ -6,15 +6,15 @@ // Tests that the Filter Editor Widget parses filter values correctly (setCssValue) const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget"); -const InspectorUtils = require("InspectorUtils"); const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html"; const {getClientCssProperties} = require("devtools/shared/fronts/css-properties"); +const {getCSSLexer} = require("devtools/shared/css/lexer"); // Verify that the given string consists of a valid CSS URL token. // Return true on success, false on error. function verifyURL(string) { - const lexer = InspectorUtils.getCSSLexer(string); + const lexer = getCSSLexer(string); const token = lexer.nextToken(); if (!token || token.tokenType !== "url") { diff --git a/devtools/client/shared/test/browser_link.js b/devtools/client/shared/test/browser_link.js index 53d81a0c09bc..43065e81b802 100644 --- a/devtools/client/shared/test/browser_link.js +++ b/devtools/client/shared/test/browser_link.js @@ -4,22 +4,22 @@ "use strict"; -// Test link helpers openWebLink, openTrustedLink. +// Test link helpers openDocLink, openTrustedLink. // Use any valid test page here. const TEST_URI = TEST_URI_ROOT + "dummy.html"; -const {openWebLink, openTrustedLink} = +const {openDocLink, openTrustedLink} = require("devtools/client/shared/link"); add_task(async function() { // Open a link to a page that will not trigger any request. info("Open web link to example.com test page"); - openWebLink(TEST_URI); + openDocLink(TEST_URI); await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); is(gBrowser.selectedBrowser.currentURI.spec, TEST_URI, - "openWebLink opened a tab with the expected url"); + "openDocLink opened a tab with the expected url"); info("Open trusted link to about:debugging"); openTrustedLink("about:debugging"); diff --git a/devtools/client/styleeditor/StyleEditorUI.jsm b/devtools/client/styleeditor/StyleEditorUI.jsm index 17982be07d67..c5457e1a83c2 100644 --- a/devtools/client/styleeditor/StyleEditorUI.jsm +++ b/devtools/client/styleeditor/StyleEditorUI.jsm @@ -28,6 +28,7 @@ const {KeyCodes} = require("devtools/client/shared/keycodes"); const {OriginalSource} = require("devtools/client/styleeditor/original-source"); loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true); +loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true); const LOAD_ERROR = "error-load"; const STYLE_EDITOR_TEMPLATE = "stylesheet"; @@ -487,7 +488,7 @@ StyleEditorUI.prototype = { */ _openLinkNewTab: function() { if (this._contextMenuStyleSheet) { - this._window.openWebLinkIn(this._contextMenuStyleSheet.href, "tab"); + openContentLink(this._contextMenuStyleSheet.href); } }, diff --git a/devtools/client/styleeditor/test/browser_styleeditor_opentab.js b/devtools/client/styleeditor/test/browser_styleeditor_opentab.js index 6a644464390e..353f70171e5f 100644 --- a/devtools/client/styleeditor/test/browser_styleeditor_opentab.js +++ b/devtools/client/styleeditor/test/browser_styleeditor_opentab.js @@ -21,11 +21,12 @@ add_task(async function() { "simple.css"; is(ui._contextMenuStyleSheet.href, url, "Correct URL for sheet"); - const originalOpenWebLinkIn = ui._window.openWebLinkIn; + const browserWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); + const originalOpenWebLinkIn = browserWindow.openWebLinkIn; const tabOpenedDefer = new Promise(resolve => { - ui._window.openWebLinkIn = newUrl => { + browserWindow.openWebLinkIn = newUrl => { // Reset the actual openWebLinkIn function before proceeding. - ui._window.openWebLinkIn = originalOpenWebLinkIn; + browserWindow.openWebLinkIn = originalOpenWebLinkIn; is(newUrl, url, "The correct tab has been opened"); resolve(); diff --git a/devtools/client/webconsole/hudservice.js b/devtools/client/webconsole/hudservice.js index 3ed5bb1b8c87..f2a899cf7309 100644 --- a/devtools/client/webconsole/hudservice.js +++ b/devtools/client/webconsole/hudservice.js @@ -16,6 +16,7 @@ loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true); loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true); loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source"); loader.lazyRequireGetter(this, "l10n", "devtools/client/webconsole/webconsole-l10n"); +loader.lazyRequireGetter(this, "openDocLink", "devtools/client/shared/link", true); const BC_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no"; // The preference prefix for all of the Browser Console filters. @@ -364,12 +365,7 @@ WebConsole.prototype = { * The URL you want to open in a new tab. */ openLink(link, e) { - const isOSX = Services.appinfo.OS == "Darwin"; - let where = "tab"; - if (e && (e.button === 1 || (e.button === 0 && (isOSX ? e.metaKey : e.ctrlKey)))) { - where = "tabshifted"; - } - this.chromeUtilsWindow.openWebLinkIn(link, where); + openDocLink(link); }, /** diff --git a/devtools/client/webconsole/test/mochitest/browser_webconsole_allow_mixedcontent_securityerrors.js b/devtools/client/webconsole/test/mochitest/browser_webconsole_allow_mixedcontent_securityerrors.js index cf6fe4942e52..5aa9ca794a61 100644 --- a/devtools/client/webconsole/test/mochitest/browser_webconsole_allow_mixedcontent_securityerrors.js +++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_allow_mixedcontent_securityerrors.js @@ -52,23 +52,10 @@ add_task(async function() { info("Clicking on the Learn More link"); const learnMoreLink = mixedActiveContentMessage.querySelector(".learn-more-link"); - let linkSimulation = await simulateLinkClick(learnMoreLink); + const linkSimulation = await simulateLinkClick(learnMoreLink); checkLink({ ...linkSimulation, expectedLink: LEARN_MORE_URI, expectedTab: "tab" }); - - const isOSX = Services.appinfo.OS == "Darwin"; - const ctrlOrCmdKeyMouseEvent = new MouseEvent("click", { - bubbles: true, - [isOSX ? "metaKey" : "ctrlKey"]: true, - view: window - }); - linkSimulation = await simulateLinkClick(learnMoreLink, ctrlOrCmdKeyMouseEvent); - checkLink({ - ...linkSimulation, - expectedLink: LEARN_MORE_URI, - expectedTab: "tabshifted" - }); }); diff --git a/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_status_code.js b/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_status_code.js index de0d08bed71c..05b3f5a9e5f5 100644 --- a/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_status_code.js +++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_status_code.js @@ -40,16 +40,12 @@ add_task(async function task() { ok(statusCodeNode.title, l10n.getStr("webConsoleMoreInfoLabel")); const { - middleMouseEvent, - ctrlOrCmdKeyMouseEvent, rightClickMouseEvent, rightClickCtrlOrCmdKeyMouseEvent, } = getMouseEvents(); const testCases = [ - { clickEvent: middleMouseEvent, link: LEARN_MORE_URI, where: "tabshifted" }, { clickEvent: null, link: LEARN_MORE_URI, where: "tab" }, - { clickEvent: ctrlOrCmdKeyMouseEvent, link: LEARN_MORE_URI, where: "tabshifted" }, { clickEvent: rightClickMouseEvent, link: null, where: null }, { clickEvent: rightClickCtrlOrCmdKeyMouseEvent, link: null, where: null } ]; @@ -75,16 +71,6 @@ add_task(async function task() { function getMouseEvents() { const isOSX = Services.appinfo.OS == "Darwin"; - const middleMouseEvent = new MouseEvent("click", { - bubbles: true, - button: 1, - view: window - }); - const ctrlOrCmdKeyMouseEvent = new MouseEvent("click", { - bubbles: true, - [isOSX ? "metaKey" : "ctrlKey"]: true, - view: window - }); const rightClickMouseEvent = new MouseEvent("contextmenu", { bubbles: true, button: 2, @@ -98,8 +84,6 @@ function getMouseEvents() { }); return { - middleMouseEvent, - ctrlOrCmdKeyMouseEvent, rightClickMouseEvent, rightClickCtrlOrCmdKeyMouseEvent, }; diff --git a/devtools/client/webconsole/test/mochitest/head.js b/devtools/client/webconsole/test/mochitest/head.js index a439a293b89e..bebd4faa11e0 100644 --- a/devtools/client/webconsole/test/mochitest/head.js +++ b/devtools/client/webconsole/test/mochitest/head.js @@ -495,16 +495,19 @@ async function closeConsole(tab = gBrowser.selectedTab) { * or null(if event not fired) */ function simulateLinkClick(element, clickEventProps) { + const browserWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); + // Override LinkIn methods to prevent navigating. - const oldOpenTrustedLinkIn = window.openTrustedLinkIn; - const oldOpenWebLinkIn = window.openWebLinkIn; + const oldOpenTrustedLinkIn = browserWindow.openTrustedLinkIn; + const oldOpenWebLinkIn = browserWindow.openWebLinkIn; const onOpenLink = new Promise((resolve) => { - window.openWebLinkIn = window.openTrustedLinkIn = function(link, where) { - window.openTrustedLinkIn = oldOpenTrustedLinkIn; - window.openWebLinkIn = oldOpenWebLinkIn; + const openLinkIn = function(link, where) { + browserWindow.openTrustedLinkIn = oldOpenTrustedLinkIn; + browserWindow.openWebLinkIn = oldOpenWebLinkIn; resolve({link: link, where}); }; + browserWindow.openWebLinkIn = browserWindow.openTrustedLinkIn = openLinkIn; if (clickEventProps) { // Click on the link using the event properties. element.dispatchEvent(clickEventProps); @@ -519,8 +522,8 @@ function simulateLinkClick(element, clickEventProps) { let timeoutId; const onTimeout = new Promise(function(resolve) { timeoutId = setTimeout(() => { - window.openTrustedLinkIn = oldOpenTrustedLinkIn; - window.openWebLinkIn = oldOpenWebLinkIn; + browserWindow.openTrustedLinkIn = oldOpenTrustedLinkIn; + browserWindow.openWebLinkIn = oldOpenWebLinkIn; timeoutId = null; resolve({link: null, where: null}); }, 1000); diff --git a/devtools/client/webconsole/utils/context-menu.js b/devtools/client/webconsole/utils/context-menu.js index 2ca48dee8f50..7d9658732bde 100644 --- a/devtools/client/webconsole/utils/context-menu.js +++ b/devtools/client/webconsole/utils/context-menu.js @@ -6,9 +6,6 @@ "use strict"; -const Services = require("Services"); -const {gDevTools} = require("devtools/client/framework/devtools"); - const Menu = require("devtools/client/framework/menu"); const MenuItem = require("devtools/client/framework/menu-item"); @@ -17,6 +14,8 @@ const { MESSAGE_SOURCE } = require("devtools/client/webconsole/constants"); const clipboardHelper = require("devtools/shared/platform/clipboard"); const { l10n } = require("devtools/client/webconsole/utils/messages"); +loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true); + /** * Create a Menu instance for the webconsole. * @@ -89,10 +88,7 @@ function createContextMenu(hud, parentNode, { if (!request) { return; } - const mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); - mainWindow.openWebLinkIn(request.url, "tab", { - triggeringPrincipal: mainWindow.document.nodePrincipal, - }); + openContentLink(request.url); }, })); diff --git a/devtools/client/webide/content/webide.js b/devtools/client/webide/content/webide.js index 3b260dcbe391..a91300e26711 100644 --- a/devtools/client/webide/content/webide.js +++ b/devtools/client/webide/content/webide.js @@ -20,6 +20,7 @@ const {GetAvailableAddons} = require("devtools/client/webide/modules/addons"); const {getJSON} = require("devtools/client/shared/getjson"); const Telemetry = require("devtools/client/shared/telemetry"); const {RuntimeScanners} = require("devtools/client/webide/modules/runtimes"); +const {openContentLink} = require("devtools/client/shared/link"); const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); @@ -177,14 +178,7 @@ var UI = { }, openInBrowser: function(url) { - // Open a URL in a Firefox window - const mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); - if (mainWindow) { - mainWindow.openWebLinkIn(url, "tab"); - mainWindow.focus(); - } else { - window.open(url); - } + openContentLink(url); }, updateTitle: function() { diff --git a/devtools/server/actors/styles.js b/devtools/server/actors/styles.js index c5deeef09b4d..0fa37b7ade3d 100644 --- a/devtools/server/actors/styles.js +++ b/devtools/server/actors/styles.js @@ -6,6 +6,7 @@ const Services = require("Services"); const protocol = require("devtools/shared/protocol"); +const {getCSSLexer} = require("devtools/shared/css/lexer"); const {LongStringActor} = require("devtools/server/actors/string"); const InspectorUtils = require("InspectorUtils"); @@ -1611,7 +1612,7 @@ function getRuleText(initialText, line, column) { const {offset: textOffset, text} = getTextAtLineColumn(initialText, line, column); - const lexer = InspectorUtils.getCSSLexer(text); + const lexer = getCSSLexer(text); // Search forward for the opening brace. while (true) { @@ -1685,7 +1686,7 @@ function getSelectorOffsets(initialText, line, column) { const {offset: textOffset, text} = getTextAtLineColumn(initialText, line, column); - const lexer = InspectorUtils.getCSSLexer(text); + const lexer = getCSSLexer(text); // Search forward for the opening brace. let endOffset; diff --git a/devtools/shared/css/lexer.js b/devtools/shared/css/lexer.js index 6057e2a2fa9b..59019ece9a94 100644 --- a/devtools/shared/css/lexer.js +++ b/devtools/shared/css/lexer.js @@ -4,7 +4,7 @@ // A CSS Lexer. This file is a bit unusual -- it is a more or less // direct translation of layout/style/nsCSSScanner.cpp and -// layout/style/CSSLexer.cpp into JS. This implements the +// layout/style/CSSLexer.cpp into JS. This implemented the // CSSLexer.webidl interface, and the intent is to try to keep it in // sync with changes to the platform CSS lexer. Due to this goal, // this file violates some naming conventions and consequently locally @@ -374,21 +374,54 @@ function Scanner(buffer) { Scanner.prototype = { /** - * @see CSSLexer.lineNumber + * The line number of the most recently returned token. Line + * numbers are 0-based. */ get lineNumber() { return this.mTokenLineNumber - 1; }, /** - * @see CSSLexer.columnNumber + * The column number of the most recently returned token. Column + * numbers are 0-based. */ get columnNumber() { return this.mTokenOffset - this.mTokenLineOffset; }, /** - * @see CSSLexer.performEOFFixup + * When EOF is reached, the last token might be unterminated in some + * ways. This method takes an input string and appends the needed + * terminators. In particular: + * + * 1. If EOF occurs mid-string, this will append the correct quote. + * 2. If EOF occurs in a url token, this will append the close paren. + * 3. If EOF occurs in a comment this will append the comment closer. + * + * A trailing backslash might also have been present in the input + * string. This is handled in different ways, depending on the + * context and arguments. + * + * If preserveBackslash is true, then the existing backslash at the + * end of inputString is preserved, and a new backslash is appended. + * That is, the input |\| is transformed to |\\|, and the + * input |'\| is transformed to |'\\'|. + * + * Otherwise, preserveBackslash is false: + * If the backslash appears in a string context, then the trailing + * backslash is dropped from inputString. That is, |"\| is + * transformed to |""|. + * If the backslash appears outside of a string context, then + * U+FFFD is appended. That is, |\| is transformed to a string + * with two characters: backslash followed by U+FFFD. + * + * Passing false for preserveBackslash makes the result conform to + * the CSS Syntax specification. However, passing true may give + * somewhat more intuitive behavior. + * + * @param inputString the input string + * @param preserveBackslash how to handle trailing backslashes + * @return the input string with the termination characters appended */ performEOFFixup: function(aInputString, aPreserveBackslash) { let result = aInputString; @@ -416,7 +449,52 @@ Scanner.prototype = { }, /** - * @see CSSLexer.nextToken + * Return the next token, or null at EOF. + * + * The token object is described by the following WebIDL definition: + * + * dictionary CSSToken { + * // The token type. + * CSSTokenType tokenType = "whitespace"; + * + * // Offset of the first character of the token. + * unsigned long startOffset = 0; + * // Offset of the character after the final character of the token. + * // This is chosen so that the offsets can be passed to |substring| + * // to yield the exact contents of the token. + * unsigned long endOffset = 0; + * + * // If the token is a number, percentage, or dimension, this holds + * // the value. This is not present for other token types. + * double number; + * // If the token is a number, percentage, or dimension, this is true + * // iff the number had an explicit sign. This is not present for + * // other token types. + * boolean hasSign; + * // If the token is a number, percentage, or dimension, this is true + * // iff the number was specified as an integer. This is not present + * // for other token types. + * boolean isInteger; + * + * // Text associated with the token. This is not present for all + * // token types. In particular it is: + * // + * // Token type Meaning + * // =============================== + * // ident The identifier. + * // function The function name. Note that the "(" is part + * // of the token but is not present in |text|. + * // at The word. + * // id The word. + * // hash The word. + * // dimension The dimension. + * // string The string contents after escape processing. + * // bad_string Ditto. + * // url The URL after escape processing. + * // bad_url Ditto. + * // symbol The symbol text. + * DOMString text; + * }; */ nextToken: function() { const token = {}; @@ -1245,8 +1323,7 @@ Scanner.prototype = { }; /** - * Create and return a new CSS lexer, conforming to the @see CSSLexer - * webidl interface. + * Create and return a new CSS lexer. * * @param {String} input the CSS text to lex * @return {CSSLexer} the new lexer diff --git a/devtools/shared/gcli/commands/screenshot.js b/devtools/shared/gcli/commands/screenshot.js index 8cf9238899d5..caacb73f98ee 100644 --- a/devtools/shared/gcli/commands/screenshot.js +++ b/devtools/shared/gcli/commands/screenshot.js @@ -13,6 +13,8 @@ const { getRect } = require("devtools/shared/layout/utils"); const defer = require("devtools/shared/defer"); const { Task } = require("devtools/shared/task"); +loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true); + loader.lazyImporter(this, "Downloads", "resource://gre/modules/Downloads.jsm"); loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm"); loader.lazyImporter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm"); @@ -160,10 +162,7 @@ exports.items = [ root.style.cursor = "pointer"; root.addEventListener("click", () => { if (imageSummary.href) { - const mainWindow = context.environment.chromeWindow; - mainWindow.openWebLinkIn(imageSummary.href, "tab", { - triggeringPrincipal: document.nodePrincipal, - }); + openContentLink(imageSummary.href); } else if (imageSummary.filename) { const file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); file.initWithPath(imageSummary.filename); diff --git a/devtools/shared/tests/unit/test_csslexer.js b/devtools/shared/tests/unit/test_csslexer.js index 58f3e17ff051..69b32d3546af 100644 --- a/devtools/shared/tests/unit/test_csslexer.js +++ b/devtools/shared/tests/unit/test_csslexer.js @@ -3,79 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -// This file is a copy of layout/style/test/test_csslexer.js, modified -// to use both our pure-JS lexer and the DOMUtils lexer for -// cross-checking. - "use strict"; const jsLexer = require("devtools/shared/css/lexer"); -const InspectorUtils = require("InspectorUtils"); - -// An object that acts like a CSSLexer but verifies that the DOM lexer -// and the JS lexer do the same thing. -function DoubleLexer(input) { - info("DoubleLexer input: " + input); - this.domLexer = InspectorUtils.getCSSLexer(input); - this.jsLexer = jsLexer.getCSSLexer(input); -} - -DoubleLexer.prototype = { - checkState: function() { - equal(this.domLexer.lineNumber, this.jsLexer.lineNumber, - "check line number"); - equal(this.domLexer.columnNumber, this.jsLexer.columnNumber, - "check column number"); - }, - - get lineNumber() { - return this.domLexer.lineNumber; - }, - - get columnNumber() { - return this.domLexer.columnNumber; - }, - - performEOFFixup: function(inputString, preserveBackslash) { - const d = this.domLexer.performEOFFixup(inputString, preserveBackslash); - const j = this.jsLexer.performEOFFixup(inputString, preserveBackslash); - - equal(d, j); - return d; - }, - - mungeNumber: function(token) { - if (token && (token.tokenType === "number" || - token.tokenType === "percentage") && - !token.isInteger) { - // The JS lexer does its computations in double, but the - // platform lexer does its computations in float. Account for - // this discrepancy in a way that's sufficient for this test. - // See https://bugzilla.mozilla.org/show_bug.cgi?id=1163047 - token.number = parseFloat(token.number.toPrecision(8)); - } - }, - - nextToken: function() { - // Check state both before and after. - this.checkState(); - - const d = this.domLexer.nextToken(); - const j = this.jsLexer.nextToken(); - - this.mungeNumber(d); - this.mungeNumber(j); - - deepEqual(d, j); - - this.checkState(); - - return d; - } -}; function test_lexer(cssText, tokenTypes) { - const lexer = new DoubleLexer(cssText); + const lexer = jsLexer.getCSSLexer(cssText); let reconstructed = ""; let lastTokenEnd = 0; let i = 0; @@ -149,7 +82,7 @@ var LEX_TESTS = [ ]; function test_lexer_linecol(cssText, locations) { - const lexer = new DoubleLexer(cssText); + const lexer = jsLexer.getCSSLexer(cssText); let i = 0; while (true) { const token = lexer.nextToken(); @@ -176,7 +109,7 @@ function test_lexer_linecol(cssText, locations) { function test_lexer_eofchar(cssText, argText, expectedAppend, expectedNoAppend) { - const lexer = new DoubleLexer(cssText); + const lexer = jsLexer.getCSSLexer(cssText); while (lexer.nextToken()) { // Nothing. } diff --git a/dom/animation/AnimationEffect.cpp b/dom/animation/AnimationEffect.cpp index f80bf5d37063..22490924f93a 100644 --- a/dom/animation/AnimationEffect.cpp +++ b/dom/animation/AnimationEffect.cpp @@ -35,6 +35,15 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationEffect) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END +AnimationEffect::AnimationEffect(nsIDocument* aDocument, + const TimingParams& aTiming) + : mDocument(aDocument) + , mTiming(aTiming) +{ +} + +AnimationEffect::~AnimationEffect() = default; + // https://drafts.csswg.org/web-animations/#current bool AnimationEffect::IsCurrent() const diff --git a/dom/animation/AnimationEffect.h b/dom/animation/AnimationEffect.h index c67cdaf11159..c5c0c757a0cf 100644 --- a/dom/animation/AnimationEffect.h +++ b/dom/animation/AnimationEffect.h @@ -34,11 +34,7 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AnimationEffect) - AnimationEffect(nsIDocument* aDocument, const TimingParams& aTiming) - : mDocument(aDocument) - , mTiming(aTiming) - { - } + AnimationEffect(nsIDocument* aDocument, const TimingParams& aTiming); virtual KeyframeEffect* AsKeyframeEffect() { return nullptr; } @@ -95,7 +91,7 @@ public: virtual bool AffectsGeometry() const = 0; protected: - virtual ~AnimationEffect() = default; + virtual ~AnimationEffect(); Nullable GetLocalTime() const; diff --git a/dom/base/DOMPrefsInternal.h b/dom/base/DOMPrefsInternal.h index 9d476f16f454..6f941ecfb44a 100644 --- a/dom/base/DOMPrefsInternal.h +++ b/dom/base/DOMPrefsInternal.h @@ -23,7 +23,6 @@ DOM_PREF(ImageBitmapExtensionsEnabled, "canvas.imagebitmap_extensions.enabled") DOM_PREF(DOMCachesEnabled, "dom.caches.enabled") DOM_PREF(DOMCachesTestingEnabled, "dom.caches.testing.enabled") DOM_PREF(PerformanceLoggingEnabled, "dom.performance.enable_user_timing_logging") -DOM_PREF(SchedulerLoggingEnabled, "dom.performance.enable_scheduler_timing") DOM_PREF(NotificationEnabled, "dom.webnotifications.enabled") DOM_PREF(NotificationEnabledInServiceWorkers, "dom.webnotifications.serviceworker.enabled") DOM_PREF(NotificationRIEnabled, "dom.webnotifications.requireinteraction.enabled") diff --git a/dom/base/DocGroup.cpp b/dom/base/DocGroup.cpp index 48990c091ae3..33bc40bedbdb 100644 --- a/dom/base/DocGroup.cpp +++ b/dom/base/DocGroup.cpp @@ -5,9 +5,9 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/DocGroup.h" -#include "mozilla/dom/DOMPrefs.h" #include "mozilla/dom/DOMTypes.h" #include "mozilla/dom/TabGroup.h" +#include "mozilla/StaticPrefs.h" #include "mozilla/Telemetry.h" #include "nsIDocShell.h" #include "nsDOMMutationObserver.h" @@ -52,8 +52,8 @@ DocGroup::DocGroup(TabGroup* aTabGroup, const nsACString& aKey) : mKey(aKey), mTabGroup(aTabGroup) { // This method does not add itself to mTabGroup->mDocGroups as the caller does it for us. - if (mozilla::dom::DOMPrefs::SchedulerLoggingEnabled()) { - mPerformanceCounter = new mozilla::PerformanceCounter(aKey); + if (mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) { + mPerformanceCounter = new mozilla::PerformanceCounter(NS_LITERAL_CSTRING("DocGroup:") + aKey); } } diff --git a/dom/chrome-webidl/InspectorUtils.webidl b/dom/chrome-webidl/InspectorUtils.webidl index e7354459adb8..a5559274c844 100644 --- a/dom/chrome-webidl/InspectorUtils.webidl +++ b/dom/chrome-webidl/InspectorUtils.webidl @@ -20,7 +20,6 @@ namespace InspectorUtils { unsigned long getRuleColumn(CSSRule rule); unsigned long getRelativeRuleLine(CSSRule rule); boolean hasRulesModifiedByCSSOM(CSSStyleSheet sheet); - [NewObject] CSSLexer getCSSLexer(DOMString text); unsigned long getSelectorCount(CSSStyleRule rule); [Throws] DOMString getSelectorText(CSSStyleRule rule, unsigned long selectorIndex); diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 8d1ef9d6dd70..2a16e66d17bf 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -19,6 +19,7 @@ #include "mozilla/Preferences.h" #include "mozilla/ProcessHangMonitorIPC.h" #include "mozilla/Unused.h" +#include "mozilla/StaticPrefs.h" #include "mozilla/TelemetryIPC.h" #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h" #include "mozilla/docshell/OfflineCacheUpdateChild.h" @@ -1386,7 +1387,7 @@ ContentChild::GetResultForRenderingInitFailure(base::ProcessId aOtherPid) mozilla::ipc::IPCResult ContentChild::RecvRequestPerformanceMetrics() { - MOZ_ASSERT(mozilla::dom::DOMPrefs::SchedulerLoggingEnabled()); + MOZ_ASSERT(mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()); nsTArray info; CollectPerformanceInfo(info); SendAddPerformanceMetrics(info); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index eeb3c8c1868f..36650669d0c2 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -96,6 +96,7 @@ #include "mozilla/ScriptPreloader.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" +#include "mozilla/StaticPrefs.h" #include "mozilla/Telemetry.h" #include "mozilla/TelemetryIPC.h" #include "mozilla/WebBrowserPersistDocumentParent.h" @@ -3310,7 +3311,7 @@ ContentParent::RecvFinishMemoryReport(const uint32_t& aGeneration) mozilla::ipc::IPCResult ContentParent::RecvAddPerformanceMetrics(nsTArray&& aMetrics) { - if (!mozilla::dom::DOMPrefs::SchedulerLoggingEnabled()) { + if (!mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) { // The pref is off, we should not get a performance metrics from the content // child return IPC_OK(); diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 84a832dc21a8..ac082f586832 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -51,10 +51,9 @@ using nscolor from "nsColor.h"; using class mozilla::WidgetCompositionEvent from "ipc/nsGUIEventIPC.h"; using struct mozilla::widget::IMENotification from "mozilla/widget/IMEData.h"; using struct mozilla::widget::IMENotificationRequests from "mozilla/widget/IMEData.h"; -using mozilla::widget::IMEState::Enabled from "mozilla/widget/IMEData.h"; -using mozilla::widget::IMEState::Open from "mozilla/widget/IMEData.h"; -using mozilla::widget::InputContextAction::Cause from "mozilla/widget/IMEData.h"; -using mozilla::widget::InputContextAction::FocusChange from "mozilla/widget/IMEData.h"; +using struct mozilla::widget::IMEState from "mozilla/widget/IMEData.h"; +using struct mozilla::widget::InputContext from "mozilla/widget/IMEData.h"; +using struct mozilla::widget::InputContextAction from "mozilla/widget/IMEData.h"; using mozilla::gfx::IntSize from "mozilla/gfx/Point.h"; using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h"; using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h"; @@ -355,17 +354,10 @@ parent: nsCString[] enabledCommands, nsCString[] disabledCommands); - nested(inside_cpow) sync GetInputContext() returns (Enabled IMEEnabled, - Open IMEOpen); + nested(inside_cpow) sync GetInputContext() returns (IMEState state); - nested(inside_cpow) async SetInputContext(Enabled IMEEnabled, - Open IMEOpen, - nsString type, - nsString inputmode, - nsString actionHint, - bool inPrivateBrowsing, - Cause cause, - FocusChange focusChange); + nested(inside_cpow) async SetInputContext(InputContext context, + InputContextAction action); sync IsParentWindowMainWidgetVisible() returns (bool visible); diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index edd2a2a1ab7b..5fb091296ebb 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2452,46 +2452,25 @@ TabParent::RecvDefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent) } mozilla::ipc::IPCResult -TabParent::RecvGetInputContext(IMEState::Enabled* aIMEEnabled, - IMEState::Open* aIMEOpen) +TabParent::RecvGetInputContext(widget::IMEState* aState) { nsCOMPtr widget = GetWidget(); if (!widget) { - *aIMEEnabled = IMEState::DISABLED; - *aIMEOpen = IMEState::OPEN_STATE_NOT_SUPPORTED; + *aState = widget::IMEState(IMEState::DISABLED, + IMEState::OPEN_STATE_NOT_SUPPORTED); return IPC_OK(); } - InputContext context = widget->GetInputContext(); - *aIMEEnabled = context.mIMEState.mEnabled; - *aIMEOpen = context.mIMEState.mOpen; + *aState = widget->GetInputContext().mIMEState; return IPC_OK(); } mozilla::ipc::IPCResult TabParent::RecvSetInputContext( - const IMEState::Enabled& aIMEEnabled, - const IMEState::Open& aIMEOpen, - const nsString& aType, - const nsString& aInputmode, - const nsString& aActionHint, - const bool& aInPrivateBrowsing, - const InputContextAction::Cause& aCause, - const InputContextAction::FocusChange& aFocusChange) + const InputContext& aContext, + const InputContextAction& aAction) { - InputContext context; - context.mIMEState.mEnabled = aIMEEnabled; - context.mIMEState.mOpen = aIMEOpen; - context.mHTMLInputType.Assign(aType); - context.mHTMLInputInputmode.Assign(aInputmode); - context.mActionHint.Assign(aActionHint); - context.mOrigin = InputContext::ORIGIN_CONTENT; - context.mInPrivateBrowsing = aInPrivateBrowsing; - - InputContextAction action(aCause, aFocusChange); - - IMEStateManager::SetInputContextForChildProcess(this, context, action); - + IMEStateManager::SetInputContextForChildProcess(this, aContext, aAction); return IPC_OK(); } diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index ce7f2b9edc94..2c4ceb13c3b2 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -241,18 +241,11 @@ public: RecvDefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent) override; virtual mozilla::ipc::IPCResult RecvGetInputContext( - widget::IMEState::Enabled* aIMEEnabled, - widget::IMEState::Open* aIMEOpen) override; + widget::IMEState* aIMEState) override; virtual mozilla::ipc::IPCResult RecvSetInputContext( - const widget::IMEState::Enabled& aIMEEnabled, - const widget::IMEState::Open& aIMEOpen, - const nsString& aType, - const nsString& aInputmode, - const nsString& aActionHint, - const bool& aInPrivateBrowsing, - const widget::InputContextAction::Cause& aCause, - const widget::InputContextAction::FocusChange& aFocusChange) override; + const widget::InputContext& aContext, + const widget::InputContextAction& aAction) override; // See nsIKeyEventInPluginCallback virtual void HandledWindowedPluginKeyEvent( diff --git a/dom/tests/browser/browser_test_performance_metrics.js b/dom/tests/browser/browser_test_performance_metrics.js index db2c2a5034dd..d38de25f8574 100644 --- a/dom/tests/browser/browser_test_performance_metrics.js +++ b/dom/tests/browser/browser_test_performance_metrics.js @@ -63,20 +63,38 @@ add_task(async function test() { await BrowserTestUtils.withNewTab({ gBrowser, url: WORKER_URL }, async function(browser) { // grab events.. - var events = []; + let worker_duration = 0; + let worker_total = 0; + let duration = 0; + let total = 0; + function getInfoFromService(subject, topic, value) { subject = subject.QueryInterface(Ci.nsIMutableArray); let enumerator = subject.enumerate(); while (enumerator.hasMoreElements()) { - let item = enumerator.getNext(); - item = item.QueryInterface(Ci.nsIPerformanceMetricsData); - if (item.pid == Services.appinfo.processID) { + let entry = enumerator.getNext(); + entry = entry.QueryInterface(Ci.nsIPerformanceMetricsData); + if (entry.pid == Services.appinfo.processID) { parent_process_event = true; } - if (item.worker) { + if (entry.worker) { worker_event = true; + worker_duration += entry.duration; + } else { + duration += entry.duration; + } + // let's look at the XPCOM data we got back + let items = entry.items.QueryInterface(Ci.nsIMutableArray); + let enumerator2 = items.enumerate(); + while (enumerator2.hasMoreElements()) { + let item = enumerator2.getNext(); + item = item.QueryInterface(Ci.nsIPerformanceMetricsDispatchCategory); + if (entry.worker) { + worker_total += item.count; + } else { + total += item.count; + } } - events.push(item); } } @@ -85,28 +103,15 @@ add_task(async function test() { // wait until we get some events back by triggering requestPerformanceMetrics await BrowserTestUtils.waitForCondition(() => { ChromeUtils.requestPerformanceMetrics(); - return events.length > 10; - }, "wait for events to come in", 500, 10); + return worker_duration > 0 && duration > 0 && parent_process_event; + }, "wait for events to come in", 250, 20); BrowserTestUtils.removeTab(page1); BrowserTestUtils.removeTab(page2); BrowserTestUtils.removeTab(page3); - // let's check the events - let duration = 0; - let total = 0; - for (let i=0; i < events.length; i++) { - duration += events[i].duration; - // let's look at the XPCOM data we got back - let items = events[i].items.QueryInterface(Ci.nsIMutableArray); - let enumerator = items.enumerate(); - while (enumerator.hasMoreElements()) { - let item = enumerator.getNext(); - item = item.QueryInterface(Ci.nsIPerformanceMetricsDispatchCategory); - total += item.count; - } - } - + Assert.ok(worker_duration > 0, "Worker duration should be positive"); + Assert.ok(worker_total > 0, "Worker count should be positive"); Assert.ok(duration > 0, "Duration should be positive"); Assert.ok(total > 0, "Should get a positive count"); Assert.ok(parent_process_event, "parent process sent back some events"); diff --git a/dom/tests/browser/ping_worker.html b/dom/tests/browser/ping_worker.html index d2c4e55c74d6..aa5ea2c43384 100644 --- a/dom/tests/browser/ping_worker.html +++ b/dom/tests/browser/ping_worker.html @@ -1,12 +1,13 @@ + diff --git a/dom/webidl/CSSLexer.webidl b/dom/webidl/CSSLexer.webidl deleted file mode 100644 index d75640b53561..000000000000 --- a/dom/webidl/CSSLexer.webidl +++ /dev/null @@ -1,168 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// The possible values for CSSToken.tokenType. -enum CSSTokenType { - // Whitespace. - "whitespace", - // A CSS comment. - "comment", - // An identifier. |text| holds the identifier text. - "ident", - // A function token. |text| holds the function name. Note that the - // function token includes (i.e., consumes) the "(" -- but this is - // not included in |text|. - "function", - // "@word". |text| holds "word", without the "@". - "at", - // "#word". |text| holds "word", without the "#". - "id", - // "#word". ID is used when "word" would have been a valid IDENT - // token without the "#"; otherwise, HASH is used. - "hash", - // A number. - "number", - // A dimensioned number. - "dimension", - // A percentage. - "percentage", - // A string. - "string", - // A "bad string". This can only be returned when a string is - // unterminated at EOF. (However, currently the lexer returns - // ordinary STRING tokens in this situation.) - "bad_string", - // A URL. |text| holds the URL. - "url", - // A "bad URL". This is a URL that is unterminated at EOF. |text| - // holds the URL. - "bad_url", - // A "symbol" is any one-character symbol. This corresponds to the - // DELIM token in the CSS specification. - "symbol", - // The "~=" token. - "includes", - // The "|=" token. - "dashmatch", - // The "^=" token. - "beginsmatch", - // The "$=" token. - "endsmatch", - // The "*=" token. - "containsmatch", - // A unicode-range token. This is currently not fully represented - // by CSSToken. - "urange", - // HTML comment delimiters, either "". Note that each - // is emitted as a separate token, and the intervening text is lexed - // as normal; whereas ordinary CSS comments are lexed as a unit. - "htmlcomment" -}; - -dictionary CSSToken { - // The token type. - CSSTokenType tokenType = "whitespace"; - - // Offset of the first character of the token. - unsigned long startOffset = 0; - // Offset of the character after the final character of the token. - // This is chosen so that the offsets can be passed to |substring| - // to yield the exact contents of the token. - unsigned long endOffset = 0; - - // If the token is a number, percentage, or dimension, this holds - // the value. This is not present for other token types. - double number; - // If the token is a number, percentage, or dimension, this is true - // iff the number had an explicit sign. This is not present for - // other token types. - boolean hasSign; - // If the token is a number, percentage, or dimension, this is true - // iff the number was specified as an integer. This is not present - // for other token types. - boolean isInteger; - - // Text associated with the token. This is not present for all - // token types. In particular it is: - // - // Token type Meaning - // =============================== - // ident The identifier. - // function The function name. Note that the "(" is part - // of the token but is not present in |text|. - // at The word. - // id The word. - // hash The word. - // dimension The dimension. - // string The string contents after escape processing. - // bad_string Ditto. - // url The URL after escape processing. - // bad_url Ditto. - // symbol The symbol text. - DOMString text; -}; - -/** - * CSSLexer is an interface to the CSS lexer. It tokenizes an - * input stream and returns CSS tokens. - * - * @see InspectorUtils.getCSSLexer to create an instance of the lexer. - */ -[ChromeOnly] -interface CSSLexer -{ - /** - * The line number of the most recently returned token. Line - * numbers are 0-based. - */ - readonly attribute unsigned long lineNumber; - - /** - * The column number of the most recently returned token. Column - * numbers are 0-based. - */ - readonly attribute unsigned long columnNumber; - - /** - * When EOF is reached, the last token might be unterminated in some - * ways. This method takes an input string and appends the needed - * terminators. In particular: - * - * 1. If EOF occurs mid-string, this will append the correct quote. - * 2. If EOF occurs in a url token, this will append the close paren. - * 3. If EOF occurs in a comment this will append the comment closer. - * - * A trailing backslash might also have been present in the input - * string. This is handled in different ways, depending on the - * context and arguments. - * - * If preserveBackslash is true, then the existing backslash at the - * end of inputString is preserved, and a new backslash is appended. - * That is, the input |\| is transformed to |\\|, and the - * input |'\| is transformed to |'\\'|. - * - * Otherwise, preserveBackslash is false: - * If the backslash appears in a string context, then the trailing - * backslash is dropped from inputString. That is, |"\| is - * transformed to |""|. - * If the backslash appears outside of a string context, then - * U+FFFD is appended. That is, |\| is transformed to a string - * with two characters: backslash followed by U+FFFD. - * - * Passing false for preserveBackslash makes the result conform to - * the CSS Syntax specification. However, passing true may give - * somewhat more intuitive behavior. - * - * @param inputString the input string - * @param preserveBackslash how to handle trailing backslashes - * @return the input string with the termination characters appended - */ - DOMString performEOFFixup(DOMString inputString, boolean preserveBackslash); - - /** - * Return the next token, or null at EOF. - */ - CSSToken? nextToken(); -}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 649918def9aa..1b5a60520998 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -436,7 +436,6 @@ WEBIDL_FILES = [ 'CSSImportRule.webidl', 'CSSKeyframeRule.webidl', 'CSSKeyframesRule.webidl', - 'CSSLexer.webidl', 'CSSMediaRule.webidl', 'CSSMozDocumentRule.webidl', 'CSSNamespaceRule.webidl', diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 24651e0a5884..3ecb9b0c2559 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -9,11 +9,11 @@ #include "js/MemoryMetrics.h" #include "MessageEventRunnable.h" #include "mozilla/ScopeExit.h" +#include "mozilla/StaticPrefs.h" #include "mozilla/dom/ClientManager.h" #include "mozilla/dom/ClientSource.h" #include "mozilla/dom/ClientState.h" #include "mozilla/dom/Console.h" -#include "mozilla/dom/DOMPrefs.h" #include "mozilla/dom/DOMTypes.h" #include "mozilla/dom/ErrorEvent.h" #include "mozilla/dom/ErrorEventBinding.h" @@ -431,7 +431,8 @@ private: // Let's be sure that it is created before any // content loading. aWorkerPrivate->EnsurePerformanceStorage(); - if (mozilla::dom::DOMPrefs::SchedulerLoggingEnabled()) { + + if (mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) { aWorkerPrivate->EnsurePerformanceCounter(); } @@ -5381,16 +5382,16 @@ void WorkerPrivate::EnsurePerformanceCounter() { AssertIsOnWorkerThread(); - MOZ_ASSERT(mozilla::dom::DOMPrefs::SchedulerLoggingEnabled()); + MOZ_ASSERT(mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()); if (!mPerformanceCounter) { - mPerformanceCounter = new PerformanceCounter(NS_ConvertUTF16toUTF8(mWorkerName)); + nsPrintfCString workerName("Worker:%s", NS_ConvertUTF16toUTF8(mWorkerName).get()); + mPerformanceCounter = new PerformanceCounter(workerName); } } PerformanceCounter* WorkerPrivate::GetPerformanceCounter() { - MOZ_ASSERT(mPerformanceCounter); return mPerformanceCounter; } diff --git a/dom/workers/WorkerThread.cpp b/dom/workers/WorkerThread.cpp index d2957197dbbb..5fbca6790a27 100644 --- a/dom/workers/WorkerThread.cpp +++ b/dom/workers/WorkerThread.cpp @@ -11,6 +11,7 @@ #include "EventQueue.h" #include "mozilla/ThreadEventQueue.h" #include "mozilla/PerformanceCounter.h" +#include "mozilla/StaticPrefs.h" #include "nsIThreadInternal.h" #include "WorkerPrivate.h" #include "WorkerRunnable.h" @@ -148,6 +149,21 @@ WorkerThread::SetWorker(const WorkerThreadFriendKey& /* aKey */, } } +void +WorkerThread::IncrementDispatchCounter() +{ + if (!mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) { + return; + } + MutexAutoLock lock(mLock); + if (mWorkerPrivate) { + PerformanceCounter* performanceCounter = mWorkerPrivate->GetPerformanceCounter(); + if (performanceCounter) { + performanceCounter->IncrementDispatchCounter(DispatchCategory::Worker); + } + } +} + nsresult WorkerThread::DispatchPrimaryRunnable(const WorkerThreadFriendKey& /* aKey */, already_AddRefed aRunnable) @@ -194,6 +210,10 @@ WorkerThread::DispatchAnyThread(const WorkerThreadFriendKey& /* aKey */, } } #endif + + // Increment the PerformanceCounter dispatch count + // to keep track of how many runnables are executed. + IncrementDispatchCounter(); nsCOMPtr runnable(aWorkerRunnable); nsresult rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); @@ -228,12 +248,6 @@ WorkerThread::Dispatch(already_AddRefed aRunnable, uint32_t aFlags) const bool onWorkerThread = PR_GetCurrentThread() == mThread; - if (GetSchedulerLoggingEnabled() && onWorkerThread && mWorkerPrivate) { - PerformanceCounter* performanceCounter = mWorkerPrivate->GetPerformanceCounter(); - if (performanceCounter) { - performanceCounter->IncrementDispatchCounter(DispatchCategory::Worker); - } - } #ifdef DEBUG if (runnable && !onWorkerThread) { @@ -272,6 +286,9 @@ WorkerThread::Dispatch(already_AddRefed aRunnable, uint32_t aFlags) } } + // Increment the PerformanceCounter dispatch count + // to keep track of how many runnables are executed. + IncrementDispatchCounter(); nsresult rv; if (runnable && onWorkerThread) { RefPtr workerRunnable = workerPrivate->MaybeWrapAsWorkerRunnable(runnable.forget()); diff --git a/dom/workers/WorkerThread.h b/dom/workers/WorkerThread.h index 9219bbb954f1..f11bbafa87e7 100644 --- a/dom/workers/WorkerThread.h +++ b/dom/workers/WorkerThread.h @@ -100,6 +100,9 @@ private: NS_IMETHOD DelayedDispatch(already_AddRefed, uint32_t) override; + + void + IncrementDispatchCounter(); }; } // namespace dom diff --git a/layout/generic/BlockReflowInput.cpp b/layout/generic/BlockReflowInput.cpp index 2bfdef0ea08d..b758d5200457 100644 --- a/layout/generic/BlockReflowInput.cpp +++ b/layout/generic/BlockReflowInput.cpp @@ -484,7 +484,7 @@ BlockReflowInput::RecoverFloats(nsLineList::iterator aLine, { WritingMode wm = mReflowInput.GetWritingMode(); if (aLine->HasFloats()) { - // Place the floats into the space-manager again. Also slide + // Place the floats into the float manager again. Also slide // them, just like the regular frames on the line. nsFloatCache* fc = aLine->GetFirstFloat(); while (fc) { diff --git a/layout/generic/BlockReflowInput.h b/layout/generic/BlockReflowInput.h index 4fab2a5f0ceb..bac1f3e44e90 100644 --- a/layout/generic/BlockReflowInput.h +++ b/layout/generic/BlockReflowInput.h @@ -254,7 +254,7 @@ public: // The coordinates within the float manager where the block is being // placed after taking into account the blocks border and // padding. This, therefore, represents the inner "content area" (in - // spacemanager coordinates) where child frames will be placed, + // float manager coordinates) where child frames will be placed, // including child blocks and floats. nscoord mFloatManagerI, mFloatManagerB; diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 664ce91ee940..534702dd9da3 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -4052,7 +4052,7 @@ nsBlockFrame::DoReflowInlineFrames(BlockReflowInput& aState, "redo line on totally empty line with non-empty band..."); // We should never hit this case if we've placed floats on the // line; if we have, then the GetFloatAvailableSpace call is wrong - // and needs to happen after the caller pops the space manager + // and needs to happen after the caller pops the float manager // state. aState.FloatManager()->AssertStateMatches(aFloatStateBeforeLine); @@ -6519,7 +6519,7 @@ nsBlockFrame::RecoverFloatsFor(nsIFrame* aFrame, // Only blocks have floats nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame); - // Don't recover any state inside a block that has its own space manager + // Don't recover any state inside a block that has its own float manager // (we don't currently have any blocks like this, though, thanks to our // use of extra frames for 'overflow') if (block && !nsBlockFrame::BlockNeedsFloatManager(block)) { diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 6e860c6f1dc2..dd2d38384f1f 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -7749,7 +7749,7 @@ nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix, uint32_t aFlags) con if (mContent) { aTo += nsPrintfCString(" [content=%p]", static_cast(mContent)); } - aTo += nsPrintfCString(" [sc=%p", static_cast(mComputedStyle)); + aTo += nsPrintfCString(" [cs=%p", static_cast(mComputedStyle)); if (mComputedStyle) { nsAtom* pseudoTag = mComputedStyle->GetPseudo(); if (pseudoTag) { diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index b06079d3c779..915cf2527caf 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -45,8 +45,6 @@ using namespace mozilla; //---------------------------------------------------------------------- -#define FIX_BUG_50257 - nsLineLayout::nsLineLayout(nsPresContext* aPresContext, nsFloatManager* aFloatManager, const ReflowInput* aOuterReflowInput, @@ -904,7 +902,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, // Note that we don't bother positioning the frame yet, because we're probably // going to end up moving it when we do the block-direction alignment. - // Adjust spacemanager coordinate system for the frame. + // Adjust float manager coordinate system for the frame. ReflowOutput reflowOutput(lineWM); #ifdef DEBUG reflowOutput.ISize(lineWM) = nscoord(0xdeadbeef); @@ -1336,7 +1334,6 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, return true; } -#ifdef FIX_BUG_50257 // another special case: always place a BR if (pfd->mFrame->IsBrFrame()) { #ifdef NOISY_CAN_PLACE_FRAME @@ -1344,7 +1341,6 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, #endif return true; } -#endif if (aNotSafeToBreak) { // There are no frames on the line that take up width and the line is diff --git a/layout/inspector/InspectorUtils.cpp b/layout/inspector/InspectorUtils.cpp index 60432cd3213b..d2505eebf7e7 100644 --- a/layout/inspector/InspectorUtils.cpp +++ b/layout/inspector/InspectorUtils.cpp @@ -30,7 +30,6 @@ #include "mozilla/StyleSheetInlines.h" #include "mozilla/dom/CharacterData.h" #include "mozilla/dom/Element.h" -#include "mozilla/dom/CSSLexer.h" #include "mozilla/dom/CSSStyleRule.h" #include "mozilla/dom/InspectorUtilsBinding.h" #include "mozilla/dom/ToJSValue.h" @@ -304,12 +303,6 @@ InspectorUtils::HasRulesModifiedByCSSOM(GlobalObject& aGlobal, StyleSheet& aShee return aSheet.HasModifiedRules(); } -/* static */ CSSLexer* -InspectorUtils::GetCSSLexer(GlobalObject& aGlobal, const nsAString& aText) -{ - return new CSSLexer(aText); -} - /* static */ uint32_t InspectorUtils::GetSelectorCount(GlobalObject& aGlobal, BindingStyleRule& aRule) diff --git a/layout/inspector/InspectorUtils.h b/layout/inspector/InspectorUtils.h index 5a4ced0221fc..3c65d7b5c38f 100644 --- a/layout/inspector/InspectorUtils.h +++ b/layout/inspector/InspectorUtils.h @@ -72,8 +72,6 @@ public: static bool HasRulesModifiedByCSSOM(GlobalObject& aGlobal, StyleSheet& aSheet); - static CSSLexer* GetCSSLexer(GlobalObject& aGlobal, const nsAString& aText); - // Utilities for working with selectors. We don't have a JS OM representation // of a single selector or a selector list yet, but given a rule we can index // into the selector list. diff --git a/layout/style/CSSLexer.cpp b/layout/style/CSSLexer.cpp deleted file mode 100644 index d60ec1e81d8c..000000000000 --- a/layout/style/CSSLexer.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/CSSLexer.h" -#include "js/Value.h" -#include "mozilla/dom/CSSLexerBinding.h" -#include "mozilla/dom/ToJSValue.h" - -namespace mozilla { -namespace dom { - -// Ensure that constants are consistent. - -#define CHECK(X, Y) \ - static_assert(static_cast(X) == static_cast(Y), \ - "nsCSSToken and CSSTokenType should have identical values") - -CHECK(eCSSToken_Whitespace, CSSTokenType::Whitespace); -CHECK(eCSSToken_Comment, CSSTokenType::Comment); -CHECK(eCSSToken_Ident, CSSTokenType::Ident); -CHECK(eCSSToken_Function, CSSTokenType::Function); -CHECK(eCSSToken_AtKeyword, CSSTokenType::At); -CHECK(eCSSToken_ID, CSSTokenType::Id); -CHECK(eCSSToken_Hash, CSSTokenType::Hash); -CHECK(eCSSToken_Number, CSSTokenType::Number); -CHECK(eCSSToken_Dimension, CSSTokenType::Dimension); -CHECK(eCSSToken_Percentage, CSSTokenType::Percentage); -CHECK(eCSSToken_String, CSSTokenType::String); -CHECK(eCSSToken_Bad_String, CSSTokenType::Bad_string); -CHECK(eCSSToken_URL, CSSTokenType::Url); -CHECK(eCSSToken_Bad_URL, CSSTokenType::Bad_url); -CHECK(eCSSToken_Symbol, CSSTokenType::Symbol); -CHECK(eCSSToken_Includes, CSSTokenType::Includes); -CHECK(eCSSToken_Dashmatch, CSSTokenType::Dashmatch); -CHECK(eCSSToken_Beginsmatch, CSSTokenType::Beginsmatch); -CHECK(eCSSToken_Endsmatch, CSSTokenType::Endsmatch); -CHECK(eCSSToken_Containsmatch, CSSTokenType::Containsmatch); -CHECK(eCSSToken_URange, CSSTokenType::Urange); -CHECK(eCSSToken_HTMLComment, CSSTokenType::Htmlcomment); - -#undef CHECK - -CSSLexer::CSSLexer(const nsAString& aText) - : mInput(aText) - , mScanner(mInput, 1) -{ -} - -CSSLexer::~CSSLexer() -{ -} - -bool -CSSLexer::WrapObject(JSContext* aCx, JS::Handle aGivenProto, - JS::MutableHandle aReflector) -{ - return CSSLexerBinding::Wrap(aCx, this, aGivenProto, aReflector); -} - -uint32_t -CSSLexer::LineNumber() -{ - // The scanner uses 1-based line numbers, but our callers expect - // 0-based. - return mScanner.GetLineNumber() - 1; -} - -uint32_t -CSSLexer::ColumnNumber() -{ - return mScanner.GetColumnNumber(); -} - -void -CSSLexer::PerformEOFFixup(const nsAString& aInputString, bool aPreserveBackslash, - nsAString& aResult) -{ - aResult.Append(aInputString); - uint32_t eofChars = mScanner.GetEOFCharacters(); - - if (aPreserveBackslash && - (eofChars & (nsCSSScanner::eEOFCharacters_DropBackslash | - nsCSSScanner::eEOFCharacters_ReplacementChar)) != 0) { - eofChars &= ~(nsCSSScanner::eEOFCharacters_DropBackslash | - nsCSSScanner::eEOFCharacters_ReplacementChar); - aResult.Append('\\'); - } - - if ((eofChars & nsCSSScanner::eEOFCharacters_DropBackslash) != 0 && - aResult.Length() > 0 && aResult.Last() == '\\') { - aResult.Truncate(aResult.Length() - 1); - } - - nsCSSScanner::AppendImpliedEOFCharacters(nsCSSScanner::EOFCharacters(eofChars), - aResult); -} - -void -CSSLexer::NextToken(Nullable& aResult) -{ - nsCSSToken token; - if (!mScanner.Next(token, eCSSScannerExclude_None)) { - return; - } - - CSSToken& resultToken(aResult.SetValue()); - - resultToken.mTokenType = static_cast(token.mType); - resultToken.mStartOffset = mScanner.GetTokenOffset(); - resultToken.mEndOffset = mScanner.GetTokenEndOffset(); - - switch (token.mType) { - case eCSSToken_Whitespace: - break; - - case eCSSToken_Ident: - case eCSSToken_Function: - case eCSSToken_AtKeyword: - case eCSSToken_ID: - case eCSSToken_Hash: - resultToken.mText.Construct(token.mIdent); - break; - - case eCSSToken_Dimension: - resultToken.mText.Construct(token.mIdent); - MOZ_FALLTHROUGH; - case eCSSToken_Number: - case eCSSToken_Percentage: - resultToken.mNumber.Construct(token.mNumber); - resultToken.mHasSign.Construct(token.mHasSign); - resultToken.mIsInteger.Construct(token.mIntegerValid); - break; - - case eCSSToken_String: - case eCSSToken_Bad_String: - case eCSSToken_URL: - case eCSSToken_Bad_URL: - resultToken.mText.Construct(token.mIdent); - /* Don't bother emitting the delimiter, as it is readily extracted - from the source string when needed. */ - break; - - case eCSSToken_Symbol: - resultToken.mText.Construct(nsString(&token.mSymbol, 1)); - break; - - case eCSSToken_Includes: - case eCSSToken_Dashmatch: - case eCSSToken_Beginsmatch: - case eCSSToken_Endsmatch: - case eCSSToken_Containsmatch: - case eCSSToken_URange: - break; - - case eCSSToken_Comment: - case eCSSToken_HTMLComment: - /* The comment text is easily extracted from the source string, - and is rarely useful. */ - break; - } -} - -} // namespace dom -} // namespace mozilla diff --git a/layout/style/CSSLexer.h b/layout/style/CSSLexer.h deleted file mode 100644 index 9d5a0851a2b4..000000000000 --- a/layout/style/CSSLexer.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CSSLexer_h___ -#define CSSLexer_h___ - -#include "mozilla/UniquePtr.h" -#include "nsCSSScanner.h" -#include "mozilla/dom/CSSLexerBinding.h" -#include "mozilla/dom/NonRefcountedDOMObject.h" - -namespace mozilla { -namespace dom { - -class CSSLexer : public NonRefcountedDOMObject -{ -public: - explicit CSSLexer(const nsAString&); - ~CSSLexer(); - - bool WrapObject(JSContext* aCx, JS::Handle aGivenProto, - JS::MutableHandle aReflector); - - uint32_t LineNumber(); - uint32_t ColumnNumber(); - void PerformEOFFixup(const nsAString& aInputString, bool aPreserveBackslash, - nsAString& aResult); - void NextToken(Nullable& aResult); - -private: - nsString mInput; - nsCSSScanner mScanner; -}; - -} // namespace dom -} // namespace mozilla - -#endif /* CSSLexer_h___ */ diff --git a/layout/style/ErrorReporter.cpp b/layout/style/ErrorReporter.cpp index 02f2b60f8111..5b33739d8592 100644 --- a/layout/style/ErrorReporter.cpp +++ b/layout/style/ErrorReporter.cpp @@ -14,7 +14,6 @@ #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/SystemGroup.h" -#include "nsCSSScanner.h" #include "nsIConsoleService.h" #include "nsIDocument.h" #include "nsIDocShell.h" diff --git a/layout/style/moz.build b/layout/style/moz.build index 72dd4ab300f0..7566c5e30dcf 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -38,7 +38,6 @@ EXPORTS += [ 'nsCSSProps.h', 'nsCSSPseudoElementList.h', 'nsCSSPseudoElements.h', - 'nsCSSScanner.h', 'nsCSSValue.h', 'nsDOMCSSAttrDeclaration.h', 'nsDOMCSSDeclaration.h', @@ -109,7 +108,6 @@ EXPORTS.mozilla.dom += [ 'CSSImportRule.h', 'CSSKeyframeRule.h', 'CSSKeyframesRule.h', - 'CSSLexer.h', 'CSSMediaRule.h', 'CSSMozDocumentRule.h', 'CSSNamespaceRule.h', @@ -150,7 +148,6 @@ UNIFIED_SOURCES += [ 'CSSImportRule.cpp', 'CSSKeyframeRule.cpp', 'CSSKeyframesRule.cpp', - 'CSSLexer.cpp', 'CSSMediaRule.cpp', 'CSSMozDocumentRule.cpp', 'CSSNamespaceRule.cpp', @@ -174,7 +171,6 @@ UNIFIED_SOURCES += [ 'nsComputedDOMStyle.cpp', 'nsCSSKeywords.cpp', 'nsCSSProps.cpp', - 'nsCSSScanner.cpp', 'nsCSSValue.cpp', 'nsDOMCSSAttrDeclaration.cpp', 'nsDOMCSSDeclaration.cpp', diff --git a/layout/style/nsCSSScanner.cpp b/layout/style/nsCSSScanner.cpp deleted file mode 100644 index acbced75b31e..000000000000 --- a/layout/style/nsCSSScanner.cpp +++ /dev/null @@ -1,1448 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -/* tokenization of CSS style sheets */ - -#include "nsCSSScanner.h" -#include "nsStyleUtil.h" -#include "nsISupportsImpl.h" -#include "mozilla/ArrayUtils.h" -#include "mozilla/css/ErrorReporter.h" -#include "mozilla/Likely.h" -#include - -using namespace mozilla; - -/* Character class tables and related helper functions. */ - -static const uint8_t IS_HEX_DIGIT = 0x01; -static const uint8_t IS_IDSTART = 0x02; -static const uint8_t IS_IDCHAR = 0x04; -static const uint8_t IS_URL_CHAR = 0x08; -static const uint8_t IS_HSPACE = 0x10; -static const uint8_t IS_VSPACE = 0x20; -static const uint8_t IS_SPACE = IS_HSPACE|IS_VSPACE; -static const uint8_t IS_STRING = 0x40; - -#define H IS_HSPACE -#define V IS_VSPACE -#define I IS_IDCHAR -#define J IS_IDSTART -#define U IS_URL_CHAR -#define S IS_STRING -#define X IS_HEX_DIGIT - -#define SH S|H -#define SU S|U -#define SUI S|U|I -#define SUIJ S|U|I|J -#define SUIX S|U|I|X -#define SUIJX S|U|I|J|X - -static const uint8_t gLexTable[] = { -// 00 01 02 03 04 05 06 07 - 0, S, S, S, S, S, S, S, -// 08 TAB LF 0B FF CR 0E 0F - S, SH, V, S, V, V, S, S, -// 10 11 12 13 14 15 16 17 - S, S, S, S, S, S, S, S, -// 18 19 1A 1B 1C 1D 1E 1F - S, S, S, S, S, S, S, S, -//SPC ! " # $ % & ' - SH, SU, 0, SU, SU, SU, SU, 0, -// ( ) * + , - . / - S, S, SU, SU, SU, SUI, SU, SU, -// 0 1 2 3 4 5 6 7 - SUIX, SUIX, SUIX, SUIX, SUIX, SUIX, SUIX, SUIX, -// 8 9 : ; < = > ? - SUIX, SUIX, SU, SU, SU, SU, SU, SU, -// @ A B C D E F G - SU,SUIJX,SUIJX,SUIJX,SUIJX,SUIJX,SUIJX, SUIJ, -// H I J K L M N O - SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, -// P Q R S T U V W - SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, -// X Y Z [ \ ] ^ _ - SUIJ, SUIJ, SUIJ, SU, J, SU, SU, SUIJ, -// ` a b c d e f g - SU,SUIJX,SUIJX,SUIJX,SUIJX,SUIJX,SUIJX, SUIJ, -// h i j k l m n o - SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, -// p q r s t u v w - SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, SUIJ, -// x y z { | } ~ 7F - SUIJ, SUIJ, SUIJ, SU, SU, SU, SU, S, -}; - -static_assert(MOZ_ARRAY_LENGTH(gLexTable) == 128, - "gLexTable expected to cover all 128 ASCII characters"); - -#undef I -#undef J -#undef U -#undef S -#undef X -#undef SH -#undef SU -#undef SUI -#undef SUIJ -#undef SUIX -#undef SUIJX - -/** - * True if 'ch' is in character class 'cls', which should be one of - * the constants above or some combination of them. All characters - * above U+007F are considered to be in 'cls'. EOF is never in 'cls'. - */ -static inline bool -IsOpenCharClass(int32_t ch, uint8_t cls) { - return ch >= 0 && (ch >= 128 || (gLexTable[ch] & cls) != 0); -} - -/** - * True if 'ch' is in character class 'cls', which should be one of - * the constants above or some combination of them. No characters - * above U+007F are considered to be in 'cls'. EOF is never in 'cls'. - */ -static inline bool -IsClosedCharClass(int32_t ch, uint8_t cls) { - return uint32_t(ch) < 128 && (gLexTable[ch] & cls) != 0; -} - -/** - * True if 'ch' is CSS whitespace, i.e. any of the ASCII characters - * TAB, LF, FF, CR, or SPC. - */ -static inline bool -IsWhitespace(int32_t ch) { - return IsClosedCharClass(ch, IS_SPACE); -} - -/** - * True if 'ch' is horizontal whitespace, i.e. TAB or SPC. - */ -static inline bool -IsHorzSpace(int32_t ch) { - return IsClosedCharClass(ch, IS_HSPACE); -} - -/** - * True if 'ch' is vertical whitespace, i.e. LF, FF, or CR. Vertical - * whitespace requires special handling when consumed, see AdvanceLine. - */ -static inline bool -IsVertSpace(int32_t ch) { - return IsClosedCharClass(ch, IS_VSPACE); -} - -/** - * True if 'ch' is a character that can appear in the middle of an identifier. - * This includes U+0000 since it is handled as U+FFFD, but for purposes of - * GatherText it should not be included in IsOpenCharClass. - */ -static inline bool -IsIdentChar(int32_t ch) { - return IsOpenCharClass(ch, IS_IDCHAR) || ch == 0; -} - -/** - * True if 'ch' is a character that by itself begins an identifier. - * This includes U+0000 since it is handled as U+FFFD, but for purposes of - * GatherText it should not be included in IsOpenCharClass. - * (This is a subset of IsIdentChar.) - */ -static inline bool -IsIdentStart(int32_t ch) { - return IsOpenCharClass(ch, IS_IDSTART) || ch == 0; -} - -/** - * True if the two-character sequence aFirstChar+aSecondChar begins an - * identifier. - */ -static inline bool -StartsIdent(int32_t aFirstChar, int32_t aSecondChar) -{ - return IsIdentStart(aFirstChar) || - (aFirstChar == '-' && (aSecondChar == '-' || IsIdentStart(aSecondChar))); -} - -/** - * True if 'ch' is a decimal digit. - */ -static inline bool -IsDigit(int32_t ch) { - return (ch >= '0') && (ch <= '9'); -} - -/** - * True if 'ch' is a hexadecimal digit. - */ -static inline bool -IsHexDigit(int32_t ch) { - return IsClosedCharClass(ch, IS_HEX_DIGIT); -} - -/** - * Assuming that 'ch' is a decimal digit, return its numeric value. - */ -static inline uint32_t -DecimalDigitValue(int32_t ch) -{ - return ch - '0'; -} - -/** - * Assuming that 'ch' is a hexadecimal digit, return its numeric value. - */ -static inline uint32_t -HexDigitValue(int32_t ch) -{ - if (IsDigit(ch)) { - return DecimalDigitValue(ch); - } else { - // Note: c&7 just keeps the low three bits which causes - // upper and lower case alphabetics to both yield their - // "relative to 10" value for computing the hex value. - return (ch & 0x7) + 9; - } -} - -/** - * If 'ch' can be the first character of a two-character match operator - * token, return the token type code for that token, otherwise return - * eCSSToken_Symbol to indicate that it can't. - */ -static inline nsCSSTokenType -MatchOperatorType(int32_t ch) -{ - switch (ch) { - case '~': return eCSSToken_Includes; - case '|': return eCSSToken_Dashmatch; - case '^': return eCSSToken_Beginsmatch; - case '$': return eCSSToken_Endsmatch; - case '*': return eCSSToken_Containsmatch; - default: return eCSSToken_Symbol; - } -} - -/* Out-of-line nsCSSToken methods. */ - -/** - * Append the textual representation of |this| to |aBuffer|. - */ -void -nsCSSToken::AppendToString(nsString& aBuffer) const -{ - switch (mType) { - case eCSSToken_Ident: - nsStyleUtil::AppendEscapedCSSIdent(mIdent, aBuffer); - break; - - case eCSSToken_AtKeyword: - aBuffer.Append('@'); - nsStyleUtil::AppendEscapedCSSIdent(mIdent, aBuffer); - break; - - case eCSSToken_ID: - case eCSSToken_Hash: - aBuffer.Append('#'); - nsStyleUtil::AppendEscapedCSSIdent(mIdent, aBuffer); - break; - - case eCSSToken_Function: - nsStyleUtil::AppendEscapedCSSIdent(mIdent, aBuffer); - aBuffer.Append('('); - break; - - case eCSSToken_URL: - case eCSSToken_Bad_URL: - aBuffer.AppendLiteral("url("); - if (mSymbol != char16_t(0)) { - nsStyleUtil::AppendEscapedCSSString(mIdent, aBuffer, mSymbol); - } else { - aBuffer.Append(mIdent); - } - if (mType == eCSSToken_URL) { - aBuffer.Append(char16_t(')')); - } - break; - - case eCSSToken_Number: - if (mIntegerValid) { - aBuffer.AppendInt(mInteger, 10); - } else { - aBuffer.AppendFloat(mNumber); - } - break; - - case eCSSToken_Percentage: - aBuffer.AppendFloat(mNumber * 100.0f); - aBuffer.Append(char16_t('%')); - break; - - case eCSSToken_Dimension: - if (mIntegerValid) { - aBuffer.AppendInt(mInteger, 10); - } else { - aBuffer.AppendFloat(mNumber); - } - nsStyleUtil::AppendEscapedCSSIdent(mIdent, aBuffer); - break; - - case eCSSToken_Bad_String: - nsStyleUtil::AppendEscapedCSSString(mIdent, aBuffer, mSymbol); - // remove the trailing quote character - aBuffer.Truncate(aBuffer.Length() - 1); - break; - - case eCSSToken_String: - nsStyleUtil::AppendEscapedCSSString(mIdent, aBuffer, mSymbol); - break; - - case eCSSToken_Symbol: - aBuffer.Append(mSymbol); - break; - - case eCSSToken_Whitespace: - aBuffer.Append(' '); - break; - - case eCSSToken_HTMLComment: - case eCSSToken_URange: - aBuffer.Append(mIdent); - break; - - case eCSSToken_Includes: - aBuffer.AppendLiteral("~="); - break; - case eCSSToken_Dashmatch: - aBuffer.AppendLiteral("|="); - break; - case eCSSToken_Beginsmatch: - aBuffer.AppendLiteral("^="); - break; - case eCSSToken_Endsmatch: - aBuffer.AppendLiteral("$="); - break; - case eCSSToken_Containsmatch: - aBuffer.AppendLiteral("*="); - break; - - default: - NS_ERROR("invalid token type"); - break; - } -} - -/* nsCSSScanner methods. */ - -nsCSSScanner::nsCSSScanner(const nsAString& aBuffer, uint32_t aLineNumber) - : mBuffer(aBuffer.BeginReading()) - , mOffset(0) - , mCount(aBuffer.Length()) - , mLineNumber(aLineNumber) - , mLineOffset(0) - , mTokenLineNumber(aLineNumber) - , mTokenLineOffset(0) - , mTokenOffset(0) - , mRecordStartOffset(0) - , mEOFCharacters(eEOFCharacters_None) - , mRecording(false) - , mSeenBadToken(false) - , mSeenVariableReference(false) -{ - MOZ_COUNT_CTOR(nsCSSScanner); -} - -nsCSSScanner::~nsCSSScanner() -{ - MOZ_COUNT_DTOR(nsCSSScanner); -} - -void -nsCSSScanner::StartRecording() -{ - MOZ_ASSERT(!mRecording, "already started recording"); - mRecording = true; - mRecordStartOffset = mOffset; -} - -void -nsCSSScanner::StopRecording() -{ - MOZ_ASSERT(mRecording, "haven't started recording"); - mRecording = false; -} - -void -nsCSSScanner::StopRecording(nsString& aBuffer) -{ - MOZ_ASSERT(mRecording, "haven't started recording"); - mRecording = false; - aBuffer.Append(mBuffer + mRecordStartOffset, - mOffset - mRecordStartOffset); -} - -uint32_t -nsCSSScanner::RecordingLength() const -{ - MOZ_ASSERT(mRecording, "haven't started recording"); - return mOffset - mRecordStartOffset; -} - -#ifdef DEBUG -bool -nsCSSScanner::IsRecording() const -{ - return mRecording; -} -#endif - -nsDependentSubstring -nsCSSScanner::GetCurrentLine() const -{ - uint32_t end = mTokenOffset; - while (end < mCount && !IsVertSpace(mBuffer[end])) { - end++; - } - return nsDependentSubstring(mBuffer + mTokenLineOffset, - mBuffer + end); -} - -/** - * Return the raw UTF-16 code unit at position |mOffset + n| within - * the read buffer. If that is beyond the end of the buffer, returns - * -1 to indicate end of input. - */ -inline int32_t -nsCSSScanner::Peek(uint32_t n) -{ - if (mOffset + n >= mCount) { - return -1; - } - return mBuffer[mOffset + n]; -} - -/** - * Advance |mOffset| over |n| code units. Advance(0) is a no-op. - * If |n| is greater than the distance to end of input, will silently - * stop at the end. May not be used to advance over a line boundary; - * AdvanceLine() must be used instead. - */ -inline void -nsCSSScanner::Advance(uint32_t n) -{ -#ifdef DEBUG - while (mOffset < mCount && n > 0) { - MOZ_ASSERT(!IsVertSpace(mBuffer[mOffset]), - "may not Advance() over a line boundary"); - mOffset++; - n--; - } -#else - if (mOffset + n >= mCount || mOffset + n < mOffset) - mOffset = mCount; - else - mOffset += n; -#endif -} - -/** - * Advance |mOffset| over a line boundary. - */ -void -nsCSSScanner::AdvanceLine() -{ - MOZ_ASSERT(IsVertSpace(mBuffer[mOffset]), - "may not AdvanceLine() over a horizontal character"); - // Advance over \r\n as a unit. - if (mBuffer[mOffset] == '\r' && mOffset + 1 < mCount && - mBuffer[mOffset+1] == '\n') - mOffset += 2; - else - mOffset += 1; - // 0 is a magical line number meaning that we don't know (i.e., script) - if (mLineNumber != 0) - mLineNumber++; - mLineOffset = mOffset; -} - -/** - * Back up |mOffset| over |n| code units. Backup(0) is a no-op. - * If |n| is greater than the distance to beginning of input, will - * silently stop at the beginning. May not be used to back up over a - * line boundary. - */ -void -nsCSSScanner::Backup(uint32_t n) -{ -#ifdef DEBUG - while (mOffset > 0 && n > 0) { - MOZ_ASSERT(!IsVertSpace(mBuffer[mOffset-1]), - "may not Backup() over a line boundary"); - mOffset--; - n--; - } -#else - if (mOffset < n) - mOffset = 0; - else - mOffset -= n; -#endif -} - -void -nsCSSScanner::SavePosition(nsCSSScannerPosition& aState) -{ - aState.mOffset = mOffset; - aState.mLineNumber = mLineNumber; - aState.mLineOffset = mLineOffset; - aState.mTokenLineNumber = mTokenLineNumber; - aState.mTokenLineOffset = mTokenLineOffset; - aState.mTokenOffset = mTokenOffset; - aState.mInitialized = true; -} - -void -nsCSSScanner::RestoreSavedPosition(const nsCSSScannerPosition& aState) -{ - MOZ_ASSERT(aState.mInitialized, "have not saved state"); - if (aState.mInitialized) { - mOffset = aState.mOffset; - mLineNumber = aState.mLineNumber; - mLineOffset = aState.mLineOffset; - mTokenLineNumber = aState.mTokenLineNumber; - mTokenLineOffset = aState.mTokenLineOffset; - mTokenOffset = aState.mTokenOffset; - } -} - -/** - * Skip over a sequence of whitespace characters (vertical or - * horizontal) starting at the current read position. - */ -void -nsCSSScanner::SkipWhitespace() -{ - for (;;) { - int32_t ch = Peek(); - if (!IsWhitespace(ch)) { // EOF counts as non-whitespace - break; - } - if (IsVertSpace(ch)) { - AdvanceLine(); - } else { - Advance(); - } - } -} - -/** - * If the given text appears at the current offset in the buffer, - * advance over the text and return true. Otherwise, return false. - * mLength is the number of characters in mDirective. - */ -bool -nsCSSScanner::CheckCommentDirective(const nsAString& aDirective) -{ - nsDependentSubstring text(&mBuffer[mOffset], &mBuffer[mCount]); - - if (StringBeginsWith(text, aDirective)) { - Advance(aDirective.Length()); - return true; - } - return false; -} - -/** - * Skip over one CSS comment starting at the current read position. - */ -void -nsCSSScanner::SkipComment() -{ - // Note that these do not start with "#" or "@" -- that is handled - // separately, below. - static NS_NAMED_LITERAL_STRING(kSourceMappingURLDirective, " sourceMappingURL="); - static NS_NAMED_LITERAL_STRING(kSourceURLDirective, " sourceURL="); - - MOZ_ASSERT(Peek() == '/' && Peek(1) == '*', "should not have been called"); - Advance(2); - - // If we saw one of the directives, this will be non-NULL and will - // point to the string into which the URL will be written. - nsString* directive = nullptr; - if (Peek() == '#' || Peek() == '@') { - // Check for the comment directives. - Advance(); - if (CheckCommentDirective(kSourceMappingURLDirective)) { - mSourceMapURL.Truncate(); - directive = &mSourceMapURL; - } else if (CheckCommentDirective(kSourceURLDirective)) { - mSourceURL.Truncate(); - directive = &mSourceURL; - } - } - - for (;;) { - int32_t ch = Peek(); - if (ch < 0) { - SetEOFCharacters(eEOFCharacters_Asterisk | eEOFCharacters_Slash); - return; - } - - if (ch == '*') { - Advance(); - ch = Peek(); - if (ch < 0) { - // In this case, even if we saw a source map directive, leave - // the "*" out of it. - SetEOFCharacters(eEOFCharacters_Slash); - return; - } - if (ch == '/') { - Advance(); - return; - } - if (directive != nullptr) { - directive->Append('*'); - } - } else if (IsVertSpace(ch)) { - AdvanceLine(); - // Done with the directive, so stop copying. - directive = nullptr; - } else if (IsWhitespace(ch)) { - Advance(); - // Done with the directive, so stop copying. - directive = nullptr; - } else { - if (directive != nullptr) { - directive->Append(ch); - } - Advance(); - } - } -} - -/** - * If there is a valid escape sequence starting at the current read - * position, consume it, decode it, append the result to |aOutput|, - * and return true. Otherwise, consume nothing, leave |aOutput| - * unmodified, and return false. If |aInString| is true, accept the - * additional form of escape sequence allowed within string-like tokens. - */ -bool -nsCSSScanner::GatherEscape(nsString& aOutput, bool aInString) -{ - MOZ_ASSERT(Peek() == '\\', "should not have been called"); - int32_t ch = Peek(1); - if (ch < 0) { - // If we are in a string (or a url() containing a string), we want to drop - // the backslash on the floor. Otherwise, we want to treat it as a U+FFFD - // character. - Advance(); - if (aInString) { - SetEOFCharacters(eEOFCharacters_DropBackslash); - } else { - aOutput.Append(UCS2_REPLACEMENT_CHAR); - SetEOFCharacters(eEOFCharacters_ReplacementChar); - } - return true; - } - if (IsVertSpace(ch)) { - if (aInString) { - // In strings (and in url() containing a string), escaped - // newlines are completely removed, to allow splitting over - // multiple lines. - Advance(); - AdvanceLine(); - return true; - } - // Outside of strings, backslash followed by a newline is not an escape. - return false; - } - - if (!IsHexDigit(ch)) { - // "Any character (except a hexadecimal digit, linefeed, carriage - // return, or form feed) can be escaped with a backslash to remove - // its special meaning." -- CSS2.1 section 4.1.3 - Advance(2); - if (ch == 0) { - aOutput.Append(UCS2_REPLACEMENT_CHAR); - } else { - aOutput.Append(ch); - } - return true; - } - - // "[at most six hexadecimal digits following a backslash] stand - // for the ISO 10646 character with that number, which must not be - // zero. (It is undefined in CSS 2.1 what happens if a style sheet - // does contain a character with Unicode codepoint zero.)" - // -- CSS2.1 section 4.1.3 - - // At this point we know we have \ followed by at least one - // hexadecimal digit, therefore the escape sequence is valid and we - // can go ahead and consume the backslash. - Advance(); - uint32_t val = 0; - int i = 0; - do { - val = val * 16 + HexDigitValue(ch); - i++; - Advance(); - ch = Peek(); - } while (i < 6 && IsHexDigit(ch)); - - // "Interpret the hex digits as a hexadecimal number. If this number is zero, - // or is greater than the maximum allowed codepoint, return U+FFFD - // REPLACEMENT CHARACTER" -- CSS Syntax Level 3 - if (MOZ_UNLIKELY(val == 0)) { - aOutput.Append(UCS2_REPLACEMENT_CHAR); - } else { - AppendUCS4ToUTF16(ENSURE_VALID_CHAR(val), aOutput); - } - - // Consume exactly one whitespace character after a - // hexadecimal escape sequence. - if (IsVertSpace(ch)) { - AdvanceLine(); - } else if (IsHorzSpace(ch)) { - Advance(); - } - return true; -} - -/** - * Consume a run of "text" beginning with the current read position, - * consisting of characters in the class |aClass| (which must be a - * suitable argument to IsOpenCharClass) plus escape sequences. - * Append the text to |aText|, after decoding escape sequences. - * - * Returns true if at least one character was appended to |aText|, - * false otherwise. - */ -bool -nsCSSScanner::GatherText(uint8_t aClass, nsString& aText) -{ - // This is all of the character classes currently used with - // GatherText. If you have a need to use this function with a - // different class, go ahead and add it. - MOZ_ASSERT(aClass == IS_STRING || - aClass == IS_IDCHAR || - aClass == IS_URL_CHAR, - "possibly-inappropriate character class"); - - uint32_t start = mOffset; - bool inString = aClass == IS_STRING; - - for (;;) { - // Consume runs of unescaped characters in one go. - uint32_t n = mOffset; - while (n < mCount && IsOpenCharClass(mBuffer[n], aClass)) { - n++; - } - if (n > mOffset) { - aText.Append(&mBuffer[mOffset], n - mOffset); - mOffset = n; - } - if (n == mCount) { - break; - } - - int32_t ch = Peek(); - MOZ_ASSERT(!IsOpenCharClass(ch, aClass), - "should not have exited the inner loop"); - if (ch == 0) { - Advance(); - aText.Append(UCS2_REPLACEMENT_CHAR); - continue; - } - - if (ch != '\\') { - break; - } - if (!GatherEscape(aText, inString)) { - break; - } - } - - return mOffset > start; -} - -/** - * Scan an Ident token. This also handles Function and URL tokens, - * both of which begin indistinguishably from an identifier. It can - * produce a Symbol token when an apparent identifier actually led - * into an invalid escape sequence. - */ -bool -nsCSSScanner::ScanIdent(nsCSSToken& aToken) -{ - if (MOZ_UNLIKELY(!GatherText(IS_IDCHAR, aToken.mIdent))) { - MOZ_ASSERT(Peek() == '\\', - "unexpected IsIdentStart character that did not begin an ident"); - aToken.mSymbol = Peek(); - Advance(); - return true; - } - - if (MOZ_LIKELY(Peek() != '(')) { - aToken.mType = eCSSToken_Ident; - return true; - } - - Advance(); - aToken.mType = eCSSToken_Function; - if (aToken.mIdent.LowerCaseEqualsLiteral("url")) { - NextURL(aToken); - } else if (aToken.mIdent.LowerCaseEqualsLiteral("var")) { - mSeenVariableReference = true; - } - return true; -} - -/** - * Scan an AtKeyword token. Also handles production of Symbol when - * an '@' is not followed by an identifier. - */ -bool -nsCSSScanner::ScanAtKeyword(nsCSSToken& aToken) -{ - MOZ_ASSERT(Peek() == '@', "should not have been called"); - - // Fall back for when '@' isn't followed by an identifier. - aToken.mSymbol = '@'; - Advance(); - - int32_t ch = Peek(); - if (StartsIdent(ch, Peek(1))) { - if (GatherText(IS_IDCHAR, aToken.mIdent)) { - aToken.mType = eCSSToken_AtKeyword; - } - } - return true; -} - -/** - * Scan a Hash token. Handles the distinction between eCSSToken_ID - * and eCSSToken_Hash, and handles production of Symbol when a '#' - * is not followed by identifier characters. - */ -bool -nsCSSScanner::ScanHash(nsCSSToken& aToken) -{ - MOZ_ASSERT(Peek() == '#', "should not have been called"); - - // Fall back for when '#' isn't followed by identifier characters. - aToken.mSymbol = '#'; - Advance(); - - int32_t ch = Peek(); - if (IsIdentChar(ch) || ch == '\\') { - nsCSSTokenType type = - StartsIdent(ch, Peek(1)) ? eCSSToken_ID : eCSSToken_Hash; - aToken.mIdent.SetLength(0); - if (GatherText(IS_IDCHAR, aToken.mIdent)) { - aToken.mType = type; - } - } - - return true; -} - -/** - * Scan a Number, Percentage, or Dimension token (all of which begin - * like a Number). Can produce a Symbol when a '.' is not followed by - * digits, or when '+' or '-' are not followed by either a digit or a - * '.' and then a digit. Can also produce a HTMLComment when it - * encounters '-->'. - */ -bool -nsCSSScanner::ScanNumber(nsCSSToken& aToken) -{ - int32_t c = Peek(); -#ifdef DEBUG - { - int32_t c2 = Peek(1); - int32_t c3 = Peek(2); - MOZ_ASSERT(IsDigit(c) || - (IsDigit(c2) && (c == '.' || c == '+' || c == '-')) || - (IsDigit(c3) && (c == '+' || c == '-') && c2 == '.'), - "should not have been called"); - } -#endif - - // Sign of the mantissa (-1 or 1). - int32_t sign = c == '-' ? -1 : 1; - // Absolute value of the integer part of the mantissa. This is a double so - // we don't run into overflow issues for consumers that only care about our - // floating-point value while still being able to express the full int32_t - // range for consumers who want integers. - double intPart = 0; - // Fractional part of the mantissa. This is a double so that when we convert - // to float at the end we'll end up rounding to nearest float instead of - // truncating down (as we would if fracPart were a float and we just - // effectively lost the last several digits). - double fracPart = 0; - // Absolute value of the power of 10 that we should multiply by (only - // relevant for numbers in scientific notation). Has to be a signed integer, - // because multiplication of signed by unsigned converts the unsigned to - // signed, so if we plan to actually multiply by expSign... - int32_t exponent = 0; - // Sign of the exponent. - int32_t expSign = 1; - - aToken.mHasSign = (c == '+' || c == '-'); - if (aToken.mHasSign) { - Advance(); - c = Peek(); - } - - bool gotDot = (c == '.'); - - if (!gotDot) { - // Scan the integer part of the mantissa. - MOZ_ASSERT(IsDigit(c), "should have been excluded by logic above"); - do { - intPart = 10*intPart + DecimalDigitValue(c); - Advance(); - c = Peek(); - } while (IsDigit(c)); - - gotDot = (c == '.') && IsDigit(Peek(1)); - } - - if (gotDot) { - // Scan the fractional part of the mantissa. - Advance(); - c = Peek(); - MOZ_ASSERT(IsDigit(c), "should have been excluded by logic above"); - // Power of ten by which we need to divide our next digit - double divisor = 10; - do { - fracPart += DecimalDigitValue(c) / divisor; - divisor *= 10; - Advance(); - c = Peek(); - } while (IsDigit(c)); - } - - bool gotE = false; - if (c == 'e' || c == 'E') { - int32_t expSignChar = Peek(1); - int32_t nextChar = Peek(2); - if (IsDigit(expSignChar) || - ((expSignChar == '-' || expSignChar == '+') && IsDigit(nextChar))) { - gotE = true; - if (expSignChar == '-') { - expSign = -1; - } - Advance(); // consumes the E - if (expSignChar == '-' || expSignChar == '+') { - Advance(); - c = nextChar; - } else { - c = expSignChar; - } - MOZ_ASSERT(IsDigit(c), "should have been excluded by logic above"); - do { - exponent = 10*exponent + DecimalDigitValue(c); - Advance(); - c = Peek(); - } while (IsDigit(c)); - } - } - - nsCSSTokenType type = eCSSToken_Number; - - // Set mIntegerValid for all cases (except %, below) because we need - // it for the "2n" in :nth-child(2n). - aToken.mIntegerValid = false; - - // Time to reassemble our number. - // Do all the math in double precision so it's truncated only once. - double value = sign * (intPart + fracPart); - if (gotE) { - // Avoid multiplication of 0 by Infinity. - if (value != 0.0) { - // Explicitly cast expSign*exponent to double to avoid issues with - // overloaded pow() on Windows. - value *= pow(10.0, double(expSign * exponent)); - } - } else if (!gotDot) { - // Clamp values outside of integer range. - if (sign > 0) { - aToken.mInteger = int32_t(std::min(intPart, double(INT32_MAX))); - } else { - aToken.mInteger = int32_t(std::max(-intPart, double(INT32_MIN))); - } - aToken.mIntegerValid = true; - } - - nsString& ident = aToken.mIdent; - - // Check for Dimension and Percentage tokens. - if (c >= 0) { - if (StartsIdent(c, Peek(1))) { - if (GatherText(IS_IDCHAR, ident)) { - type = eCSSToken_Dimension; - } - } else if (c == '%') { - Advance(); - type = eCSSToken_Percentage; - value = value / 100.0f; - aToken.mIntegerValid = false; - } - } - MOZ_ASSERT(!IsNaN(value), "The value should not be NaN"); - aToken.mNumber = value; - aToken.mType = type; - return true; -} - -/** - * Scan a string constant ('foo' or "foo"). Will always produce - * either a String or a Bad_String token; the latter occurs when the - * close quote is missing. Always returns true (for convenience in Next()). - */ -bool -nsCSSScanner::ScanString(nsCSSToken& aToken) -{ - int32_t aStop = Peek(); - MOZ_ASSERT(aStop == '"' || aStop == '\'', "should not have been called"); - aToken.mType = eCSSToken_String; - aToken.mSymbol = char16_t(aStop); // Remember how it's quoted. - Advance(); - - for (;;) { - GatherText(IS_STRING, aToken.mIdent); - - int32_t ch = Peek(); - if (ch == -1) { - AddEOFCharacters(aStop == '"' ? eEOFCharacters_DoubleQuote : - eEOFCharacters_SingleQuote); - break; // EOF ends a string token with no error. - } - if (ch == aStop) { - Advance(); - break; - } - // Both " and ' are excluded from IS_STRING. - if (ch == '"' || ch == '\'') { - aToken.mIdent.Append(ch); - Advance(); - continue; - } - - mSeenBadToken = true; - aToken.mType = eCSSToken_Bad_String; - break; - } - return true; -} - -/** - * Scan a unicode-range token. These match the regular expression - * - * u\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})? - * - * However, some such tokens are "invalid". There are three valid forms: - * - * u+[0-9a-f]{x} 1 <= x <= 6 - * u+[0-9a-f]{x}\?{y} 1 <= x+y <= 6 - * u+[0-9a-f]{x}-[0-9a-f]{y} 1 <= x <= 6, 1 <= y <= 6 - * - * All unicode-range tokens have their text recorded in mIdent; valid ones - * are also decoded into mInteger and mInteger2, and mIntegerValid is set. - * Note that this does not validate the numeric range, only the syntactic - * form. - */ -bool -nsCSSScanner::ScanURange(nsCSSToken& aResult) -{ - int32_t intro1 = Peek(); - int32_t intro2 = Peek(1); - int32_t ch = Peek(2); - - MOZ_ASSERT((intro1 == 'u' || intro1 == 'U') && - intro2 == '+' && - (IsHexDigit(ch) || ch == '?'), - "should not have been called"); - - aResult.mIdent.Append(intro1); - aResult.mIdent.Append(intro2); - Advance(2); - - bool valid = true; - bool haveQues = false; - uint32_t low = 0; - uint32_t high = 0; - int i = 0; - - do { - aResult.mIdent.Append(ch); - if (IsHexDigit(ch)) { - if (haveQues) { - valid = false; // All question marks should be at the end. - } - low = low*16 + HexDigitValue(ch); - high = high*16 + HexDigitValue(ch); - } else { - haveQues = true; - low = low*16 + 0x0; - high = high*16 + 0xF; - } - - i++; - Advance(); - ch = Peek(); - } while (i < 6 && (IsHexDigit(ch) || ch == '?')); - - if (ch == '-' && IsHexDigit(Peek(1))) { - if (haveQues) { - valid = false; - } - - aResult.mIdent.Append(ch); - Advance(); - ch = Peek(); - high = 0; - i = 0; - do { - aResult.mIdent.Append(ch); - high = high*16 + HexDigitValue(ch); - - i++; - Advance(); - ch = Peek(); - } while (i < 6 && IsHexDigit(ch)); - } - - aResult.mInteger = low; - aResult.mInteger2 = high; - aResult.mIntegerValid = valid; - aResult.mType = eCSSToken_URange; - return true; -} - -#ifdef DEBUG -/* static */ void -nsCSSScanner::AssertEOFCharactersValid(uint32_t c) -{ - MOZ_ASSERT(c == eEOFCharacters_None || - c == eEOFCharacters_ReplacementChar || - c == eEOFCharacters_Slash || - c == (eEOFCharacters_Asterisk | - eEOFCharacters_Slash) || - c == eEOFCharacters_DoubleQuote || - c == eEOFCharacters_SingleQuote || - c == (eEOFCharacters_DropBackslash | - eEOFCharacters_DoubleQuote) || - c == (eEOFCharacters_DropBackslash | - eEOFCharacters_SingleQuote) || - c == eEOFCharacters_CloseParen || - c == (eEOFCharacters_ReplacementChar | - eEOFCharacters_CloseParen) || - c == (eEOFCharacters_DoubleQuote | - eEOFCharacters_CloseParen) || - c == (eEOFCharacters_SingleQuote | - eEOFCharacters_CloseParen) || - c == (eEOFCharacters_DropBackslash | - eEOFCharacters_DoubleQuote | - eEOFCharacters_CloseParen) || - c == (eEOFCharacters_DropBackslash | - eEOFCharacters_SingleQuote | - eEOFCharacters_CloseParen), - "invalid EOFCharacters value"); -} -#endif - -void -nsCSSScanner::SetEOFCharacters(uint32_t aEOFCharacters) -{ - mEOFCharacters = EOFCharacters(aEOFCharacters); -} - -void -nsCSSScanner::AddEOFCharacters(uint32_t aEOFCharacters) -{ - mEOFCharacters = EOFCharacters(mEOFCharacters | aEOFCharacters); -} - -static const char16_t kImpliedEOFCharacters[] = { - UCS2_REPLACEMENT_CHAR, '*', '/', '"', '\'', ')', 0 -}; - -/* static */ void -nsCSSScanner::AppendImpliedEOFCharacters(EOFCharacters aEOFCharacters, - nsAString& aResult) -{ - // First, ignore eEOFCharacters_DropBackslash. - uint32_t c = aEOFCharacters >> 1; - - // All of the remaining EOFCharacters bits represent appended characters, - // and the bits are in the order that they need appending. - for (const char16_t* p = kImpliedEOFCharacters; *p && c; p++, c >>= 1) { - if (c & 1) { - aResult.Append(*p); - } - } - - MOZ_ASSERT(c == 0, "too many bits in mEOFCharacters"); -} - -/** - * Consume the part of an URL token after the initial 'url('. Caller - * is assumed to have consumed 'url(' already. Will always produce - * either an URL or a Bad_URL token. - * - * Exposed for use by nsCSSParser::ParseMozDocumentRule, which applies - * the special lexical rules for URL tokens in a nonstandard context. - */ -void -nsCSSScanner::NextURL(nsCSSToken& aToken) -{ - SkipWhitespace(); - - // aToken.mIdent may be "url" at this point; clear that out - aToken.mIdent.Truncate(); - - bool hasString = false; - int32_t ch = Peek(); - // Do we have a string? - if (ch == '"' || ch == '\'') { - ScanString(aToken); - if (MOZ_UNLIKELY(aToken.mType == eCSSToken_Bad_String)) { - aToken.mType = eCSSToken_Bad_URL; - return; - } - MOZ_ASSERT(aToken.mType == eCSSToken_String, "unexpected token type"); - hasString = true; - } else { - // Otherwise, this is the start of a non-quoted url (which may be empty). - aToken.mSymbol = char16_t(0); - GatherText(IS_URL_CHAR, aToken.mIdent); - } - - // Consume trailing whitespace and then look for a close parenthesis. - SkipWhitespace(); - ch = Peek(); - // ch can be less than zero indicating EOF - if (MOZ_LIKELY(ch < 0 || ch == ')')) { - Advance(); - aToken.mType = eCSSToken_URL; - if (ch < 0) { - AddEOFCharacters(eEOFCharacters_CloseParen); - } - } else { - mSeenBadToken = true; - aToken.mType = eCSSToken_Bad_URL; - if (!hasString) { - // Consume until before the next right parenthesis, which follows - // how is consumed in CSS Syntax 3 spec. - // Note that, we only do this when "url(" is not followed by a - // string, because in the spec, "url(" followed by a string is - // handled as a url function rather than a , so the - // rest of content before ")" should be consumed in balance, - // which will be done by the parser. - // The closing ")" is not consumed here. It is left to the parser - // so that the parser can handle both cases. - do { - if (IsVertSpace(ch)) { - AdvanceLine(); - } else { - Advance(); - } - ch = Peek(); - } while (ch >= 0 && ch != ')'); - } - } -} - -/** - * Primary scanner entry point. Consume one token and fill in - * |aToken| accordingly. Will skip over any number of comments first, - * and will also skip over rather than return whitespace and comment - * tokens, depending on the value of |aSkip|. - * - * Returns true if it successfully consumed a token, false if EOF has - * been reached. Will always advance the current read position by at - * least one character unless called when already at EOF. - */ -bool -nsCSSScanner::Next(nsCSSToken& aToken, nsCSSScannerExclude aSkip) -{ - int32_t ch; - - // do this here so we don't have to do it in dozens of other places - aToken.mIdent.Truncate(); - aToken.mType = eCSSToken_Symbol; - - for (;;) { - // Consume any number of comments, and possibly also whitespace tokens, - // in between other tokens. - mTokenOffset = mOffset; - mTokenLineOffset = mLineOffset; - mTokenLineNumber = mLineNumber; - - ch = Peek(); - if (IsWhitespace(ch)) { - SkipWhitespace(); - if (aSkip != eCSSScannerExclude_WhitespaceAndComments) { - aToken.mType = eCSSToken_Whitespace; - return true; - } - continue; // start again at the beginning - } - if (ch == '/' && Peek(1) == '*') { - SkipComment(); - if (aSkip == eCSSScannerExclude_None) { - aToken.mType = eCSSToken_Comment; - return true; - } - continue; // start again at the beginning - } - break; - } - - // EOF - if (ch < 0) { - return false; - } - - // 'u' could be UNICODE-RANGE or an identifier-family token - if (ch == 'u' || ch == 'U') { - int32_t c2 = Peek(1); - int32_t c3 = Peek(2); - if (c2 == '+' && (IsHexDigit(c3) || c3 == '?')) { - return ScanURange(aToken); - } - return ScanIdent(aToken); - } - - // identifier family - if (IsIdentStart(ch)) { - return ScanIdent(aToken); - } - - // number family - if (IsDigit(ch)) { - return ScanNumber(aToken); - } - - if (ch == '.' && IsDigit(Peek(1))) { - return ScanNumber(aToken); - } - - if (ch == '+') { - int32_t c2 = Peek(1); - if (IsDigit(c2) || (c2 == '.' && IsDigit(Peek(2)))) { - return ScanNumber(aToken); - } - } - - // '-' can start an identifier-family token, a number-family token, - // or an HTML-comment - if (ch == '-') { - int32_t c2 = Peek(1); - int32_t c3 = Peek(2); - if (IsIdentStart(c2) || (c2 == '-' && c3 != '>')) { - return ScanIdent(aToken); - } - if (IsDigit(c2) || (c2 == '.' && IsDigit(c3))) { - return ScanNumber(aToken); - } - if (c2 == '-' && c3 == '>') { - Advance(3); - aToken.mType = eCSSToken_HTMLComment; - aToken.mIdent.AssignLiteral("-->"); - return true; - } - } - - // the other HTML-comment token - if (ch == '<' && Peek(1) == '!' && Peek(2) == '-' && Peek(3) == '-') { - Advance(4); - aToken.mType = eCSSToken_HTMLComment; - aToken.mIdent.AssignLiteral(" -}; - -// Classification of tokens used to determine if a "/**/" string must be -// inserted if pasting token streams together when serializing. We include -// values corresponding to eCSSToken_Dashmatch and eCSSToken_Containsmatch, -// as css-syntax does not treat these as whole tokens, but we will still -// need to insert a "/**/" string between a '|' delim and a '|=' dashmatch -// and between a '/' delim and a '*=' containsmatch. -// -// https://drafts.csswg.org/css-syntax/#serialization -enum nsCSSTokenSerializationType { - eCSSTokenSerialization_Nothing, - eCSSTokenSerialization_Whitespace, - eCSSTokenSerialization_AtKeyword_or_Hash, - eCSSTokenSerialization_Number, - eCSSTokenSerialization_Dimension, - eCSSTokenSerialization_Percentage, - eCSSTokenSerialization_URange, - eCSSTokenSerialization_URL_or_BadURL, - eCSSTokenSerialization_Function, - eCSSTokenSerialization_Ident, - eCSSTokenSerialization_CDC, - eCSSTokenSerialization_DashMatch, - eCSSTokenSerialization_ContainsMatch, - eCSSTokenSerialization_Symbol_Hash, // '#' - eCSSTokenSerialization_Symbol_At, // '@' - eCSSTokenSerialization_Symbol_Dot_or_Plus, // '.', '+' - eCSSTokenSerialization_Symbol_Minus, // '-' - eCSSTokenSerialization_Symbol_OpenParen, // '(' - eCSSTokenSerialization_Symbol_Question, // '?' - eCSSTokenSerialization_Symbol_Assorted, // '$', '^', '~' - eCSSTokenSerialization_Symbol_Equals, // '=' - eCSSTokenSerialization_Symbol_Bar, // '|' - eCSSTokenSerialization_Symbol_Slash, // '/' - eCSSTokenSerialization_Symbol_Asterisk, // '*' - eCSSTokenSerialization_Other // anything else -}; - -// A single token returned from the scanner. mType is always -// meaningful; comments above describe which other fields are -// meaningful for which token types. -struct nsCSSToken { - nsAutoString mIdent; - float mNumber; - int32_t mInteger; - int32_t mInteger2; - nsCSSTokenType mType; - char16_t mSymbol; - bool mIntegerValid; - bool mHasSign; - - nsCSSToken() - : mNumber(0), mInteger(0), mInteger2(0), mType(eCSSToken_Whitespace), - mSymbol('\0'), mIntegerValid(false), mHasSign(false) - {} - - bool IsSymbol(char16_t aSymbol) const { - return mType == eCSSToken_Symbol && mSymbol == aSymbol; - } - - void AppendToString(nsString& aBuffer) const; -}; - -// Represents an nsCSSScanner's saved position in the input buffer. -class nsCSSScannerPosition { - friend class nsCSSScanner; -public: - nsCSSScannerPosition() - : mOffset(0) - , mLineNumber(0) - , mLineOffset(0) - , mTokenLineNumber(0) - , mTokenLineOffset(0) - , mTokenOffset(0) - , mInitialized(false) - { - } - - uint32_t LineNumber() { - MOZ_ASSERT(mInitialized); - return mLineNumber; - } - - uint32_t LineOffset() { - MOZ_ASSERT(mInitialized); - return mLineOffset; - } - -private: - uint32_t mOffset; - uint32_t mLineNumber; - uint32_t mLineOffset; - uint32_t mTokenLineNumber; - uint32_t mTokenLineOffset; - uint32_t mTokenOffset; - bool mInitialized; -}; - -enum nsCSSScannerExclude { - // Return all tokens, including whitespace and comments. - eCSSScannerExclude_None, - // Include whitespace but exclude comments. - eCSSScannerExclude_Comments, - // Exclude whitespace and comments. - eCSSScannerExclude_WhitespaceAndComments -}; - -// nsCSSScanner tokenizes an input stream using the CSS2.1 forward -// compatible tokenization rules. Used internally by nsCSSParser; -// not available for use by other code. -class nsCSSScanner { - public: - // |aLineNumber == 1| is the beginning of a file, use |aLineNumber == 0| - // when the line number is unknown. The scanner does not take - // ownership of |aBuffer|, so the caller must be sure to keep it - // alive for the lifetime of the scanner. - nsCSSScanner(const nsAString& aBuffer, uint32_t aLineNumber); - ~nsCSSScanner(); - - // Reset or check whether a BAD_URL or BAD_STRING token has been seen. - void ClearSeenBadToken() { mSeenBadToken = false; } - bool SeenBadToken() const { return mSeenBadToken; } - - // Reset or check whether a "var(" FUNCTION token has been seen. - void ClearSeenVariableReference() { mSeenVariableReference = false; } - bool SeenVariableReference() const { return mSeenVariableReference; } - - // Get the 1-based line number of the last character of - // the most recently processed token. - uint32_t GetLineNumber() const { return mTokenLineNumber; } - - // Get the 0-based column number of the first character of - // the most recently processed token. - uint32_t GetColumnNumber() const - { return mTokenOffset - mTokenLineOffset; } - - uint32_t GetTokenOffset() const - { return mTokenOffset; } - - uint32_t GetTokenEndOffset() const - { return mOffset; } - - const nsAString& GetSourceMapURL() const - { return mSourceMapURL; } - - const nsAString& GetSourceURL() const - { return mSourceURL; } - - // Get the text of the line containing the first character of - // the most recently processed token. - nsDependentSubstring GetCurrentLine() const; - - // Get the next token. Return false on EOF. aTokenResult is filled - // in with the data for the token. aSkip controls whether - // whitespace and/or comment tokens are ever returned. - bool Next(nsCSSToken& aTokenResult, nsCSSScannerExclude aSkip); - - // Get the body of an URL token (everything after the 'url('). - // This is exposed for use by nsCSSParser::ParseMozDocumentRule, - // which, for historical reasons, must make additional function - // tokens behave like url(). Please do not add new uses to the - // parser. - void NextURL(nsCSSToken& aTokenResult); - - // This is exposed for use by nsCSSParser::ParsePseudoClassWithNthPairArg, - // because "2n-1" is a single DIMENSION token, and "n-1" is a single - // IDENT token, but the :nth() selector syntax wants to interpret - // them the same as "2n -1" and "n -1" respectively. Please do not - // add new uses to the parser. - // - // Note: this function may not be used to back up over a line boundary. - void Backup(uint32_t n); - - // Starts recording the input stream from the current position. - void StartRecording(); - - // Abandons recording of the input stream. - void StopRecording(); - - // Stops recording of the input stream and appends the recorded - // input to aBuffer. - void StopRecording(nsString& aBuffer); - - // Returns the length of the current recording. - uint32_t RecordingLength() const; - -#ifdef DEBUG - bool IsRecording() const; -#endif - - // Stores the current scanner offset into the specified object. - void SavePosition(nsCSSScannerPosition& aState); - - // Resets the scanner offset to a position saved by SavePosition. - void RestoreSavedPosition(const nsCSSScannerPosition& aState); - - enum EOFCharacters { - eEOFCharacters_None = 0x0000, - - // to handle \ inside strings - eEOFCharacters_DropBackslash = 0x0001, - - // to handle \ outside strings - eEOFCharacters_ReplacementChar = 0x0002, - - // to close comments - eEOFCharacters_Asterisk = 0x0004, - eEOFCharacters_Slash = 0x0008, - - // to close double-quoted strings - eEOFCharacters_DoubleQuote = 0x0010, - - // to close single-quoted strings - eEOFCharacters_SingleQuote = 0x0020, - - // to close URLs - eEOFCharacters_CloseParen = 0x0040, - }; - - // Appends any characters to the specified string the input stream to make the - // last token not rely on special EOF handling behavior. - // - // If eEOFCharacters_DropBackslash is in aEOFCharacters, it is ignored. - static void AppendImpliedEOFCharacters(EOFCharacters aEOFCharacters, - nsAString& aString); - - EOFCharacters GetEOFCharacters() const { -#ifdef DEBUG - AssertEOFCharactersValid(mEOFCharacters); -#endif - return mEOFCharacters; - } - -#ifdef DEBUG - static void AssertEOFCharactersValid(uint32_t c); -#endif - -protected: - int32_t Peek(uint32_t n = 0); - void Advance(uint32_t n = 1); - void AdvanceLine(); - - void SkipWhitespace(); - bool CheckCommentDirective(const nsAString& aDirective); - void SkipComment(); - - bool GatherEscape(nsString& aOutput, bool aInString); - bool GatherText(uint8_t aClass, nsString& aIdent); - - bool ScanIdent(nsCSSToken& aResult); - bool ScanAtKeyword(nsCSSToken& aResult); - bool ScanHash(nsCSSToken& aResult); - bool ScanNumber(nsCSSToken& aResult); - bool ScanString(nsCSSToken& aResult); - bool ScanURange(nsCSSToken& aResult); - - void SetEOFCharacters(uint32_t aEOFCharacters); - void AddEOFCharacters(uint32_t aEOFCharacters); - - const char16_t *mBuffer; - uint32_t mOffset; - uint32_t mCount; - - uint32_t mLineNumber; - uint32_t mLineOffset; - - uint32_t mTokenLineNumber; - uint32_t mTokenLineOffset; - uint32_t mTokenOffset; - - uint32_t mRecordStartOffset; - EOFCharacters mEOFCharacters; - - bool mRecording; - bool mSeenBadToken; - bool mSeenVariableReference; - - nsString mSourceMapURL; - nsString mSourceURL; -}; - -// Token for the grid-template-areas micro-syntax -// http://dev.w3.org/csswg/css-grid/#propdef-grid-template-areas -struct MOZ_STACK_CLASS nsCSSGridTemplateAreaToken { - nsAutoString mName; // Empty for a null cell, non-empty for a named cell - bool isTrash; // True for a trash token, mName is ignored in this case. -}; - -// Scanner for the grid-template-areas micro-syntax -class nsCSSGridTemplateAreaScanner { -public: - explicit nsCSSGridTemplateAreaScanner(const nsAString& aBuffer); - - // Get the next token. Return false on EOF. - // aTokenResult is filled in with the data for the token. - bool Next(nsCSSGridTemplateAreaToken& aTokenResult); - -private: - const char16_t *mBuffer; - uint32_t mOffset; - uint32_t mCount; -}; - -#endif /* nsCSSScanner_h___ */ diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 1ad1117a7e34..f1392ea921eb 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -20,6 +20,7 @@ #include "nsIScrollableFrame.h" #include "nsContentUtils.h" #include "nsIContent.h" +#include "nsThemeConstants.h" #include "nsDOMCSSRect.h" #include "nsDOMCSSRGBColor.h" diff --git a/layout/style/nsStyleUtil.h b/layout/style/nsStyleUtil.h index 25a05e8b843e..df828a889538 100644 --- a/layout/style/nsStyleUtil.h +++ b/layout/style/nsStyleUtil.h @@ -26,6 +26,9 @@ struct nsCSSValueList; namespace mozilla { class FontSlantStyle; +namespace dom { +class Element; +} } // Style utility functions diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp index c2a5809259c8..56438b8da0ef 100755 --- a/layout/style/nsTransitionManager.cpp +++ b/layout/style/nsTransitionManager.cpp @@ -41,6 +41,7 @@ using mozilla::TimeDuration; using mozilla::dom::Animation; using mozilla::dom::AnimationPlayState; using mozilla::dom::CSSTransition; +using mozilla::dom::Nullable; using namespace mozilla; using namespace mozilla::css; diff --git a/layout/style/test/moz.build b/layout/style/test/moz.build index ac0893abb8b0..27f8d7b3f356 100644 --- a/layout/style/test/moz.build +++ b/layout/style/test/moz.build @@ -30,7 +30,6 @@ HostSimplePrograms([ MOCHITEST_MANIFESTS += [ 'mochitest.ini', ] -XPCSHELL_TESTS_MANIFESTS += ['xpcshell.ini'] BROWSER_CHROME_MANIFESTS += ['browser.ini'] MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini'] diff --git a/layout/style/test/test_csslexer.js b/layout/style/test/test_csslexer.js deleted file mode 100644 index 16d589d9f1c6..000000000000 --- a/layout/style/test/test_csslexer.js +++ /dev/null @@ -1,169 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -Cu.importGlobalProperties(["InspectorUtils"]); - -function test_lexer(cssText, tokenTypes) { - let lexer = InspectorUtils.getCSSLexer(cssText); - let reconstructed = ''; - let lastTokenEnd = 0; - let i = 0; - while (true) { - let token = lexer.nextToken(); - if (!token) { - break; - } - let combined = token.tokenType; - if (token.text) - combined += ":" + token.text; - equal(combined, tokenTypes[i]); - ok(token.endOffset > token.startOffset); - equal(token.startOffset, lastTokenEnd); - lastTokenEnd = token.endOffset; - reconstructed += cssText.substring(token.startOffset, token.endOffset); - ++i; - } - // Ensure that we saw the correct number of tokens. - equal(i, tokenTypes.length); - // Ensure that the reported offsets cover all the text. - equal(reconstructed, cssText); -} - -var LEX_TESTS = [ - ["simple", ["ident:simple"]], - ["simple: { hi; }", - ["ident:simple", "symbol::", - "whitespace", "symbol:{", - "whitespace", "ident:hi", - "symbol:;", "whitespace", - "symbol:}"]], - ["/* whatever */", ["comment"]], - ["'string'", ["string:string"]], - ['"string"', ["string:string"]], - ["rgb(1,2,3)", ["function:rgb", "number", - "symbol:,", "number", - "symbol:,", "number", - "symbol:)"]], - ["@media", ["at:media"]], - ["#hibob", ["id:hibob"]], - ["#123", ["hash:123"]], - ["23px", ["dimension:px"]], - ["23%", ["percentage"]], - ["url(http://example.com)", ["url:http://example.com"]], - ["url('http://example.com')", ["url:http://example.com"]], - ["url( 'http://example.com' )", - ["url:http://example.com"]], - // In CSS Level 3, this is an ordinary URL, not a BAD_URL. - ["url(http://example.com", ["url:http://example.com"]], - ["url(http://example.com @", ["bad_url:http://example.com"]], - ["quo\\ting", ["ident:quoting"]], - ["'bad string\n", ["bad_string:bad string", "whitespace"]], - ["~=", ["includes"]], - ["|=", ["dashmatch"]], - ["^=", ["beginsmatch"]], - ["$=", ["endsmatch"]], - ["*=", ["containsmatch"]], - - // URANGE may be on the way out, and it isn't used by devutils, so - // let's skip it. - - ["", ["htmlcomment", "whitespace", "ident:html", - "whitespace", "ident:comment", "whitespace", - "htmlcomment"]], - - // earlier versions of CSS had "bad comment" tokens, but in level 3, - // unterminated comments are just comments. - ["/* bad comment", ["comment"]] -]; - -function test_lexer_linecol(cssText, locations) { - let lexer = InspectorUtils.getCSSLexer(cssText); - let i = 0; - while (true) { - let token = lexer.nextToken(); - let startLine = lexer.lineNumber; - let startColumn = lexer.columnNumber; - - // We do this in a bit of a funny way so that we can also test the - // location of the EOF. - let combined = ":" + startLine + ":" + startColumn; - if (token) - combined = token.tokenType + combined; - - equal(combined, locations[i]); - ++i; - - if (!token) { - break; - } - } - // Ensure that we saw the correct number of tokens. - equal(i, locations.length); -} - -function test_lexer_eofchar(cssText, argText, expectedAppend, - expectedNoAppend) { - let lexer = InspectorUtils.getCSSLexer(cssText); - while (lexer.nextToken()) { - // Nothing. - } - - info("EOF char test, input = " + cssText); - - let result = lexer.performEOFFixup(argText, true); - equal(result, expectedAppend); - - result = lexer.performEOFFixup(argText, false); - equal(result, expectedNoAppend); -} - -var LINECOL_TESTS = [ - ["simple", ["ident:0:0", ":0:6"]], - ["\n stuff", ["whitespace:0:0", "ident:1:4", ":1:9"]], - ['"string with \\\nnewline" \r\n', ["string:0:0", "whitespace:1:8", - ":2:0"]] -]; - -var EOFCHAR_TESTS = [ - ["hello", "hello"], - ["hello \\", "hello \\\\", "hello \\\uFFFD"], - ["'hello", "'hello'"], - ["\"hello", "\"hello\""], - ["'hello\\", "'hello\\\\'", "'hello'"], - ["\"hello\\", "\"hello\\\\\"", "\"hello\""], - ["/*hello", "/*hello*/"], - ["/*hello*", "/*hello*/"], - ["/*hello\\", "/*hello\\*/"], - ["url(hello", "url(hello)"], - ["url('hello", "url('hello')"], - ["url(\"hello", "url(\"hello\")"], - ["url(hello\\", "url(hello\\\\)", "url(hello\\\uFFFD)"], - ["url('hello\\", "url('hello\\\\')", "url('hello')"], - ["url(\"hello\\", "url(\"hello\\\\\")", "url(\"hello\")"], -]; - -function run_test() -{ - let text, result; - for ([text, result] of LEX_TESTS) { - test_lexer(text, result); - } - - for ([text, result] of LINECOL_TESTS) { - test_lexer_linecol(text, result); - } - - for ([text, expectedAppend, expectedNoAppend] of EOFCHAR_TESTS) { - if (!expectedNoAppend) { - expectedNoAppend = expectedAppend; - } - test_lexer_eofchar(text, text, expectedAppend, expectedNoAppend); - } - - // Ensure that passing a different inputString to performEOFFixup - // doesn't cause an assertion trying to strip a backslash from the - // end of an empty string. - test_lexer_eofchar("'\\", "", "\\'", "'"); -} diff --git a/layout/style/test/xpcshell.ini b/layout/style/test/xpcshell.ini deleted file mode 100644 index 0ecd798d70cd..000000000000 --- a/layout/style/test/xpcshell.ini +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -head = - -[test_csslexer.js] diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt index 7dbb3e745347..adf959ea1ecc 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt @@ -347,6 +347,20 @@ class GeckoSessionTestRuleTest : BaseSessionTest(noErrorCollector = true) { }) } + @Test fun waitUntilCalled_zeroCount() { + // Support having @AssertCalled(count = 0) annotations for waitUntilCalled calls. + sessionRule.session.loadTestPath(HELLO_HTML_PATH) + sessionRule.waitUntilCalled(object : Callbacks.ProgressDelegate, Callbacks.ScrollDelegate { + @AssertCalled(count = 1) + override fun onPageStop(session: GeckoSession, success: Boolean) { + } + + @AssertCalled(count = 0) + override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) { + } + }) + } + @Test fun forCallbacksDuringWait_anyMethod() { sessionRule.session.loadTestPath(HELLO_HTML_PATH) sessionRule.waitForPageStop() diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TextInputDelegateTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TextInputDelegateTest.kt new file mode 100644 index 000000000000..08e2afd9e929 --- /dev/null +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TextInputDelegateTest.kt @@ -0,0 +1,193 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +package org.mozilla.geckoview.test + +import android.os.SystemClock +import org.mozilla.geckoview.GeckoSession +import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled +import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDevToolsAPI +import org.mozilla.geckoview.test.util.Callbacks + +import android.support.test.filters.MediumTest +import android.view.KeyEvent + +import org.hamcrest.Matchers.* +import org.junit.Assume.assumeThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Parameterized.Parameter + +@MediumTest +@RunWith(Parameterized::class) +@WithDevToolsAPI +class TextInputDelegateTest : BaseSessionTest() { + // "parameters" needs to be a static field, so it has to be in a companion object. + companion object { + @get:Parameterized.Parameters(name = "{0}") + @JvmStatic + val parameters: List> = listOf( + arrayOf("#input"), + arrayOf("#textarea"), + arrayOf("#contenteditable"), + arrayOf("#designmode")) + } + + @field:Parameter(0) @JvmField var id: String = "" + + private fun pressKey(keyCode: Int) { + // Create a Promise to listen to the key event, and wait on it below. + val promise = mainSession.evaluateJS( + "new Promise(r => window.addEventListener('keyup', r, { once: true }))") + val time = SystemClock.uptimeMillis() + val keyEvent = KeyEvent(time, time, KeyEvent.ACTION_DOWN, keyCode, 0); + mainSession.textInput.onKeyDown(keyCode, keyEvent) + mainSession.textInput.onKeyUp(keyCode, KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP)) + promise.asJSPromise().value + } + + @Test fun restartInput() { + // Check that restartInput is called on focus and blur. + mainSession.loadTestPath(SELECTION_ACTION_PATH) + mainSession.waitForPageStop() + + mainSession.evaluateJS("$('$id').focus()") + mainSession.waitUntilCalled(object : Callbacks.TextInputDelegate { + @AssertCalled(count = 1) + override fun restartInput(session: GeckoSession, reason: Int) { + assertThat("Reason should be correct", + reason, equalTo(GeckoSession.TextInputDelegate.RESTART_REASON_FOCUS)) + } + }) + + mainSession.evaluateJS("$('$id').blur()") + mainSession.waitUntilCalled(object : Callbacks.TextInputDelegate { + @AssertCalled(count = 1) + override fun restartInput(session: GeckoSession, reason: Int) { + assertThat("Reason should be correct", + reason, equalTo(GeckoSession.TextInputDelegate.RESTART_REASON_BLUR)) + } + + // Also check that showSoftInput/hideSoftInput are not called before a user action. + @AssertCalled(count = 0) + override fun showSoftInput(session: GeckoSession) { + } + + @AssertCalled(count = 0) + override fun hideSoftInput(session: GeckoSession) { + } + }) + } + + @Test fun restartInput_temporaryFocus() { + // Our user action trick doesn't work for design-mode, so we can't test that here. + assumeThat("Not in designmode", id, not(equalTo("#designmode"))) + + mainSession.loadTestPath(SELECTION_ACTION_PATH) + mainSession.waitForPageStop() + + // Focus the input once here and once below, but we should only get a + // single restartInput or showSoftInput call for the second focus. + mainSession.evaluateJS("$('$id').focus(); $('$id').blur()") + + // Simulate a user action so we're allowed to show/hide the keyboard. + pressKey(KeyEvent.KEYCODE_CTRL_LEFT) + mainSession.evaluateJS("$('$id').focus()") + + mainSession.waitUntilCalled(object : Callbacks.TextInputDelegate { + @AssertCalled(count = 1, order = [1]) + override fun restartInput(session: GeckoSession, reason: Int) { + assertThat("Reason should be correct", + reason, equalTo(GeckoSession.TextInputDelegate.RESTART_REASON_FOCUS)) + } + + @AssertCalled(count = 1, order = [2]) + override fun showSoftInput(session: GeckoSession) { + super.showSoftInput(session) + } + + @AssertCalled(count = 0) + override fun hideSoftInput(session: GeckoSession) { + super.hideSoftInput(session) + } + }) + } + + @Test fun restartInput_temporaryBlur() { + // Our user action trick doesn't work for design-mode, so we can't test that here. + assumeThat("Not in designmode", id, not(equalTo("#designmode"))) + + mainSession.loadTestPath(SELECTION_ACTION_PATH) + mainSession.waitForPageStop() + + // Simulate a user action so we're allowed to show/hide the keyboard. + pressKey(KeyEvent.KEYCODE_CTRL_LEFT) + mainSession.evaluateJS("$('$id').focus()") + mainSession.waitUntilCalled(GeckoSession.TextInputDelegate::class, + "restartInput", "showSoftInput") + + // We should get a pair of restartInput calls for the blur/focus, + // but only one showSoftInput call and no hideSoftInput call. + mainSession.evaluateJS("$('$id').blur(); $('$id').focus()") + + mainSession.waitUntilCalled(object : Callbacks.TextInputDelegate { + @AssertCalled(count = 2, order = [1]) + override fun restartInput(session: GeckoSession, reason: Int) { + assertThat("Reason should be correct", reason, equalTo(forEachCall( + GeckoSession.TextInputDelegate.RESTART_REASON_BLUR, + GeckoSession.TextInputDelegate.RESTART_REASON_FOCUS))) + } + + @AssertCalled(count = 1, order = [2]) + override fun showSoftInput(session: GeckoSession) { + } + + @AssertCalled(count = 0) + override fun hideSoftInput(session: GeckoSession) { + } + }) + } + + @Test fun showHideSoftInput() { + // Our user action trick doesn't work for design-mode, so we can't test that here. + assumeThat("Not in designmode", id, not(equalTo("#designmode"))) + + mainSession.loadTestPath(SELECTION_ACTION_PATH) + mainSession.waitForPageStop() + + // Simulate a user action so we're allowed to show/hide the keyboard. + pressKey(KeyEvent.KEYCODE_CTRL_LEFT) + + mainSession.evaluateJS("$('$id').focus()") + mainSession.waitUntilCalled(object : Callbacks.TextInputDelegate { + @AssertCalled(count = 1, order = [1]) + override fun restartInput(session: GeckoSession, reason: Int) { + } + + @AssertCalled(count = 1, order = [2]) + override fun showSoftInput(session: GeckoSession) { + } + + @AssertCalled(count = 0) + override fun hideSoftInput(session: GeckoSession) { + } + }) + + mainSession.evaluateJS("$('$id').blur()") + mainSession.waitUntilCalled(object : Callbacks.TextInputDelegate { + @AssertCalled(count = 1, order = [1]) + override fun restartInput(session: GeckoSession, reason: Int) { + } + + @AssertCalled(count = 0) + override fun showSoftInput(session: GeckoSession) { + } + + @AssertCalled(count = 1, order = [2]) + override fun hideSoftInput(session: GeckoSession) { + } + }) + } +} diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java index e4bcfe8f502e..a84361c93599 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java @@ -97,6 +97,7 @@ public class GeckoSessionTestRule extends UiThreadTestRule { public static final String APK_URI_PREFIX = "resource://android/"; private static final Method sGetNextMessage; + private static final Method sOnPageStart; private static final Method sOnPageStop; private static final Method sOnNewSession; private static final Method sOnCrash; @@ -105,6 +106,8 @@ public class GeckoSessionTestRule extends UiThreadTestRule { try { sGetNextMessage = MessageQueue.class.getDeclaredMethod("next"); sGetNextMessage.setAccessible(true); + sOnPageStart = GeckoSession.ProgressDelegate.class.getMethod( + "onPageStart", GeckoSession.class, String.class); sOnPageStop = GeckoSession.ProgressDelegate.class.getMethod( "onPageStop", GeckoSession.class, boolean.class); sOnNewSession = GeckoSession.NavigationDelegate.class.getMethod( @@ -1354,17 +1357,25 @@ public class GeckoSessionTestRule extends UiThreadTestRule { // load ends with the first onPageStop call, so ignore everything from the session // until the first onPageStop call. + // For the cached session, we may get multiple initial loads. We should specifically look + // for an about:blank load, and wait until that has stopped. + final boolean lookForAboutBlank = session.equals(sCachedSession); + try { // We cannot detect initial page load without progress delegate. assertThat("ProgressDelegate cannot be null-delegate when opening session", GeckoSession.ProgressDelegate.class, not(isIn(mNullDelegates))); mCallRecordHandler = new CallRecordHandler() { + private boolean mIsAboutBlank = !lookForAboutBlank; + @Override public boolean handleCall(final Method method, final Object[] args) { final boolean matching = DEFAULT_DELEGATES.contains( method.getDeclaringClass()) && session.equals(args[0]); - if (matching && sOnPageStop.equals(method)) { + if (matching && sOnPageStart.equals(method)) { + mIsAboutBlank = "about:blank".equals(args[1]); + } else if (matching && mIsAboutBlank && sOnPageStop.equals(method)) { mCallRecordHandler = null; } return matching; @@ -1706,7 +1717,7 @@ public class GeckoSessionTestRule extends UiThreadTestRule { throw new RuntimeException(e); } final AssertCalled ac = getAssertCalled(callbackMethod, callback); - if (ac != null && ac.value()) { + if (ac != null && ac.value() && ac.count() != 0) { methodCalls.add(new MethodCall(session, method, ac, /* target */ null)); } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java index 926a0f00f284..0f3c32e1dcec 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java @@ -1209,22 +1209,37 @@ import android.view.inputmethod.EditorInfo; switch (type) { case SessionTextInput.EditableListener.NOTIFY_IME_OF_FOCUS: + if (mFocusedChild != null) { + // Already focused, so blur first. + icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_BLUR, + /* toggleSoftInput */ false); + } + mFocusedChild = child; mNeedSync = false; mText.syncShadowText(/* listener */ null); - // Do not reset mIMEState here; see comments in notifyIMEContext - if (mIMEState != SessionTextInput.EditableListener.IME_STATE_DISABLED) { - icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_FOCUS); + // Most of the time notifyIMEContext comes _before_ notifyIME, but sometimes it + // comes _after_ notifyIME. In that case, the state is disabled here, and + // notifyIMEContext is responsible for calling restartInput. + if (mIMEState == SessionTextInput.EditableListener.IME_STATE_DISABLED) { + mIMEState = SessionTextInput.EditableListener.IME_STATE_UNKNOWN; + } else { + icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_FOCUS, + /* toggleSoftInput */ true); } break; case SessionTextInput.EditableListener.NOTIFY_IME_OF_BLUR: - mFocusedChild = null; + if (mFocusedChild != null) { + mFocusedChild = null; + icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_BLUR, + /* toggleSoftInput */ true); + } break; case SessionTextInput.EditableListener.NOTIFY_IME_OPEN_VKB: - toggleSoftInput(/* force */ true); + toggleSoftInput(/* force */ true, mIMEState); return; // Don't notify listener. case SessionTextInput.EditableListener.NOTIFY_IME_TO_COMMIT_COMPOSITION: { @@ -1244,7 +1259,8 @@ import android.view.inputmethod.EditorInfo; } } // No longer have composition; perform reset. - icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_CONTENT_CHANGE); + icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_CONTENT_CHANGE, + /* toggleSoftInput */ false); return; // Don't notify listener. } @@ -1311,24 +1327,26 @@ import android.view.inputmethod.EditorInfo; mListener.notifyIMEContext(state, typeHint, modeHint, actionHint, flags); } - // On focus, the notifyIMEContext call comes *before* the - // notifyIME(NOTIFY_IME_OF_FOCUS) call, but we need to call restartInput during - // notifyIME, so we skip restartInput here. On blur, the notifyIMEContext call - // comes *after* the notifyIME(NOTIFY_IME_OF_BLUR) call, and we need to call - // restartInput here. - - // In either case, there is nothing to do here if we were disabled before. - if (oldState == SessionTextInput.EditableListener.IME_STATE_DISABLED) { + if (state == SessionTextInput.EditableListener.IME_STATE_DISABLED || + mFocusedChild == null) { return; } - if (state == SessionTextInput.EditableListener.IME_STATE_DISABLED) { - icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_BLUR); - } else if (mFocusedChild != null) { - icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_CONTENT_CHANGE); + + // We changed state while focused. If the old state is unknown, it means this + // notifyIMEContext call came _after_ the notifyIME call, so we need to call + // restartInput(FOCUS) here (see comment in icNotifyIME). Otherwise, this change + // counts as a content change. + if (oldState == SessionTextInput.EditableListener.IME_STATE_UNKNOWN) { + icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_FOCUS, + /* toggleSoftInput */ true); + } else if (oldState != SessionTextInput.EditableListener.IME_STATE_DISABLED) { + icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_CONTENT_CHANGE, + /* toggleSoftInput */ false); } } - private void icRestartInput(@GeckoSession.TextInputDelegate.RestartReason final int reason) { + private void icRestartInput(@GeckoSession.TextInputDelegate.RestartReason final int reason, + final boolean toggleSoftInput) { if (DEBUG) { assertOnIcThread(); } @@ -1336,13 +1354,28 @@ import android.view.inputmethod.EditorInfo; ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { - mSoftInputReentrancyGuard.incrementAndGet(); + if (DEBUG) { + Log.d(LOGTAG, "restartInput(" + reason + ", " + toggleSoftInput + ')'); + } + if (toggleSoftInput) { + mSoftInputReentrancyGuard.incrementAndGet(); + } mSession.getTextInput().getDelegate().restartInput(mSession, reason); + if (!toggleSoftInput) { + return; + } postToInputConnection(new Runnable() { @Override public void run() { - toggleSoftInput(/* force */ false); + int state = mIMEState; + if (reason == GeckoSession.TextInputDelegate.RESTART_REASON_BLUR && + mFocusedChild == null) { + // On blur, notifyIMEContext() is called after notifyIME(). Therefore, + // mIMEState is not up-to-date here and we need to override it. + state = SessionTextInput.EditableListener.IME_STATE_DISABLED; + } + toggleSoftInput(/* force */ false, state); } }); } @@ -1362,7 +1395,7 @@ import android.view.inputmethod.EditorInfo; if (state == SessionTextInput.EditableListener.IME_STATE_DISABLED) { outAttrs.inputType = InputType.TYPE_NULL; - toggleSoftInput(/* force */ false); + toggleSoftInput(/* force */ false, state); return; } @@ -1431,12 +1464,14 @@ import android.view.inputmethod.EditorInfo; outAttrs.imeOptions |= InputMethods.IME_FLAG_NO_PERSONALIZED_LEARNING; } - toggleSoftInput(/* force */ false); + toggleSoftInput(/* force */ false, state); } - /* package */ void toggleSoftInput(final boolean force) { + /* package */ void toggleSoftInput(final boolean force, final int state) { + if (DEBUG) { + Log.d(LOGTAG, "toggleSoftInput"); + } // Can be called from UI or IC thread. - final int state = mIMEState; final int flags = mIMEFlags; // There are three paths that toggleSoftInput() can be called: @@ -1472,12 +1507,22 @@ import android.view.inputmethod.EditorInfo; SessionTextInput.EditableListener.IME_FLAG_USER_ACTION) != 0); if (!force && (isReentrant || !isFocused || !isUserAction)) { + if (DEBUG) { + Log.d(LOGTAG, "toggleSoftInput: no-op, reentrant=" + isReentrant + + ", focused=" + isFocused + ", user=" + isUserAction); + } return; } if (state == SessionTextInput.EditableListener.IME_STATE_DISABLED) { + if (DEBUG) { + Log.d(LOGTAG, "hideSoftInput"); + } mSession.getTextInput().getDelegate().hideSoftInput(mSession); return; } + if (DEBUG) { + Log.d(LOGTAG, "showSoftInput"); + } mSession.getEventDispatcher().dispatch("GeckoView:ZoomToInput", null); mSession.getTextInput().getDelegate().showSoftInput(mSession); } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java index ed5a825e4317..87fec7dbab2f 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java @@ -85,6 +85,7 @@ public final class SessionTextInput { @WrapForJNI final int NOTIFY_IME_TO_CANCEL_COMPOSITION = 9; // IME enabled state for notifyIMEContext(). + final int IME_STATE_UNKNOWN = -1; final int IME_STATE_DISABLED = 0; final int IME_STATE_ENABLED = 1; final int IME_STATE_PASSWORD = 2; diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index a72cf5e5a796..f00b3da6a7a3 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -390,7 +390,7 @@ ; NOTE: This must match the config checks in ; /toolkit/components/backgroundhangmonitor/moz.build. -#if defined(NIGHTLY_BUILD) && !defined(MOZ_DEBUG) && !defined(MOZ_TSAN) +#if defined(NIGHTLY_BUILD) && !defined(MOZ_DEBUG) && !defined(MOZ_TSAN) && !defined(MOZ_ASAN) @BINPATH@/components/BHRTelemetryService.js @BINPATH@/components/BHRTelemetryService.manifest #endif diff --git a/modules/libpref/init/StaticPrefList.h b/modules/libpref/init/StaticPrefList.h index 8d27122a132d..bc9d11513ce1 100644 --- a/modules/libpref/init/StaticPrefList.h +++ b/modules/libpref/init/StaticPrefList.h @@ -105,6 +105,12 @@ VARCACHE_PREF( bool, true ) +VARCACHE_PREF( + "dom.performance.enable_scheduler_timing", + dom_performance_enable_scheduler_timing, + RelaxedAtomicBool, false +) + // If true. then the service worker interception and the ServiceWorkerManager // will live in the parent process. This only takes effect on browser start. // Note, this is not currently safe to use for normal browsing yet. diff --git a/python/mozbuild/mozbuild/backend/base.py b/python/mozbuild/mozbuild/backend/base.py index 50e39e2e0081..ecd472362885 100644 --- a/python/mozbuild/mozbuild/backend/base.py +++ b/python/mozbuild/mozbuild/backend/base.py @@ -174,9 +174,10 @@ class BuildBackend(LoggingMixin): with self._write_file(list_file) as fh: fh.write('\n'.join(sorted(self._backend_output_files))) else: - # Always update its mtime. - with open(list_file, 'a'): - os.utime(list_file, None) + # Always update its mtime if we're not in dry-run mode. + if not self.dry_run: + with open(list_file, 'a'): + os.utime(list_file, None) # Write out the list of input files for the backend with self._write_file('%s.in' % list_file) as fh: diff --git a/python/mozbuild/mozbuild/vendor_rust.py b/python/mozbuild/mozbuild/vendor_rust.py index b8f346c897ef..01ce0cb1312d 100644 --- a/python/mozbuild/mozbuild/vendor_rust.py +++ b/python/mozbuild/mozbuild/vendor_rust.py @@ -148,6 +148,7 @@ Please commit or stash these changes before vendoring, or re-run with `--ignore- 'Apache-2.0 / MIT', 'Apache-2.0/MIT', 'Apache-2 / MIT', + 'BSD-2-Clause', 'CC0-1.0', 'ISC', 'ISC/Apache-2.0', @@ -162,9 +163,6 @@ Please commit or stash these changes before vendoring, or re-run with `--ignore- # Licenses for code used at build time (e.g. code generators). Please see the above # comments before adding anything to this list. BUILDTIME_LICENSE_WHITELIST = { - 'BSD-2-Clause': [ - 'Inflector', - ], 'BSD-3-Clause': [ 'adler32', 'bindgen', diff --git a/taskcluster/taskgraph/transforms/job/run_task.py b/taskcluster/taskgraph/transforms/job/run_task.py index c2e314c2e312..f48570248b43 100644 --- a/taskcluster/taskgraph/transforms/job/run_task.py +++ b/taskcluster/taskgraph/transforms/job/run_task.py @@ -94,10 +94,6 @@ def docker_worker_run_task(config, job, taskdesc): 'skip-untrusted': True, }) - # This must match EXIT_PURGE_CACHES in taskcluster/scripts/run-task - worker.setdefault('retry-exit-status', []).append(72) - worker.setdefault('purge-caches-exit-status', []).append(72) - run_command = run['command'] if isinstance(run_command, basestring): run_command = ['bash', '-cx', run_command] diff --git a/taskcluster/taskgraph/transforms/task.py b/taskcluster/taskgraph/transforms/task.py index 003a3197eeec..dca46c98972c 100644 --- a/taskcluster/taskgraph/transforms/task.py +++ b/taskcluster/taskgraph/transforms/task.py @@ -786,6 +786,17 @@ def build_docker_worker_payload(config, task, task_def): if 'max-run-time' in worker: payload['maxRunTime'] = worker['max-run-time'] + run_task = payload.get('command', [''])[0].endswith('run-task') + + # run-task exits EXIT_PURGE_CACHES if there is a problem with caches. + # Automatically retry the tasks and purge caches if we see this exit + # code. + # TODO move this closer to code adding run-task once bug 1469697 is + # addressed. + if run_task: + worker.setdefault('retry-exit-status', []).append(72) + worker.setdefault('purge-caches-exit-status', []).append(72) + payload['onExitStatus'] = {} if 'retry-exit-status' in worker: payload['onExitStatus']['retry'] = worker['retry-exit-status'] @@ -802,8 +813,6 @@ def build_docker_worker_payload(config, task, task_def): } payload['artifacts'] = artifacts - run_task = payload.get('command', [''])[0].endswith('run-task') - if isinstance(worker.get('docker-image'), basestring): out_of_tree_image = worker['docker-image'] run_task = run_task or out_of_tree_image.startswith( diff --git a/third_party/rust/encoding_rs/.cargo-checksum.json b/third_party/rust/encoding_rs/.cargo-checksum.json index edc4cfc4f763..25d32663062b 100644 --- a/third_party/rust/encoding_rs/.cargo-checksum.json +++ b/third_party/rust/encoding_rs/.cargo-checksum.json @@ -1 +1 @@ -{"files":{".travis.yml":"262b9925451db996c31e4bb8bb6bbdb36c3f6009f407db5ed4b0ed25cd72d8c8","CONTRIBUTING.md":"0d4bfb1068c2b2f32b7bc7833662dfff628d86b0f31068266027c719fb727360","COPYRIGHT":"20d4fff11cca11529df3f02096fbe8ffe350219cdb07cdedea34e6a762866da5","Cargo.toml":"599b8383cc4c55f2e878afcb647406599b144975ac2c1b9589c133a83db72b1d","Ideas.md":"7fbeddb0f8ba7b233673ee705997adc2fddb1636a17fe662532b35ef2810a51d","LICENSE-APACHE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","LICENSE-MIT":"74aa8b6d04c36bb640ee81187a3f24a2fa94e36d4c1d4f2ca164c3784ae87a83","README.md":"de6046ac303d043f7ea5b622e80ee91e3d7d64afb0c6a4cf6ef7086c073722c4","doc/Big5.txt":"f73a2edc5cb6c2d140ba6e07f4542e1c4a234950378acde1df93480f0ca0be0b","doc/EUC-JP.txt":"ee2818b907d0137f40a9ab9fd525fc700a44dbdddb6cf0c157a656566bae4bf1","doc/EUC-KR.txt":"71d9e2ccf3b124e8bdfb433c8cf2773fd878077038d0cec3c7237a50f4a78a30","doc/GBK.txt":"c1b522b5a799884e5001da661f42c5a8f4d0acb9ef1d74b206f22b5f65365606","doc/IBM866.txt":"a5a433e804d0f83af785015179fbc1d9b0eaf1f7960efcd04093e136b51fbd0e","doc/ISO-2022-JP.txt":"af86684f5a8f0e2868d7b2c292860140c3d2e5527530ca091f1b28198e8e2fe6","doc/ISO-8859-10.txt":"6d3949ad7c81ca176895101ed81a1db7df1060d64e262880b94bd31bb344ab4d","doc/ISO-8859-13.txt":"3951dd89cf93f7729148091683cf8511f4529388b7dc8dcd0d62eaed55be93fa","doc/ISO-8859-14.txt":"3d330784a0374fd255a38b47949675cc7168c800530534b0a01cac6edc623adc","doc/ISO-8859-15.txt":"24b1084aab5127a85aab99153f86e24694d0a3615f53b5ce23683f97cf66c47a","doc/ISO-8859-16.txt":"ce0272559b92ba76d7a7e476f6424ae4a5cc72e75b183611b08392e44add4d25","doc/ISO-8859-2.txt":"18ceff88c13d1b5ba455a3919b1e3de489045c4c3d2dd7e8527c125c75d54aad","doc/ISO-8859-3.txt":"21798404c68f4f5db59223362f24999da96968c0628427321fccce7d2849a130","doc/ISO-8859-4.txt":"d27f6520c6c5bfbcc19176b71d081cdb3bccde1622bb3e420d5680e812632d53","doc/ISO-8859-5.txt":"a10ec8d6ea7a78ad15da7275f6cb1a3365118527e28f9af6d0d5830501303f3a","doc/ISO-8859-6.txt":"ccda8a2efc96115336bdd77776637b9712425e44fbcf745353b9057fbef144e7","doc/ISO-8859-7.txt":"17900fa1f27a445958f0a77d7d9056be375a6bd7ee4492aa680c7c1500bab85e","doc/ISO-8859-8-I.txt":"8357555646d54265a9b9ffa3e68b08d132312f1561c60108ff9b8b1167b6ecf2","doc/ISO-8859-8.txt":"72cd6f3afb7b4a9c16a66a362473315770b7755d72c86c870e52fc3eba86c8af","doc/KOI8-R.txt":"839cf19a38da994488004ed7814b1f6151640156a9a2af02bf2efca745fb5966","doc/KOI8-U.txt":"0cc76624ed1f024183e2298b7e019957da2c70c8ca06e0fc4e6f353f50a5054f","doc/Shift_JIS.txt":"34c49141818cb9ddbcf59cc858f78a79be8ad148d563f26415108ae1f148443f","doc/UTF-16BE.txt":"e2e280d8acbaa6d2a6b3569d60e17500a285f2baa0df3363dd85537cd5a1ef8f","doc/UTF-16LE.txt":"70bdc170e3fc5298ba68f10125fb5eeb8b077036cc96bb4416c4de396f6d76c1","doc/UTF-8.txt":"ea7bae742e613010ced002cf4b601a737d2203fad65e115611451bc4428f548a","doc/gb18030.txt":"dc71378a8f07a2d8659f69ee81fb8791fef56ba86f124b429978285237bb4a7b","doc/macintosh.txt":"57491e53866711b4672d9b9ff35380b9dac9e0d8e3d6c20bdd6140603687c023","doc/replacement.txt":"4b6c3bbd7999d9d4108a281594bd02d13607e334a95465afff8c2c08d395f0e4","doc/windows-1250.txt":"61296bb6a21cdab602300d32ecfba434cb82de5ac3bc88d58710d2f125e28d39","doc/windows-1251.txt":"7deea1c61dea1485c8ff02db2c7d578db7a9aab63ab1cfd02ec04b515864689e","doc/windows-1252.txt":"933ef3bdddfce5ee132b9f1a1aa8b47423d2587bbe475b19028d0a6d38e180b6","doc/windows-1253.txt":"1a38748b88e99071a5c7b3d5456ead4caedeabab50d50d658be105bc113714de","doc/windows-1254.txt":"f8372f86c6f8d642563cd6ddc025260553292a39423df1683a98670bd7bf2b47","doc/windows-1255.txt":"4e5852494730054e2da258a74e1b9d780abbcdd8ce22ebc218ca2efe9e90493d","doc/windows-1256.txt":"c0879c5172abedead302a406e8f60d9cd9598694a0ffa4fd288ffe4fef7b8ea1","doc/windows-1257.txt":"c28a0c9f964fcb2b46d21f537c402446501a2800670481d6abf9fd9e9018d523","doc/windows-1258.txt":"5019ae4d61805c79aacbf17c93793342dbb098d65a1837783bc3e2c6d6a23602","doc/windows-874.txt":"4ef0e4501c5feba8b17aee1818602ed44b36ca8475db771ce2fc16d392cabecc","doc/x-mac-cyrillic.txt":"58be154d8a888ca3d484b83b44f749823ef339ab27f14d90ca9a856f5050a8bd","doc/x-user-defined.txt":"f9cd07c4321bf5cfb0be4bdddd251072999b04a6cf7a6f5bc63709a84e2c1ffc","generate-encoding-data.py":"bbe134cc013a17c32bd51e868a2ee4f2c3e2205e40afb9fc60f98fa9758961b8","rustfmt.toml":"85c1a3b4382fd89e991cbb81b70fb52780472edc064c963943cdaaa56e0a2030","src/ascii.rs":"578ebe13e54f1f4eb1f69c29526b25f57e22f5d941c7359f1d3a01f032bce2ed","src/big5.rs":"659ddb66f82142468774715cd9ecdc8356005fec9d91e46d54ae2280a88adb4b","src/data.rs":"18793e23f98d9aba1ed2858c43b2103c9c05a8571429b2128510c68d05ba8cb6","src/euc_jp.rs":"b178172274af1a6be38d388e5a2468e7140e9facf63d2f679f4ed77c3fc20a92","src/euc_kr.rs":"e27bd9c4e2172bb02004dd2a1fcbe02118415a1aec6a90b5632c9eac6f12ba7d","src/gb18030.rs":"752869f47ad0cad02ad3ccb6fde3eb277fa528e10e3d9432a90563db610d9dc2","src/handles.rs":"0598439bf66bd26699cba7e9e2fec001f8c57f3a510578fc3cc118ddc36d6831","src/iso_2022_jp.rs":"d904a92d49fc5878e6cb82a21185051c801489cf2800db8f37269965ca7974cc","src/lib.rs":"5fe2fd8a76f9144e4f080d23483096edd675db0e1a647fa41748743434c617b9","src/macros.rs":"04b3d81f1701b746dcce466ad2c3202a0a3e9c00f1d8db336276660640a12d7b","src/mem.rs":"b408367cef7ba93e9e90ec16cc62f763da58bcdbdb1ec0a856c6651e059ffead","src/replacement.rs":"61e0ae52664b3856f1ed202af6266b441ffcf5e2c9b22976047540e4a3252b41","src/shift_jis.rs":"7106d0a9785425b5af2323f3046e6130bd5125effa4ab484d84b529914855d67","src/simd_funcs.rs":"6139e2c7eebd8f3f35b8ce70891c6731595e5d18fa48b11f04f88c6342335935","src/single_byte.rs":"986571bb52f2cf7779c1dc8bdd8058c3183524a512bc2fcca6637eeee394b573","src/test_data/big5_in.txt":"4c5a8691f8dc717311889c63894026d2fb62725a86c4208ca274a9cc8d42a503","src/test_data/big5_in_ref.txt":"99d399e17750cf9c7cf30bb253dbfe35b81c4fcbdead93cfa48b1429213473c7","src/test_data/big5_out.txt":"6193ca97c297aa20e09396038d18e938bb7ea331c26f0f2454097296723a0b13","src/test_data/big5_out_ref.txt":"36567691f557df144f6cc520015a87038dfa156f296fcf103b56ae9a718be1fc","src/test_data/euc_kr_in.txt":"c86a7224f3215fa0d04e685622a752fdc72763e8ae076230c7fd62de57ec4074","src/test_data/euc_kr_in_ref.txt":"1f419f4ca47d708b54c73c461545a022ae2e20498fdbf8005a483d752a204883","src/test_data/euc_kr_out.txt":"e7f32e026f70be1e1b58e0047baf7d3d2c520269c4f9b9992e158b4decb0a1a3","src/test_data/euc_kr_out_ref.txt":"c9907857980b20b8e9e3b584482ed6567a2be6185d72237b6322f0404944924e","src/test_data/gb18030_in.txt":"ab7231b2d3e9afacdbd7d7f3b9e5361a7ff9f7e1cfdb4f3bd905b9362b309e53","src/test_data/gb18030_in_ref.txt":"dc5069421adca2043c55f5012b55a76fdff651d22e6e699fd0978f8d5706815c","src/test_data/gb18030_out.txt":"f0208d527f5ca63de7d9a0323be8d5cf12d8a104b2943d92c2701f0c3364dac1","src/test_data/gb18030_out_ref.txt":"6819fe47627e4ea01027003fc514b9f21a1322e732d7f1fb92cc6c5455bc6c07","src/test_data/iso_2022_jp_in.txt":"cd24bbdcb1834e25db54646fbf4c41560a13dc7540f6be3dba4f5d97d44513af","src/test_data/iso_2022_jp_in_ref.txt":"3dc4e6a5e06471942d086b16c9440945e78415f6f3f47e43717e4bc2eac2cdf5","src/test_data/iso_2022_jp_out.txt":"9b6f015329dda6c3f9ee5ce6dbd6fa9c89acc21283e886836c78b8d833480c21","src/test_data/iso_2022_jp_out_ref.txt":"78cb260093a20116ad9a42f43b05d1848c5ab100b6b9a850749809e943884b35","src/test_data/jis0208_in.txt":"6df3030553ffb0a6615bb33dc8ea9dca6d9623a9028e2ffec754ce3c3da824cc","src/test_data/jis0208_in_ref.txt":"3dc4e6a5e06471942d086b16c9440945e78415f6f3f47e43717e4bc2eac2cdf5","src/test_data/jis0208_out.txt":"4ec24477e1675ce750733bdc3c5add1cd27b6bd4ce1f09289564646e9654e857","src/test_data/jis0208_out_ref.txt":"c3e1cef5032b2b1d93a406f31ff940c4e2dfe8859b8b17ca2761fee7a75a0e48","src/test_data/jis0212_in.txt":"c011f0dd72bd7c8cd922df9374ef8d2769a77190514c77f6c62b415852eeb9fe","src/test_data/jis0212_in_ref.txt":"7d9458b3d2f73e7092a7f505c08ce1d233dde18aa679fbcf9889256239cc9e06","src/test_data/shift_jis_in.txt":"02e389ccef0dd2122e63f503899402cb7f797912c2444cc80ab93131116c5524","src/test_data/shift_jis_in_ref.txt":"512f985950ca902e643c88682dba9708b7c38d3c5ec2925168ab00ac94ab19f9","src/test_data/shift_jis_out.txt":"5fbc44da7bf639bf6cfe0fa1fd3eba7102b88f81919c9ea991302712f69426fb","src/test_data/shift_jis_out_ref.txt":"466322c6fed8286c64582731755290c2296508efdd258826e6279686649b481f","src/test_labels_names.rs":"c962c7aeac3d9ef2aca70c9e21983b231d4cf998cb06879374b0401e5149d1da","src/testing.rs":"b299d27055f3b068de66cc10a75c024b881c48bc093627c01e0b1f8bd7d94666","src/utf_16.rs":"32b6ecc3696542fb269a93c915e7402dde545e793f9599cf7dc63688fa226e0d","src/utf_8.rs":"249ec511970051d71fa863e659ab0b46ce9283d5a628b9b437619b41d4334459","src/utf_8_core.rs":"fe218284dbb9111a2336ff1684d7f58b55d9c4fe7363089a29e57f52901f0707","src/variant.rs":"e2c988a645429fd7457d7fbc487b838ab7ec7bfb8943d192992f18bbac8973f0","src/x_user_defined.rs":"1d25920ec5d005cbd623f34473d68991521aa3a83e52d4eb9b645711f10cd484"},"package":"c6848cbd169668c2338be9940ac8968179edcd8704248e1e0c885a306c42772e"} \ No newline at end of file +{"files":{".travis.yml":"262b9925451db996c31e4bb8bb6bbdb36c3f6009f407db5ed4b0ed25cd72d8c8","CONTRIBUTING.md":"0d4bfb1068c2b2f32b7bc7833662dfff628d86b0f31068266027c719fb727360","COPYRIGHT":"20d4fff11cca11529df3f02096fbe8ffe350219cdb07cdedea34e6a762866da5","Cargo.toml":"2e1a7590a783bfc137d626f1e9b9611aa90dbc130fcf8608a1c40d25a2e40685","Ideas.md":"7fbeddb0f8ba7b233673ee705997adc2fddb1636a17fe662532b35ef2810a51d","LICENSE-APACHE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","LICENSE-MIT":"74aa8b6d04c36bb640ee81187a3f24a2fa94e36d4c1d4f2ca164c3784ae87a83","README.md":"51ae50670105f27828c80f413c18b15604df98f25642fca93f4a6d489768c908","doc/Big5.txt":"f73a2edc5cb6c2d140ba6e07f4542e1c4a234950378acde1df93480f0ca0be0b","doc/EUC-JP.txt":"ee2818b907d0137f40a9ab9fd525fc700a44dbdddb6cf0c157a656566bae4bf1","doc/EUC-KR.txt":"71d9e2ccf3b124e8bdfb433c8cf2773fd878077038d0cec3c7237a50f4a78a30","doc/GBK.txt":"c1b522b5a799884e5001da661f42c5a8f4d0acb9ef1d74b206f22b5f65365606","doc/IBM866.txt":"a5a433e804d0f83af785015179fbc1d9b0eaf1f7960efcd04093e136b51fbd0e","doc/ISO-2022-JP.txt":"af86684f5a8f0e2868d7b2c292860140c3d2e5527530ca091f1b28198e8e2fe6","doc/ISO-8859-10.txt":"6d3949ad7c81ca176895101ed81a1db7df1060d64e262880b94bd31bb344ab4d","doc/ISO-8859-13.txt":"3951dd89cf93f7729148091683cf8511f4529388b7dc8dcd0d62eaed55be93fa","doc/ISO-8859-14.txt":"3d330784a0374fd255a38b47949675cc7168c800530534b0a01cac6edc623adc","doc/ISO-8859-15.txt":"24b1084aab5127a85aab99153f86e24694d0a3615f53b5ce23683f97cf66c47a","doc/ISO-8859-16.txt":"ce0272559b92ba76d7a7e476f6424ae4a5cc72e75b183611b08392e44add4d25","doc/ISO-8859-2.txt":"18ceff88c13d1b5ba455a3919b1e3de489045c4c3d2dd7e8527c125c75d54aad","doc/ISO-8859-3.txt":"21798404c68f4f5db59223362f24999da96968c0628427321fccce7d2849a130","doc/ISO-8859-4.txt":"d27f6520c6c5bfbcc19176b71d081cdb3bccde1622bb3e420d5680e812632d53","doc/ISO-8859-5.txt":"a10ec8d6ea7a78ad15da7275f6cb1a3365118527e28f9af6d0d5830501303f3a","doc/ISO-8859-6.txt":"ccda8a2efc96115336bdd77776637b9712425e44fbcf745353b9057fbef144e7","doc/ISO-8859-7.txt":"17900fa1f27a445958f0a77d7d9056be375a6bd7ee4492aa680c7c1500bab85e","doc/ISO-8859-8-I.txt":"8357555646d54265a9b9ffa3e68b08d132312f1561c60108ff9b8b1167b6ecf2","doc/ISO-8859-8.txt":"72cd6f3afb7b4a9c16a66a362473315770b7755d72c86c870e52fc3eba86c8af","doc/KOI8-R.txt":"839cf19a38da994488004ed7814b1f6151640156a9a2af02bf2efca745fb5966","doc/KOI8-U.txt":"0cc76624ed1f024183e2298b7e019957da2c70c8ca06e0fc4e6f353f50a5054f","doc/Shift_JIS.txt":"34c49141818cb9ddbcf59cc858f78a79be8ad148d563f26415108ae1f148443f","doc/UTF-16BE.txt":"e2e280d8acbaa6d2a6b3569d60e17500a285f2baa0df3363dd85537cd5a1ef8f","doc/UTF-16LE.txt":"70bdc170e3fc5298ba68f10125fb5eeb8b077036cc96bb4416c4de396f6d76c1","doc/UTF-8.txt":"ea7bae742e613010ced002cf4b601a737d2203fad65e115611451bc4428f548a","doc/gb18030.txt":"dc71378a8f07a2d8659f69ee81fb8791fef56ba86f124b429978285237bb4a7b","doc/macintosh.txt":"57491e53866711b4672d9b9ff35380b9dac9e0d8e3d6c20bdd6140603687c023","doc/replacement.txt":"4b6c3bbd7999d9d4108a281594bd02d13607e334a95465afff8c2c08d395f0e4","doc/windows-1250.txt":"61296bb6a21cdab602300d32ecfba434cb82de5ac3bc88d58710d2f125e28d39","doc/windows-1251.txt":"7deea1c61dea1485c8ff02db2c7d578db7a9aab63ab1cfd02ec04b515864689e","doc/windows-1252.txt":"933ef3bdddfce5ee132b9f1a1aa8b47423d2587bbe475b19028d0a6d38e180b6","doc/windows-1253.txt":"1a38748b88e99071a5c7b3d5456ead4caedeabab50d50d658be105bc113714de","doc/windows-1254.txt":"f8372f86c6f8d642563cd6ddc025260553292a39423df1683a98670bd7bf2b47","doc/windows-1255.txt":"4e5852494730054e2da258a74e1b9d780abbcdd8ce22ebc218ca2efe9e90493d","doc/windows-1256.txt":"c0879c5172abedead302a406e8f60d9cd9598694a0ffa4fd288ffe4fef7b8ea1","doc/windows-1257.txt":"c28a0c9f964fcb2b46d21f537c402446501a2800670481d6abf9fd9e9018d523","doc/windows-1258.txt":"5019ae4d61805c79aacbf17c93793342dbb098d65a1837783bc3e2c6d6a23602","doc/windows-874.txt":"4ef0e4501c5feba8b17aee1818602ed44b36ca8475db771ce2fc16d392cabecc","doc/x-mac-cyrillic.txt":"58be154d8a888ca3d484b83b44f749823ef339ab27f14d90ca9a856f5050a8bd","doc/x-user-defined.txt":"f9cd07c4321bf5cfb0be4bdddd251072999b04a6cf7a6f5bc63709a84e2c1ffc","generate-encoding-data.py":"bbe134cc013a17c32bd51e868a2ee4f2c3e2205e40afb9fc60f98fa9758961b8","rustfmt.toml":"85c1a3b4382fd89e991cbb81b70fb52780472edc064c963943cdaaa56e0a2030","src/ascii.rs":"616bed165d244096d944e2d746405ec9fa671ba5fff1528a89172ea52f3a2975","src/big5.rs":"659ddb66f82142468774715cd9ecdc8356005fec9d91e46d54ae2280a88adb4b","src/data.rs":"18793e23f98d9aba1ed2858c43b2103c9c05a8571429b2128510c68d05ba8cb6","src/euc_jp.rs":"b178172274af1a6be38d388e5a2468e7140e9facf63d2f679f4ed77c3fc20a92","src/euc_kr.rs":"e27bd9c4e2172bb02004dd2a1fcbe02118415a1aec6a90b5632c9eac6f12ba7d","src/gb18030.rs":"752869f47ad0cad02ad3ccb6fde3eb277fa528e10e3d9432a90563db610d9dc2","src/handles.rs":"0598439bf66bd26699cba7e9e2fec001f8c57f3a510578fc3cc118ddc36d6831","src/iso_2022_jp.rs":"d904a92d49fc5878e6cb82a21185051c801489cf2800db8f37269965ca7974cc","src/lib.rs":"c8b973a715f5131830bfa7d88380fcec03cab1b302d22d72149e8284ba924df0","src/macros.rs":"04b3d81f1701b746dcce466ad2c3202a0a3e9c00f1d8db336276660640a12d7b","src/mem.rs":"b408367cef7ba93e9e90ec16cc62f763da58bcdbdb1ec0a856c6651e059ffead","src/replacement.rs":"61e0ae52664b3856f1ed202af6266b441ffcf5e2c9b22976047540e4a3252b41","src/shift_jis.rs":"7106d0a9785425b5af2323f3046e6130bd5125effa4ab484d84b529914855d67","src/simd_funcs.rs":"6139e2c7eebd8f3f35b8ce70891c6731595e5d18fa48b11f04f88c6342335935","src/single_byte.rs":"986571bb52f2cf7779c1dc8bdd8058c3183524a512bc2fcca6637eeee394b573","src/test_data/big5_in.txt":"4c5a8691f8dc717311889c63894026d2fb62725a86c4208ca274a9cc8d42a503","src/test_data/big5_in_ref.txt":"99d399e17750cf9c7cf30bb253dbfe35b81c4fcbdead93cfa48b1429213473c7","src/test_data/big5_out.txt":"6193ca97c297aa20e09396038d18e938bb7ea331c26f0f2454097296723a0b13","src/test_data/big5_out_ref.txt":"36567691f557df144f6cc520015a87038dfa156f296fcf103b56ae9a718be1fc","src/test_data/euc_kr_in.txt":"c86a7224f3215fa0d04e685622a752fdc72763e8ae076230c7fd62de57ec4074","src/test_data/euc_kr_in_ref.txt":"1f419f4ca47d708b54c73c461545a022ae2e20498fdbf8005a483d752a204883","src/test_data/euc_kr_out.txt":"e7f32e026f70be1e1b58e0047baf7d3d2c520269c4f9b9992e158b4decb0a1a3","src/test_data/euc_kr_out_ref.txt":"c9907857980b20b8e9e3b584482ed6567a2be6185d72237b6322f0404944924e","src/test_data/gb18030_in.txt":"ab7231b2d3e9afacdbd7d7f3b9e5361a7ff9f7e1cfdb4f3bd905b9362b309e53","src/test_data/gb18030_in_ref.txt":"dc5069421adca2043c55f5012b55a76fdff651d22e6e699fd0978f8d5706815c","src/test_data/gb18030_out.txt":"f0208d527f5ca63de7d9a0323be8d5cf12d8a104b2943d92c2701f0c3364dac1","src/test_data/gb18030_out_ref.txt":"6819fe47627e4ea01027003fc514b9f21a1322e732d7f1fb92cc6c5455bc6c07","src/test_data/iso_2022_jp_in.txt":"cd24bbdcb1834e25db54646fbf4c41560a13dc7540f6be3dba4f5d97d44513af","src/test_data/iso_2022_jp_in_ref.txt":"3dc4e6a5e06471942d086b16c9440945e78415f6f3f47e43717e4bc2eac2cdf5","src/test_data/iso_2022_jp_out.txt":"9b6f015329dda6c3f9ee5ce6dbd6fa9c89acc21283e886836c78b8d833480c21","src/test_data/iso_2022_jp_out_ref.txt":"78cb260093a20116ad9a42f43b05d1848c5ab100b6b9a850749809e943884b35","src/test_data/jis0208_in.txt":"6df3030553ffb0a6615bb33dc8ea9dca6d9623a9028e2ffec754ce3c3da824cc","src/test_data/jis0208_in_ref.txt":"3dc4e6a5e06471942d086b16c9440945e78415f6f3f47e43717e4bc2eac2cdf5","src/test_data/jis0208_out.txt":"4ec24477e1675ce750733bdc3c5add1cd27b6bd4ce1f09289564646e9654e857","src/test_data/jis0208_out_ref.txt":"c3e1cef5032b2b1d93a406f31ff940c4e2dfe8859b8b17ca2761fee7a75a0e48","src/test_data/jis0212_in.txt":"c011f0dd72bd7c8cd922df9374ef8d2769a77190514c77f6c62b415852eeb9fe","src/test_data/jis0212_in_ref.txt":"7d9458b3d2f73e7092a7f505c08ce1d233dde18aa679fbcf9889256239cc9e06","src/test_data/shift_jis_in.txt":"02e389ccef0dd2122e63f503899402cb7f797912c2444cc80ab93131116c5524","src/test_data/shift_jis_in_ref.txt":"512f985950ca902e643c88682dba9708b7c38d3c5ec2925168ab00ac94ab19f9","src/test_data/shift_jis_out.txt":"5fbc44da7bf639bf6cfe0fa1fd3eba7102b88f81919c9ea991302712f69426fb","src/test_data/shift_jis_out_ref.txt":"466322c6fed8286c64582731755290c2296508efdd258826e6279686649b481f","src/test_labels_names.rs":"c962c7aeac3d9ef2aca70c9e21983b231d4cf998cb06879374b0401e5149d1da","src/testing.rs":"b299d27055f3b068de66cc10a75c024b881c48bc093627c01e0b1f8bd7d94666","src/utf_16.rs":"32b6ecc3696542fb269a93c915e7402dde545e793f9599cf7dc63688fa226e0d","src/utf_8.rs":"249ec511970051d71fa863e659ab0b46ce9283d5a628b9b437619b41d4334459","src/utf_8_core.rs":"fe218284dbb9111a2336ff1684d7f58b55d9c4fe7363089a29e57f52901f0707","src/variant.rs":"e2c988a645429fd7457d7fbc487b838ab7ec7bfb8943d192992f18bbac8973f0","src/x_user_defined.rs":"1d25920ec5d005cbd623f34473d68991521aa3a83e52d4eb9b645711f10cd484"},"package":"22b758dc5e2c2b9e1dc3aa7a8be71035eed9742c907b7567627527af4c03324e"} \ No newline at end of file diff --git a/third_party/rust/encoding_rs/Cargo.toml b/third_party/rust/encoding_rs/Cargo.toml index f7564bec36a7..c08b881f1204 100644 --- a/third_party/rust/encoding_rs/Cargo.toml +++ b/third_party/rust/encoding_rs/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "encoding_rs" -version = "0.8.0" +version = "0.8.1" authors = ["Henri Sivonen "] description = "A Gecko-oriented implementation of the Encoding Standard" homepage = "https://docs.rs/encoding_rs/" diff --git a/third_party/rust/encoding_rs/README.md b/third_party/rust/encoding_rs/README.md index 860af07655b4..9073c1e1c242 100644 --- a/third_party/rust/encoding_rs/README.md +++ b/third_party/rust/encoding_rs/README.md @@ -237,6 +237,12 @@ used in Firefox. ## Release Notes +### 0.8.1 + +* Tweaked loop unrolling and memory alignment for SSE2 conversions between + UTF-16 and Latin1 in the `mem` module to increase the performance when + converting long buffers. + ### 0.8.0 * Changed the minimum supported version of Rust to 1.21.0 (semver breaking diff --git a/third_party/rust/encoding_rs/src/ascii.rs b/third_party/rust/encoding_rs/src/ascii.rs index 3c8a0a9e454e..e3b3173d57f9 100644 --- a/third_party/rust/encoding_rs/src/ascii.rs +++ b/third_party/rust/encoding_rs/src/ascii.rs @@ -409,11 +409,6 @@ macro_rules! latin1_simd_check_align { let mut offset = 0usize; if SIMD_STRIDE_SIZE <= len { let len_minus_stride = len - SIMD_STRIDE_SIZE; - // XXX Should we first process one stride unconditionally as unaligned to - // avoid the cost of the branchiness below if the first stride fails anyway? - // XXX Should we just use unaligned SSE2 access unconditionally? It seems that - // on Haswell, it would make sense to just use unaligned and not bother - // checking. Need to benchmark older architectures before deciding. let dst_masked = (dst as usize) & SIMD_ALIGNMENT_MASK; if ((src as usize) & SIMD_ALIGNMENT_MASK) == 0 { if dst_masked == 0 { @@ -465,6 +460,83 @@ macro_rules! latin1_simd_check_align { } } } + while offset < len { + let code_unit = *(src.offset(offset as isize)); + *(dst.offset(offset as isize)) = code_unit as $dst_unit; + offset += 1; + } + } + }; +} + +#[allow(unused_macros)] +macro_rules! latin1_simd_check_align_unrolled { + ( + $name:ident, + $src_unit:ty, + $dst_unit:ty, + $stride_both_aligned:ident, + $stride_src_aligned:ident, + $stride_dst_aligned:ident, + $stride_neither_aligned:ident + ) => { + #[inline(always)] + pub unsafe fn $name(src: *const $src_unit, dst: *mut $dst_unit, len: usize) { + let unit_size = ::std::mem::size_of::<$src_unit>(); + let mut offset = 0usize; + if SIMD_STRIDE_SIZE <= len { + let mut until_alignment = ((SIMD_STRIDE_SIZE - ((src as usize) & SIMD_ALIGNMENT_MASK)) + & SIMD_ALIGNMENT_MASK) / unit_size; + while until_alignment != 0 { + *(dst.offset(offset as isize)) = *(src.offset(offset as isize)) as $dst_unit; + offset += 1; + until_alignment -= 1; + } + let len_minus_stride = len - SIMD_STRIDE_SIZE; + if SIMD_STRIDE_SIZE * 2 <= len { + let len_minus_stride_times_two = len_minus_stride - SIMD_STRIDE_SIZE; + if (dst.offset(offset as isize) as usize) & SIMD_ALIGNMENT_MASK == 0 { + loop { + $stride_both_aligned( + src.offset(offset as isize), + dst.offset(offset as isize), + ); + offset += SIMD_STRIDE_SIZE; + $stride_both_aligned( + src.offset(offset as isize), + dst.offset(offset as isize), + ); + offset += SIMD_STRIDE_SIZE; + if offset > len_minus_stride_times_two { + break; + } + } + } else { + loop { + $stride_src_aligned( + src.offset(offset as isize), + dst.offset(offset as isize), + ); + offset += SIMD_STRIDE_SIZE; + $stride_src_aligned( + src.offset(offset as isize), + dst.offset(offset as isize), + ); + offset += SIMD_STRIDE_SIZE; + if offset > len_minus_stride_times_two { + break; + } + } + } + } + if offset < len_minus_stride { + $stride_src_aligned( + src.offset(offset as isize), + dst.offset(offset as isize), + ); + offset += SIMD_STRIDE_SIZE; + } + } while offset < len { let code_unit = *(src.offset(offset as isize)); // On x86_64, this loop autovectorizes but in the pack @@ -650,7 +722,7 @@ cfg_if! { ascii_simd_unalign!(basic_latin_to_ascii, u16, u8, basic_latin_to_ascii_stride_neither_aligned); latin1_simd_unalign!(unpack_latin1, u8, u16, unpack_stride_neither_aligned); latin1_simd_unalign!(pack_latin1, u16, u8, pack_stride_neither_aligned); - } else if #[cfg(all(feature = "simd-accel", any(target_feature = "sse2", all(target_endian = "little", target_feature = "neon"))))] { + } else if #[cfg(all(feature = "simd-accel", target_endian = "little", target_feature = "neon"))] { // SIMD with different instructions for aligned and unaligned loads and stores. // // Newer microarchitectures are not supposed to have a performance difference between @@ -693,6 +765,45 @@ cfg_if! { ascii_simd_check_align!(basic_latin_to_ascii, u16, u8, basic_latin_to_ascii_stride_both_aligned, basic_latin_to_ascii_stride_src_aligned, basic_latin_to_ascii_stride_dst_aligned, basic_latin_to_ascii_stride_neither_aligned); latin1_simd_check_align!(unpack_latin1, u8, u16, unpack_stride_both_aligned, unpack_stride_src_aligned, unpack_stride_dst_aligned, unpack_stride_neither_aligned); latin1_simd_check_align!(pack_latin1, u16, u8, pack_stride_both_aligned, pack_stride_src_aligned, pack_stride_dst_aligned, pack_stride_neither_aligned); + } else if #[cfg(all(feature = "simd-accel", target_feature = "sse2"))] { + // SIMD with different instructions for aligned and unaligned loads and stores. + // + // Newer microarchitectures are not supposed to have a performance difference between + // aligned and unaligned SSE2 loads and stores when the address is actually aligned, + // but the benchmark results I see don't agree. + + pub const SIMD_STRIDE_SIZE: usize = 16; + + pub const MAX_STRIDE_SIZE: usize = 16; + + pub const SIMD_ALIGNMENT_MASK: usize = 15; + + ascii_to_ascii_simd_stride!(ascii_to_ascii_stride_both_aligned, load16_aligned, store16_aligned); + ascii_to_ascii_simd_stride!(ascii_to_ascii_stride_src_aligned, load16_aligned, store16_unaligned); + ascii_to_ascii_simd_stride!(ascii_to_ascii_stride_dst_aligned, load16_unaligned, store16_aligned); + ascii_to_ascii_simd_stride!(ascii_to_ascii_stride_neither_aligned, load16_unaligned, store16_unaligned); + + ascii_to_basic_latin_simd_stride!(ascii_to_basic_latin_stride_both_aligned, load16_aligned, store8_aligned); + ascii_to_basic_latin_simd_stride!(ascii_to_basic_latin_stride_src_aligned, load16_aligned, store8_unaligned); + ascii_to_basic_latin_simd_stride!(ascii_to_basic_latin_stride_dst_aligned, load16_unaligned, store8_aligned); + ascii_to_basic_latin_simd_stride!(ascii_to_basic_latin_stride_neither_aligned, load16_unaligned, store8_unaligned); + + unpack_simd_stride!(unpack_stride_both_aligned, load16_aligned, store8_aligned); + unpack_simd_stride!(unpack_stride_src_aligned, load16_aligned, store8_unaligned); + + basic_latin_to_ascii_simd_stride!(basic_latin_to_ascii_stride_both_aligned, load8_aligned, store16_aligned); + basic_latin_to_ascii_simd_stride!(basic_latin_to_ascii_stride_src_aligned, load8_aligned, store16_unaligned); + basic_latin_to_ascii_simd_stride!(basic_latin_to_ascii_stride_dst_aligned, load8_unaligned, store16_aligned); + basic_latin_to_ascii_simd_stride!(basic_latin_to_ascii_stride_neither_aligned, load8_unaligned, store16_unaligned); + + pack_simd_stride!(pack_stride_both_aligned, load8_aligned, store16_aligned); + pack_simd_stride!(pack_stride_src_aligned, load8_aligned, store16_unaligned); + + ascii_simd_check_align!(ascii_to_ascii, u8, u8, ascii_to_ascii_stride_both_aligned, ascii_to_ascii_stride_src_aligned, ascii_to_ascii_stride_dst_aligned, ascii_to_ascii_stride_neither_aligned); + ascii_simd_check_align!(ascii_to_basic_latin, u8, u16, ascii_to_basic_latin_stride_both_aligned, ascii_to_basic_latin_stride_src_aligned, ascii_to_basic_latin_stride_dst_aligned, ascii_to_basic_latin_stride_neither_aligned); + ascii_simd_check_align!(basic_latin_to_ascii, u16, u8, basic_latin_to_ascii_stride_both_aligned, basic_latin_to_ascii_stride_src_aligned, basic_latin_to_ascii_stride_dst_aligned, basic_latin_to_ascii_stride_neither_aligned); + latin1_simd_check_align_unrolled!(unpack_latin1, u8, u16, unpack_stride_both_aligned, unpack_stride_src_aligned, unpack_stride_dst_aligned, unpack_stride_neither_aligned); + latin1_simd_check_align_unrolled!(pack_latin1, u16, u8, pack_stride_both_aligned, pack_stride_src_aligned, pack_stride_dst_aligned, pack_stride_neither_aligned); } else if #[cfg(all(target_endian = "little", target_pointer_width = "64"))] { // Aligned ALU word, little-endian, 64-bit diff --git a/third_party/rust/encoding_rs/src/lib.rs b/third_party/rust/encoding_rs/src/lib.rs index af39e39a104f..52cf7f17d12e 100644 --- a/third_party/rust/encoding_rs/src/lib.rs +++ b/third_party/rust/encoding_rs/src/lib.rs @@ -8,7 +8,7 @@ // except according to those terms. #![cfg_attr(feature = "cargo-clippy", allow(doc_markdown, inline_always, new_ret_no_self))] -#![doc(html_root_url = "https://docs.rs/encoding_rs/0.8.0")] +#![doc(html_root_url = "https://docs.rs/encoding_rs/0.8.1")] //! encoding_rs is a Gecko-oriented Free Software / Open Source implementation //! of the [Encoding Standard](https://encoding.spec.whatwg.org/) in Rust. @@ -220,18 +220,20 @@ //! multiplication operations. //! //! Additionally, performance is a non-goal for the ASCII-incompatible -//! ISO-2022-JP and UTF-16 encodings, which are rarely used on the Web. For -//! clarity, this means that performance is a non-goal for UTF-16 as used on -//! the wire as an interchange encoding (UTF-16 on the `[u8]` side of the API). -//! Good performance for UTF-16 used as an in-RAM Unicode representation -//! (UTF-16 the `[u16]` side of the API) is a goal. +//! ISO-2022-JP encoding, which are rarely used on the Web. Instead of +//! performance, the decoder for ISO-2022-JP optimizes for ease/clarity +//! of implementation. //! //! Despite the focus on the Web, encoding_rs may well be useful for decoding //! email, although you'll need to implement UTF-7 decoding and label handling //! by other means. (Due to the Web focus, patches to add UTF-7 are unwelcome //! in encoding_rs itself.) Also, despite the browser focus, the hope is that //! non-browser applications that wish to consume Web content or submit Web -//! forms in a Web-compatible way will find encoding_rs useful. +//! forms in a Web-compatible way will find encoding_rs useful. While +//! encoding_rs does not try to match Windows behavior, many of the encodings +//! are close enough to legacy encodings implemented by Windows that +//! applications that need to consume data in legacy Windows encodins may +//! find encoding_rs useful. //! //! # Streaming & Non-Streaming; Rust & C/C++ //! diff --git a/toolkit/components/backgroundhangmonitor/moz.build b/toolkit/components/backgroundhangmonitor/moz.build index 243f81d2097d..6b7141be5af0 100644 --- a/toolkit/components/backgroundhangmonitor/moz.build +++ b/toolkit/components/backgroundhangmonitor/moz.build @@ -11,9 +11,11 @@ # BHR disabled for Release builds because of bug 965392. # BHR disabled for debug builds because of bug 979069. # BHR disabled for TSan builds because of bug 1121216. +# BHR disabled for ASan builds because of bug 1445441. if CONFIG['NIGHTLY_BUILD'] and \ not CONFIG['MOZ_DEBUG'] and \ - not CONFIG['MOZ_TSAN']: + not CONFIG['MOZ_TSAN'] and \ + not CONFIG['MOZ_ASAN']: DEFINES['MOZ_ENABLE_BACKGROUND_HANG_MONITOR'] = 1 EXTRA_COMPONENTS += [ diff --git a/toolkit/components/places/UnifiedComplete.js b/toolkit/components/places/UnifiedComplete.js index bdc8e2527aa1..161ca0d15fa8 100644 --- a/toolkit/components/places/UnifiedComplete.js +++ b/toolkit/components/places/UnifiedComplete.js @@ -1757,18 +1757,19 @@ Search.prototype = { let flags = Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS | Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; let fixupInfo = null; + let searchUrl = this._trimmedOriginalSearchString; try { - fixupInfo = Services.uriFixup.getFixupURIInfo(this._originalSearchString, + fixupInfo = Services.uriFixup.getFixupURIInfo(searchUrl, flags); } catch (e) { if (e.result == Cr.NS_ERROR_MALFORMED_URI && !Prefs.get("keyword.enabled")) { let value = PlacesUtils.mozActionURI("visiturl", { - url: this._originalSearchString, - input: this._originalSearchString, + url: searchUrl, + input: searchUrl, }); this._addMatch({ value, - comment: this._originalSearchString, + comment: searchUrl, style: "action visiturl", frecency: Infinity }); @@ -1805,7 +1806,7 @@ Search.prototype = { let value = PlacesUtils.mozActionURI("visiturl", { url: escapedURL, - input: this._originalSearchString, + input: searchUrl, }); let match = { @@ -1822,7 +1823,7 @@ Search.prototype = { // By default we won't provide an icon, but for the subset of urls with a // host we'll check for a typed slash and set favicon for the host part. if (hostExpected && - (this._trimmedOriginalSearchString.endsWith("/") || uri.pathQueryRef.length > 1)) { + (searchUrl.endsWith("/") || uri.pathQueryRef.length > 1)) { match.icon = `page-icon:${uri.prePath}/`; } diff --git a/toolkit/components/places/tests/unifiedcomplete/test_visit_url.js b/toolkit/components/places/tests/unifiedcomplete/test_visit_url.js index b250dd41056c..41b328bb13a4 100644 --- a/toolkit/components/places/tests/unifiedcomplete/test_visit_url.js +++ b/toolkit/components/places/tests/unifiedcomplete/test_visit_url.js @@ -54,6 +54,17 @@ add_task(async function() { matches: [ { uri: makeActionURI("visiturl", {url: "about:config", input: "about:config"}), title: "about:config", style: [ "action", "visiturl", "heuristic" ] } ] }); + info("visit url, with non-standard whitespace"); + await check_autocomplete({ + search: "https://www.mozilla.org\u2028", + searchParam: "enable-actions", + matches: [{ + uri: makeActionURI("visiturl", {url: "https://www.mozilla.org/", + input: "https://www.mozilla.org"}), + title: "https://www.mozilla.org/", + style: [ "action", "visiturl", "heuristic" ]}] + }); + // This is distinct because of how we predict being able to url autofill via // host lookups. info("visit url, host matching visited host but not visited url"); diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index 1003a9c21297..65bcde728689 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -769,14 +769,7 @@ PuppetWidget::SetInputContext(const InputContext& aContext, if (!mTabChild) { return; } - mTabChild->SendSetInputContext(aContext.mIMEState.mEnabled, - aContext.mIMEState.mOpen, - aContext.mHTMLInputType, - aContext.mHTMLInputInputmode, - aContext.mActionHint, - aContext.mInPrivateBrowsing, - aAction.mCause, - aAction.mFocusChange); + mTabChild->SendSetInputContext(aContext, aAction); } InputContext @@ -798,11 +791,7 @@ PuppetWidget::GetInputContext() // chrome widget is set to new context. InputContext context; if (mTabChild) { - IMEState::Enabled enabled; - IMEState::Open open; - mTabChild->SendGetInputContext(&enabled, &open); - context.mIMEState.mEnabled = enabled; - context.mIMEState.mOpen = open; + mTabChild->SendGetInputContext(&context.mIMEState); } return context; } diff --git a/widget/android/GeckoEditableSupport.cpp b/widget/android/GeckoEditableSupport.cpp index 55e0987face4..5e544b5d0b80 100644 --- a/widget/android/GeckoEditableSupport.cpp +++ b/widget/android/GeckoEditableSupport.cpp @@ -1188,10 +1188,10 @@ GeckoEditableSupport::OnImeUpdateComposition(int32_t aStart, int32_t aEnd, } #ifdef DEBUG_ANDROID_IME - const NS_ConvertUTF16toUTF8 data(event.mData); + const NS_ConvertUTF16toUTF8 data(string); const char* text = data.get(); ALOGIME("IME: IME_SET_TEXT: text=\"%s\", length=%u, range=%u", - text, event.mData.Length(), event.mRanges->Length()); + text, string.Length(), mIMERanges->Length()); #endif // DEBUG_ANDROID_IME if (NS_WARN_IF(NS_FAILED(BeginInputTransaction(mDispatcher)))) { @@ -1254,6 +1254,8 @@ GeckoEditableSupport::NotifyIME(TextEventDispatcher* aTextEventDispatcher, case NOTIFY_IME_OF_FOCUS: { ALOGIME("IME: NOTIFY_IME_OF_FOCUS"); + mIMEFocusCount++; + RefPtr self(this); RefPtr dispatcher = aTextEventDispatcher; @@ -1264,7 +1266,7 @@ GeckoEditableSupport::NotifyIME(TextEventDispatcher* aTextEventDispatcher, nsCOMPtr widget = dispatcher->GetWidget(); --mIMEMaskEventsCount; - if (mIMEMaskEventsCount || !widget || widget->Destroyed()) { + if (!mIMEFocusCount || !widget || widget->Destroyed()) { return; } @@ -1300,10 +1302,16 @@ GeckoEditableSupport::NotifyIME(TextEventDispatcher* aTextEventDispatcher, case NOTIFY_IME_OF_BLUR: { ALOGIME("IME: NOTIFY_IME_OF_BLUR"); - if (!mIMEMaskEventsCount) { - mEditable->NotifyIME(EditableListener::NOTIFY_IME_OF_BLUR); - OnRemovedFrom(mDispatcher); - } + mIMEFocusCount--; + MOZ_ASSERT(mIMEFocusCount >= 0); + + RefPtr self(this); + nsAppShell::PostEvent([this, self] { + if (!mIMEFocusCount) { + mEditable->NotifyIME(EditableListener::NOTIFY_IME_OF_BLUR); + OnRemovedFrom(mDispatcher); + } + }); // Mask events because we lost focus. Unmask on the next focus. mIMEMaskEventsCount++; @@ -1391,29 +1399,25 @@ GeckoEditableSupport::SetInputContext(const InputContext& aContext, return; } - if (mIMEUpdatingContext) { - return; - } - mIMEUpdatingContext = true; - - RefPtr self(this); const bool inPrivateBrowsing = mInputContext.mInPrivateBrowsing; - const bool isUserAction = aAction.IsHandlingUserInput() || aContext.mHasHandledUserInput; + const bool isUserAction = + aAction.IsHandlingUserInput() || aContext.mHasHandledUserInput; const int32_t flags = (inPrivateBrowsing ? EditableListener::IME_FLAG_PRIVATE_BROWSING : 0) | (isUserAction ? EditableListener::IME_FLAG_USER_ACTION : 0); - nsAppShell::PostEvent([this, self, flags] { + // Post an event to keep calls in order relative to NotifyIME. + nsAppShell::PostEvent([this, self = RefPtr(this), + flags, context = mInputContext] { nsCOMPtr widget = GetWidget(); - mIMEUpdatingContext = false; if (!widget || widget->Destroyed()) { return; } - mEditable->NotifyIMEContext(mInputContext.mIMEState.mEnabled, - mInputContext.mHTMLInputType, - mInputContext.mHTMLInputInputmode, - mInputContext.mActionHint, + mEditable->NotifyIMEContext(context.mIMEState.mEnabled, + context.mHTMLInputType, + context.mHTMLInputInputmode, + context.mActionHint, flags); }); } diff --git a/widget/android/GeckoEditableSupport.h b/widget/android/GeckoEditableSupport.h index 9c27f1a332b8..4ad28a61efb7 100644 --- a/widget/android/GeckoEditableSupport.h +++ b/widget/android/GeckoEditableSupport.h @@ -101,7 +101,7 @@ class GeckoEditableSupport final AutoTArray mIMETextChanges; RefPtr mIMERanges; int32_t mIMEMaskEventsCount; // Mask events when > 0. - bool mIMEUpdatingContext; + int32_t mIMEFocusCount; // We are focused when > 0. bool mIMESelectionChanged; bool mIMETextChangedDuringFlush; bool mIMEMonitorCursor; @@ -180,7 +180,7 @@ public: , mEditableAttached(!mIsRemote) , mIMERanges(new TextRangeArray()) , mIMEMaskEventsCount(1) // Mask IME events since there's no focus yet - , mIMEUpdatingContext(false) + , mIMEFocusCount(0) , mIMESelectionChanged(false) , mIMETextChangedDuringFlush(false) , mIMEMonitorCursor(false) diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 82e5cd3832f4..a2b5d4f81900 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -534,13 +534,14 @@ nsAppShell::Init() if (obsServ) { obsServ->AddObserver(this, "browser-delayed-startup-finished", false); obsServ->AddObserver(this, "profile-after-change", false); - obsServ->AddObserver(this, "tab-child-created", false); obsServ->AddObserver(this, "quit-application", false); obsServ->AddObserver(this, "quit-application-granted", false); obsServ->AddObserver(this, "xpcom-shutdown", false); if (XRE_IsParentProcess()) { obsServ->AddObserver(this, "chrome-document-loaded", false); + } else { + obsServ->AddObserver(this, "content-document-global-created", false); } } @@ -660,19 +661,33 @@ nsAppShell::Observe(nsISupports* aSubject, mozilla::PrefsHelper::OnPrefChange(aData); } - } else if (!strcmp(aTopic, "tab-child-created")) { + } else if (!strcmp(aTopic, "content-document-global-created")) { // Associate the PuppetWidget of the newly-created TabChild with a // GeckoEditableChild instance. MOZ_ASSERT(!XRE_IsParentProcess()); + nsCOMPtr domWindow = do_QueryInterface(aSubject); + MOZ_ASSERT(domWindow); + nsCOMPtr domWidget = widget::WidgetUtils::DOMWindowToWidget( + nsPIDOMWindowOuter::From(domWindow)); + NS_ENSURE_TRUE(domWidget, NS_OK); + dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); - nsCOMPtr ptabChild = do_QueryInterface(aSubject); - NS_ENSURE_TRUE(contentChild && ptabChild, NS_OK); + dom::TabChild* tabChild = domWidget->GetOwningTabChild(); + RefPtr widget(tabChild->WebWidget()); + NS_ENSURE_TRUE(contentChild && tabChild && widget, NS_OK); + + widget::TextEventDispatcherListener* listener = + widget->GetNativeTextEventDispatcherListener(); + if (listener && listener != + static_cast(widget)) { + // We already set a listener before. + return NS_OK; + } // Get the content/tab ID in order to get the correct // IGeckoEditableParent object, which GeckoEditableChild uses to // communicate with the parent process. - const auto tabChild = static_cast(ptabChild.get()); const uint64_t contentId = contentChild->GetID(); const uint64_t tabId = tabChild->GetTabId(); NS_ENSURE_TRUE(contentId && tabId, NS_OK); @@ -681,9 +696,8 @@ nsAppShell::Observe(nsISupports* aSubject, contentId, tabId); NS_ENSURE_TRUE(editableParent, NS_OK); - RefPtr widget(tabChild->WebWidget()); auto editableChild = java::GeckoEditableChild::New(editableParent); - NS_ENSURE_TRUE(widget && editableChild, NS_OK); + NS_ENSURE_TRUE(editableChild, NS_OK); RefPtr editableSupport = new widget::GeckoEditableSupport(editableChild); diff --git a/widget/nsGUIEventIPC.h b/widget/nsGUIEventIPC.h index 9edf874c9461..f87684cfd1db 100644 --- a/widget/nsGUIEventIPC.h +++ b/widget/nsGUIEventIPC.h @@ -969,6 +969,63 @@ struct ParamTraits { }; +template<> +struct ParamTraits +{ + typedef mozilla::widget::IMEState paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mEnabled); + WriteParam(aMsg, aParam.mOpen); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->mEnabled) && + ReadParam(aMsg, aIter, &aResult->mOpen); + } +}; + +template<> +struct ParamTraits + : ContiguousEnumSerializerInclusive< + mozilla::widget::InputContext::Origin, + mozilla::widget::InputContext::Origin::ORIGIN_MAIN, + mozilla::widget::InputContext::Origin::ORIGIN_CONTENT> +{ +}; + +template<> +struct ParamTraits +{ + typedef mozilla::widget::InputContext paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mIMEState); + WriteParam(aMsg, aParam.mHTMLInputType); + WriteParam(aMsg, aParam.mHTMLInputInputmode); + WriteParam(aMsg, aParam.mActionHint); + WriteParam(aMsg, aParam.mOrigin); + WriteParam(aMsg, aParam.mMayBeIMEUnaware); + WriteParam(aMsg, aParam.mHasHandledUserInput); + WriteParam(aMsg, aParam.mInPrivateBrowsing); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->mIMEState) && + ReadParam(aMsg, aIter, &aResult->mHTMLInputType) && + ReadParam(aMsg, aIter, &aResult->mHTMLInputInputmode) && + ReadParam(aMsg, aIter, &aResult->mActionHint) && + ReadParam(aMsg, aIter, &aResult->mOrigin) && + ReadParam(aMsg, aIter, &aResult->mMayBeIMEUnaware) && + ReadParam(aMsg, aIter, &aResult->mHasHandledUserInput) && + ReadParam(aMsg, aIter, &aResult->mInPrivateBrowsing); + } +}; + template<> struct ParamTraits : ContiguousEnumSerializerInclusive< @@ -988,6 +1045,24 @@ struct ParamTraits { }; +template<> +struct ParamTraits +{ + typedef mozilla::widget::InputContextAction paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mCause); + WriteParam(aMsg, aParam.mFocusChange); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->mCause) && + ReadParam(aMsg, aIter, &aResult->mFocusChange); + } +}; + template<> struct ParamTraits { diff --git a/xpcom/tests/gtest/TestThreadMetrics.cpp b/xpcom/tests/gtest/TestThreadMetrics.cpp index fb7b5c222a3b..1499504271be 100644 --- a/xpcom/tests/gtest/TestThreadMetrics.cpp +++ b/xpcom/tests/gtest/TestThreadMetrics.cpp @@ -9,12 +9,14 @@ #include "mozilla/AbstractThread.h" #include "mozilla/Preferences.h" #include "mozilla/dom/DocGroup.h" +#include "mozilla/dom/DOMPrefs.h" #include "mozilla/dom/TabGroup.h" #include "mozilla/SchedulerGroup.h" #include "mozilla/TaskCategory.h" #include "mozilla/PerformanceCounter.h" #include "nsThreadUtils.h" #include "nsServiceManagerUtils.h" +#include "nsThread.h" using namespace mozilla; using mozilla::Runnable; @@ -48,20 +50,20 @@ class TimedRunnable final : public Runnable { public: explicit TimedRunnable(uint32_t aExecutionTime1, uint32_t aExecutionTime2, - bool aRecursive) + uint32_t aSubExecutionTime) : Runnable("TimedRunnable") , mExecutionTime1(aExecutionTime1) , mExecutionTime2(aExecutionTime2) - , mRecursive(aRecursive) + , mSubExecutionTime(aSubExecutionTime) { } NS_IMETHODIMP Run() { PR_Sleep(PR_MillisecondsToInterval(mExecutionTime1 + 5)); - if (mRecursive) { + if (mSubExecutionTime > 0) { // Dispatch another runnable so nsThread::ProcessNextEvent is called recursively nsCOMPtr thread = do_GetMainThread(); - nsCOMPtr runnable = new TimedRunnable(155, 0, false); + nsCOMPtr runnable = new TimedRunnable(mSubExecutionTime, 0, 0); thread->Dispatch(runnable, NS_DISPATCH_NORMAL); (void)NS_ProcessNextEvent(thread, false); } @@ -69,9 +71,9 @@ public: return NS_OK; } private: - uint32_t mExecutionTime1; - uint32_t mExecutionTime2; - bool mRecursive; + uint32_t mExecutionTime1; + uint32_t mExecutionTime2; + uint32_t mSubExecutionTime; }; @@ -104,24 +106,37 @@ protected: mDispatchCount = (uint32_t)TaskCategory::Other + 1; } - virtual void TearDown() { + void resetCounters() { // let's reset the counters mCounter->ResetPerformanceCounters(); for (uint32_t i = 0; i < mDispatchCount; i++) { ASSERT_EQ(mCounter->GetDispatchCounter()[i], 0u); } ASSERT_EQ(mCounter->GetExecutionDuration(), 0u); + } + + virtual void TearDown() { + // let's reset the counters + resetCounters(); + // and remove the document from the doc group (actually, a nullptr) mDocGroup->RemoveDocument(nullptr); mDocGroup = nullptr; Preferences::SetBool(prefKey, mOldPref); + ProcessAllEvents(); + } + + // this is used to get rid of transient events + void initScheduler() { + ProcessAllEvents(); } nsresult Dispatch(uint32_t aExecutionTime1, uint32_t aExecutionTime2, - bool aRecursive) { + uint32_t aSubExecutionTime) { + ProcessAllEvents(); nsCOMPtr runnable = new TimedRunnable(aExecutionTime1, aExecutionTime2, - aRecursive); + aSubExecutionTime); runnable = new SchedulerGroup::Runnable(runnable.forget(), mSchedulerGroup, mDocGroup); return mDocGroup->Dispatch(TaskCategory::Other, runnable.forget()); @@ -144,9 +159,10 @@ protected: TEST_F(ThreadMetrics, CollectMetrics) { nsresult rv; + initScheduler(); - // Dispatching a runnable that will last for +100ms - rv = Dispatch(100, 0, false); + // Dispatching a runnable that will last for +50ms + rv = Dispatch(25, 25, 0); ASSERT_TRUE(NS_SUCCEEDED(rv)); // Flush the queue @@ -164,8 +180,8 @@ TEST_F(ThreadMetrics, CollectMetrics) // Did we get incremented in the docgroup ? uint64_t duration = mCounter->GetExecutionDuration(); - ASSERT_GE(duration, 100000u); - ASSERT_LT(duration, 150000u); + ASSERT_GE(duration, 50000u); + ASSERT_LT(duration, 200000u); } @@ -173,9 +189,11 @@ TEST_F(ThreadMetrics, CollectRecursiveMetrics) { nsresult rv; - // Dispatching a runnable that will last for +100ms - // and run another one recursively - rv = Dispatch(50, 50, true); + initScheduler(); + + // Dispatching a runnable that will last for +50ms + // and run another one recursively that lasts for 200ms + rv = Dispatch(25, 25, 200); ASSERT_TRUE(NS_SUCCEEDED(rv)); // Flush the queue @@ -193,8 +211,8 @@ TEST_F(ThreadMetrics, CollectRecursiveMetrics) // did we get incremented in the docgroup ? uint64_t duration = mCounter->GetExecutionDuration(); - ASSERT_GE(duration, 100000u); + ASSERT_GE(duration, 50000u); // let's make sure we don't count the time spent in recursive calls - ASSERT_LT(duration, 150000u); + ASSERT_LT(duration, 200000u); } diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index ec20fce83a74..bfc7d3e50a10 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -32,6 +32,7 @@ #include "mozilla/Scheduler.h" #include "mozilla/SchedulerGroup.h" #include "mozilla/Services.h" +#include "mozilla/StaticPrefs.h" #include "mozilla/SystemGroup.h" #include "nsXPCOMPrivate.h" #include "mozilla/ChaosMode.h" @@ -47,7 +48,6 @@ #include "ThreadEventTarget.h" #include "mozilla/dom/ContentChild.h" -#include "mozilla/dom/DOMPrefs.h" #ifdef XP_LINUX #include @@ -581,14 +581,6 @@ nsThread::~nsThread() #endif } -bool -nsThread::GetSchedulerLoggingEnabled() { - if (!NS_IsMainThread() || !mozilla::Preferences::IsServiceAvailable()) { - return false; - } - return mozilla::dom::DOMPrefs::SchedulerLoggingEnabled(); -} - nsresult nsThread::Init(const nsACString& aName) { @@ -1003,7 +995,7 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) HangMonitor::NotifyActivity(); } - bool schedulerLoggingEnabled = GetSchedulerLoggingEnabled(); + bool schedulerLoggingEnabled = mozilla::StaticPrefs::dom_performance_enable_scheduler_timing(); if (schedulerLoggingEnabled && mNestedEventLoopDepth > mCurrentEventLoopDepth && mCurrentPerformanceCounter) { diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h index 4749298c8f6b..aeacc2d9312f 100644 --- a/xpcom/threads/nsThread.h +++ b/xpcom/threads/nsThread.h @@ -176,7 +176,6 @@ protected: // Set to true if this thread creates a JSRuntime. bool mCanInvokeJS; - bool GetSchedulerLoggingEnabled(); mozilla::TimeStamp mNextIdleDeadline; // Used to track which event is being executed by ProcessNextEvent nsCOMPtr mCurrentEvent;