diff --git a/browser/base/content/webext-panels.js b/browser/base/content/webext-panels.js index 74586ead0621..6ea706faed46 100644 --- a/browser/base/content/webext-panels.js +++ b/browser/base/content/webext-panels.js @@ -5,28 +5,60 @@ XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent", "resource://gre/modules/ExtensionParent.jsm"); +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); + +var { + promiseEvent, +} = ExtensionUtils; + +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + +function getBrowser(sidebar) { + let browser = document.getElementById("webext-panels-browser"); + if (browser) { + return Promise.resolve(browser); + } + + browser = document.createElementNS(XUL_NS, "browser"); + browser.setAttribute("id", "webext-panels-browser"); + browser.setAttribute("type", "content"); + browser.setAttribute("flex", "1"); + browser.setAttribute("disableglobalhistory", "true"); + browser.setAttribute("webextension-view-type", "sidebar"); + browser.setAttribute("context", "contentAreaContextMenu"); + browser.setAttribute("tooltip", "aHTMLTooltip"); + browser.setAttribute("onclick", "window.parent.contentAreaClick(event, true);"); + + let readyPromise; + if (sidebar.remote) { + browser.setAttribute("remote", "true"); + browser.setAttribute("remoteType", + E10SUtils.getRemoteTypeForURI(sidebar.uri, true, + E10SUtils.EXTENSION_REMOTE_TYPE)); + readyPromise = promiseEvent(browser, "XULFrameLoaderCreated"); + } else { + readyPromise = Promise.resolve(); + } + document.documentElement.appendChild(browser); + + return readyPromise.then(() => { + browser.messageManager.loadFrameScript("chrome://browser/content/content.js", false); + ExtensionParent.apiManager.emit("extension-browser-inserted", browser); + return browser; + }); +} function loadWebPanel() { let sidebarURI = new URL(location); - let uri = sidebarURI.searchParams.get("panel"); - let remote = sidebarURI.searchParams.get("remote"); - let browser = document.getElementById("webext-panels-browser"); - if (remote) { - let remoteType = E10SUtils.getRemoteTypeForURI(uri, true, - E10SUtils.EXTENSION_REMOTE_TYPE); - browser.setAttribute("remote", "true"); - browser.setAttribute("remoteType", remoteType); - } else { - browser.removeAttribute("remote"); - browser.removeAttribute("remoteType"); - } - browser.loadURI(uri); + let sidebar = { + uri: sidebarURI.searchParams.get("panel"), + remote: sidebarURI.searchParams.get("remote"), + }; + getBrowser(sidebar).then(browser => { + browser.loadURI(sidebar.uri); + }); } function load() { - let browser = document.getElementById("webext-panels-browser"); - browser.messageManager.loadFrameScript("chrome://browser/content/content.js", true); - ExtensionParent.apiManager.emit("extension-browser-inserted", browser); - this.loadWebPanel(); } diff --git a/browser/base/content/webext-panels.xul b/browser/base/content/webext-panels.xul index 35e8eb0f2619..57be52ffa549 100644 --- a/browser/base/content/webext-panels.xul +++ b/browser/base/content/webext-panels.xul @@ -65,9 +65,4 @@ - diff --git a/browser/components/extensions/ext-browsingData.js b/browser/components/extensions/ext-browsingData.js index cb99d2123109..a927b42682cd 100644 --- a/browser/components/extensions/ext-browsingData.js +++ b/browser/components/extensions/ext-browsingData.js @@ -187,21 +187,24 @@ extensions.registerSchemaAPI("browsingData", "addon_parent", context => { // since will be the start of what is returned by Sanitizer.getClearRange // divided by 1000 to convert to ms. - let since = Sanitizer.getClearRange()[0] / 1000; + // If Sanitizer.getClearRange returns undefined that means the range is + // currently "Everything", so we should set since to 0. + let clearRange = Sanitizer.getClearRange(); + let since = clearRange ? clearRange[0] / 1000 : 0; let options = {since}; let dataToRemove = {}; let dataRemovalPermitted = {}; for (let item of PREF_LIST) { - dataToRemove[item] = Preferences.get(`${PREF_DOMAIN}${item}`); + // The property formData needs a different case than the + // formdata preference. + const name = item === "formdata" ? "formData" : item; + dataToRemove[name] = Preferences.get(`${PREF_DOMAIN}${item}`); // Firefox doesn't have the same concept of dataRemovalPermitted // as Chrome, so it will always be true. - dataRemovalPermitted[item] = true; + dataRemovalPermitted[name] = true; } - // formData has a different case than the pref formdata. - dataToRemove.formData = Preferences.get(`${PREF_DOMAIN}formdata`); - dataRemovalPermitted.formData = true; return Promise.resolve({options, dataToRemove, dataRemovalPermitted}); }, diff --git a/browser/components/extensions/ext-utils.js b/browser/components/extensions/ext-utils.js index 3ff88cd0545c..2bfe44f75163 100644 --- a/browser/components/extensions/ext-utils.js +++ b/browser/components/extensions/ext-utils.js @@ -544,8 +544,9 @@ class Tab extends TabBase { if (extension.tabManager.hasTabPermission(tabData)) { let entries = tabData.state ? tabData.state.entries : tabData.entries; - result.url = entries[0].url; - result.title = entries[0].title; + let entry = entries[entries.length - 1]; + result.url = entry.url; + result.title = entry.title; if (tabData.image) { result.favIconUrl = tabData.image; } diff --git a/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js b/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js index 413f7bde601f..546753b6f84b 100644 --- a/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js +++ b/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js @@ -95,3 +95,36 @@ add_task(function* test_sessions_get_recently_closed() { yield extension.unload(); }); + +add_task(function* test_sessions_get_recently_closed_navigated() { + function background() { + browser.sessions.getRecentlyClosed({maxResults: 1}).then(recentlyClosed => { + let tab = recentlyClosed[0].window.tabs[0]; + browser.test.assertEq("http://example.com/", tab.url, + "Tab in closed window has the expected url."); + browser.test.assertTrue(tab.title.includes("mochitest index"), + "Tab in closed window has the expected title."); + browser.test.notifyPass("getRecentlyClosed with navigation"); + }); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["sessions", "tabs"], + }, + background, + }); + + // Test with a window with navigation history. + let win = yield BrowserTestUtils.openNewBrowserWindow(); + for (let url of ["about:robots", "about:mozilla", "http://example.com/"]) { + yield BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, url); + yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser); + } + + yield BrowserTestUtils.closeWindow(win); + + yield extension.startup(); + yield extension.awaitFinish(); + yield extension.unload(); +}); diff --git a/browser/components/extensions/test/browser/browser_ext_sidebarAction.js b/browser/components/extensions/test/browser/browser_ext_sidebarAction.js index e6fb985f3e29..1e27fb082b38 100644 --- a/browser/components/extensions/test/browser/browser_ext_sidebarAction.js +++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction.js @@ -126,6 +126,49 @@ add_task(function* sidebar_empty_panel() { yield extension.unload(); }); +add_task(function* sidebar_tab_query_bug_1340739() { + let data = { + manifest: { + "permissions": [ + "tabs", + ], + "sidebar_action": { + "default_panel": "sidebar.html", + }, + }, + useAddonManager: "temporary", + files: { + "sidebar.html": ` + + + + + + + A Test Sidebar + + `, + "sidebar.js": function() { + Promise.all([ + browser.tabs.query({}).then((tabs) => { + browser.test.assertEq(1, tabs.length, "got tab without currentWindow"); + }), + browser.tabs.query({currentWindow: true}).then((tabs) => { + browser.test.assertEq(1, tabs.length, "got tab with currentWindow"); + }), + ]).then(() => { + browser.test.sendMessage("sidebar"); + }); + }, + }, + }; + + let extension = ExtensionTestUtils.loadExtension(data); + yield extension.startup(); + yield extension.awaitMessage("sidebar"); + yield extension.unload(); +}); + add_task(function* cleanup() { // This is set on initial sidebar install. Services.prefs.clearUserPref("extensions.sidebar-button.shown"); diff --git a/browser/components/extensions/test/xpcshell/test_ext_browsingData_settings.js b/browser/components/extensions/test/xpcshell/test_ext_browsingData_settings.js index 68ed74d05ea2..a641db7f2d16 100644 --- a/browser/components/extensions/test/xpcshell/test_ext_browsingData_settings.js +++ b/browser/components/extensions/test/xpcshell/test_ext_browsingData_settings.js @@ -2,12 +2,15 @@ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; +XPCOMUtils.defineLazyModuleGetter(this, "Preferences", + "resource://gre/modules/Preferences.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Sanitizer", "resource:///modules/Sanitizer.jsm"); const PREF_DOMAIN = "privacy.cpd."; +const SETTINGS_LIST = ["cache", "cookies", "history", "formData", "downloads"].sort(); -add_task(function* testSettings() { +add_task(function* testSettingsProperties() { function background() { browser.test.onMessage.addListener(msg => { browser.browsingData.settings().then(settings => { @@ -25,21 +28,19 @@ add_task(function* testSettings() { yield extension.startup(); - let branch = Services.prefs.getBranch(PREF_DOMAIN); - extension.sendMessage("settings"); let settings = yield extension.awaitMessage("settings"); - let since = Sanitizer.getClearRange()[0] / 1000; - - // Because it is based on the current timestamp, we cannot know the exact - // value to expect for since, so allow a 10s variance. - ok(Math.abs(settings.options.since - since) < 10000, - "settings.options contains the expected since value."); + // Verify that we get the keys back we expect. + deepEqual(Object.keys(settings.dataToRemove).sort(), SETTINGS_LIST, + "dataToRemove contains expected properties."); + deepEqual(Object.keys(settings.dataRemovalPermitted).sort(), SETTINGS_LIST, + "dataToRemove contains expected properties."); let dataTypeSet = settings.dataToRemove; for (let key of Object.keys(dataTypeSet)) { - equal(branch.getBoolPref(key.toLowerCase()), dataTypeSet[key], `${key} property of dataToRemove matches the expected pref.`); + equal(Preferences.get(`${PREF_DOMAIN}${key.toLowerCase()}`), dataTypeSet[key], + `${key} property of dataToRemove matches the expected pref.`); } dataTypeSet = settings.dataRemovalPermitted; @@ -48,29 +49,69 @@ add_task(function* testSettings() { } // Explicitly set a pref to both true and false and then check. - const SINGLE_PREF = "cache"; + const SINGLE_OPTION = "cache"; + const SINGLE_PREF = "privacy.cpd.cache"; do_register_cleanup(() => { - branch.clearUserPref(SINGLE_PREF); + Preferences.reset(SINGLE_PREF); }); - branch.setBoolPref(SINGLE_PREF, true); + Preferences.set(SINGLE_PREF, true); extension.sendMessage("settings"); settings = yield extension.awaitMessage("settings"); + equal(settings.dataToRemove[SINGLE_OPTION], true, "Preference that was set to true returns true."); - equal(settings.dataToRemove[SINGLE_PREF], true, "Preference that was set to true returns true."); - - branch.setBoolPref(SINGLE_PREF, false); + Preferences.set(SINGLE_PREF, false); extension.sendMessage("settings"); settings = yield extension.awaitMessage("settings"); - - equal(settings.dataToRemove[SINGLE_PREF], false, "Preference that was set to false returns false."); - - do_register_cleanup(() => { - branch.clearUserPref(SINGLE_PREF); - }); + equal(settings.dataToRemove[SINGLE_OPTION], false, "Preference that was set to false returns false."); + + yield extension.unload(); +}); + +add_task(function* testSettingsSince() { + const TIMESPAN_PREF = "privacy.sanitize.timeSpan"; + const TEST_DATA = { + TIMESPAN_5MIN: Date.now() - 5 * 60 * 1000, + TIMESPAN_HOUR: Date.now() - 60 * 60 * 1000, + TIMESPAN_2HOURS: Date.now() - 2 * 60 * 60 * 1000, + TIMESPAN_EVERYTHING: 0, + }; + + function background() { + browser.test.onMessage.addListener(msg => { + browser.browsingData.settings().then(settings => { + browser.test.sendMessage("settings", settings); + }); + }); + } + + let extension = ExtensionTestUtils.loadExtension({ + background, + manifest: { + permissions: ["browsingData"], + }, + }); + + yield extension.startup(); + + do_register_cleanup(() => { + Preferences.reset(TIMESPAN_PREF); + }); + + for (let timespan in TEST_DATA) { + Preferences.set(TIMESPAN_PREF, Sanitizer[timespan]); + + extension.sendMessage("settings"); + let settings = yield extension.awaitMessage("settings"); + + // Because it is based on the current timestamp, we cannot know the exact + // value to expect for since, so allow a 10s variance. + ok(Math.abs(settings.options.since - TEST_DATA[timespan]) < 10000, + "settings.options contains the expected since value."); + } yield extension.unload(); }); diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index 134969d62429..a8e3da7a8eb4 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -91,6 +91,7 @@ webextPerms.description.history=Access browsing history # %S will be replaced with the name of the application webextPerms.description.nativeMessaging=Exchange messages with programs other than %S webextPerms.description.notifications=Display notifications to you +webextPerms.description.privacy=Read and modify privacy settings webextPerms.description.sessions=Access recently closed tabs webextPerms.description.tabs=Access browser tabs webextPerms.description.topSites=Access browsing history diff --git a/config/check_spidermonkey_style.py b/config/check_spidermonkey_style.py index fdbb07674a37..c0f722894b26 100644 --- a/config/check_spidermonkey_style.py +++ b/config/check_spidermonkey_style.py @@ -88,6 +88,7 @@ included_inclnames_to_ignore = set([ 'unicode/udat.h', # ICU 'unicode/udatpg.h', # ICU 'unicode/uenum.h', # ICU + 'unicode/uloc.h', # ICU 'unicode/unorm2.h', # ICU 'unicode/unum.h', # ICU 'unicode/unumsys.h', # ICU diff --git a/devtools/client/netmonitor/components/request-list-content.js b/devtools/client/netmonitor/components/request-list-content.js index d176c9c88eeb..a6afa89ee31f 100644 --- a/devtools/client/netmonitor/components/request-list-content.js +++ b/devtools/client/netmonitor/components/request-list-content.js @@ -158,9 +158,9 @@ const RequestListContent = createClass({ return false; } - if (requestItem.responseContent && target.closest(".requests-menu-icon-and-file")) { + if (requestItem.responseContent && target.closest(".requests-list-icon-and-file")) { return setTooltipImageContent(tooltip, itemEl, requestItem); - } else if (requestItem.cause && target.closest(".requests-menu-cause-stack")) { + } else if (requestItem.cause && target.closest(".requests-list-cause-stack")) { return setTooltipStackTraceContent(tooltip, requestItem); } @@ -237,7 +237,7 @@ const RequestListContent = createClass({ return ( div({ ref: "contentEl", - className: "requests-menu-contents", + className: "requests-list-contents", tabIndex: 0, onKeyDown: this.onKeyDown, }, diff --git a/devtools/client/netmonitor/components/request-list-empty.js b/devtools/client/netmonitor/components/request-list-empty.js index b089b1db7054..8ddd1f1916bc 100644 --- a/devtools/client/netmonitor/components/request-list-empty.js +++ b/devtools/client/netmonitor/components/request-list-empty.js @@ -31,15 +31,13 @@ const RequestListEmptyNotice = createClass({ render() { return div( { - id: "requests-menu-empty-notice", - className: "request-list-empty-notice", + className: "requests-list-empty-notice", }, - div({ id: "notice-reload-message" }, + div({ className: "notice-reload-message" }, span(null, L10N.getStr("netmonitor.reloadNotice1")), button( { - id: "requests-menu-reload-notice-button", - className: "devtools-button", + className: "devtools-toolbarbutton requests-list-reload-notice-button", "data-standalone": true, onClick: this.props.onReloadClick, }, @@ -47,12 +45,11 @@ const RequestListEmptyNotice = createClass({ ), span(null, L10N.getStr("netmonitor.reloadNotice3")) ), - div({ id: "notice-perf-message" }, + div({ className: "notice-perf-message" }, span(null, L10N.getStr("netmonitor.perfNotice1")), button({ - id: "requests-menu-perf-notice-button", title: L10N.getStr("netmonitor.perfNotice3"), - className: "devtools-button", + className: "devtools-button requests-list-perf-notice-button", "data-standalone": true, onClick: this.props.onPerfClick, }), diff --git a/devtools/client/netmonitor/components/request-list-header.js b/devtools/client/netmonitor/components/request-list-header.js index e45e24aa7a70..24362dbb129d 100644 --- a/devtools/client/netmonitor/components/request-list-header.js +++ b/devtools/client/netmonitor/components/request-list-header.js @@ -78,8 +78,8 @@ const RequestListHeader = createClass({ const { sort, scale, waterfallWidth, onHeaderClick } = this.props; return div( - { id: "requests-menu-toolbar", className: "devtools-toolbar" }, - div({ id: "toolbar-labels" }, + { className: "devtools-toolbar requests-list-toolbar" }, + div({ className: "toolbar-labels" }, HEADERS.map(header => { const name = header.name; const boxName = header.boxName || name; @@ -96,8 +96,8 @@ const RequestListHeader = createClass({ return div( { - id: `requests-menu-${boxName}-header-box`, - className: `requests-menu-header requests-menu-${boxName}`, + id: `requests-list-${boxName}-header-box`, + className: `requests-list-header requests-list-${boxName}`, key: name, ref: "header", // Used to style the next column. @@ -105,8 +105,8 @@ const RequestListHeader = createClass({ }, button( { - id: `requests-menu-${name}-button`, - className: `requests-menu-header-button requests-menu-${name}`, + id: `requests-list-${name}-button`, + className: `requests-list-header-button requests-list-${name}`, "data-sorted": sorted, title: sortedTitle, onClick: () => onHeaderClick(name), @@ -163,7 +163,7 @@ function waterfallDivisionLabels(waterfallWidth, scale) { labels.push(div( { key: labels.length, - className: "requests-menu-timings-division", + className: "requests-list-timings-division", "data-division-scale": divisionScale, style: { width } }, @@ -175,11 +175,11 @@ function waterfallDivisionLabels(waterfallWidth, scale) { } function WaterfallLabel(waterfallWidth, scale, label) { - let className = "button-text requests-menu-waterfall-label-wrapper"; + let className = "button-text requests-list-waterfall-label-wrapper"; if (waterfallWidth != null && scale != null) { label = waterfallDivisionLabels(waterfallWidth, scale); - className += " requests-menu-waterfall-visible"; + className += " requests-list-waterfall-visible"; } return div({ className }, label); diff --git a/devtools/client/netmonitor/components/request-list-item.js b/devtools/client/netmonitor/components/request-list-item.js index 75cecf850533..2c6aa1dce051 100644 --- a/devtools/client/netmonitor/components/request-list-item.js +++ b/devtools/client/netmonitor/components/request-list-item.js @@ -175,9 +175,9 @@ const StatusColumn = createFactory(createClass({ } return ( - div({ className: "requests-menu-subitem requests-menu-status", title }, - div({ className: "requests-menu-status-icon", "data-code": code }), - span({ className: "subitem-label requests-menu-status-code" }, status), + div({ className: "requests-list-subitem requests-list-status", title }, + div({ className: "requests-list-status-icon", "data-code": code }), + span({ className: "subitem-label requests-list-status-code" }, status) ) ); } @@ -197,8 +197,8 @@ const MethodColumn = createFactory(createClass({ render() { const { method } = this.props.item; return ( - div({ className: "requests-menu-subitem requests-menu-method-box" }, - span({ className: "subitem-label requests-menu-method" }, method) + div({ className: "requests-list-subitem requests-list-method-box" }, + span({ className: "subitem-label requests-list-method" }, method) ) ); } @@ -224,15 +224,15 @@ const FileColumn = createFactory(createClass({ const { urlDetails, responseContentDataUri } = this.props.item; return ( - div({ className: "requests-menu-subitem requests-menu-icon-and-file" }, + div({ className: "requests-list-subitem requests-list-icon-and-file" }, img({ - className: "requests-menu-icon", + className: "requests-list-icon", src: responseContentDataUri, hidden: !responseContentDataUri, "data-type": responseContentDataUri ? "thumbnail" : undefined, }), div({ - className: "subitem-label requests-menu-file", + className: "subitem-label requests-list-file", title: urlDetails.unicodeUrl, }, urlDetails.baseNameWithQuery, @@ -277,13 +277,13 @@ const DomainColumn = createFactory(createClass({ let title = urlDetails.host + (remoteAddress ? ` (${remoteAddress})` : ""); return ( - div({ className: "requests-menu-subitem requests-menu-security-and-domain" }, + div({ className: "requests-list-subitem requests-list-security-and-domain" }, div({ className: iconClassList.join(" "), title: iconTitle, onClick: onSecurityIconClick, }), - span({ className: "subitem-label requests-menu-domain", title }, urlDetails.host), + span({ className: "subitem-label requests-list-domain", title }, urlDetails.host), ) ); } @@ -316,11 +316,11 @@ const CauseColumn = createFactory(createClass({ return ( div({ - className: "requests-menu-subitem requests-menu-cause", + className: "requests-list-subitem requests-list-cause", title: causeUri, }, span({ - className: "requests-menu-cause-stack", + className: "requests-list-cause-stack", hidden: !causeHasStack, }, "JS"), span({ className: "subitem-label" }, causeType), @@ -356,7 +356,7 @@ const TypeColumn = createFactory(createClass({ return ( div({ - className: "requests-menu-subitem requests-menu-type", + className: "requests-list-subitem requests-list-type", title: mimeType, }, span({ className: "subitem-label" }, abbrevType), @@ -401,7 +401,7 @@ const TransferredSizeColumn = createFactory(createClass({ return ( div({ - className: "requests-menu-subitem requests-menu-transferred", + className: "requests-list-subitem requests-list-transferred", title: text, }, span({ className }, text), @@ -431,7 +431,7 @@ const ContentSizeColumn = createFactory(createClass({ return ( div({ - className: "requests-menu-subitem subitem-label requests-menu-size", + className: "requests-list-subitem subitem-label requests-list-size", title: text, }, span({ className: "subitem-label" }, text), @@ -464,9 +464,9 @@ const WaterfallColumn = createFactory(createClass({ const { item, firstRequestStartedMillis } = this.props; return ( - div({ className: "requests-menu-subitem requests-menu-waterfall" }, + div({ className: "requests-list-subitem requests-list-waterfall" }, div({ - className: "requests-menu-timings", + className: "requests-list-timings", style: { paddingInlineStart: `${item.startedMillis - firstRequestStartedMillis}px`, }, @@ -499,7 +499,7 @@ function timingBoxes(item) { if (width > 0) { boxes.push(div({ key, - className: "requests-menu-timings-box " + key, + className: "requests-list-timings-box " + key, style: { width } })); } @@ -510,8 +510,8 @@ function timingBoxes(item) { let text = L10N.getFormatStr("networkMenu.totalMS", totalTime); boxes.push(div({ key: "total", - className: "requests-menu-timings-total", - title: text, + className: "requests-list-timings-total", + title: text }, text)); } diff --git a/devtools/client/netmonitor/components/toolbar.js b/devtools/client/netmonitor/components/toolbar.js index cf5fc827bc66..71111b8ee369 100644 --- a/devtools/client/netmonitor/components/toolbar.js +++ b/devtools/client/netmonitor/components/toolbar.js @@ -96,12 +96,11 @@ const Toolbar = createClass({ .replace("#4", getFormattedTime(millis)); let buttons = requestFilterTypes.map(([type, checked]) => { - let classList = ["devtools-button"]; + let classList = ["devtools-button", `requests-list-filter-${type}-button`]; checked && classList.push("checked"); return ( button({ - id: `requests-menu-filter-${type}-button`, className: classList.join(" "), key: type, onClick: this.toggleRequestFilterType, @@ -118,17 +117,15 @@ const Toolbar = createClass({ span({ className: "devtools-toolbar devtools-toolbar-container" }, span({ className: "devtools-toolbar-group" }, button({ - id: "requests-menu-clear-button", - className: "devtools-button devtools-clear-icon", + className: "devtools-button devtools-clear-icon requests-list-clear-button", title: TOOLBAR_CLEAR, onClick: clearRequests, }), - div({ id: "requests-menu-filter-buttons" }, buttons), + div({ id: "requests-list-filter-buttons" }, buttons), ), span({ className: "devtools-toolbar-group" }, button({ - id: "requests-menu-network-summary-button", - className: "devtools-button", + className: "devtools-button requests-list-network-summary-button", title: count ? text : L10N.getStr("netmonitor.toolbar.perf"), onClick: openStatistics, }, diff --git a/devtools/client/netmonitor/request-list-context-menu.js b/devtools/client/netmonitor/request-list-context-menu.js index 0bc0f742d217..a7762acce277 100644 --- a/devtools/client/netmonitor/request-list-context-menu.js +++ b/devtools/client/netmonitor/request-list-context-menu.js @@ -50,7 +50,7 @@ RequestListContextMenu.prototype = { let menu = new Menu(); menu.append(new MenuItem({ - id: "request-menu-context-copy-url", + id: "request-list-context-copy-url", label: L10N.getStr("netmonitor.context.copyUrl"), accesskey: L10N.getStr("netmonitor.context.copyUrl.accesskey"), visible: !!selectedRequest, @@ -58,7 +58,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "request-menu-context-copy-url-params", + id: "request-list-context-copy-url-params", label: L10N.getStr("netmonitor.context.copyUrlParams"), accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"), visible: !!(selectedRequest && getUrlQuery(selectedRequest.url)), @@ -66,7 +66,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "request-menu-context-copy-post-data", + id: "request-list-context-copy-post-data", label: L10N.getStr("netmonitor.context.copyPostData"), accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"), visible: !!(selectedRequest && selectedRequest.requestPostData), @@ -74,7 +74,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "request-menu-context-copy-as-curl", + id: "request-list-context-copy-as-curl", label: L10N.getStr("netmonitor.context.copyAsCurl"), accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"), visible: !!selectedRequest, @@ -87,7 +87,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "request-menu-context-copy-request-headers", + id: "request-list-context-copy-request-headers", label: L10N.getStr("netmonitor.context.copyRequestHeaders"), accesskey: L10N.getStr("netmonitor.context.copyRequestHeaders.accesskey"), visible: !!(selectedRequest && selectedRequest.requestHeaders), @@ -95,7 +95,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "response-menu-context-copy-response-headers", + id: "response-list-context-copy-response-headers", label: L10N.getStr("netmonitor.context.copyResponseHeaders"), accesskey: L10N.getStr("netmonitor.context.copyResponseHeaders.accesskey"), visible: !!(selectedRequest && selectedRequest.responseHeaders), @@ -103,7 +103,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "request-menu-context-copy-response", + id: "request-list-context-copy-response", label: L10N.getStr("netmonitor.context.copyResponse"), accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"), visible: !!(selectedRequest && @@ -114,7 +114,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "request-menu-context-copy-image-as-data-uri", + id: "request-list-context-copy-image-as-data-uri", label: L10N.getStr("netmonitor.context.copyImageAsDataUri"), accesskey: L10N.getStr("netmonitor.context.copyImageAsDataUri.accesskey"), visible: !!(selectedRequest && @@ -129,7 +129,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "request-menu-context-copy-all-as-har", + id: "request-list-context-copy-all-as-har", label: L10N.getStr("netmonitor.context.copyAllAsHar"), accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"), visible: this.sortedRequests.size > 0, @@ -137,7 +137,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "request-menu-context-save-all-as-har", + id: "request-list-context-save-all-as-har", label: L10N.getStr("netmonitor.context.saveAllAsHar"), accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"), visible: this.sortedRequests.size > 0, @@ -150,7 +150,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "request-menu-context-resend", + id: "request-list-context-resend", label: L10N.getStr("netmonitor.context.editAndResend"), accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"), visible: !!(window.NetMonitorController.supportsCustomRequest && @@ -164,7 +164,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "request-menu-context-newtab", + id: "request-list-context-newtab", label: L10N.getStr("netmonitor.context.newTab"), accesskey: L10N.getStr("netmonitor.context.newTab.accesskey"), visible: !!selectedRequest, @@ -172,7 +172,7 @@ RequestListContextMenu.prototype = { })); menu.append(new MenuItem({ - id: "request-menu-context-perf", + id: "request-list-context-perf", label: L10N.getStr("netmonitor.context.perfTools"), accesskey: L10N.getStr("netmonitor.context.perfTools.accesskey"), visible: !!window.NetMonitorController.supportsPerfStats, diff --git a/devtools/client/netmonitor/request-list-tooltip.js b/devtools/client/netmonitor/request-list-tooltip.js index 9ebba990578d..1a6d1900f336 100644 --- a/devtools/client/netmonitor/request-list-tooltip.js +++ b/devtools/client/netmonitor/request-list-tooltip.js @@ -32,7 +32,7 @@ async function setTooltipImageContent(tooltip, itemEl, requestItem) { let options = { maxDim, naturalWidth, naturalHeight }; setImageTooltip(tooltip, tooltip.doc, src, options); - return itemEl.querySelector(".requests-menu-icon"); + return itemEl.querySelector(".requests-list-icon"); } async function setTooltipStackTraceContent(tooltip, requestItem) { diff --git a/devtools/client/netmonitor/shared/components/headers-panel.js b/devtools/client/netmonitor/shared/components/headers-panel.js index 1108ec227f20..7ab65b5461f4 100644 --- a/devtools/client/netmonitor/shared/components/headers-panel.js +++ b/devtools/client/netmonitor/shared/components/headers-panel.js @@ -178,7 +178,7 @@ const HeadersPanel = createClass({ className: "tabpanel-summary-label headers-summary-label", }, SUMMARY_STATUS), div({ - className: "requests-menu-status-icon", + className: "requests-list-status-icon", "data-code": code, }), input({ diff --git a/devtools/client/netmonitor/shared/components/timings-panel.js b/devtools/client/netmonitor/shared/components/timings-panel.js index 44b3e6d6197f..53d63eebac2f 100644 --- a/devtools/client/netmonitor/shared/components/timings-panel.js +++ b/devtools/client/netmonitor/shared/components/timings-panel.js @@ -40,20 +40,20 @@ function TimingsPanel({ span({ className: "tabpanel-summary-label timings-label" }, L10N.getStr(`netmonitor.timings.${type}`) ), - div({ className: "requests-menu-timings-container" }, + div({ className: "requests-list-timings-container" }, span({ - className: "requests-menu-timings-offset", + className: "requests-list-timings-offset", style: { width: `calc(${offsetScale} * (100% - ${TIMINGS_END_PADDING})`, }, }), span({ - className: `requests-menu-timings-box ${type}`, + className: `requests-list-timings-box ${type}`, style: { width: `calc(${timelineScale} * (100% - ${TIMINGS_END_PADDING}))`, }, }), - span({ className: "requests-menu-timings-total" }, + span({ className: "requests-list-timings-total" }, L10N.getFormatStr("networkMenu.totalMS", timings[type]) ) ), diff --git a/devtools/client/netmonitor/test/browser_net_accessibility-02.js b/devtools/client/netmonitor/test/browser_net_accessibility-02.js index 7e4523698259..1fa389e20d23 100644 --- a/devtools/client/netmonitor/test/browser_net_accessibility-02.js +++ b/devtools/client/netmonitor/test/browser_net_accessibility-02.js @@ -36,7 +36,7 @@ add_task(function* () { }); yield wait; - document.querySelector(".requests-menu-contents").focus(); + document.querySelector(".requests-list-contents").focus(); check(-1, false); diff --git a/devtools/client/netmonitor/test/browser_net_autoscroll.js b/devtools/client/netmonitor/test/browser_net_autoscroll.js index c0bcf3d32e69..bdafc8aa4274 100644 --- a/devtools/client/netmonitor/test/browser_net_autoscroll.js +++ b/devtools/client/netmonitor/test/browser_net_autoscroll.js @@ -16,7 +16,7 @@ add_task(function* () { // Wait until the first request makes the empty notice disappear yield waitForRequestListToAppear(); - let requestsContainer = document.querySelector(".requests-menu-contents"); + let requestsContainer = document.querySelector(".requests-list-contents"); ok(requestsContainer, "Container element exists as expected."); // (1) Check that the scroll position is maintained at the bottom @@ -57,7 +57,7 @@ add_task(function* () { function waitForRequestListToAppear() { info("Waiting until the empty notice disappears and is replaced with the list"); - return waitUntil(() => !!document.querySelector(".requests-menu-contents")); + return waitUntil(() => !!document.querySelector(".requests-list-contents")); } function* waitForRequestsToOverflowContainer() { diff --git a/devtools/client/netmonitor/test/browser_net_cause.js b/devtools/client/netmonitor/test/browser_net_cause.js index 756f6a7d223c..ea353925d728 100644 --- a/devtools/client/netmonitor/test/browser_net_cause.js +++ b/devtools/client/netmonitor/test/browser_net_cause.js @@ -145,7 +145,7 @@ add_task(function* () { // Sort the requests by cause and check the order EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-cause-button")); + document.querySelector("#requests-list-cause-button")); let expectedOrder = EXPECTED_REQUESTS.map(r => r.causeType).sort(); expectedOrder.forEach((expectedCause, i) => { const cause = getSortedRequests(gStore.getState()).get(i).cause.type; diff --git a/devtools/client/netmonitor/test/browser_net_clear.js b/devtools/client/netmonitor/test/browser_net_clear.js index 11e5702d9502..15a38005928b 100644 --- a/devtools/client/netmonitor/test/browser_net_clear.js +++ b/devtools/client/netmonitor/test/browser_net_clear.js @@ -16,7 +16,7 @@ add_task(function* () { let { EVENTS } = windowRequire("devtools/client/netmonitor/constants"); let detailsPane = document.querySelector("#details-pane"); let detailsPanelToggleButton = document.querySelector(".network-details-panel-toggle"); - let clearButton = document.querySelector("#requests-menu-clear-button"); + let clearButton = document.querySelector(".requests-list-clear-button"); gStore.dispatch(Actions.batchEnable(false)); diff --git a/devtools/client/netmonitor/test/browser_net_copy_as_curl.js b/devtools/client/netmonitor/test/browser_net_copy_as_curl.js index dd1ed13e6f45..1267d7e2f3f5 100644 --- a/devtools/client/netmonitor/test/browser_net_copy_as_curl.js +++ b/devtools/client/netmonitor/test/browser_net_copy_as_curl.js @@ -59,7 +59,7 @@ add_task(function* () { // Context menu is appending in XUL document, we must select it from // toolbox.doc monitor.toolbox.doc - .querySelector("#request-menu-context-copy-as-curl").click(); + .querySelector("#request-list-context-copy-as-curl").click(); }, function validate(result) { if (typeof result !== "string") { return false; diff --git a/devtools/client/netmonitor/test/browser_net_copy_headers.js b/devtools/client/netmonitor/test/browser_net_copy_headers.js index 0fcf53efdd68..fb41c1728311 100644 --- a/devtools/client/netmonitor/test/browser_net_copy_headers.js +++ b/devtools/client/netmonitor/test/browser_net_copy_headers.js @@ -44,7 +44,7 @@ add_task(function* () { // Context menu is appending in XUL document, we must select it from // toolbox.doc monitor.toolbox.doc - .querySelector("#request-menu-context-copy-request-headers").click(); + .querySelector("#request-list-context-copy-request-headers").click(); }, function validate(result) { // Sometimes, a "Cookie" header is left over from other tests. Remove it: result = String(result).replace(/Cookie: [^\n]+\n/, ""); @@ -69,7 +69,7 @@ add_task(function* () { // Context menu is appending in XUL document, we must select it from // _oolbox.doc monitor.toolbox.doc - .querySelector("#response-menu-context-copy-response-headers").click(); + .querySelector("#response-list-context-copy-response-headers").click(); }, function validate(result) { // Fake the "Last-Modified" and "Date" headers because they will vary: result = String(result) diff --git a/devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js b/devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js index f1d2c042a446..6beb81aa3a1a 100644 --- a/devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js +++ b/devtools/client/netmonitor/test/browser_net_copy_image_as_data_uri.js @@ -28,8 +28,8 @@ add_task(function* () { // Context menu is appending in XUL document, we must select it from // toolbox.doc monitor.toolbox.doc - .querySelector("#request-menu-context-copy-image-as-data-uri").click(); - }, TEST_IMAGE_DATA_URI); + .querySelector("#request-list-context-copy-image-as-data-uri").click(); + }, TEST_IMAGE_DATA_URI); ok(true, "Clipboard contains the currently selected image as data uri."); diff --git a/devtools/client/netmonitor/test/browser_net_copy_params.js b/devtools/client/netmonitor/test/browser_net_copy_params.js index 354177d13e7b..96a3fbada4dc 100644 --- a/devtools/client/netmonitor/test/browser_net_copy_params.js +++ b/devtools/client/netmonitor/test/browser_net_copy_params.js @@ -63,7 +63,7 @@ add_task(function* () { EventUtils.sendMouseEvent({ type: "contextmenu" }, document.querySelectorAll(".request-list-item")[index]); let copyUrlParamsNode = monitor.toolbox.doc - .querySelector("#request-menu-context-copy-url-params"); + .querySelector("#request-list-context-copy-url-params"); is(!!copyUrlParamsNode, !hidden, "The \"Copy URL Parameters\" context menu item should" + (hidden ? " " : " not ") + "be hidden."); @@ -76,7 +76,7 @@ add_task(function* () { document.querySelectorAll(".request-list-item")[index]); yield waitForClipboardPromise(function setup() { monitor.toolbox.doc - .querySelector("#request-menu-context-copy-url-params").click(); + .querySelector("#request-list-context-copy-url-params").click(); }, queryString); ok(true, "The url query string copied from the selected item is correct."); } @@ -87,7 +87,7 @@ add_task(function* () { EventUtils.sendMouseEvent({ type: "contextmenu" }, document.querySelectorAll(".request-list-item")[index]); let copyPostDataNode = monitor.toolbox.doc - .querySelector("#request-menu-context-copy-post-data"); + .querySelector("#request-list-context-copy-post-data"); is(!!copyPostDataNode, !hidden, "The \"Copy POST Data\" context menu item should" + (hidden ? " " : " not ") + "be hidden."); @@ -100,7 +100,7 @@ add_task(function* () { document.querySelectorAll(".request-list-item")[index]); yield waitForClipboardPromise(function setup() { monitor.toolbox.doc - .querySelector("#request-menu-context-copy-post-data").click(); + .querySelector("#request-list-context-copy-post-data").click(); }, postData); ok(true, "The post data string copied from the selected item is correct."); } diff --git a/devtools/client/netmonitor/test/browser_net_copy_response.js b/devtools/client/netmonitor/test/browser_net_copy_response.js index 8ea68090f6b6..510b1653a9d7 100644 --- a/devtools/client/netmonitor/test/browser_net_copy_response.js +++ b/devtools/client/netmonitor/test/browser_net_copy_response.js @@ -30,7 +30,7 @@ add_task(function* () { // Context menu is appending in XUL document, we must select it from // toolbox.doc monitor.toolbox.doc - .querySelector("#request-menu-context-copy-response").click(); + .querySelector("#request-list-context-copy-response").click(); }, EXPECTED_RESULT); yield teardown(monitor); diff --git a/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js b/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js index e01355f95600..d0d3398ac533 100644 --- a/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js +++ b/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js @@ -30,7 +30,7 @@ add_task(function* () { // Context menu is appending in XUL document, we must select it from // toolbox.doc monitor.toolbox.doc - .querySelector("#request-menu-context-copy-image-as-data-uri").click(); + .querySelector("#request-list-context-copy-image-as-data-uri").click(); }, function check(text) { return text.startsWith("data:") && !/undefined/.test(text); }); diff --git a/devtools/client/netmonitor/test/browser_net_copy_url.js b/devtools/client/netmonitor/test/browser_net_copy_url.js index f8c8005b799b..e7ecacc3d45c 100644 --- a/devtools/client/netmonitor/test/browser_net_copy_url.js +++ b/devtools/client/netmonitor/test/browser_net_copy_url.js @@ -31,7 +31,7 @@ add_task(function* () { // Context menu is appending in XUL document, we must select it from // toolbox.doc monitor.toolbox.doc - .querySelector("#request-menu-context-copy-url").click(); + .querySelector("#request-list-context-copy-url").click(); }, requestItem.url); yield teardown(monitor); diff --git a/devtools/client/netmonitor/test/browser_net_filter-01.js b/devtools/client/netmonitor/test/browser_net_filter-01.js index 4f0b0ac2d4bc..53e77694d017 100644 --- a/devtools/client/netmonitor/test/browser_net_filter-01.js +++ b/devtools/client/netmonitor/test/browser_net_filter-01.js @@ -166,93 +166,94 @@ add_task(function* () { testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-html-button")); + document.querySelector(".requests-list-filter-html-button")); testFilterButtons(monitor, "html"); testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]); // Reset filters EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-css-button")); + document.querySelector(".requests-list-filter-css-button")); testFilterButtons(monitor, "css"); testContents([0, 1, 0, 0, 0, 0, 0, 0, 0]); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-js-button")); + document.querySelector(".requests-list-filter-js-button")); testFilterButtons(monitor, "js"); testContents([0, 0, 1, 0, 0, 0, 0, 0, 0]); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-xhr-button")); + document.querySelector(".requests-list-filter-xhr-button")); testFilterButtons(monitor, "xhr"); testContents([1, 1, 1, 1, 1, 1, 1, 1, 0]); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-fonts-button")); + document.querySelector(".requests-list-filter-fonts-button")); testFilterButtons(monitor, "fonts"); testContents([0, 0, 0, 1, 0, 0, 0, 0, 0]); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-images-button")); + document.querySelector(".requests-list-filter-images-button")); testFilterButtons(monitor, "images"); testContents([0, 0, 0, 0, 1, 0, 0, 0, 0]); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-media-button")); + document.querySelector(".requests-list-filter-media-button")); testFilterButtons(monitor, "media"); testContents([0, 0, 0, 0, 0, 1, 1, 0, 0]); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-flash-button")); + document.querySelector(".requests-list-filter-flash-button")); testFilterButtons(monitor, "flash"); testContents([0, 0, 0, 0, 0, 0, 0, 1, 0]); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-ws-button")); + document.querySelector(".requests-list-filter-ws-button")); testFilterButtons(monitor, "ws"); testContents([0, 0, 0, 0, 0, 0, 0, 0, 1]); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); + testFilterButtons(monitor, "all"); testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]); // Text in filter box that matches nothing should hide all. EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); setFreetextFilter("foobar"); testContents([0, 0, 0, 0, 0, 0, 0, 0, 0]); // Text in filter box that matches should filter out everything else. EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); setFreetextFilter("sample"); testContents([1, 1, 1, 0, 0, 0, 0, 0, 0]); // Text in filter box that matches should filter out everything else. EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); setFreetextFilter("SAMPLE"); testContents([1, 1, 1, 0, 0, 0, 0, 0, 0]); // Test negative filtering (only show unmatched items) EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); setFreetextFilter("-sample"); testContents([0, 0, 0, 1, 1, 1, 1, 1, 1]); @@ -261,9 +262,9 @@ add_task(function* () { // Enable filtering for html and css; should show request of both type. setFreetextFilter(""); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-html-button")); + document.querySelector(".requests-list-filter-html-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-css-button")); + document.querySelector(".requests-list-filter-css-button")); testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]); testContents([1, 1, 0, 0, 0, 0, 0, 0, 0]); @@ -273,35 +274,35 @@ add_task(function* () { testContents([1, 1, 0, 0, 0, 0, 0, 0, 0]); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-flash-button")); + document.querySelector(".requests-list-filter-flash-button")); setFreetextFilter(""); testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0]); testContents([1, 1, 0, 0, 0, 0, 0, 1, 0]); // Disable some filters. Only one left active. EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-css-button")); + document.querySelector(".requests-list-filter-css-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-flash-button")); + document.querySelector(".requests-list-filter-flash-button")); testFilterButtons(monitor, "html"); testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]); // Disable last active filter. Should toggle to all. EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-html-button")); + document.querySelector(".requests-list-filter-html-button")); testFilterButtons(monitor, "all"); testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]); // Enable few filters and click on all. Only "all" should be checked. EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-html-button")); + document.querySelector(".requests-list-filter-html-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-css-button")); + document.querySelector(".requests-list-filter-css-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-ws-button")); + document.querySelector(".requests-list-filter-ws-button")); testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 0, 1]); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); testFilterButtons(monitor, "all"); testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]); diff --git a/devtools/client/netmonitor/test/browser_net_filter-02.js b/devtools/client/netmonitor/test/browser_net_filter-02.js index 4b3cdf92ab4b..39e466960964 100644 --- a/devtools/client/netmonitor/test/browser_net_filter-02.js +++ b/devtools/client/netmonitor/test/browser_net_filter-02.js @@ -166,7 +166,7 @@ add_task(function* () { info("Testing html filtering."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-html-button")); + document.querySelector(".requests-list-filter-html-button")); testFilterButtons(monitor, "html"); testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]); @@ -191,7 +191,7 @@ add_task(function* () { info("Resetting filters."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-all-button")); + document.querySelector(".requests-list-filter-all-button")); testFilterButtons(monitor, "all"); testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); diff --git a/devtools/client/netmonitor/test/browser_net_filter-03.js b/devtools/client/netmonitor/test/browser_net_filter-03.js index 3421450bc3b6..e445afb9d4a1 100644 --- a/devtools/client/netmonitor/test/browser_net_filter-03.js +++ b/devtools/client/netmonitor/test/browser_net_filter-03.js @@ -64,13 +64,13 @@ add_task(function* () { info("Sorting by size, ascending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-size-button")); + document.querySelector("#requests-list-size-button")); testFilterButtons(monitor, "all"); testContents([6, 4, 5, 0, 1, 2, 3], 7, 6); info("Testing html filtering."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-html-button")); + document.querySelector(".requests-list-filter-html-button")); testFilterButtons(monitor, "html"); testContents([6, 4, 5, 0, 1, 2, 3], 1, 6); @@ -98,9 +98,9 @@ add_task(function* () { function resetSorting() { EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-waterfall-button")); + document.querySelector("#requests-list-waterfall-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-size-button")); + document.querySelector("#requests-list-size-button")); } function getSelectedIndex(state) { diff --git a/devtools/client/netmonitor/test/browser_net_footer-summary.js b/devtools/client/netmonitor/test/browser_net_footer-summary.js index 7eaada4c2943..852c2f0a3392 100644 --- a/devtools/client/netmonitor/test/browser_net_footer-summary.js +++ b/devtools/client/netmonitor/test/browser_net_footer-summary.js @@ -40,7 +40,7 @@ add_task(function* () { let buttons = ["html", "css", "js", "xhr", "fonts", "images", "media", "flash"]; for (let button of buttons) { - let buttonEl = document.querySelector(`#requests-menu-filter-${button}-button`); + let buttonEl = document.querySelector(`.requests-list-filter-${button}-button`); EventUtils.sendMouseEvent({ type: "click" }, buttonEl); testStatus(); } @@ -49,8 +49,8 @@ add_task(function* () { yield teardown(monitor); function testStatus() { - let value = document.querySelector("#requests-menu-network-summary-button").textContent; - info("Current summary: " + value); + let value = document.querySelector(".requests-list-network-summary-button").textContent; + info("Current summary: " + value); let state = gStore.getState(); let totalRequestsCount = state.requests.requests.size; diff --git a/devtools/client/netmonitor/test/browser_net_icon-preview.js b/devtools/client/netmonitor/test/browser_net_icon-preview.js index f9b2d13dc38f..ac872f2f63ab 100644 --- a/devtools/client/netmonitor/test/browser_net_icon-preview.js +++ b/devtools/client/netmonitor/test/browser_net_icon-preview.js @@ -62,11 +62,11 @@ add_task(function* () { } function checkImageThumbnail() { - is(document.querySelectorAll(".requests-menu-icon[data-type=thumbnail]").length, 1, + is(document.querySelectorAll(".requests-list-icon[data-type=thumbnail]").length, 1, "There should be only one image request with a thumbnail displayed."); - is(document.querySelector(".requests-menu-icon[data-type=thumbnail]").src, TEST_IMAGE_DATA_URI, - "The image requests-menu-icon thumbnail is displayed correctly."); - is(document.querySelector(".requests-menu-icon[data-type=thumbnail]").hidden, false, - "The image requests-menu-icon thumbnail should not be hidden."); + is(document.querySelector(".requests-list-icon[data-type=thumbnail]").src, TEST_IMAGE_DATA_URI, + "The image requests-list-icon thumbnail is displayed correctly."); + is(document.querySelector(".requests-list-icon[data-type=thumbnail]").hidden, false, + "The image requests-list-icon thumbnail should not be hidden."); } }); diff --git a/devtools/client/netmonitor/test/browser_net_image-tooltip.js b/devtools/client/netmonitor/test/browser_net_image-tooltip.js index 56604e57d659..43c031e37bd1 100644 --- a/devtools/client/netmonitor/test/browser_net_image-tooltip.js +++ b/devtools/client/netmonitor/test/browser_net_image-tooltip.js @@ -55,7 +55,7 @@ add_task(function* test() { document.querySelectorAll(".request-list-item")[1]); info("Checking if the image thumbnail is hidden when mouse leaves the menu widget"); - let requestsListContents = document.querySelector(".requests-menu-contents"); + let requestsListContents = document.querySelector(".requests-list-contents"); EventUtils.synthesizeMouse(requestsListContents, 0, 0, { type: "mouseout" }, monitor.panelWin); yield waitUntil(() => !toolboxDoc.querySelector(".tooltip-container.tooltip-visible")); @@ -72,7 +72,7 @@ add_task(function* test() { * with the expected content. */ function* showTooltipAndVerify(toolboxDoc, target) { - let anchor = target.querySelector(".requests-menu-file"); + let anchor = target.querySelector(".requests-list-file"); yield showTooltipOn(toolboxDoc, anchor); info("Tooltip was successfully opened for the image request."); @@ -95,7 +95,7 @@ add_task(function* test() { */ function* hideTooltipAndVerify(toolboxDoc, target) { // Hovering over the "method" column hides the tooltip. - let anchor = target.querySelector(".requests-menu-method"); + let anchor = target.querySelector(".requests-list-method"); let win = anchor.ownerDocument.defaultView; EventUtils.synthesizeMouseAtCenter(anchor, { type: "mousemove" }, win); diff --git a/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js b/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js index e3d5ba4d5632..842efb7df23a 100644 --- a/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js +++ b/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js @@ -35,7 +35,7 @@ add_task(function* () { // Context menu is appending in XUL document, we must select it from // toolbox.doc monitor.toolbox.doc - .querySelector("#request-menu-context-newtab").click(); + .querySelector("#request-list-context-newtab").click(); yield onTabOpen; ok(true, "A new tab has been opened"); diff --git a/devtools/client/netmonitor/test/browser_net_reload-button.js b/devtools/client/netmonitor/test/browser_net_reload-button.js index b42bb42021c1..b8f910f113ff 100644 --- a/devtools/client/netmonitor/test/browser_net_reload-button.js +++ b/devtools/client/netmonitor/test/browser_net_reload-button.js @@ -15,7 +15,7 @@ add_task(function* () { let wait = waitForNetworkEvents(monitor, 1); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-reload-notice-button")); + document.querySelector(".requests-list-reload-notice-button")); yield wait; is(document.querySelectorAll(".request-list-item").length, 1, diff --git a/devtools/client/netmonitor/test/browser_net_reload-markers.js b/devtools/client/netmonitor/test/browser_net_reload-markers.js index 0c6efa53d513..1a7953b9cb01 100644 --- a/devtools/client/netmonitor/test/browser_net_reload-markers.js +++ b/devtools/client/netmonitor/test/browser_net_reload-markers.js @@ -12,8 +12,7 @@ add_task(function* () { info("Starting test... "); let { document, windowRequire } = monitor.panelWin; - let { EVENTS } = windowRequire("devtools/client/netmonitor/constants"); - let button = document.querySelector("#requests-menu-reload-notice-button"); + let button = document.querySelector(".requests-list-reload-notice-button"); button.click(); let markers = []; diff --git a/devtools/client/netmonitor/test/browser_net_security-icon-click.js b/devtools/client/netmonitor/test/browser_net_security-icon-click.js index 15211324c28a..5464e1499e1a 100644 --- a/devtools/client/netmonitor/test/browser_net_security-icon-click.js +++ b/devtools/client/netmonitor/test/browser_net_security-icon-click.js @@ -32,7 +32,7 @@ add_task(function* () { info("Sorting the items by filename."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-file-button")); + document.querySelector("#requests-list-file-button")); info("Testing that security icon can be clicked after the items were sorted."); diff --git a/devtools/client/netmonitor/test/browser_net_security-state.js b/devtools/client/netmonitor/test/browser_net_security-state.js index 27476e2d5449..2d937cf25b72 100644 --- a/devtools/client/netmonitor/test/browser_net_security-state.js +++ b/devtools/client/netmonitor/test/browser_net_security-state.js @@ -29,8 +29,8 @@ add_task(function* () { yield performRequests(); for (let subitemNode of Array.from(document.querySelectorAll( - "requests-menu-subitem.requests-menu-security-and-domain"))) { - let domain = subitemNode.querySelector(".requests-menu-domain").textContent; + "requests-list-subitem.requests-list-security-and-domain"))) { + let domain = subitemNode.querySelector(".requests-list-domain").textContent; info("Found a request to " + domain); ok(domain in EXPECTED_SECURITY_STATES, "Domain " + domain + " was expected."); diff --git a/devtools/client/netmonitor/test/browser_net_simple-request-details.js b/devtools/client/netmonitor/test/browser_net_simple-request-details.js index bdc40585e70b..43d119111096 100644 --- a/devtools/client/netmonitor/test/browser_net_simple-request-details.js +++ b/devtools/client/netmonitor/test/browser_net_simple-request-details.js @@ -245,27 +245,27 @@ add_task(function* () { is(tabEl.getAttribute("selected"), "true", "The timings tab in the network details pane should be selected."); - ok(tabpanel.querySelector("#timings-summary-blocked .requests-menu-timings-total") + ok(tabpanel.querySelector("#timings-summary-blocked .requests-list-timings-total") .getAttribute("value").match(/[0-9]+/), "The blocked timing info does not appear to be correct."); - ok(tabpanel.querySelector("#timings-summary-dns .requests-menu-timings-total") + ok(tabpanel.querySelector("#timings-summary-dns .requests-list-timings-total") .getAttribute("value").match(/[0-9]+/), "The dns timing info does not appear to be correct."); - ok(tabpanel.querySelector("#timings-summary-connect .requests-menu-timings-total") + ok(tabpanel.querySelector("#timings-summary-connect .requests-list-timings-total") .getAttribute("value").match(/[0-9]+/), "The connect timing info does not appear to be correct."); - ok(tabpanel.querySelector("#timings-summary-send .requests-menu-timings-total") + ok(tabpanel.querySelector("#timings-summary-send .requests-list-timings-total") .getAttribute("value").match(/[0-9]+/), "The send timing info does not appear to be correct."); - ok(tabpanel.querySelector("#timings-summary-wait .requests-menu-timings-total") + ok(tabpanel.querySelector("#timings-summary-wait .requests-list-timings-total") .getAttribute("value").match(/[0-9]+/), "The wait timing info does not appear to be correct."); - ok(tabpanel.querySelector("#timings-summary-receive .requests-menu-timings-total") + ok(tabpanel.querySelector("#timings-summary-receive .requests-list-timings-total") .getAttribute("value").match(/[0-9]+/), "The receive timing info does not appear to be correct."); } diff --git a/devtools/client/netmonitor/test/browser_net_simple-request.js b/devtools/client/netmonitor/test/browser_net_simple-request.js index f7051c16ddee..b43a9e9d7495 100644 --- a/devtools/client/netmonitor/test/browser_net_simple-request.js +++ b/devtools/client/netmonitor/test/browser_net_simple-request.js @@ -29,7 +29,7 @@ add_task(function* () { is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"), true, "The pane toggle button should be disabled when the frontend is opened."); - ok(document.querySelector("#requests-menu-empty-notice"), + ok(document.querySelector(".requests-list-empty-notice"), "An empty notice should be displayed when the frontend is opened."); is(gStore.getState().requests.requests.size, 0, "The requests menu should be empty when the frontend is opened."); @@ -41,7 +41,7 @@ add_task(function* () { is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"), false, "The pane toggle button should be enabled after the first request."); - ok(!document.querySelector("#requests-menu-empty-notice"), + ok(!document.querySelector(".requests-list-empty-notice"), "The empty notice should be hidden after the first request."); is(gStore.getState().requests.requests.size, 1, "The requests menu should not be empty after the first request."); @@ -53,7 +53,7 @@ add_task(function* () { is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"), false, "The pane toggle button should be still be enabled after a reload."); - ok(!document.querySelector("#requests-menu-empty-notice"), + ok(!document.querySelector(".requests-list-empty-notice"), "The empty notice should be still hidden after a reload."); is(gStore.getState().requests.requests.size, 1, "The requests menu should not be empty after a reload."); @@ -65,7 +65,7 @@ add_task(function* () { is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"), true, "The pane toggle button should be disabled when after clear."); - ok(document.querySelector("#requests-menu-empty-notice"), + ok(document.querySelector(".requests-list-empty-notice"), "An empty notice should be displayed again after clear."); is(gStore.getState().requests.requests.size, 0, "The requests menu should be empty after clear."); diff --git a/devtools/client/netmonitor/test/browser_net_sort-01.js b/devtools/client/netmonitor/test/browser_net_sort-01.js index 657347af8dda..ab746055ebb6 100644 --- a/devtools/client/netmonitor/test/browser_net_sort-01.js +++ b/devtools/client/netmonitor/test/browser_net_sort-01.js @@ -66,7 +66,7 @@ add_task(function* () { info("Testing status sort, ascending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-status-button")); + document.querySelector("#requests-list-status-button")); testHeaders("status", "ascending"); testContents([0, 1, 2, 3, 4], 0); @@ -81,7 +81,7 @@ add_task(function* () { info("Testing status sort, descending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-status-button")); + document.querySelector("#requests-list-status-button")); testHeaders("status", "descending"); testContents([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 9); @@ -96,13 +96,13 @@ add_task(function* () { info("Testing status sort yet again, ascending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-status-button")); + document.querySelector("#requests-list-status-button")); testHeaders("status", "ascending"); testContents([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 0); info("Testing status sort yet again, descending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-status-button")); + document.querySelector("#requests-list-status-button")); testHeaders("status", "descending"); testContents([14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 14); @@ -110,8 +110,8 @@ add_task(function* () { function testHeaders(sortType, direction) { let doc = monitor.panelWin.document; - let target = doc.querySelector("#requests-menu-" + sortType + "-button"); - let headers = doc.querySelectorAll(".requests-menu-header-button"); + let target = doc.querySelector("#requests-list-" + sortType + "-button"); + let headers = doc.querySelectorAll(".requests-list-header-button"); for (let header of headers) { if (header != target) { diff --git a/devtools/client/netmonitor/test/browser_net_sort-02.js b/devtools/client/netmonitor/test/browser_net_sort-02.js index 3cc2abd46042..83f98b35ee21 100644 --- a/devtools/client/netmonitor/test/browser_net_sort-02.js +++ b/devtools/client/netmonitor/test/browser_net_sort-02.js @@ -66,127 +66,127 @@ add_task(function* () { info("Testing status sort, ascending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-status-button")); + document.querySelector("#requests-list-status-button")); testHeaders("status", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing status sort, descending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-status-button")); + document.querySelector("#requests-list-status-button")); testHeaders("status", "descending"); testContents([4, 3, 2, 1, 0]); info("Testing status sort, ascending. Checking sort loops correctly."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-status-button")); + document.querySelector("#requests-list-status-button")); testHeaders("status", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing method sort, ascending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-method-button")); + document.querySelector("#requests-list-method-button")); testHeaders("method", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing method sort, descending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-method-button")); + document.querySelector("#requests-list-method-button")); testHeaders("method", "descending"); testContents([4, 3, 2, 1, 0]); info("Testing method sort, ascending. Checking sort loops correctly."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-method-button")); + document.querySelector("#requests-list-method-button")); testHeaders("method", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing file sort, ascending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-file-button")); + document.querySelector("#requests-list-file-button")); testHeaders("file", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing file sort, descending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-file-button")); + document.querySelector("#requests-list-file-button")); testHeaders("file", "descending"); testContents([4, 3, 2, 1, 0]); info("Testing file sort, ascending. Checking sort loops correctly."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-file-button")); + document.querySelector("#requests-list-file-button")); testHeaders("file", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing type sort, ascending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-type-button")); + document.querySelector("#requests-list-type-button")); testHeaders("type", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing type sort, descending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-type-button")); + document.querySelector("#requests-list-type-button")); testHeaders("type", "descending"); testContents([4, 3, 2, 1, 0]); info("Testing type sort, ascending. Checking sort loops correctly."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-type-button")); + document.querySelector("#requests-list-type-button")); testHeaders("type", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing transferred sort, ascending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-transferred-button")); + document.querySelector("#requests-list-transferred-button")); testHeaders("transferred", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing transferred sort, descending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-transferred-button")); + document.querySelector("#requests-list-transferred-button")); testHeaders("transferred", "descending"); testContents([4, 3, 2, 1, 0]); info("Testing transferred sort, ascending. Checking sort loops correctly."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-transferred-button")); + document.querySelector("#requests-list-transferred-button")); testHeaders("transferred", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing size sort, ascending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-size-button")); + document.querySelector("#requests-list-size-button")); testHeaders("size", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing size sort, descending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-size-button")); + document.querySelector("#requests-list-size-button")); testHeaders("size", "descending"); testContents([4, 3, 2, 1, 0]); info("Testing size sort, ascending. Checking sort loops correctly."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-size-button")); + document.querySelector("#requests-list-size-button")); testHeaders("size", "ascending"); testContents([0, 1, 2, 3, 4]); info("Testing waterfall sort, ascending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-waterfall-button")); + document.querySelector("#requests-list-waterfall-button")); testHeaders("waterfall", "ascending"); testContents([0, 2, 4, 3, 1]); info("Testing waterfall sort, descending."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-waterfall-button")); + document.querySelector("#requests-list-waterfall-button")); testHeaders("waterfall", "descending"); testContents([4, 2, 0, 1, 3]); info("Testing waterfall sort, ascending. Checking sort loops correctly."); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-waterfall-button")); + document.querySelector("#requests-list-waterfall-button")); testHeaders("waterfall", "ascending"); testContents([0, 2, 4, 3, 1]); @@ -201,8 +201,8 @@ add_task(function* () { function testHeaders(sortType, direction) { let doc = monitor.panelWin.document; - let target = doc.querySelector("#requests-menu-" + sortType + "-button"); - let headers = doc.querySelectorAll(".requests-menu-header-button"); + let target = doc.querySelector("#requests-list-" + sortType + "-button"); + let headers = doc.querySelectorAll(".requests-list-header-button"); for (let header of headers) { if (header != target) { diff --git a/devtools/client/netmonitor/test/browser_net_statistics-02.js b/devtools/client/netmonitor/test/browser_net_statistics-02.js index 10a3988e9a45..65d1e8b06a74 100644 --- a/devtools/client/netmonitor/test/browser_net_statistics-02.js +++ b/devtools/client/netmonitor/test/browser_net_statistics-02.js @@ -17,15 +17,15 @@ add_task(function* () { let Actions = windowRequire("devtools/client/netmonitor/actions/index"); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-html-button")); + document.querySelector(".requests-list-filter-html-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-css-button")); + document.querySelector(".requests-list-filter-css-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-js-button")); + document.querySelector(".requests-list-filter-js-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-ws-button")); + document.querySelector(".requests-list-filter-ws-button")); EventUtils.sendMouseEvent({ type: "click" }, - document.querySelector("#requests-menu-filter-other-button")); + document.querySelector(".requests-list-filter-other-button")); testFilterButtonsCustom(monitor, [0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1]); info("The correct filtering predicates are used before entering perf. analysis mode."); diff --git a/devtools/client/netmonitor/test/browser_net_status-codes.js b/devtools/client/netmonitor/test/browser_net_status-codes.js index aca5119e7be8..088e69337de3 100644 --- a/devtools/client/netmonitor/test/browser_net_status-codes.js +++ b/devtools/client/netmonitor/test/browser_net_status-codes.js @@ -165,7 +165,7 @@ add_task(function* () { is(summaryValues[0].value, uri, "The url summary value is incorrect."); is(summaryValues[1].value, method, "The method summary value is incorrect."); - is(panel.querySelector(".requests-menu-status-icon").dataset.code, status, + is(panel.querySelector(".requests-list-status-icon").dataset.code, status, "The status summary code is incorrect."); is(summaryValues[3].value, status + " " + statusText, "The status summary value is incorrect."); diff --git a/devtools/client/netmonitor/test/browser_net_timeline_ticks.js b/devtools/client/netmonitor/test/browser_net_timeline_ticks.js index 14bfd046e87e..7a15bf9e765f 100644 --- a/devtools/client/netmonitor/test/browser_net_timeline_ticks.js +++ b/devtools/client/netmonitor/test/browser_net_timeline_ticks.js @@ -19,14 +19,14 @@ add_task(function* () { // Disable transferred size column support for this test. // Without this, the waterfall only has enough room for one division, which // would remove most of the value of this test. - // $("#requests-menu-transferred-header-box").hidden = true; - // $("#requests-menu-item-template .requests-menu-transferred").hidden = true; + // $("#requests-list-transferred-header-box").hidden = true; + // $("#requests-list-item-template .requests-list-transferred").hidden = true; RequestsMenu.lazyUpdate = false; - ok($("#requests-menu-waterfall-label"), + ok($("#requests-list-waterfall-label"), "An timeline label should be displayed when the frontend is opened."); - ok($all(".requests-menu-timings-division").length == 0, + ok($all(".requests-list-timings-division").length == 0, "No tick labels should be displayed when the frontend is opened."); ok(!RequestsMenu._canvas, "No canvas should be created when the frontend is opened."); @@ -41,12 +41,12 @@ add_task(function* () { NetMonitorController.NetworkEventsHandler.clearMarkers(); RequestsMenu._flushWaterfallViews(true); - ok(!$("#requests-menu-waterfall-label"), + ok(!$("#requests-list-waterfall-label"), "The timeline label should be hidden after the first request."); - ok($all(".requests-menu-timings-division").length >= 3, + ok($all(".requests-list-timings-division").length >= 3, "There should be at least 3 tick labels in the network requests header."); - let timingDivisionEls = $all(".requests-menu-timings-division"); + let timingDivisionEls = $all(".requests-list-timings-division"); is(timingDivisionEls[0].textContent, L10N.getFormatStr("networkMenu.millisecond", 0), "The first tick label has correct value"); is(timingDivisionEls[1].textContent, L10N.getFormatStr("networkMenu.millisecond", 80), diff --git a/devtools/client/netmonitor/test/browser_net_timing-division.js b/devtools/client/netmonitor/test/browser_net_timing-division.js index 1d9cf182e912..d6b174ba4e66 100644 --- a/devtools/client/netmonitor/test/browser_net_timing-division.js +++ b/devtools/client/netmonitor/test/browser_net_timing-division.js @@ -25,11 +25,11 @@ add_task(function* () { yield wait; let milDivs = document.querySelectorAll( - ".requests-menu-timings-division[data-division-scale=millisecond]"); + ".requests-list-timings-division[data-division-scale=millisecond]"); let secDivs = document.querySelectorAll( - ".requests-menu-timings-division[data-division-scale=second]"); + ".requests-list-timings-division[data-division-scale=second]"); let minDivs = document.querySelectorAll( - ".requests-menu-timings-division[data-division-scale=minute]"); + ".requests-list-timings-division[data-division-scale=minute]"); info("Number of millisecond divisions: " + milDivs.length); info("Number of second divisions: " + secDivs.length); diff --git a/devtools/client/netmonitor/test/components/filter-buttons.test.js b/devtools/client/netmonitor/test/components/filter-buttons.test.js index 557b3c9afeac..65081ffeb75c 100644 --- a/devtools/client/netmonitor/test/components/filter-buttons.test.js +++ b/devtools/client/netmonitor/test/components/filter-buttons.test.js @@ -94,8 +94,8 @@ function asExpected(wrapper, expectTypes, description) { let className = expectTypes[type] ? "devtools-button checked" : "devtools-button"; it(`'${type}' button is ${checked} ${description}`, () => { - expect(wrapper.find(`#requests-menu-filter-${type}-button`).html()) - .toBe(``); }); } diff --git a/devtools/client/netmonitor/test/head.js b/devtools/client/netmonitor/test/head.js index 29a75bb32fba..13585517ca28 100644 --- a/devtools/client/netmonitor/test/head.js +++ b/devtools/client/netmonitor/test/head.js @@ -275,32 +275,32 @@ function verifyRequestItemTarget(document, requestList, requestItem, aMethod, is(requestItem.url, aUrl, "The attached url is correct."); } - is(target.querySelector(".requests-menu-method").textContent, + is(target.querySelector(".requests-list-method").textContent, aMethod, "The displayed method is correct."); if (fuzzyUrl) { - ok(target.querySelector(".requests-menu-file").textContent.startsWith( + ok(target.querySelector(".requests-list-file").textContent.startsWith( name + (query ? "?" + query : "")), "The displayed file is correct."); - ok(target.querySelector(".requests-menu-file").getAttribute("title").startsWith(unicodeUrl), + ok(target.querySelector(".requests-list-file").getAttribute("title").startsWith(unicodeUrl), "The tooltip file is correct."); } else { - is(target.querySelector(".requests-menu-file").textContent, + is(target.querySelector(".requests-list-file").textContent, name + (query ? "?" + query : ""), "The displayed file is correct."); - is(target.querySelector(".requests-menu-file").getAttribute("title"), + is(target.querySelector(".requests-list-file").getAttribute("title"), unicodeUrl, "The tooltip file is correct."); } - is(target.querySelector(".requests-menu-domain").textContent, + is(target.querySelector(".requests-list-domain").textContent, hostPort, "The displayed domain is correct."); let domainTooltip = hostPort + (remoteAddress ? " (" + remoteAddress + ")" : ""); - is(target.querySelector(".requests-menu-domain").getAttribute("title"), + is(target.querySelector(".requests-list-domain").getAttribute("title"), domainTooltip, "The tooltip domain is correct."); if (status !== undefined) { - let value = target.querySelector(".requests-menu-status-icon").getAttribute("data-code"); - let codeValue = target.querySelector(".requests-menu-status-code").textContent; - let tooltip = target.querySelector(".requests-menu-status").getAttribute("title"); + let value = target.querySelector(".requests-list-status-icon").getAttribute("data-code"); + let codeValue = target.querySelector(".requests-list-status-code").textContent; + let tooltip = target.querySelector(".requests-list-status").getAttribute("title"); info("Displayed status: " + value); info("Displayed code: " + codeValue); info("Tooltip status: " + tooltip); @@ -309,40 +309,40 @@ function verifyRequestItemTarget(document, requestList, requestItem, aMethod, is(tooltip, status + " " + statusText, "The tooltip status is correct."); } if (cause !== undefined) { - let value = target.querySelector(".requests-menu-cause > .subitem-label").textContent; - let tooltip = target.querySelector(".requests-menu-cause").getAttribute("title"); + let value = target.querySelector(".requests-list-cause > .subitem-label").textContent; + let tooltip = target.querySelector(".requests-list-cause").getAttribute("title"); info("Displayed cause: " + value); info("Tooltip cause: " + tooltip); is(value, cause.type, "The displayed cause is correct."); is(tooltip, cause.loadingDocumentUri, "The tooltip cause is correct.") } if (type !== undefined) { - let value = target.querySelector(".requests-menu-type").textContent; - let tooltip = target.querySelector(".requests-menu-type").getAttribute("title"); + let value = target.querySelector(".requests-list-type").textContent; + let tooltip = target.querySelector(".requests-list-type").getAttribute("title"); info("Displayed type: " + value); info("Tooltip type: " + tooltip); is(value, type, "The displayed type is correct."); is(tooltip, fullMimeType, "The tooltip type is correct."); } if (transferred !== undefined) { - let value = target.querySelector(".requests-menu-transferred").textContent; - let tooltip = target.querySelector(".requests-menu-transferred").getAttribute("title"); + let value = target.querySelector(".requests-list-transferred").textContent; + let tooltip = target.querySelector(".requests-list-transferred").getAttribute("title"); info("Displayed transferred size: " + value); info("Tooltip transferred size: " + tooltip); is(value, transferred, "The displayed transferred size is correct."); is(tooltip, transferred, "The tooltip transferred size is correct."); } if (size !== undefined) { - let value = target.querySelector(".requests-menu-size").textContent; - let tooltip = target.querySelector(".requests-menu-size").getAttribute("title"); + let value = target.querySelector(".requests-list-size").textContent; + let tooltip = target.querySelector(".requests-list-size").getAttribute("title"); info("Displayed size: " + value); info("Tooltip size: " + tooltip); is(value, size, "The displayed size is correct."); is(tooltip, size, "The tooltip size is correct."); } if (time !== undefined) { - let value = target.querySelector(".requests-menu-timings-total").textContent; - let tooltip = target.querySelector(".requests-menu-timings-total").getAttribute("title"); + let value = target.querySelector(".requests-list-timings-total").textContent; + let tooltip = target.querySelector(".requests-list-timings-total").getAttribute("title"); info("Displayed time: " + value); info("Tooltip time: " + tooltip); ok(~~(value.match(/[0-9]+/)) >= 0, "The displayed time is correct."); @@ -385,9 +385,9 @@ function waitFor(subject, eventName) { */ function testFilterButtons(monitor, filterType) { let doc = monitor.panelWin.document; - let target = doc.querySelector("#requests-menu-filter-" + filterType + "-button"); + let target = doc.querySelector(".requests-list-filter-" + filterType + "-button"); ok(target, `Filter button '${filterType}' was found`); - let buttons = [...doc.querySelectorAll("#requests-menu-filter-buttons button")]; + let buttons = [...doc.querySelectorAll("#requests-list-filter-buttons button")]; ok(buttons.length > 0, "More than zero filter buttons were found"); // Only target should be checked. @@ -405,7 +405,7 @@ function testFilterButtons(monitor, filterType) { */ function testFilterButtonsCustom(aMonitor, aIsChecked) { let doc = aMonitor.panelWin.document; - let buttons = doc.querySelectorAll("#requests-menu-filter-buttons button"); + let buttons = doc.querySelectorAll("#requests-list-filter-buttons button"); for (let i = 0; i < aIsChecked.length; i++) { let button = buttons[i]; if (aIsChecked[i]) { diff --git a/devtools/client/themes/netmonitor.css b/devtools/client/themes/netmonitor.css index 250bdc46dc6f..afd9b8fe8773 100644 --- a/devtools/client/themes/netmonitor.css +++ b/devtools/client/themes/netmonitor.css @@ -11,7 +11,7 @@ box-sizing: border-box; } -#toolbar-labels { +.toolbar-labels { overflow: hidden; display: flex; flex: auto; @@ -96,22 +96,22 @@ font-size: 120%; } -#notice-perf-message { +.notice-perf-message { margin-top: 2px; } -#requests-menu-perf-notice-button { +.requests-list-perf-notice-button { min-width: 30px; min-height: 26px; margin: 0 5px; vertical-align: middle; } -#requests-menu-perf-notice-button::before { +.requests-list-perf-notice-button::before { background-image: url(images/profiler-stopwatch.svg); } -#requests-menu-reload-notice-button { +.requests-list-reload-notice-button { font-size: inherit; min-height: 26px; margin: 0 5px; @@ -119,21 +119,21 @@ /* Network requests table */ -#requests-menu-toolbar { +.requests-list-toolbar { display: flex; padding: 0; } -#requests-menu-filter-buttons { +.requests-list-filter-buttons { display: flex; flex-wrap: nowrap; } -.theme-firebug #requests-menu-toolbar { +.theme-firebug .requests-list-toolbar { height: 19px !important; } -.requests-menu-contents { +.requests-list-contents { display: flex; flex-direction: column; overflow-x: hidden; @@ -142,7 +142,7 @@ --timings-rev-scale: 1; } -.requests-menu-subitem { +.requests-list-subitem { display: flex; flex: none; box-sizing: border-box; @@ -157,12 +157,12 @@ text-overflow: ellipsis; } -.requests-menu-header { +.requests-list-header { display: flex; flex: none; } -.requests-menu-header-button { +.requests-list-header-button { display: flex; align-items: center; flex: auto; @@ -187,27 +187,27 @@ font-weight: inherit !important; } -.requests-menu-header-button::-moz-focus-inner { +.requests-list-header-button::-moz-focus-inner { border: 0; padding: 0; } -.requests-menu-header:first-child .requests-menu-header-button { +.requests-list-header:first-child .requests-list-header-button { border-width: 0; } -.requests-menu-header-button:hover { +.requests-list-header-button:hover { background-color: rgba(0, 0, 0, 0.1); } -.requests-menu-header-button > .button-text { +.requests-list-header-button > .button-text { flex: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.requests-menu-header-button > .button-icon { +.requests-list-header-button > .button-icon { flex: none; height: 4px; margin-inline-start: 3px; @@ -215,32 +215,32 @@ width: 7px; } -.requests-menu-header-button[data-sorted=ascending] > .button-icon { +.requests-list-header-button[data-sorted=ascending] > .button-icon { background-image: var(--sort-ascending-image); } -.requests-menu-header-button[data-sorted=descending] > .button-icon { +.requests-list-header-button[data-sorted=descending] > .button-icon { background-image: var(--sort-descending-image); } -.requests-menu-waterfall-label-wrapper { +.requests-list-waterfall-label-wrapper { display: flex; } -.requests-menu-header-button[data-sorted], -.requests-menu-header-button[data-sorted]:hover { +.requests-list-header-button[data-sorted], +.requests-list-header-button[data-sorted]:hover { background-color: var(--theme-selection-background); color: var(--theme-selection-color); } -.requests-menu-header-button[data-sorted], -.requests-menu-header[data-active] + .requests-menu-header .requests-menu-header-button { +.requests-list-header-button[data-sorted], +.requests-list-header[data-active] + .requests-list-header .requests-list-header-button { border-image: linear-gradient(var(--theme-splitter-color), var(--theme-splitter-color)) 1 1; } /* Firebug theme support for Network panel header */ -.theme-firebug .requests-menu-header { +.theme-firebug .requests-list-header { padding: 0 !important; font-weight: bold; background: linear-gradient(rgba(255, 255, 255, 0.05), @@ -248,24 +248,24 @@ #C8D2DC; } -.theme-firebug .requests-menu-header-button { +.theme-firebug .requests-list-header-button { min-height: 17px; } -.theme-firebug .requests-menu-header-button > .button-icon { +.theme-firebug .requests-list-header-button > .button-icon { height: 7px; } -.theme-firebug .requests-menu-header-button[data-sorted] { +.theme-firebug .requests-list-header-button[data-sorted] { background-color: #AAC3DC; } -:root[platform="linux"].theme-firebug .requests-menu-header-button[data-sorted] { +:root[platform="linux"].theme-firebug .requests-list-header-button[data-sorted] { background-color: #FAC8AF !important; color: inherit !important; } -.theme-firebug .requests-menu-header:hover:active { +.theme-firebug .requests-list-header:hover:active { background-image: linear-gradient(rgba(0, 0, 0, 0.1), transparent); } @@ -273,35 +273,35 @@ /* Network requests table: specific column dimensions */ -.requests-menu-status { +.requests-list-status { max-width: 6em; text-align: center; width: 10vw; } -.requests-menu-method, -.requests-menu-method-box { +.requests-list-method, +.requests-list-method-box { max-width: 7em; text-align: center; width: 10vw; } -.requests-menu-icon-and-file { +.requests-list-icon-and-file { width: 22vw; } -.requests-menu-icon { +.requests-list-icon { background: transparent; width: 15px; height: 15px; margin-inline-end: 4px; } -.requests-menu-icon { +.requests-list-icon { outline: 1px solid var(--table-splitter-color); } -.requests-menu-security-and-domain { +.requests-list-security-and-domain { width: 14vw; } @@ -336,25 +336,25 @@ background-image: url(chrome://devtools/skin/images/globe.svg); } -.requests-menu-type, -.requests-menu-size { +.requests-list-type, +.requests-list-size { max-width: 6em; width: 8vw; justify-content: center; } -.requests-menu-transferred { +.requests-list-transferred { max-width: 8em; width: 8vw; justify-content: center; } -.requests-menu-cause { +.requests-list-cause { max-width: 8em; width: 8vw; } -.requests-menu-cause-stack { +.requests-list-cause-stack { background-color: var(--theme-body-color-alt); color: var(--theme-body-background); font-size: 8px; @@ -367,19 +367,19 @@ -moz-user-select: none; } -.request-list-item.selected .requests-menu-transferred.theme-comment { +.request-list-item.selected .requests-list-transferred.theme-comment { color: var(--theme-selection-color); } /* Network requests table: status codes */ -.requests-menu-status-code { +.requests-list-status-code { margin-inline-start: 3px !important; width: 3em; margin-inline-end: -3em !important; } -.requests-menu-status-icon { +.requests-list-status-icon { background: #fff; height: 10px; width: 10px; @@ -390,29 +390,29 @@ box-sizing: border-box; } -.request-list-item.selected .requests-menu-status-icon { +.request-list-item.selected .requests-list-status-icon { filter: brightness(1.3); } -.requests-menu-status-icon:not([data-code]) { +.requests-list-status-icon:not([data-code]) { background-color: var(--theme-content-color2); } -.requests-menu-status-icon[data-code="cached"] { +.requests-list-status-icon[data-code="cached"] { border: 2px solid var(--theme-content-color2); background-color: transparent; } -.requests-menu-status-icon[data-code^="1"] { +.requests-list-status-icon[data-code^="1"] { background-color: var(--theme-highlight-blue); } -.requests-menu-status-icon[data-code^="2"] { +.requests-list-status-icon[data-code^="2"] { background-color: var(--theme-highlight-green); } /* 3xx are triangles */ -.requests-menu-status-icon[data-code^="3"] { +.requests-list-status-icon[data-code^="3"] { background-color: transparent; width: 0; height: 0; @@ -423,12 +423,12 @@ } /* 4xx and 5xx are squares - error codes */ -.requests-menu-status-icon[data-code^="4"] { +.requests-list-status-icon[data-code^="4"] { background-color: var(--theme-highlight-red); border-radius: 0; /* squares */ } -.requests-menu-status-icon[data-code^="5"] { +.requests-list-status-icon[data-code^="5"] { background-color: var(--theme-highlight-pink); border-radius: 0; transform: rotate(45deg); @@ -436,16 +436,16 @@ /* Network requests table: waterfall header */ -.requests-menu-waterfall { +.requests-list-waterfall { flex: auto; padding-inline-start: 0; } -.requests-menu-waterfall-label-wrapper:not(.requests-menu-waterfall-visible) { +.requests-list-waterfall-label-wrapper:not(.requests-list-waterfall-visible) { padding-inline-start: 16px; } -.requests-menu-timings-division { +.requests-list-timings-division { padding-top: 2px; padding-inline-start: 4px; font-size: 75%; @@ -457,34 +457,34 @@ flex: initial; } -.requests-menu-timings-division:not(:first-child) { +.requests-list-timings-division:not(:first-child) { border-inline-start: 1px dashed; } -.requests-menu-timings-division:-moz-locale-dir(ltr) { +.requests-list-timings-division:-moz-locale-dir(ltr) { transform-origin: left center; } -.requests-menu-timings-division:-moz-locale-dir(rtl) { +.requests-list-timings-division:-moz-locale-dir(rtl) { transform-origin: right center; } -.theme-dark .requests-menu-timings-division { +.theme-dark .requests-list-timings-division { border-inline-start-color: #5a6169 !important; } -.theme-light .requests-menu-timings-division { +.theme-light .requests-list-timings-division { border-inline-start-color: #585959 !important; } -.requests-menu-timings-division[data-division-scale=second], -.requests-menu-timings-division[data-division-scale=minute] { +.requests-list-timings-division[data-division-scale=second], +.requests-list-timings-division[data-division-scale=minute] { font-weight: 600; } /* Network requests table: waterfall items */ -.requests-menu-subitem.requests-menu-waterfall { +.requests-list-subitem.requests-list-waterfall { padding-inline-start: 0; padding-inline-end: 4px; /* Background created on a in js. */ @@ -494,34 +494,34 @@ background-position: left center; } -.requests-menu-subitem.requests-menu-waterfall:-moz-locale-dir(rtl) { +.requests-list-subitem.requests-list-waterfall:-moz-locale-dir(rtl) { background-position: right center; } -.requests-menu-timings { +.requests-list-timings { display: flex; flex: none; align-items: center; transform: scaleX(var(--timings-scale)); } -.requests-menu-timings:-moz-locale-dir(ltr) { +.requests-list-timings:-moz-locale-dir(ltr) { transform-origin: left center; } -.requests-menu-timings:-moz-locale-dir(rtl) { +.requests-list-timings:-moz-locale-dir(rtl) { transform-origin: right center; } -.requests-menu-timings-total:-moz-locale-dir(ltr) { +.requests-list-timings-total:-moz-locale-dir(ltr) { transform-origin: left center; } -.requests-menu-timings-total:-moz-locale-dir(rtl) { +.requests-list-timings-total:-moz-locale-dir(rtl) { transform-origin: right center; } -.requests-menu-timings-total { +.requests-list-timings-total { display: inline-block; padding-inline-start: 4px; font-size: 85%; @@ -531,37 +531,37 @@ transform: scaleX(var(--timings-rev-scale)); } -.requests-menu-timings-box { +.requests-list-timings-box { display: inline-block; height: 9px; } -.theme-firebug .requests-menu-timings-box { +.theme-firebug .requests-list-timings-box { background-image: linear-gradient(rgba(255, 255, 255, 0.3), rgba(0, 0, 0, 0.2)); height: 16px; } -.requests-menu-timings-box.blocked { +.requests-list-timings-box.blocked { background-color: var(--timing-blocked-color); } -.requests-menu-timings-box.dns { +.requests-list-timings-box.dns { background-color: var(--timing-dns-color); } -.requests-menu-timings-box.connect { +.requests-list-timings-box.connect { background-color: var(--timing-connect-color); } -.requests-menu-timings-box.send { +.requests-list-timings-box.send { background-color: var(--timing-send-color); } -.requests-menu-timings-box.wait { +.requests-list-timings-box.wait { background-color: var(--timing-wait-color); } -.requests-menu-timings-box.receive { +.requests-list-timings-box.receive { background-color: var(--timing-receive-color); } @@ -595,27 +595,27 @@ background: #EFEFEF; } -.theme-firebug .requests-menu-subitem { +.theme-firebug .requests-list-subitem { padding: 1px; } /* HTTP Status Column */ -.theme-firebug .requests-menu-subitem.requests-menu-status { +.theme-firebug .requests-list-subitem.requests-list-status { font-weight: bold; } /* Method Column */ -.theme-firebug .requests-menu-subitem.requests-menu-method-box { +.theme-firebug .requests-list-subitem.requests-list-method-box { color: rgb(128, 128, 128); } -.request-list-item.selected .requests-menu-method { +.request-list-item.selected .requests-list-method { color: var(--theme-selection-color); } /* Size Column */ -.theme-firebug .requests-menu-subitem.requests-menu-size { +.theme-firebug .requests-list-subitem.requests-list-size { justify-content: end; padding-inline-end: 4px; } @@ -706,23 +706,23 @@ width: 10em; } -.requests-menu-timings-container { +.requests-list-timings-container { display: flex; flex: 1; align-items: center; } -.requests-menu-timings-offset { +.requests-list-timings-offset { transition: width 0.2s ease-out; } -.requests-menu-timings-box { +.requests-list-timings-box { border: none; min-width: 1px; transition: width 0.2s ease-out; } -.theme-firebug .requests-menu-timings-total { +.theme-firebug .requests-list-timings-total { color: var(--theme-body-color); } @@ -805,7 +805,7 @@ /* Performance analysis buttons */ -#requests-menu-network-summary-button { +.requests-list-network-summary-button { display: flex; flex-wrap: nowrap; align-items: center; @@ -818,7 +818,7 @@ min-width: 0; } -#requests-menu-network-summary-button > .summary-info-icon { +.requests-list-network-summary-button > .summary-info-icon { background-image: url(images/profiler-stopwatch.svg); filter: var(--icon-filter); width: 16px; @@ -826,13 +826,13 @@ opacity: 0.8; } -#requests-menu-network-summary-button > .summary-info-text { +.requests-list-network-summary-button > .summary-info-text { opacity: 0.8; margin-inline-start: 0.5em; } -#requests-menu-network-summary-button:hover > .summary-info-icon, -#requests-menu-network-summary-button:hover > .summary-info-text { +.requests-list-network-summary-button:hover > .summary-info-icon, +.requests-list-network-summary-button:hover > .summary-info-text { opacity: 1; } @@ -1010,51 +1010,51 @@ @media (max-width: 700px) { #toolbar-spacer, .network-details-panel-toggle, - #requests-menu-network-summary-button > .summary-info-text { + .requests-list-network-summary-button > .summary-info-text { display: none; } - #requests-menu-toolbar { + .requests-list-toolbar { height: 22px; } - .requests-menu-header-button { + .requests-list-header-button { min-height: 22px; padding-left: 8px; } - .requests-menu-status { + .requests-list-status { max-width: none; width: 10vw; } - .requests-menu-status-code { + .requests-list-status-code { width: auto; } - .requests-menu-method, - .requests-menu-method-box { + .requests-list-method, + .requests-list-method-box { max-width: none; width: 12vw; } - .requests-menu-icon-and-file { + .requests-list-icon-and-file { width: 22vw; } - .requests-menu-security-and-domain { + .requests-list-security-and-domain { width: 16vw; } - .requests-menu-cause, - .requests-menu-type, - .requests-menu-transferred, - .requests-menu-size { + .requests-list-cause, + .requests-list-type, + .requests-list-transferred, + .requests-list-size { max-width: none; width: 10vw; } - .requests-menu-waterfall { + .requests-list-waterfall { display: none; } @@ -1071,11 +1071,11 @@ } /* Platform overrides (copied in from the old platform specific files) */ -:root[platform="win"] .requests-menu-header-button > .button-box { +:root[platform="win"] .requests-list-header-button > .button-box { padding: 0; } -:root[platform="win"] .requests-menu-timings-division { +:root[platform="win"] .requests-list-timings-division { padding-top: 1px; font-size: 90%; } @@ -1090,7 +1090,7 @@ /* Responsive sidebar */ @media (max-width: 700px) { - :root[platform="linux"] .requests-menu-header-button { + :root[platform="linux"] .requests-list-header-button { font-size: 85%; } } @@ -1233,7 +1233,7 @@ margin-inline-end: 6px; } -.headers-summary .requests-menu-status-icon { +.headers-summary .requests-list-status-icon { min-width: 10px; } diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 042f5b7287d0..3e79f93d5d9f 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -388,20 +388,23 @@ nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const elem = elem->GetParent(); } while(elem); - // Now resolve against all xml:base attrs - for (uint32_t i = baseAttrs.Length() - 1; i != uint32_t(-1); --i) { - nsCOMPtr newBase; - nsresult rv = NS_NewURI(getter_AddRefs(newBase), baseAttrs[i], - doc->GetDocumentCharacterSet().get(), base); - // Do a security check, almost the same as nsDocument::SetBaseURL() - // Only need to do this on the final uri - if (NS_SUCCEEDED(rv) && i == 0) { - rv = nsContentUtils::GetSecurityManager()-> - CheckLoadURIWithPrincipal(NodePrincipal(), newBase, - nsIScriptSecurityManager::STANDARD); - } - if (NS_SUCCEEDED(rv)) { - base.swap(newBase); + if (!baseAttrs.IsEmpty()) { + doc->WarnOnceAbout(nsIDocument::eXMLBaseAttribute); + // Now resolve against all xml:base attrs + for (uint32_t i = baseAttrs.Length() - 1; i != uint32_t(-1); --i) { + nsCOMPtr newBase; + nsresult rv = NS_NewURI(getter_AddRefs(newBase), baseAttrs[i], + doc->GetDocumentCharacterSet().get(), base); + // Do a security check, almost the same as nsDocument::SetBaseURL() + // Only need to do this on the final uri + if (NS_SUCCEEDED(rv) && i == 0) { + rv = nsContentUtils::GetSecurityManager()-> + CheckLoadURIWithPrincipal(NodePrincipal(), newBase, + nsIScriptSecurityManager::STANDARD); + } + if (NS_SUCCEEDED(rv)) { + base.swap(newBase); + } } } diff --git a/dom/base/nsDeprecatedOperationList.h b/dom/base/nsDeprecatedOperationList.h index b326403e77e3..ae2530f41e6e 100644 --- a/dom/base/nsDeprecatedOperationList.h +++ b/dom/base/nsDeprecatedOperationList.h @@ -51,3 +51,4 @@ DEPRECATED_OPERATION(LenientSetter) DEPRECATED_OPERATION(FileLastModifiedDate) DEPRECATED_OPERATION(ImageBitmapRenderingContext_TransferImageBitmap) DEPRECATED_OPERATION(URLCreateObjectURL_MediaStream) +DEPRECATED_OPERATION(XMLBaseAttribute) diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 159e942b2348..33395f6399c2 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1473,9 +1473,9 @@ nsIDocument::~nsIDocument() } bool -nsDocument::IsAboutPage() +nsDocument::IsAboutPage() const { - nsCOMPtr principal = GetPrincipal(); + nsCOMPtr principal = NodePrincipal(); nsCOMPtr uri; principal->GetURI(getter_AddRefs(uri)); bool isAboutScheme = true; @@ -10148,7 +10148,13 @@ nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation, return; } mDeprecationWarnedAbout[aOperation] = true; - const_cast(this)->SetDocumentAndPageUseCounter(OperationToUseCounter(aOperation)); + // Don't count deprecated operations for about pages since those pages + // are almost in our control, and we always need to remove uses there + // before we remove the operation itself anyway. + if (!static_cast(this)->IsAboutPage()) { + const_cast(this)-> + SetDocumentAndPageUseCounter(OperationToUseCounter(aOperation)); + } uint32_t flags = asError ? nsIScriptError::errorFlag : nsIScriptError::warningFlag; nsContentUtils::ReportToConsole(flags, diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 5272e9616b2d..c9c5342f9ee9 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -1527,7 +1527,7 @@ private: void ClearAllBoxObjects(); // Returns true if the scheme for the url for this document is "about" - bool IsAboutPage(); + bool IsAboutPage() const; // These are not implemented and not supported. nsDocument(const nsDocument& aOther); diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index f71c20ec0608..218fcb5da0ab 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -323,3 +323,5 @@ GeolocationInsecureRequestIsForbidden=A Geolocation request can only be fulfille LargeAllocationNonWin32=This page would be loaded in a new process due to a Large-Allocation header, however Large-Allocation process creation is disabled on non-Win32 platforms. # LOCALIZATION NOTE: Do not translate URL.createObjectURL(MediaStream). URLCreateObjectURL_MediaStream=URL.createObjectURL(MediaStream) is deprecated and will be removed soon. +# LOCALIZATION NOTE: Do not translate xml:base. +XMLBaseAttributeWarning=Use of xml:base attribute is deprecated and will be removed soon. Please remove any use of it. diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index dae5e3c69aa2..89ae656554b7 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -310,6 +310,66 @@ LocalAllocPolicy::Cancel() mTokenRequest.DisconnectIfExists(); } +/** + * This class tracks shutdown promises to ensure all decoders are shut down + * completely before MFR continues the rest of the shutdown procedure. + */ +class MediaFormatReader::ShutdownPromisePool +{ +public: + ShutdownPromisePool() + : mOnShutdownComplete(new ShutdownPromise::Private(__func__)) + { + } + + // Return a promise which will be resolved when all the tracking promises + // are resolved. Note no more promises should be added for tracking once + // this function is called. + RefPtr Shutdown(); + + // Track a shutdown promise. + void Track(RefPtr aPromise); + + // Shut down a decoder and track its shutdown promise. + void ShutdownDecoder(already_AddRefed aDecoder) + { + Track(RefPtr(aDecoder)->Shutdown()); + } + +private: + bool mShutdown = false; + const RefPtr mOnShutdownComplete; + nsTHashtable> mPromises; +}; + +RefPtr +MediaFormatReader::ShutdownPromisePool::Shutdown() +{ + MOZ_DIAGNOSTIC_ASSERT(!mShutdown); + mShutdown = true; + if (mPromises.Count() == 0) { + mOnShutdownComplete->Resolve(true, __func__); + } + return mOnShutdownComplete; +} + +void +MediaFormatReader::ShutdownPromisePool::Track(RefPtr aPromise) +{ + MOZ_DIAGNOSTIC_ASSERT(!mShutdown); + MOZ_DIAGNOSTIC_ASSERT(!mPromises.Contains(aPromise)); + mPromises.PutEntry(aPromise); + aPromise->Then( + AbstractThread::GetCurrent(), __func__, + [aPromise, this]() { + MOZ_DIAGNOSTIC_ASSERT(mPromises.Contains(aPromise)); + mPromises.RemoveEntry(aPromise); + if (mShutdown && mPromises.Count() == 0) { + mOnShutdownComplete->Resolve(true, __func__); + } + }); +} + class MediaFormatReader::DecoderFactory { using InitPromise = MediaDataDecoder::InitPromise; @@ -323,8 +383,10 @@ public: , mOwner(WrapNotNull(aOwner)) { } void CreateDecoder(TrackType aTrack); - // Shutdown any decoder pending initialization. - RefPtr ShutdownDecoder(TrackType aTrack) + + // Shutdown any decoder pending initialization and reset mAudio/mVideo to its + // pristine state so CreateDecoder() is ready to be called again immediately. + void ShutdownDecoder(TrackType aTrack) { MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack || aTrack == TrackInfo::kVideoTrack); @@ -332,18 +394,11 @@ public: data.mPolicy->Cancel(); data.mTokenRequest.DisconnectIfExists(); data.mInitRequest.DisconnectIfExists(); - if (!data.mDecoder) { - return ShutdownPromise::CreateAndResolve(true, __func__); + if (data.mDecoder) { + mOwner->mShutdownPromisePool->ShutdownDecoder(data.mDecoder.forget()); } - if (data.mShutdownRequest.Exists()) { - // A shutdown is already in progress due to a prior initialization error, - // return the existing promise. - data.mShutdownRequest.Disconnect(); - RefPtr p = data.mShutdownPromise.forget(); - return p; - } - RefPtr decoder = data.mDecoder.forget(); - return decoder->Shutdown(); + data.mStage = Stage::None; + MOZ_ASSERT(!data.mToken); } private: @@ -371,8 +426,6 @@ private: RefPtr mDecoder; MozPromiseRequestHolder mTokenRequest; MozPromiseRequestHolder mInitRequest; - MozPromiseRequestHolder mShutdownRequest; - RefPtr mShutdownPromise; } mAudio, mVideo; void RunStage(Data& aData); @@ -583,18 +636,8 @@ MediaFormatReader::DecoderFactory::DoInitDecoder(Data& aData) MOZ_RELEASE_ASSERT(!ownerData.mDecoder, "Can't have a decoder already set"); aData.mStage = Stage::None; - aData.mShutdownPromise = aData.mDecoder->Shutdown(); - aData.mShutdownPromise - ->Then( - mOwner->OwnerThread(), __func__, - [this, &aData, aError]() { - aData.mShutdownRequest.Complete(); - aData.mShutdownPromise = nullptr; - aData.mDecoder = nullptr; - mOwner->NotifyError(aData.mTrack, aError); - }, - []() { MOZ_RELEASE_ASSERT(false, "Can't ever get here"); }) - ->Track(aData.mShutdownRequest); + mOwner->mShutdownPromisePool->ShutdownDecoder(aData.mDecoder.forget()); + mOwner->NotifyError(aData.mTrack, aError); }) ->Track(aData.mInitRequest); } @@ -978,6 +1021,7 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder, , mSeekScheduled(false) , mVideoFrameContainer(aVideoFrameContainer) , mDecoderFactory(new DecoderFactory(this)) + , mShutdownPromisePool(new ShutdownPromisePool()) { MOZ_ASSERT(aDemuxer); MOZ_COUNT_CTOR(MediaFormatReader); @@ -1015,14 +1059,12 @@ MediaFormatReader::Shutdown() mVideo.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__); } - nsTArray> promises; - if (HasAudio()) { mAudio.ResetDemuxer(); mAudio.mTrackDemuxer->BreakCycles(); mAudio.mTrackDemuxer = nullptr; mAudio.ResetState(); - promises.AppendElement(ShutdownDecoderWithPromise(TrackInfo::kAudioTrack)); + mShutdownPromisePool->Track(ShutdownDecoderWithPromise(TrackInfo::kAudioTrack)); } if (HasVideo()) { @@ -1030,24 +1072,20 @@ MediaFormatReader::Shutdown() mVideo.mTrackDemuxer->BreakCycles(); mVideo.mTrackDemuxer = nullptr; mVideo.ResetState(); - promises.AppendElement(ShutdownDecoderWithPromise(TrackInfo::kVideoTrack)); + mShutdownPromisePool->Track(ShutdownDecoderWithPromise(TrackInfo::kVideoTrack)); } - promises.AppendElement(mDemuxer->Shutdown()); + mShutdownPromisePool->Track(mDemuxer->Shutdown()); mDemuxer = nullptr; mCompositorUpdatedListener.DisconnectIfExists(); mOnTrackWaitingForKeyListener.Disconnect(); - RefPtr p = mShutdownPromise.Ensure(__func__); - ShutdownPromise::All(OwnerThread(), promises) + mShutdown = true; + return mShutdownPromisePool->Shutdown() ->Then(OwnerThread(), __func__, this, &MediaFormatReader::TearDownDecoders, &MediaFormatReader::TearDownDecoders); - - mShutdown = true; - - return p; } RefPtr @@ -1075,7 +1113,8 @@ MediaFormatReader::ShutdownDecoderWithPromise(TrackType aTrack) // in the Decoder Factory. // This will be a no-op until we're processing the final decoder shutdown // prior to the MediaFormatReader being shutdown. - return mDecoderFactory->ShutdownDecoder(aTrack); + mDecoderFactory->ShutdownDecoder(aTrack); + return ShutdownPromise::CreateAndResolve(true, __func__); } // Finally, let's just shut down the currently active decoder. @@ -1099,7 +1138,7 @@ MediaFormatReader::ShutdownDecoder(TrackType aTrack) Unused << ShutdownDecoderWithPromise(aTrack); } -void +RefPtr MediaFormatReader::TearDownDecoders() { if (mAudio.mTaskQueue) { @@ -1117,12 +1156,7 @@ MediaFormatReader::TearDownDecoders() mPlatform = nullptr; mVideoFrameContainer = nullptr; - if (mShutdownPromise.IsEmpty()) { - return; - } - - MediaDecoderReader::Shutdown(); - mShutdownPromise.Resolve(true, __func__); + return MediaDecoderReader::Shutdown(); } void diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index 82b796f739ff..b98e96b11fb3 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -575,6 +575,9 @@ private: class DecoderFactory; UniquePtr mDecoderFactory; + class ShutdownPromisePool; + UniquePtr mShutdownPromisePool; + MediaEventListener mCompositorUpdatedListener; MediaEventListener mOnTrackWaitingForKeyListener; @@ -592,8 +595,7 @@ private: void ShutdownDecoder(TrackType aTrack); RefPtr ShutdownDecoderWithPromise(TrackType aTrack); - void TearDownDecoders(); - MozPromiseHolder mShutdownPromise; + RefPtr TearDownDecoders(); }; } // namespace mozilla diff --git a/dom/media/eme/MediaKeySystemAccess.cpp b/dom/media/eme/MediaKeySystemAccess.cpp index 32e4d4163281..6b43f3f6ae3e 100644 --- a/dom/media/eme/MediaKeySystemAccess.cpp +++ b/dom/media/eme/MediaKeySystemAccess.cpp @@ -272,21 +272,16 @@ GetSupportedKeySystems() clearkey.mSessionTypes.AppendElement(MediaKeySessionType::Persistent_license); } #if defined(XP_WIN) - // Clearkey CDM uses WMF decoders on Windows. - if (WMFDecoderModule::HasAAC()) { - clearkey.mMP4.SetCanDecryptAndDecode(EME_CODEC_AAC); - } else { - clearkey.mMP4.SetCanDecrypt(EME_CODEC_AAC); - } + // Clearkey CDM uses WMF's H.264 decoder on Windows. if (WMFDecoderModule::HasH264()) { clearkey.mMP4.SetCanDecryptAndDecode(EME_CODEC_H264); } else { clearkey.mMP4.SetCanDecrypt(EME_CODEC_H264); } #else - clearkey.mMP4.SetCanDecrypt(EME_CODEC_AAC); clearkey.mMP4.SetCanDecrypt(EME_CODEC_H264); #endif + clearkey.mMP4.SetCanDecrypt(EME_CODEC_AAC); if (Preferences::GetBool("media.eme.vp9-in-mp4.enabled", false)) { clearkey.mMP4.SetCanDecrypt(EME_CODEC_VP9); } diff --git a/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp b/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp index 6183e949ab5c..8c9f1c3cab8f 100644 --- a/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp +++ b/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp @@ -9,7 +9,6 @@ #include "WidevineUtils.h" #include "WidevineFileIO.h" #include "GMPPlatform.h" -#include #include #include "TimeUnits.h" @@ -161,43 +160,6 @@ WidevineDecryptor::SetServerCertificate(uint32_t aPromiseId, CDM()->SetServerCertificate(aPromiseId, aServerCert, aServerCertSize); } -class WidevineDecryptedBlock : public cdm::DecryptedBlock { -public: - - WidevineDecryptedBlock() - : mBuffer(nullptr) - , mTimestamp(0) - { - } - - ~WidevineDecryptedBlock() override { - if (mBuffer) { - mBuffer->Destroy(); - mBuffer = nullptr; - } - } - - void SetDecryptedBuffer(cdm::Buffer* aBuffer) override { - mBuffer = aBuffer; - } - - cdm::Buffer* DecryptedBuffer() override { - return mBuffer; - } - - void SetTimestamp(int64_t aTimestamp) override { - mTimestamp = aTimestamp; - } - - int64_t Timestamp() const override { - return mTimestamp; - } - -private: - cdm::Buffer* mBuffer; - int64_t mTimestamp; -}; - cdm::Time WidevineDecryptor::ThrottleDecrypt(cdm::Time aWallTime, cdm::Time aSampleDuration) { @@ -327,28 +289,6 @@ WidevineDecryptor::DecryptingComplete() Release(); } -class WidevineBuffer : public cdm::Buffer { -public: - explicit WidevineBuffer(size_t aSize) { - CDM_LOG("WidevineBuffer(size=%" PRIuSIZE ") created", aSize); - mBuffer.SetLength(aSize); - } - ~WidevineBuffer() override { - CDM_LOG("WidevineBuffer(size=%" PRIu32 ") destroyed", Size()); - } - void Destroy() override { delete this; } - uint32_t Capacity() const override { return mBuffer.Length(); }; - uint8_t* Data() override { return mBuffer.Elements(); } - void SetSize(uint32_t aSize) override { mBuffer.SetLength(aSize); } - uint32_t Size() const override { return mBuffer.Length(); } - -private: - WidevineBuffer(const WidevineBuffer&); - void operator=(const WidevineBuffer&); - - nsTArray mBuffer; -}; - Buffer* WidevineDecryptor::Allocate(uint32_t aCapacity) { diff --git a/dom/media/gmp/widevine-adapter/WidevineUtils.cpp b/dom/media/gmp/widevine-adapter/WidevineUtils.cpp index 3eaae43abe69..e7eb02873a3d 100644 --- a/dom/media/gmp/widevine-adapter/WidevineUtils.cpp +++ b/dom/media/gmp/widevine-adapter/WidevineUtils.cpp @@ -5,10 +5,12 @@ #include "WidevineUtils.h" #include "WidevineDecryptor.h" +#include #include "gmp-api/gmp-errors.h" #include #include +#include namespace mozilla { @@ -76,4 +78,90 @@ CDMWrapper::~CDMWrapper() mCDM = nullptr; } +WidevineBuffer::WidevineBuffer(size_t aSize) +{ + CDM_LOG("WidevineBuffer(size=%" PRIuSIZE ") created", aSize); + mBuffer.SetLength(aSize); +} + +WidevineBuffer::~WidevineBuffer() +{ + CDM_LOG("WidevineBuffer(size=%" PRIu32 ") destroyed", Size()); +} + +void +WidevineBuffer::Destroy() +{ + delete this; +} + +uint32_t +WidevineBuffer::Capacity() const +{ + return mBuffer.Length(); +} + +uint8_t* +WidevineBuffer::Data() +{ + return mBuffer.Elements(); +} + +void +WidevineBuffer::SetSize(uint32_t aSize) +{ + mBuffer.SetLength(aSize); +} + +uint32_t +WidevineBuffer::Size() const +{ + return mBuffer.Length(); +} + +nsTArray +WidevineBuffer::ExtractBuffer() { + nsTArray out; + out.SwapElements(mBuffer); + return out; +} + +WidevineDecryptedBlock::WidevineDecryptedBlock() + : mBuffer(nullptr) + , mTimestamp(0) +{ +} + +WidevineDecryptedBlock::~WidevineDecryptedBlock() +{ + if (mBuffer) { + mBuffer->Destroy(); + mBuffer = nullptr; + } +} + +void +WidevineDecryptedBlock::SetDecryptedBuffer(cdm::Buffer* aBuffer) +{ + mBuffer = aBuffer; +} + +cdm::Buffer* +WidevineDecryptedBlock::DecryptedBuffer() +{ + return mBuffer; +} + +void +WidevineDecryptedBlock::SetTimestamp(int64_t aTimestamp) +{ + mTimestamp = aTimestamp; +} + +int64_t +WidevineDecryptedBlock::Timestamp() const +{ + return mTimestamp; +} + } // namespace mozilla diff --git a/dom/media/gmp/widevine-adapter/WidevineUtils.h b/dom/media/gmp/widevine-adapter/WidevineUtils.h index 9c67a20058d0..9c7136d6773e 100644 --- a/dom/media/gmp/widevine-adapter/WidevineUtils.h +++ b/dom/media/gmp/widevine-adapter/WidevineUtils.h @@ -61,6 +61,43 @@ void InitInputBuffer(const GMPEncryptedBufferMetadata* aCrypto, cdm::InputBuffer &aInputBuffer, nsTArray &aSubsamples); +class WidevineBuffer : public cdm::Buffer +{ +public: + explicit WidevineBuffer(size_t aSize); + ~WidevineBuffer() override; + void Destroy() override; + uint32_t Capacity() const override; + uint8_t* Data() override; + void SetSize(uint32_t aSize) override; + uint32_t Size() const override; + + // Moves contents of buffer out into temporary. + // Note: This empties the buffer. + nsTArray ExtractBuffer(); + +private: + nsTArray mBuffer; + WidevineBuffer(const WidevineBuffer&); + void operator=(const WidevineBuffer&); +}; + +class WidevineDecryptedBlock : public cdm::DecryptedBlock +{ +public: + + WidevineDecryptedBlock(); + ~WidevineDecryptedBlock() override; + void SetDecryptedBuffer(cdm::Buffer* aBuffer) override; + cdm::Buffer* DecryptedBuffer() override; + void SetTimestamp(int64_t aTimestamp) override; + int64_t Timestamp() const override; + +private: + cdm::Buffer* mBuffer; + int64_t mTimestamp; +}; + } // namespace mozilla #endif // WidevineUtils_h_ diff --git a/dom/media/gmp/widevine-adapter/moz.build b/dom/media/gmp/widevine-adapter/moz.build index 89152fd664ac..212d7f7794f8 100644 --- a/dom/media/gmp/widevine-adapter/moz.build +++ b/dom/media/gmp/widevine-adapter/moz.build @@ -14,6 +14,11 @@ SOURCES += [ 'WidevineVideoFrame.cpp', ] +EXPORTS += [ + 'WidevineDecryptor.h', + 'WidevineUtils.h' +] + FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ diff --git a/dom/media/webrtc/MediaTrackConstraints.cpp b/dom/media/webrtc/MediaTrackConstraints.cpp index de127bdb55d7..2b4d4cafc753 100644 --- a/dom/media/webrtc/MediaTrackConstraints.cpp +++ b/dom/media/webrtc/MediaTrackConstraints.cpp @@ -297,6 +297,10 @@ NormalizedConstraints::NormalizedConstraints( : NormalizedConstraintSet(*aOthers[0]) , mBadConstraint(nullptr) { + for (auto& entry : aOthers[0]->mAdvanced) { + mAdvanced.push_back(entry); + } + // Create a list of member pointers. nsTArray list; NormalizedConstraints dummy(dom::MediaTrackConstraints(), &list); diff --git a/gfx/vr/gfxVROculus.cpp b/gfx/vr/gfxVROculus.cpp index 0803b82863a3..555de00b7792 100644 --- a/gfx/vr/gfxVROculus.cpp +++ b/gfx/vr/gfxVROculus.cpp @@ -836,10 +836,20 @@ VRControllerOculus::VRControllerOculus(dom::GamepadHand aHand) : VRControllerHost(VRDeviceType::Oculus) { MOZ_COUNT_CTOR_INHERITED(VRControllerOculus, VRControllerHost); - mControllerInfo.mControllerName.AssignLiteral("Oculus Touch ("); - mControllerInfo.mControllerName.AppendPrintf("%s%s", - GamepadHandValues::strings[uint32_t(aHand)].value, - ")"); + + char* touchID = ""; + switch (aHand) { + case dom::GamepadHand::Left: + touchID = "Oculus Touch (Left)"; + break; + case dom::GamepadHand::Right: + touchID = "Oculus Touch (Right)"; + break; + default: + MOZ_ASSERT(false); + break; + } + mControllerInfo.mControllerName = touchID; mControllerInfo.mMappingType = GamepadMappingType::_empty; mControllerInfo.mHand = aHand; mControllerInfo.mNumButtons = kNumOculusButton; @@ -988,10 +998,13 @@ VRSystemManagerOculus::HandleInput() // Start to process pose ovrTrackingState state = ovr_GetTrackingState(mSession, 0.0, false); - ovrPoseStatef& pose(state.HandPoses[i]); + // HandPoses is ordered by ovrControllerType_LTouch and ovrControllerType_RTouch, + // therefore, we can't get its state by the index of mOculusController. + const uint32_t handIdx = static_cast(controller->GetHand()) - 1; + ovrPoseStatef& pose(state.HandPoses[handIdx]); GamepadPoseState poseState; - if (state.HandStatusFlags[i] & ovrStatus_OrientationTracked) { + if (state.HandStatusFlags[handIdx] & ovrStatus_OrientationTracked) { poseState.flags |= GamepadCapabilityFlags::Cap_Orientation; poseState.orientation[0] = pose.ThePose.Orientation.x; poseState.orientation[1] = pose.ThePose.Orientation.y; @@ -1006,7 +1019,7 @@ VRSystemManagerOculus::HandleInput() poseState.angularAcceleration[1] = pose.AngularAcceleration.y; poseState.angularAcceleration[2] = pose.AngularAcceleration.z; } - if (state.HandStatusFlags[i] & ovrStatus_PositionTracked) { + if (state.HandStatusFlags[handIdx] & ovrStatus_PositionTracked) { poseState.flags |= GamepadCapabilityFlags::Cap_Position; poseState.position[0] = pose.ThePose.Position.x; poseState.position[1] = pose.ThePose.Position.y; @@ -1039,10 +1052,10 @@ VRSystemManagerOculus::HandleButtonPress(uint32_t aControllerIdx, for (uint32_t i = 0; i < kNumOculusButton; ++i) { switch (hand) { - case mozilla::dom::GamepadHand::Left: + case dom::GamepadHand::Left: buttonMask = kOculusTouchLButton[i]; break; - case mozilla::dom::GamepadHand::Right: + case dom::GamepadHand::Right: buttonMask = kOculusTouchRButton[i]; break; default: @@ -1080,6 +1093,7 @@ VRSystemManagerOculus::HandlePoseTracking(uint32_t aControllerIdx, const GamepadPoseState& aPose, VRControllerHost* aController) { + MOZ_ASSERT(aController); if (aPose != aController->GetPose()) { aController->SetPose(aPose); NewPoseState(aControllerIdx, aPose); diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index 73dbc9a3ef2d..db5bff2202f0 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -33,6 +33,7 @@ #include "unicode/udat.h" #include "unicode/udatpg.h" #include "unicode/uenum.h" +#include "unicode/uloc.h" #include "unicode/unum.h" #include "unicode/unumsys.h" #include "unicode/upluralrules.h" @@ -167,6 +168,12 @@ uloc_countAvailable() MOZ_CRASH("uloc_countAvailable: Intl API disabled"); } +UBool +uloc_isRightToLeft(const char* locale) +{ + MOZ_CRASH("uloc_isRightToLeft: Intl API disabled"); +} + struct UFormattable; void @@ -4031,6 +4038,34 @@ js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp) return true; } +bool +js::intl_GetLocaleInfo(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + JSAutoByteString locale(cx, args[0].toString()); + if (!locale) + return false; + + RootedObject info(cx, NewBuiltinClassInstance(cx)); + if (!info) + return false; + + if (!DefineProperty(cx, info, cx->names().locale, args[0])) + return false; + + bool rtl = uloc_isRightToLeft(icuLocale(locale.ptr())); + + RootedValue dir(cx, StringValue(rtl ? cx->names().rtl : cx->names().ltr)); + + if (!DefineProperty(cx, info, cx->names().direction, dir)) + return false; + + args.rval().setObject(*info); + return true; +} + const Class js::IntlClass = { js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_Intl) diff --git a/js/src/builtin/Intl.h b/js/src/builtin/Intl.h index 1d08153afe07..2be53e8da4d1 100644 --- a/js/src/builtin/Intl.h +++ b/js/src/builtin/Intl.h @@ -509,6 +509,20 @@ intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp); extern MOZ_MUST_USE bool intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp); +/** + * Returns a plain object with locale information for a single valid locale + * (callers must perform this validation). The object will have these + * properties: + * + * direction + * a string with a value "ltr" for left-to-right locale, and "rtl" for + * right-to-left locale. + * locale + * a BCP47 compilant locale string for the resolved locale. + */ +extern MOZ_MUST_USE bool +intl_GetLocaleInfo(JSContext* cx, unsigned argc, Value* vp); + /** * Returns an Array with CLDR-based fields display names. * The function takes three arguments: diff --git a/js/src/builtin/Intl.js b/js/src/builtin/Intl.js index 897d8a4fe5f2..941ca68ef06b 100644 --- a/js/src/builtin/Intl.js +++ b/js/src/builtin/Intl.js @@ -3337,3 +3337,22 @@ function Intl_getDisplayNames(locales, options) { return result; } +function Intl_getLocaleInfo(locales) { + const requestedLocales = CanonicalizeLocaleList(locales); + + // In the future, we may want to expose uloc_getAvailable and use it here. + const DateTimeFormat = dateTimeFormatInternalProperties; + const localeData = DateTimeFormat.localeData; + + const localeOpt = new Record(); + localeOpt.localeMatcher = "best fit"; + + const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat), + requestedLocales, + localeOpt, + DateTimeFormat.relevantExtensionKeys, + localeData); + + return intl_GetLocaleInfo(r.locale); +} + diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 0f797bf822c8..89e012c925e2 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -939,6 +939,7 @@ AddIntlExtras(JSContext* cx, unsigned argc, Value* vp) static const JSFunctionSpec funcs[] = { JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0), + JS_SELF_HOSTED_FN("getLocaleInfo", "Intl_getLocaleInfo", 1, 0), JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0), JS_FS_END }; diff --git a/js/src/tests/Intl/getLocaleInfo.js b/js/src/tests/Intl/getLocaleInfo.js new file mode 100644 index 000000000000..22d4f537998e --- /dev/null +++ b/js/src/tests/Intl/getLocaleInfo.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras")) +/* 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/. */ + +// Tests the getCalendarInfo function with a diverse set of arguments. + +function checkLocaleInfo(info, expected) +{ + assertEq(Object.getPrototypeOf(info), Object.prototype); + + assertEq(info.direction, expected.direction); + assertEq(info.locale, expected.locale); +} + +addIntlExtras(Intl); + +let gLI = Intl.getLocaleInfo; + +assertEq(gLI.length, 1); + +checkLocaleInfo(gLI('en-US'), { + direction: "ltr", + locale: "en-US" +}); + +checkLocaleInfo(gLI('fr'), { + direction: "ltr", + locale: "fr" +}); + +checkLocaleInfo(gLI('ar'), { + direction: "rtl", + locale: "ar" +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 4730cb6b90df..7a97e5e75506 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -91,6 +91,7 @@ macro(defineSetter, defineSetter, "__defineSetter__") \ macro(delete, delete_, "delete") \ macro(deleteProperty, deleteProperty, "deleteProperty") \ + macro(direction, direction, "direction") \ macro(displayURL, displayURL, "displayURL") \ macro(do, do_, "do") \ macro(done, done, "done") \ @@ -210,6 +211,7 @@ macro(locale, locale, "locale") \ macro(lookupGetter, lookupGetter, "__lookupGetter__") \ macro(lookupSetter, lookupSetter, "__lookupSetter__") \ + macro(ltr, ltr, "ltr") \ macro(MapConstructorInit, MapConstructorInit, "MapConstructorInit") \ macro(MapIterator, MapIterator, "Map Iterator") \ macro(maximumFractionDigits, maximumFractionDigits, "maximumFractionDigits") \ @@ -298,6 +300,7 @@ macro(resumeGenerator, resumeGenerator, "resumeGenerator") \ macro(return, return_, "return") \ macro(revoke, revoke, "revoke") \ + macro(rtl, rtl, "rtl") \ macro(script, script, "script") \ macro(scripts, scripts, "scripts") \ macro(second, second, "second") \ diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 89322a379720..52124da7486f 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -2615,6 +2615,7 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0), JS_FN("intl_FormatNumber", intl_FormatNumber, 2,0), JS_FN("intl_GetCalendarInfo", intl_GetCalendarInfo, 1,0), + JS_FN("intl_GetLocaleInfo", intl_GetLocaleInfo, 1,0), JS_FN("intl_ComputeDisplayNames", intl_ComputeDisplayNames, 3,0), JS_FN("intl_IsValidTimeZoneName", intl_IsValidTimeZoneName, 1,0), JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0), diff --git a/layout/style/crashtests/1340344.html b/layout/style/crashtests/1340344.html new file mode 100644 index 000000000000..5d0a3c85fdba --- /dev/null +++ b/layout/style/crashtests/1340344.html @@ -0,0 +1,15 @@ + + + + + + + diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/crashtests.list index c209431e1654..011adb6fab85 100644 --- a/layout/style/crashtests/crashtests.list +++ b/layout/style/crashtests/crashtests.list @@ -169,3 +169,4 @@ load 1321357-1.html load 1328535-1.html load 1331272.html HTTP load 1333001-1.html +pref(dom.animations-api.core.enabled,true) load 1340344.html diff --git a/layout/style/moz.build b/layout/style/moz.build index f5cd92302332..02241b46286d 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -148,6 +148,7 @@ EXPORTS.mozilla.css += [ UNIFIED_SOURCES += [ 'AnimationCollection.cpp', 'AnimationCommon.cpp', + 'BindingStyleRule.cpp', 'CounterStyleManager.cpp', 'CSS.cpp', 'CSSLexer.cpp', @@ -208,6 +209,7 @@ UNIFIED_SOURCES += [ 'ServoCSSRuleList.cpp', 'ServoDeclarationBlock.cpp', 'ServoElementSnapshot.cpp', + 'ServoSpecifiedValues.cpp', 'ServoStyleRule.cpp', 'ServoStyleSet.cpp', 'ServoStyleSheet.cpp', @@ -217,21 +219,13 @@ UNIFIED_SOURCES += [ 'SVGAttrAnimationRuleProcessor.cpp', ] -# - BindingStyleRule.cpp doesn't _really_ needs to be built separately, -# except insofar as it shifts unified build boundaries, causing -# Unified_cpp_layout_style4.cpp to include nsStyleCoord.cpp, which -# includes, via nsStyleCoord.h, , which ends up including -# , which fails in much the way described in -# . # - nsCSSRuleProcessor.cpp needs to be built separately because it uses # plarena.h. # - nsLayoutStylesheetCache.cpp needs to be built separately because it uses # nsExceptionHandler.h, which includes windows.h. SOURCES += [ - 'BindingStyleRule.cpp', 'nsCSSRuleProcessor.cpp', 'nsLayoutStylesheetCache.cpp', - 'ServoSpecifiedValues.cpp', ] include('/ipc/chromium/chromium-config.mozbuild') diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index f4f1abf8af96..efbd0146bf7f 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -6936,7 +6936,12 @@ CSSParserImpl::ParseHue(float& aAngle) // The '0' value is handled by parsing, so use VARIANT_ANGLE flag // instead of VARIANT_ANGLE_OR_ZERO. if (ParseSingleTokenVariant(angleValue, VARIANT_ANGLE, nullptr)) { + // Convert double value of GetAngleValueInDegrees() to float. aAngle = angleValue.GetAngleValueInDegrees(); + // And then clamp it as finite values in float. + aAngle = mozilla::clamped(aAngle, + -std::numeric_limits::max(), + std::numeric_limits::max()); return true; } diff --git a/layout/style/nsStyleCoord.cpp b/layout/style/nsStyleCoord.cpp index 2eaf09104985..8d155e9ad496 100644 --- a/layout/style/nsStyleCoord.cpp +++ b/layout/style/nsStyleCoord.cpp @@ -5,10 +5,14 @@ /* representation of length values in computed style data */ -#include "nsStyleCoord.h" #include "mozilla/HashFunctions.h" #include "mozilla/PodOperations.h" +// nsStyleCoord.h must not be the first header in a unified source file, +// otherwise it may not build with MSVC due to a bug in our STL wrapper. +// See bug 1331102. +#include "nsStyleCoord.h" + using namespace mozilla; nsStyleCoord::nsStyleCoord(nsStyleUnit aUnit) diff --git a/media/libstagefright/binding/DecoderData.cpp b/media/libstagefright/binding/DecoderData.cpp index d1971484231f..a06e533b1e62 100644 --- a/media/libstagefright/binding/DecoderData.cpp +++ b/media/libstagefright/binding/DecoderData.cpp @@ -192,7 +192,7 @@ MP4VideoInfo::Update(const MetaData* aMetaData, const char* aMimeType) #ifdef MOZ_RUST_MP4PARSE static void UpdateTrackProtectedInfo(mozilla::TrackInfo& aConfig, - const mp4parser_sinf_info& aSinf) + const mp4parse_sinf_info& aSinf) { if (aSinf.is_encrypted != 0) { aConfig.mCrypto.mValid = true; diff --git a/media/libstagefright/binding/include/mp4parse.h b/media/libstagefright/binding/include/mp4parse.h index 721c1e261bab..1f10293bce07 100644 --- a/media/libstagefright/binding/include/mp4parse.h +++ b/media/libstagefright/binding/include/mp4parse.h @@ -48,20 +48,30 @@ typedef struct mp4parse_track_info { int64_t media_time; } mp4parse_track_info; +typedef struct mp4parse_indice { + uint64_t start_offset; + uint64_t end_offset; + uint64_t start_composition; + uint64_t end_composition; + uint64_t start_decode; + bool sync; +} mp4parse_indice; + typedef struct mp4parse_byte_data { uint32_t length; uint8_t const* data; + mp4parse_indice const* indices; } mp4parse_byte_data; typedef struct mp4parse_pssh_info { mp4parse_byte_data data; } mp4parse_pssh_info; -typedef struct mp4parser_sinf_info { +typedef struct mp4parse_sinf_info { uint32_t is_encrypted; uint8_t iv_size; mp4parse_byte_data kid; -} mp4parser_sinf_info; +} mp4parse_sinf_info; typedef struct mp4parse_track_audio_info { uint16_t channels; @@ -69,7 +79,7 @@ typedef struct mp4parse_track_audio_info { uint32_t sample_rate; uint16_t profile; mp4parse_byte_data codec_specific_config; - mp4parser_sinf_info protected_data; + mp4parse_sinf_info protected_data; } mp4parse_track_audio_info; typedef struct mp4parse_track_video_info { @@ -78,7 +88,7 @@ typedef struct mp4parse_track_video_info { uint16_t image_width; uint16_t image_height; mp4parse_byte_data extra_data; - mp4parser_sinf_info protected_data; + mp4parse_sinf_info protected_data; } mp4parse_track_video_info; typedef struct mp4parse_fragment_info { @@ -116,6 +126,8 @@ mp4parse_error mp4parse_get_track_audio_info(mp4parse_parser* parser, uint32_t t /// Fill the supplied `mp4parse_track_video_info` with metadata for `track`. mp4parse_error mp4parse_get_track_video_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_video_info* info); +mp4parse_error mp4parse_get_indice_table(mp4parse_parser* parser, uint32_t track_id, mp4parse_byte_data* indices); + /// Fill the supplied `mp4parse_fragment_info` with metadata from fragmented file. mp4parse_error mp4parse_get_fragment_info(mp4parse_parser* parser, mp4parse_fragment_info* info); diff --git a/media/libstagefright/binding/mp4parse-cargo.patch b/media/libstagefright/binding/mp4parse-cargo.patch index bfa0ab74b424..ec8e3de2919a 100644 --- a/media/libstagefright/binding/mp4parse-cargo.patch +++ b/media/libstagefright/binding/mp4parse-cargo.patch @@ -27,11 +27,14 @@ diff --git a/media/libstagefright/binding/mp4parse_capi/Cargo.toml b/media/libst index aeeebc65..5c0836a 100644 --- a/media/libstagefright/binding/mp4parse_capi/Cargo.toml +++ b/media/libstagefright/binding/mp4parse_capi/Cargo.toml -@@ -18,18 +18,12 @@ exclude = [ +@@ -18,21 +18,12 @@ exclude = [ "*.mp4", ] -build = "build.rs" +- +-[badges] +-travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" } +build = false [dependencies] diff --git a/media/libstagefright/binding/mp4parse/Cargo.toml b/media/libstagefright/binding/mp4parse/Cargo.toml index 2403005f2de3..44ff9e5a0646 100644 --- a/media/libstagefright/binding/mp4parse/Cargo.toml +++ b/media/libstagefright/binding/mp4parse/Cargo.toml @@ -10,6 +10,7 @@ authors = [ description = "Parser for ISO base media file format (mp4)" documentation = "https://mp4parse-docs.surge.sh/mp4parse/" license = "MPL-2.0" +categories = ["multimedia::video"] repository = "https://github.com/mozilla/mp4parse-rust" @@ -18,6 +19,9 @@ exclude = [ "*.mp4", ] +[badges] +travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" } + [dependencies] byteorder = "1.0.0" bitreader = { version = "0.2.0" } diff --git a/media/libstagefright/binding/mp4parse/src/boxes.rs b/media/libstagefright/binding/mp4parse/src/boxes.rs index 97f65137ef46..fc9085b4b7d9 100644 --- a/media/libstagefright/binding/mp4parse/src/boxes.rs +++ b/media/libstagefright/binding/mp4parse/src/boxes.rs @@ -134,4 +134,6 @@ box_database!( TrackEncryptionBox 0x74656e63, // "tenc" ProtectionSchemeInformationBox 0x73696e66, // "sinf" OriginalFormatBox 0x66726d61, // "frma" + MP3AudioSampleEntry 0x2e6d7033, // ".mp3" - from F4V. + CompositionOffsetBox 0x63747473, // "ctts" ); diff --git a/media/libstagefright/binding/mp4parse/src/lib.rs b/media/libstagefright/binding/mp4parse/src/lib.rs index 9260a9ec7f76..c6c195355356 100644 --- a/media/libstagefright/binding/mp4parse/src/lib.rs +++ b/media/libstagefright/binding/mp4parse/src/lib.rs @@ -151,46 +151,64 @@ struct MediaHeaderBox { // Chunk offset box 'stco' or 'co64' #[derive(Debug)] -struct ChunkOffsetBox { - offsets: Vec, +pub struct ChunkOffsetBox { + pub offsets: Vec, } // Sync sample box 'stss' #[derive(Debug)] -struct SyncSampleBox { - samples: Vec, +pub struct SyncSampleBox { + pub samples: Vec, } // Sample to chunk box 'stsc' #[derive(Debug)] -struct SampleToChunkBox { - samples: Vec, +pub struct SampleToChunkBox { + pub samples: Vec, } #[derive(Debug)] -struct SampleToChunk { - first_chunk: u32, - samples_per_chunk: u32, - sample_description_index: u32, +pub struct SampleToChunk { + pub first_chunk: u32, + pub samples_per_chunk: u32, + pub sample_description_index: u32, } // Sample size box 'stsz' #[derive(Debug)] -struct SampleSizeBox { - sample_size: u32, - sample_sizes: Vec, +pub struct SampleSizeBox { + pub sample_size: u32, + pub sample_sizes: Vec, } // Time to sample box 'stts' #[derive(Debug)] -struct TimeToSampleBox { - samples: Vec, +pub struct TimeToSampleBox { + pub samples: Vec, +} + +#[repr(C)] +#[derive(Debug)] +pub struct Sample { + pub sample_count: u32, + pub sample_delta: u32, +} + +#[derive(Debug, Clone, Copy)] +pub enum TimeOffsetVersion { + Version0(u32), + Version1(i32), +} + +#[derive(Debug, Clone)] +pub struct TimeOffset { + pub sample_count: u32, + pub time_offset: TimeOffsetVersion, } #[derive(Debug)] -struct Sample { - sample_count: u32, - sample_delta: u32, +pub struct CompositionOffsetBox { + pub samples: Vec, } // Handler reference box 'hdlr' @@ -228,6 +246,7 @@ pub enum AudioCodecSpecific { ES_Descriptor(ES_Descriptor), FLACSpecificBox(FLACSpecificBox), OpusSpecificBox(OpusSpecificBox), + MP3, } #[derive(Debug, Clone)] @@ -396,6 +415,7 @@ pub struct TrackScaledTime(pub u64, pub usize); /// A fragmented file contains no sample data in stts, stsc, and stco. #[derive(Debug, Default)] pub struct EmptySampleTableBoxes { + // TODO: Track has stts, stsc and stco, this structure can be discarded. pub empty_stts : bool, pub empty_stsc : bool, pub empty_stco : bool, @@ -421,6 +441,12 @@ pub struct Track { pub empty_sample_boxes: EmptySampleTableBoxes, pub data: Option, pub tkhd: Option, // TODO(kinetik): find a nicer way to export this. + pub stts: Option, + pub stsc: Option, + pub stsz: Option, + pub stco: Option, // It is for stco or co64. + pub stss: Option, + pub ctts: Option, } impl Track { @@ -840,30 +866,41 @@ fn read_stbl(f: &mut BMFFBox, track: &mut Track) -> Result<()> { } BoxType::TimeToSampleBox => { let stts = read_stts(&mut b)?; - track.empty_sample_boxes.empty_stts = stts.samples.is_empty(); log!("{:?}", stts); + track.empty_sample_boxes.empty_stts = stts.samples.is_empty(); + track.stts = Some(stts); } BoxType::SampleToChunkBox => { let stsc = read_stsc(&mut b)?; - track.empty_sample_boxes.empty_stsc = stsc.samples.is_empty(); log!("{:?}", stsc); + track.empty_sample_boxes.empty_stsc = stsc.samples.is_empty(); + track.stsc = Some(stsc); } BoxType::SampleSizeBox => { let stsz = read_stsz(&mut b)?; log!("{:?}", stsz); + track.stsz = Some(stsz); } BoxType::ChunkOffsetBox => { let stco = read_stco(&mut b)?; track.empty_sample_boxes.empty_stco = stco.offsets.is_empty(); log!("{:?}", stco); + track.stco = Some(stco); } BoxType::ChunkLargeOffsetBox => { let co64 = read_co64(&mut b)?; log!("{:?}", co64); + track.stco = Some(co64); } BoxType::SyncSampleBox => { let stss = read_stss(&mut b)?; log!("{:?}", stss); + track.stss = Some(stss); + } + BoxType::CompositionOffsetBox => { + let ctts = read_ctts(&mut b)?; + log!("{:?}", ctts); + track.ctts = Some(ctts); } _ => skip_box_content(&mut b)?, }; @@ -1115,6 +1152,45 @@ fn read_stsc(src: &mut BMFFBox) -> Result { }) } +fn read_ctts(src: &mut BMFFBox) -> Result { + let (version, _) = read_fullbox_extra(src)?; + + let counts = be_u32(src)?; + + if src.bytes_left() < (counts as usize * 8) { + return Err(Error::InvalidData("insufficient data in 'ctts' box")); + } + + let mut offsets = Vec::new(); + for _ in 0..counts { + let (sample_count, time_offset) = match version { + 0 => { + let count = be_u32(src)?; + let offset = TimeOffsetVersion::Version0(be_u32(src)?); + (count, offset) + }, + 1 => { + let count = be_u32(src)?; + let offset = TimeOffsetVersion::Version1(be_i32(src)?); + (count, offset) + }, + _ => { + return Err(Error::InvalidData("unsupported version in 'ctts' box")); + } + }; + offsets.push(TimeOffset { + sample_count: sample_count, + time_offset: time_offset, + }); + } + + skip_box_remain(src)?; + + Ok(CompositionOffsetBox { + samples: offsets, + }) +} + /// Parse a stsz box. fn read_stsz(src: &mut BMFFBox) -> Result { let (_, _) = read_fullbox_extra(src)?; @@ -1433,7 +1509,6 @@ fn read_dfla(src: &mut BMFFBox) -> Result { if blocks.is_empty() { return Err(Error::InvalidData("FLACSpecificBox missing metadata")); } else if blocks[0].block_type != 0 { - println!("flac metadata block:\n {:?}", blocks[0]); return Err(Error::InvalidData( "FLACSpecificBox must have STREAMINFO metadata first")); } else if blocks[0].data.len() != 34 { @@ -1602,7 +1677,8 @@ fn read_video_sample_entry(src: &mut BMFFBox, track: &mut Track) -> } BoxType::VPCodecConfigurationBox => { // vpcC if (name != BoxType::VP8SampleEntry && - name != BoxType::VP9SampleEntry) || + name != BoxType::VP9SampleEntry && + name != BoxType::ProtectedVisualSampleEntry) || codec_specific.is_some() { return Err(Error::InvalidData("malformed video sample entry")); } @@ -1687,6 +1763,9 @@ fn read_audio_sample_entry(src: &mut BMFFBox, track: &mut Track) -> // Skip chan/etc. for now. let mut codec_specific = None; + if name == BoxType::MP3AudioSampleEntry { + codec_specific = Some(AudioCodecSpecific::MP3); + } let mut protection_info = Vec::new(); let mut iter = src.box_iter(); while let Some(mut b) = iter.next_box()? { @@ -1889,16 +1968,18 @@ fn read_buf(src: &mut T, size: usize) -> Result> { // - zero or more byte strings, with a single null terminating the string. // - zero byte strings with no null terminator (i.e. zero space in the box for the string) // - length-prefixed strings with no null terminator (e.g. bear_rotate_0.mp4) +// - multiple byte strings where more than one byte is a null. fn read_null_terminated_string(src: &mut T, mut size: usize) -> Result { let mut buf = Vec::new(); while size > 0 { let c = src.read_u8()?; + size -= 1; if c == 0 { break; } buf.push(c); - size -= 1; } + skip(src, size)?; String::from_utf8(buf).map_err(From::from) } diff --git a/media/libstagefright/binding/mp4parse/src/tests.rs b/media/libstagefright/binding/mp4parse/src/tests.rs index 15c4cfb5dadf..921168b937ed 100644 --- a/media/libstagefright/binding/mp4parse/src/tests.rs +++ b/media/libstagefright/binding/mp4parse/src/tests.rs @@ -884,3 +884,19 @@ fn read_esds() { assert_eq!(es.audio_channel_count, Some(6)); assert_eq!(es.codec_esds, aac_esds); } + +#[test] +fn read_null_terminated_string() { + let tests = vec![ + vec![0u8], // Short null-terminated string. + vec![65u8, 0u8], // Normal null-terminated string. + vec![], // Empty string (no data). + vec![4u8, 65u8, 66u8, 67u8, 68u8], // Length-prefixed string, not null-terminated. + vec![0u8, 0u8], // Doubly null-terminated string. + ]; + for v in tests.iter() { + let mut c = Cursor::new(v); + super::read_null_terminated_string(&mut c, v.len()).expect("string read failed"); + assert_eq!(c.position(), v.len() as u64); + } +} diff --git a/media/libstagefright/binding/mp4parse/tests/public.rs b/media/libstagefright/binding/mp4parse/tests/public.rs index 09548ea905b4..ab94cd610440 100644 --- a/media/libstagefright/binding/mp4parse/tests/public.rs +++ b/media/libstagefright/binding/mp4parse/tests/public.rs @@ -92,6 +92,9 @@ fn public_api() { assert!(opus.version > 0); "Opus" } + mp4::AudioCodecSpecific::MP3 => { + "MP3" + } }, "ES"); assert!(a.samplesize > 0); assert!(a.samplerate > 0); diff --git a/media/libstagefright/binding/mp4parse_capi/src/lib.rs b/media/libstagefright/binding/mp4parse_capi/src/lib.rs index 443e708bedab..deba559a6084 100644 --- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs +++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs @@ -55,6 +55,7 @@ use mp4parse::TrackTimeScale; use mp4parse::TrackScaledTime; use mp4parse::serialize_opus_header; use mp4parse::CodecType; +use mp4parse::Track; // rusty-cheddar's C enum generation doesn't namespace enum members by // prefixing them, so we're forced to do it in our member names until @@ -113,17 +114,31 @@ pub struct mp4parse_track_info { // TODO(kinetik): include crypto guff } +#[repr(C)] +#[derive(Default, Debug, PartialEq)] +pub struct mp4parse_indice { + pub start_offset: u64, + pub end_offset: u64, + pub start_composition: u64, + pub end_composition: u64, + pub start_decode: u64, + pub sync: bool, +} + #[repr(C)] pub struct mp4parse_byte_data { pub length: u32, + // cheddar can't handle generic type, so it needs to be multiple data types here. pub data: *const u8, + pub indices: *const mp4parse_indice, } impl Default for mp4parse_byte_data { fn default() -> Self { mp4parse_byte_data { length: 0, - data: std::ptr::null_mut(), + data: std::ptr::null(), + indices: std::ptr::null(), } } } @@ -133,6 +148,10 @@ impl mp4parse_byte_data { self.length = data.len() as u32; self.data = data.as_ptr(); } + fn set_indices(&mut self, data: &Vec) { + self.length = data.len() as u32; + self.indices = data.as_ptr(); + } } #[repr(C)] @@ -143,7 +162,7 @@ pub struct mp4parse_pssh_info { #[repr(C)] #[derive(Default)] -pub struct mp4parser_sinf_info { +pub struct mp4parse_sinf_info { pub is_encrypted: u32, pub iv_size: u8, pub kid: mp4parse_byte_data, @@ -157,7 +176,7 @@ pub struct mp4parse_track_audio_info { pub sample_rate: u32, pub profile: u16, pub codec_specific_config: mp4parse_byte_data, - pub protected_data: mp4parser_sinf_info, + pub protected_data: mp4parse_sinf_info, } #[repr(C)] @@ -168,7 +187,7 @@ pub struct mp4parse_track_video_info { pub image_width: u16, pub image_height: u16, pub extra_data: mp4parse_byte_data, - pub protected_data: mp4parser_sinf_info, + pub protected_data: mp4parse_sinf_info, } #[repr(C)] @@ -187,6 +206,7 @@ struct Wrap { poisoned: bool, opus_header: HashMap>, pssh_data: Vec, + sample_table: HashMap>, } #[repr(C)] @@ -221,6 +241,10 @@ impl mp4parse_parser { fn pssh_data_mut(&mut self) -> &mut Vec { &mut self.0.pssh_data } + + fn sample_table_mut(&mut self) -> &mut HashMap> { + &mut self.0.sample_table + } } #[repr(C)] @@ -266,6 +290,7 @@ pub unsafe extern fn mp4parse_new(io: *const mp4parse_io) -> *mut mp4parse_parse poisoned: false, opus_header: HashMap::new(), pssh_data: Vec::new(), + sample_table: HashMap::new(), })); Box::into_raw(parser) @@ -401,6 +426,8 @@ pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track mp4parse_codec::MP4PARSE_CODEC_MP3, AudioCodecSpecific::ES_Descriptor(_) => mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, + AudioCodecSpecific::MP3 => + mp4parse_codec::MP4PARSE_CODEC_MP3, }, Some(SampleEntry::Video(ref video)) => match video.codec_specific { VideoCodecSpecific::VPxConfig(_) => @@ -536,6 +563,7 @@ pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut mp4parse_parser, } } } + AudioCodecSpecific::MP3 => (), } match audio.protection_info.iter().find(|sinf| sinf.tenc.is_some()) { @@ -615,6 +643,367 @@ pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser, MP4PARSE_OK } +#[no_mangle] +pub unsafe extern fn mp4parse_get_indice_table(parser: *mut mp4parse_parser, track_id: u32, indices: *mut mp4parse_byte_data) -> mp4parse_error { + if parser.is_null() || (*parser).poisoned() { + return MP4PARSE_ERROR_BADARG; + } + + // Initialize fields to default values to ensure all fields are always valid. + *indices = Default::default(); + + let context = (*parser).context(); + let tracks = &context.tracks; + let track = match tracks.iter().find(|track| track.track_id == Some(track_id)) { + Some(t) => t, + _ => return MP4PARSE_ERROR_INVALID, + }; + + let index_table = (*parser).sample_table_mut(); + match index_table.get(&track_id) { + Some(v) => { + (*indices).set_indices(v); + return MP4PARSE_OK; + }, + _ => {}, + } + + // Find the track start offset time from 'elst'. + // 'media_time' maps start time onward, 'empty_duration' adds time offset + // before first frame is displayed. + let offset_time = + match (&track.empty_duration, &track.media_time, &context.timescale) { + (&Some(empty_duration), &Some(media_time), &Some(scale)) => { + (empty_duration.0 - media_time.0) as i64 * scale.0 as i64 + }, + (&Some(empty_duration), _, &Some(scale)) => { + empty_duration.0 as i64 * scale.0 as i64 + }, + (_, &Some(media_time), &Some(scale)) => { + (0 - media_time.0) as i64 * scale.0 as i64 + }, + _ => 0, + }; + + match create_sample_table(track, offset_time) { + Some(v) => { + (*indices).set_indices(&v); + index_table.insert(track_id, v); + return MP4PARSE_OK; + }, + _ => {}, + } + + MP4PARSE_ERROR_INVALID +} + +// Convert a 'ctts' compact table to full table by iterator, +// (sample_with_the_same_offset_count, offset) => (offset), (offset), (offset) ... +// +// For example: +// (2, 10), (4, 9) into (10, 10, 9, 9, 9, 9) by calling next_offset_time(). +struct TimeOffsetIterator<'a> { + cur_sample_range: std::ops::Range, + cur_offset: i64, + ctts_iter: Option>, +} + +impl<'a> Iterator for TimeOffsetIterator<'a> { + type Item = i64; + + fn next(&mut self) -> Option { + let has_sample = self.cur_sample_range.next() + .or_else(|| { + // At end of current TimeOffset, find the next TimeOffset. + let iter = match self.ctts_iter { + Some(ref mut v) => v, + _ => return None, + }; + let offset_version; + self.cur_sample_range = match iter.next() { + Some(v) => { + offset_version = v.time_offset; + (0 .. v.sample_count) + }, + _ => { + offset_version = mp4parse::TimeOffsetVersion::Version0(0); + (0 .. 0) + }, + }; + + self.cur_offset = match offset_version { + mp4parse::TimeOffsetVersion::Version0(i) => i as i64, + mp4parse::TimeOffsetVersion::Version1(i) => i as i64, + }; + + self.cur_sample_range.next() + }); + + has_sample.and(Some(self.cur_offset)) + } +} + +impl<'a> TimeOffsetIterator<'a> { + fn next_offset_time(&mut self) -> i64 { + match self.next() { + Some(v) => v as i64, + _ => 0, + } + } +} + +// Convert 'stts' compact table to full table by iterator, +// (sample_count_with_the_same_time, time) => (time, time, time) ... repeats +// sample_count_with_the_same_time. +// +// For example: +// (2, 3000), (1, 2999) to (3000, 3000, 2999). +struct TimeToSampleIteraor<'a> { + cur_sample_count: std::ops::Range, + cur_sample_delta: u32, + stts_iter: std::slice::Iter<'a, mp4parse::Sample>, +} + +impl<'a> Iterator for TimeToSampleIteraor<'a> { + type Item = u32; + + fn next(&mut self) -> Option { + let has_sample = self.cur_sample_count.next() + .or_else(|| { + self.cur_sample_count = match self.stts_iter.next() { + Some(v) => { + self.cur_sample_delta = v.sample_delta; + (0 .. v.sample_count) + }, + _ => (0 .. 0), + }; + + self.cur_sample_count.next() + }); + + has_sample.and(Some(self.cur_sample_delta)) + } +} + +impl<'a> TimeToSampleIteraor<'a> { + fn next_delta(&mut self) -> u32 { + match self.next() { + Some(v) => v, + _ => 0, + } + } +} + +// Convert 'stco' compact table to full table by iterator. +// (start_chunk_num, sample_number) => (start_chunk_num, sample_number), +// (start_chunk_num + 1, sample_number), +// (start_chunk_num + 2, sample_number), +// ... +// (next start_chunk_num, next sample_number), +// ... +// +// For example: +// (1, 5), (5, 10), (9, 2) => (1, 5), (2, 5), (3, 5), (4, 5), (5, 10), (6, 10), +// (7, 10), (8, 10), (9, 2) +struct SampleToChunkIterator<'a> { + chunks: std::ops::Range, + sample_count: u32, + stsc_peek_iter: std::iter::Peekable>, + remain_chunk_count: u32, // total chunk number from 'stco'. +} + +impl<'a> Iterator for SampleToChunkIterator<'a> { + type Item = (u32, u32); + + fn next(&mut self) -> Option<(u32, u32)> { + let has_chunk = self.chunks.next() + .or_else(|| { + self.chunks = match (self.stsc_peek_iter.next(), self.stsc_peek_iter.peek()) { + (Some(next), Some(peek)) => { + self.sample_count = next.samples_per_chunk; + ((next.first_chunk - 1) .. (peek.first_chunk - 1)) + }, + (Some(next), None) => { + self.sample_count = next.samples_per_chunk; + // Total chunk number in 'stsc' could be different to 'stco', + // there could be more chunks at the last 'stsc' record. + ((next.first_chunk - 1) .. next.first_chunk + self.remain_chunk_count -1) + }, + _ => (0 .. 0), + }; + self.remain_chunk_count -= self.chunks.len() as u32; + self.chunks.next() + }); + + has_chunk.map_or(None, |id| { Some((id, self.sample_count)) }) + } +} + +// A helper struct to convert track time to us. +struct PresentationTime { + time: i64, + scale: TrackTimeScale +} + +impl PresentationTime { + fn new(time: i64, scale: TrackTimeScale) -> PresentationTime { + PresentationTime { + time: time, + scale: scale, + } + } + + fn to_us(&self) -> i64 { + let track_time = TrackScaledTime(self.time as u64, self.scale.1); + match track_time_to_us(track_time, self.scale) { + Some(v) => v as i64, + _ => 0, + } + } +} + +fn create_sample_table(track: &Track, track_offset_time: i64) -> Option> { + let timescale = match track.timescale { + Some(t) => t, + _ => return None, + }; + + let (stsc, stco, stsz, stts) = + match (&track.stsc, &track.stco, &track.stsz, &track.stts) { + (&Some(ref a), &Some(ref b), &Some(ref c), &Some(ref d)) => (a, b, c, d), + _ => return None, + }; + + // According to spec, no sync table means every sample is sync sample. + let has_sync_table = match track.stss { + Some(_) => true, + _ => false, + }; + + let mut sample_table = Vec::new(); + let mut sample_size_iter = stsz.sample_sizes.iter(); + + // Get 'stsc' iterator for (chunk_id, chunk_sample_count) and calculate the sample + // offset address. + let stsc_iter = SampleToChunkIterator { + chunks: (0 .. 0), + sample_count: 0, + stsc_peek_iter: stsc.samples.as_slice().iter().peekable(), + remain_chunk_count: stco.offsets.len() as u32, + }; + + for i in stsc_iter { + let chunk_id = i.0 as usize; + let sample_counts = i.1; + let mut cur_position: u64 = stco.offsets[chunk_id]; + for _ in 0 .. sample_counts { + let start_offset = cur_position; + let end_offset = match (stsz.sample_size, sample_size_iter.next()) { + (_, Some(t)) => start_offset + *t as u64, + (t, _) if t > 0 => start_offset + t as u64, + _ => 0, + }; + if end_offset == 0 { + return None; + } + cur_position = end_offset; + + sample_table.push( + mp4parse_indice { + start_offset: start_offset, + end_offset: end_offset, + start_composition: 0, + end_composition: 0, + start_decode: 0, + sync: !has_sync_table, + } + ); + } + } + + // Mark the sync sample in sample_table according to 'stss'. + match &track.stss { + &Some(ref v) => { + for iter in &v.samples { + sample_table[(iter - 1) as usize].sync = true; + } + }, + _ => {} + } + + let ctts_iter = match &track.ctts { + &Some(ref v) => Some(v.samples.as_slice().iter()), + _ => None, + }; + + let mut ctts_offset_iter = TimeOffsetIterator { + cur_sample_range: (0 .. 0), + cur_offset: 0, + ctts_iter: ctts_iter, + }; + + let mut stts_iter = TimeToSampleIteraor { + cur_sample_count: (0 .. 0), + cur_sample_delta: 0, + stts_iter: stts.samples.as_slice().iter(), + }; + + // sum_delta is the sum of stts_iter delta. + // According to sepc: + // decode time => DT(n) = DT(n-1) + STTS(n) + // composition time => CT(n) = DT(n) + CTTS(n) + // Note: + // composition time needs to add the track offset time from 'elst' table. + let mut sum_delta = PresentationTime::new(0, timescale); + for sample in sample_table.as_mut_slice() { + let decode_time = sum_delta.to_us(); + sum_delta.time += stts_iter.next_delta() as i64; + + // ctts_offset is the current sample offset time. + let ctts_offset = PresentationTime::new(ctts_offset_iter.next_offset_time(), timescale); + + let start_composition = (decode_time + ctts_offset.to_us() + track_offset_time) as u64; + let end_composition = (sum_delta.to_us() + ctts_offset.to_us() + track_offset_time) as u64; + + sample.start_decode = decode_time as u64; + sample.start_composition = start_composition; + sample.end_composition = end_composition; + } + + // Correct composition end time due to 'ctts' causes composition time re-ordering. + // + // Composition end time is not in specification. However, gecko needs it, so we need to + // calculate to correct the composition end time. + if track.ctts.is_some() { + // Create an index table refers to sample_table and sorted by start_composisiton time. + let mut sort_table = Vec::new(); + sort_table.reserve(sample_table.len()); + for i in 0 .. sample_table.len() { + sort_table.push(i); + } + + sort_table.sort_by_key(|i| { + match sample_table.get(*i) { + Some(v) => { + v.start_composition + }, + _ => 0, + } + }); + + let iter = sort_table.iter(); + for i in 0 .. (iter.len() - 1) { + let current_index = sort_table[i] as usize; + let peek_index = sort_table[i + 1] as usize; + let next_start_composition_time = sample_table[peek_index].start_composition; + let ref mut sample = sample_table[current_index]; + sample.end_composition = next_start_composition_time; + } + } + + Some(sample_table) +} + /// Fill the supplied `mp4parse_fragment_info` with metadata from fragmented file. #[no_mangle] pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, info: *mut mp4parse_fragment_info) -> mp4parse_error { diff --git a/media/libstagefright/binding/update-rust.sh b/media/libstagefright/binding/update-rust.sh index a8a462f6db2c..7ec3a5149c5e 100755 --- a/media/libstagefright/binding/update-rust.sh +++ b/media/libstagefright/binding/update-rust.sh @@ -2,7 +2,7 @@ # Script to update mp4parse-rust sources to latest upstream # Default version. -VER=v0.6.0 +VER=6dfc85b277f8a072083b71f23cc05981b22a10bc # Accept version or commit from the command line. if test -n "$1"; then diff --git a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp index 2383cdf16c29..80edd9d75af0 100755 --- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp +++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp @@ -823,26 +823,24 @@ WebrtcVideoConduit::GetRTCPSenderReport(DOMHighResTimeStamp* timestamp, unsigned int* packetsSent, uint64_t* bytesSent) { - { - CSFLogVerbose(logTag, "%s for VideoConduit:%p", __FUNCTION__, this); - MutexAutoLock lock(mCodecMutex); - if (!mSendStream) { - return false; - } - - const webrtc::VideoSendStream::Stats& stats = mSendStream->GetStats(); - *packetsSent = 0; - for (auto entry: stats.substreams){ - *packetsSent += entry.second.rtp_stats.transmitted.packets; - // NG -- per https://www.w3.org/TR/webrtc-stats/ this is only payload bytes - *bytesSent += entry.second.rtp_stats.MediaPayloadBytes(); - } - // Note: timestamp is not correct per the spec... should be time the rtcp - // was received (remote) or sent (local) - *timestamp = webrtc::Clock::GetRealTimeClock()->TimeInMilliseconds(); - return true; + CSFLogVerbose(logTag, "%s for VideoConduit:%p", __FUNCTION__, this); + MutexAutoLock lock(mCodecMutex); + if (!mSendStream) { + return false; } - return false; + + const webrtc::VideoSendStream::Stats& stats = mSendStream->GetStats(); + *packetsSent = 0; + for (auto entry: stats.substreams){ + *packetsSent += entry.second.rtp_stats.transmitted.packets; + // NG -- per https://www.w3.org/TR/webrtc-stats/ this is only payload bytes + *bytesSent += entry.second.rtp_stats.MediaPayloadBytes(); + } + + // Note: timestamp is not correct per the spec... should be time the rtcp + // was received (remote) or sent (local) + *timestamp = webrtc::Clock::GetRealTimeClock()->TimeInMilliseconds(); + return true; } MediaConduitErrorCode diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index c4ac87737cee..4d501c4976ec 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -628,8 +628,10 @@ pref("media.mediasource.enabled", true); pref("media.mediadrm-widevinecdm.visible", true); +#ifdef NIGHTLY_BUILD // Enable EME (Encrypted Media Extensions) pref("media.eme.enabled", true); +#endif // optimize images memory usage pref("image.downscale-during-decode.enabled", true); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 341a3bcf6993..0243488fd4d8 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -1218,7 +1218,7 @@ var BrowserApp = { if (fullscreenState) { this.fullscreenTransitionTab = newTab; - doc.exitFullscreen(); + this.selectedBrowser.contentDocument.exitFullscreen(); } if (typeof aParams.tabIndex == "number") { diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 8fb12e67155a..a48191ff7f40 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -3248,6 +3248,12 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI, // share the request context - see bug 1236650 httpChannel->SetRequestContextID(mRequestContextID); + // Preserve the loading order + nsCOMPtr p = do_QueryInterface(newChannel); + if (p) { + p->SetPriority(mPriority); + } + if (httpInternal) { // Convey third party cookie, conservative, and spdy flags. httpInternal->SetThirdPartyFlags(mThirdPartyFlags); diff --git a/netwerk/test/unit/test_channel_priority.js b/netwerk/test/unit/test_channel_priority.js new file mode 100644 index 000000000000..490ad69b5d45 --- /dev/null +++ b/netwerk/test/unit/test_channel_priority.js @@ -0,0 +1,91 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */ +/* globals Cu, Ci, Assert, run_next_test, add_test, do_register_cleanup */ +/* globals runningInParent, do_send_remote_message */ +/* globals ChannelListener */ + +'use strict'; + +/* globals NetUtil*/ +Cu.import('resource://gre/modules/NetUtil.jsm'); +/* globals HttpServer */ +Cu.import('resource://testing-common/httpd.js'); + +let httpserver; +let port; + +function startHttpServer() { + httpserver = new HttpServer(); + + httpserver.registerPathHandler('/resource', (metadata, response) => { + response.setStatusLine(metadata.httpVersion, 200, 'OK'); + response.setHeader('Content-Type', 'text/plain', false); + response.setHeader('Cache-Control', 'no-cache', false); + response.bodyOutputStream.write("data", 4); + }); + + httpserver.registerPathHandler('/redirect', (metadata, response) => { + response.setStatusLine(metadata.httpVersion, 302, 'Redirect'); + response.setHeader('Location', '/resource', false); + response.setHeader('Cache-Control', 'no-cache', false); + }); + + httpserver.start(-1); + port = httpserver.identity.primaryPort; +} + +function stopHttpServer() { + httpserver.stop(()=>{}); +} + +function makeRequest(uri) { + let requestChannel = NetUtil.newChannel({uri, loadUsingSystemPrincipal: true}); + requestChannel.QueryInterface(Ci.nsISupportsPriority); + requestChannel.priority = Ci.nsISupportsPriority.PRIORITY_HIGHEST; + requestChannel.asyncOpen2(new ChannelListener(checkResponse, requestChannel)); +} + +function checkResponse(request, buffer, requestChannel) { + requestChannel.QueryInterface(Ci.nsISupportsPriority); + Assert.equal(requestChannel.priority, Ci.nsISupportsPriority.PRIORITY_HIGHEST); + + // the response channel can be different (if it was redirected) + let responseChannel = request.QueryInterface(Ci.nsISupportsPriority); + Assert.equal(responseChannel.priority, Ci.nsISupportsPriority.PRIORITY_HIGHEST); + + run_next_test(); +} + +add_test(function test_regular_request() { + makeRequest(`http://localhost:${port}/resource`); +}); + +add_test(function test_redirect() { + makeRequest(`http://localhost:${port}/redirect`); +}); + +function run_test() { // jshint ignore:line + if (!runningInParent) { + // add a task to report test finished to parent process at the end of test queue, + // since do_register_cleanup is not available in child xpcshell test script. + add_test(function () { + do_send_remote_message('finished'); + run_next_test(); + }); + + // waiting for parent process to assign server port via configPort() + return; + } + + startHttpServer(); + do_register_cleanup(stopHttpServer); + run_next_test(); +} + +// This is used by unit_ipc/test_channel_priority_wrap.js for e10s XPCShell test +function configPort(serverPort) { // jshint ignore:line + port = serverPort; + run_next_test(); +} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 120e5d3580c7..e380ba7dc15a 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -381,3 +381,4 @@ skip-if = os == "android" [test_rusturl.js] [test_trackingProtection_annotateChannels.js] [test_race_cache_network.js] +[test_channel_priority.js] diff --git a/netwerk/test/unit_ipc/test_channel_priority_wrap.js b/netwerk/test/unit_ipc/test_channel_priority_wrap.js new file mode 100644 index 000000000000..a53a529e1796 --- /dev/null +++ b/netwerk/test/unit_ipc/test_channel_priority_wrap.js @@ -0,0 +1,50 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */ +/* globals Cu, do_register_cleanup, do_test_finished */ +/* globals run_test_in_child, sendCommand, do_await_remote_message */ + +'use strict'; + +/* globals HttpServer */ +Cu.import('resource://testing-common/httpd.js'); + +let httpserver; +let port; + +function startHttpServer() { + httpserver = new HttpServer(); + + httpserver.registerPathHandler('/resource', (metadata, response) => { + response.setStatusLine(metadata.httpVersion, 200, 'OK'); + response.setHeader('Content-Type', 'text/plain', false); + response.setHeader('Cache-Control', 'no-cache', false); + response.bodyOutputStream.write("data", 4); + }); + + httpserver.registerPathHandler('/redirect', (metadata, response) => { + response.setStatusLine(metadata.httpVersion, 302, 'Redirect'); + response.setHeader('Location', '/resource', false); + response.setHeader('Cache-Control', 'no-cache', false); + }); + + httpserver.start(-1); + port = httpserver.identity.primaryPort; +} + +function stopHttpServer() { + httpserver.stop(()=>{}); +} + +function run_test() { // jshint ignore:line + do_register_cleanup(stopHttpServer); + + run_test_in_child('../unit/test_channel_priority.js', () => { + startHttpServer(); + sendCommand(`configPort(${port});`); + do_await_remote_message('finished').then(() => { + do_test_finished(); + }); + }); +} diff --git a/netwerk/test/unit_ipc/xpcshell.ini b/netwerk/test/unit_ipc/xpcshell.ini index a1f98765643f..8a6a1ea29d4a 100644 --- a/netwerk/test/unit_ipc/xpcshell.ini +++ b/netwerk/test/unit_ipc/xpcshell.ini @@ -55,6 +55,7 @@ support-files = !/netwerk/test/unit/data/signed_win.exe !/netwerk/test/unit/test_alt-data_simple.js !/netwerk/test/unit/test_alt-data_stream.js + !/netwerk/test/unit/test_channel_priority.js [test_bug528292_wrap.js] [test_bug248970_cookie_wrap.js] @@ -99,3 +100,4 @@ skip-if = true [test_channel_id.js] [test_trackingProtection_annotateChannels_wrap1.js] [test_trackingProtection_annotateChannels_wrap2.js] +[test_channel_priority_wrap.js] diff --git a/python/mozbuild/mozbuild/base.py b/python/mozbuild/mozbuild/base.py index 30535ebc07f3..c33ddeddaf2a 100644 --- a/python/mozbuild/mozbuild/base.py +++ b/python/mozbuild/mozbuild/base.py @@ -777,16 +777,22 @@ class MachCommandConditions(object): """Must have a mercurial source checkout.""" if hasattr(cls, 'substs'): top_srcdir = cls.substs.get('top_srcdir') - return top_srcdir and os.path.isdir(os.path.join(top_srcdir, '.hg')) - return False + elif hasattr(cls, 'topsrcdir'): + top_srcdir = cls.topsrcdir + else: + return False + return top_srcdir and os.path.isdir(os.path.join(top_srcdir, '.hg')) @staticmethod def is_git(cls): """Must have a git source checkout.""" if hasattr(cls, 'substs'): top_srcdir = cls.substs.get('top_srcdir') - return top_srcdir and os.path.isdir(os.path.join(top_srcdir, '.git')) - return False + elif hasattr(cls, 'topsrcdir'): + top_srcdir = cls.topsrcdir + else: + return False + return top_srcdir and os.path.exists(os.path.join(top_srcdir, '.git')) class PathArgument(object): diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index da8eee35f5c1..08711bfd1fa0 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -695,9 +695,9 @@ class Clobber(MachCommandBase): raise if 'python' in what: - if os.path.isdir(mozpath.join(self.topsrcdir, '.hg')): + if conditions.is_hg(self): cmd = ['hg', 'purge', '--all', '-I', 'glob:**.py[co]'] - elif os.path.isdir(mozpath.join(self.topsrcdir, '.git')): + elif conditions.is_git(self): cmd = ['git', 'clean', '-f', '-x', '*.py[co]'] else: cmd = ['find', '.', '-type', 'f', '-name', '*.py[co]', '-delete'] diff --git a/services/common/kinto-storage-adapter.js b/services/common/kinto-storage-adapter.js index bcfcc0b9e82e..6448620a2c6b 100644 --- a/services/common/kinto-storage-adapter.js +++ b/services/common/kinto-storage-adapter.js @@ -173,6 +173,11 @@ const statements = { "scanAllRecords": `SELECT * FROM collection_data;`, "clearCollectionMetadata": `DELETE FROM collection_metadata;`, + + "calculateStorage": ` + SELECT collection_name, SUM(LENGTH(record)) as size, COUNT(record) as num_records + FROM collection_data + GROUP BY collection_name;`, }; const createStatements = [ @@ -380,6 +385,17 @@ class FirefoxAdapter extends Kinto.adapters.BaseAdapter { }); } + calculateStorage() { + return this._executeStatement(statements.calculateStorage, {}) + .then(result => { + return Array.from(result, row => ({ + collectionName: row.getResultByName("collection_name"), + size: row.getResultByName("size"), + numRecords: row.getResultByName("num_records"), + })); + }); + } + /** * Reset the sync status of every record and collection we have * access to. diff --git a/servo/Cargo.lock b/servo/Cargo.lock index c1e574396acf..9ee6cb8a9890 100644 --- a/servo/Cargo.lock +++ b/servo/Cargo.lock @@ -154,7 +154,7 @@ dependencies = [ "cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "clang-sys 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.20.4 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.20.5 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -281,7 +281,7 @@ dependencies = [ "ipc-channel 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "offscreen_gl_context 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "offscreen_gl_context 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "servo_config 0.0.1", "webrender_traits 0.16.0 (git+https://github.com/servo/webrender)", ] @@ -345,14 +345,14 @@ dependencies = [ [[package]] name = "clap" -version = "2.20.4" +version = "2.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "term_size 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -442,7 +442,7 @@ dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", - "offscreen_gl_context 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "offscreen_gl_context 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "profile_traits 0.0.1", "script_traits 0.0.1", "serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -462,7 +462,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -521,7 +521,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -608,7 +608,7 @@ dependencies = [ "msg 0.0.1", "serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -633,7 +633,7 @@ dependencies = [ name = "domobject_derive" version = "0.0.1" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1075,7 +1075,7 @@ name = "heapsize_derive" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1119,7 +1119,7 @@ dependencies = [ "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", "tendril 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1275,7 +1275,7 @@ dependencies = [ name = "jstraceable_derive" version = "0.0.1" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1329,7 +1329,7 @@ dependencies = [ "selectors 0.18.0", "serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "servo_config 0.0.1", "servo_geometry 0.0.1", "servo_url 0.0.1", @@ -1373,7 +1373,7 @@ dependencies = [ "script_traits 0.0.1", "selectors 0.18.0", "serde_derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "servo_config 0.0.1", "servo_geometry 0.0.1", "servo_url 0.0.1", @@ -1801,7 +1801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "offscreen_gl_context" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cgl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2020,7 +2020,7 @@ dependencies = [ "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "servo_config 0.0.1", "task_info 0.0.1", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2072,7 +2072,7 @@ dependencies = [ [[package]] name = "quote" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2128,7 +2128,7 @@ dependencies = [ "aho-corasick 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2221,7 +2221,7 @@ dependencies = [ "msg 0.0.1", "net_traits 0.0.1", "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "offscreen_gl_context 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "offscreen_gl_context 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "open 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2238,7 +2238,7 @@ dependencies = [ "script_traits 0.0.1", "selectors 0.18.0", "serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "servo_atoms 0.0.1", "servo_config 0.0.1", "servo_geometry 0.0.1", @@ -2317,7 +2317,7 @@ dependencies = [ "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", - "offscreen_gl_context 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "offscreen_gl_context 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "profile_traits 0.0.1", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2354,7 +2354,7 @@ name = "serde_codegen" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "serde_codegen_internals 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.54.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2374,7 +2374,7 @@ name = "serde_codegen_internals" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2382,14 +2382,14 @@ name = "serde_derive" version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "serde_codegen_internals 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2798,16 +2798,25 @@ name = "syn" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "0.11.4" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synom" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2816,7 +2825,7 @@ name = "synstructure" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2872,7 +2881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2912,7 +2921,7 @@ dependencies = [ [[package]] name = "term_size" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2931,7 +2940,7 @@ dependencies = [ [[package]] name = "thread_local" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3169,7 +3178,7 @@ dependencies = [ "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "offscreen_gl_context 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "offscreen_gl_context 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "servo-dwrote 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "threadpool 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3189,7 +3198,7 @@ dependencies = [ "gleam 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "offscreen_gl_context 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "offscreen_gl_context 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "servo-dwrote 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3345,7 +3354,7 @@ dependencies = [ "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" "checksum cgl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8bdd78cca65a739cb5475dbf6b6bbb49373e327f4a6f2b499c0f98632df38c10" "checksum clang-sys 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f98f0715ff67f27ca6a2f8f0ffc2a56f8edbc7acd57489c29eadc3a15c4eafe" -"checksum clap 2.20.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a60af5cb867dd4ee2378398acde80c73b466b58a963f598061ce7e394800998d" +"checksum clap 2.20.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7db281b0520e97fbd15cd615dcd8f8bcad0c26f5f7d5effe705f090f39e9a758" "checksum cmake 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "a3a6805df695087e7c1bcd9a82e03ad6fb864c8e67ac41b1348229ce5b7f0407" "checksum cocoa 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d55b620aff4da7d4b9d85f2974cc62a097146623b75e3f36734fe68d8cef493e" "checksum color_quant 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a475fc4af42d83d28adf72968d9bcfaf035a1a9381642d8e85d8a04957767b0d" @@ -3453,7 +3462,7 @@ dependencies = [ "checksum num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a225d1e2717567599c24f88e49f00856c6e825a12125181ee42c4257e3688d39" "checksum objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "877f30f37acef6749b1841cceab289707f211aecfc756553cd63976190e6cc2e" "checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba" -"checksum offscreen_gl_context 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b33309fc17d50be59b466fe26a337023f297e8c9e9032ca0ccfdcdf3c0c627d0" +"checksum offscreen_gl_context 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ac875ea951d7d695a1cc8c370777d6a0e2b7355ca49506034683df09b24b1bc" "checksum ogg 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "426d8dc59cdd206be1925461087350385c0a02f291d87625829c6d08e72b457b" "checksum ogg_metadata 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e755cc735fa6faa709cb23048433d9201d6caa85fa96215386ccdd5e9b40ad01" "checksum open 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3478ed1686bd1300c8a981a940abc92b06fac9cbef747f4c668d4e032ff7b842" @@ -3477,7 +3486,7 @@ dependencies = [ "checksum png 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3cb773e9a557edb568ce9935cf783e3cdcabe06a9449d41b3e5506d88e582c82" "checksum quasi 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dcbf815446dc6a0afbc72d88f9a8aa71b608d10b168e09437c80c0fd6fd410c9" "checksum quasi_codegen 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b06172e92ab0099427609854ffb1512c377be5fc4beaf572ae5d5a01b8359596" -"checksum quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b44fd83db28b83c1c58187159934906e5e955c812e211df413b76b03c909a5" +"checksum quote 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "08de3f12e670f83f61e450443cbae34496a35b665691fd8e99b24ec662f75865" "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" "checksum rayon 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50c575b58c2b109e2fbc181820cbe177474f35610ff9e357dc75f6bac854ffbf" "checksum redox_syscall 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd35cc9a8bdec562c757e3d43c1526b5c6d2653e23e2315065bc25556550753" @@ -3497,7 +3506,7 @@ dependencies = [ "checksum serde_codegen_internals 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "afad7924a009f859f380e4a2e3a509a845c2ac66435fcead74a4d983b21ae806" "checksum serde_codegen_internals 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3172bf2940b975c0e4f6ab42a511c0a4407d4f46ccef87a9d3615db5c26fa96" "checksum serde_derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6af30425c5161deb200aac4803c62b903eb3be7e889c5823d0e16c4ce0ce989c" -"checksum serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e095e4e94e7382b76f48e93bd845ffddda62df8dfd4c163b1bfa93d40e22e13a" +"checksum serde_json 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb96d30e4e6f9fc52e08f51176d078b6f79b981dc3ed4134f7b850be9f446a8" "checksum servo-dwrote 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f013da79c3fb2a9653534b064cd2ca62e10f8b6d19ed8fdc885cb2873412789" "checksum servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "21069a884c33fe6ee596975e1f3849ed88c4ec857fbaf11d33672d8ebe051217" "checksum servo-fontconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93f799b649b4a2bf362398910eca35240704c7e765e780349b2bb1070d892262" @@ -3520,7 +3529,8 @@ dependencies = [ "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)" = "58fd09df59565db3399efbba34ba8a2fec1307511ebd245d0061ff9d42691673" -"checksum syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f94368aae82bb29656c98443a7026ca931a659e8d19dcdc41d6e273054e820" +"checksum syn 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0e28da8d02d75d1e58b89258e0741128f0b0d8a8309fb5c627be0fbd37a76c67" +"checksum synom 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8fece1853fb872b0acdc3ff88f37c474018e125ef81cd4cb8c0ca515746b62ed" "checksum synstructure 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a811f8e51453cada27c033be6b5fdac6e4e63981983702eb85b4c897a25ecc6c" "checksum syntex 0.54.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb3f52553a966675982404dc34028291b347e0c9a9c0b0b34f2da6be8a0443f8" "checksum syntex_errors 0.54.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dee2f6e49c075f71332bb775219d5982bee6732d26227fa1ae1b53cdb12f5cc5" @@ -3530,9 +3540,9 @@ dependencies = [ "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" "checksum tendril 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cebf864c2d90394a1b66d6fe45963f9a177f2af81a0edea5060f77627f9c4587" "checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989" -"checksum term_size 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "71662702fe5cd2cf95edd4ad655eea42f24a87a0e44059cbaa4e55260b7bc331" +"checksum term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "07b6c1ac5b3fffd75073276bca1ceed01f67a28537097a2a9539e116e50fb21a" "checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a" -"checksum thread_local 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7793b722f0f77ce716e7f1acf416359ca32ff24d04ffbac4269f44a4a83be05d" +"checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7" "checksum thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf947d192a9be60ef5131cc7a4648886ba89d712f16700ebbf80c8a69d05d48f" "checksum threadpool 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "59f6d3eff89920113dac9db44dde461d71d01e88a5b57b258a0466c32b5d7fe1" "checksum time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "211b63c112206356ef1ff9b19355f43740fc3f85960c598a93d3a3d3ba7beade" diff --git a/servo/components/compositing/compositor.rs b/servo/components/compositing/compositor.rs index 2673af8b439d..b7eb2633e68b 100644 --- a/servo/components/compositing/compositor.rs +++ b/servo/components/compositing/compositor.rs @@ -27,7 +27,7 @@ use script_traits::{TouchpadPressurePhase, TouchEventType, TouchId, WindowSizeDa use script_traits::CompositorEvent::{self, MouseMoveEvent, MouseButtonEvent, TouchEvent, TouchpadPressureEvent}; use servo_config::opts; use servo_config::prefs::PREFS; -use servo_geometry::ScreenPx; +use servo_geometry::DeviceIndependentPixel; use servo_url::ServoUrl; use std::collections::HashMap; use std::fs::File; @@ -155,10 +155,10 @@ pub struct IOCompositor { /// "Desktop-style" zoom that resizes the viewport to fit the window. /// See `ViewportPx` docs in util/geom.rs for details. - page_zoom: ScaleFactor, + page_zoom: ScaleFactor, /// The device pixel ratio for this window. - scale_factor: ScaleFactor, + scale_factor: ScaleFactor, channel_to_self: Box, @@ -378,7 +378,7 @@ impl IOCompositor { fn new(window: Rc, state: InitialCompositorState) -> IOCompositor { let window_size = window.framebuffer_size(); - let scale_factor = window.scale_factor(); + let scale_factor = window.hidpi_factor(); let composite_target = match opts::get().output_file { Some(_) => CompositeTarget::PngFile, None => CompositeTarget::Window @@ -756,7 +756,7 @@ impl IOCompositor { } fn send_window_size(&self, size_type: WindowSizeType) { - let dppx = self.page_zoom * self.device_pixels_per_screen_px(); + let dppx = self.page_zoom * self.hidpi_factor(); let initial_viewport = self.window_size.to_f32() / dppx; let visible_viewport = initial_viewport / self.viewport_zoom; let msg = ConstellationMsg::WindowSize(WindowSizeData { @@ -889,7 +889,7 @@ impl IOCompositor { debug!("compositor resizing to {:?}", new_size.to_untyped()); // A size change could also mean a resolution change. - let new_scale_factor = self.window.scale_factor(); + let new_scale_factor = self.window.hidpi_factor(); if self.scale_factor != new_scale_factor { self.scale_factor = new_scale_factor; self.update_zoom_transform(); @@ -948,7 +948,7 @@ impl IOCompositor { }; if let Some(pipeline) = self.pipeline(root_pipeline_id) { - let dppx = self.page_zoom * self.device_pixels_per_screen_px(); + let dppx = self.page_zoom * self.hidpi_factor(); let translated_point = (point / dppx).to_untyped(); let event_to_send = match mouse_window_event { MouseWindowEvent::Click(button, _) => { @@ -986,7 +986,7 @@ impl IOCompositor { return; } - let dppx = self.page_zoom * self.device_pixels_per_screen_px(); + let dppx = self.page_zoom * self.hidpi_factor(); let event_to_send = MouseMoveEvent(Some((cursor / dppx).to_untyped())); let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event_to_send); if let Some(pipeline) = self.pipeline(root_pipeline_id) { @@ -1012,7 +1012,7 @@ impl IOCompositor { fn on_touch_down(&mut self, identifier: TouchId, point: TypedPoint2D) { self.touch_handler.on_touch_down(identifier, point); - let dppx = self.page_zoom * self.device_pixels_per_screen_px(); + let dppx = self.page_zoom * self.hidpi_factor(); let translated_point = (point / dppx).to_untyped(); self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Down, identifier, @@ -1042,7 +1042,7 @@ impl IOCompositor { }); } TouchAction::DispatchEvent => { - let dppx = self.page_zoom * self.device_pixels_per_screen_px(); + let dppx = self.page_zoom * self.hidpi_factor(); let translated_point = (point / dppx).to_untyped(); self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Move, identifier, @@ -1053,7 +1053,7 @@ impl IOCompositor { } fn on_touch_up(&mut self, identifier: TouchId, point: TypedPoint2D) { - let dppx = self.page_zoom * self.device_pixels_per_screen_px(); + let dppx = self.page_zoom * self.hidpi_factor(); let translated_point = (point / dppx).to_untyped(); self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Up, identifier, @@ -1066,7 +1066,7 @@ impl IOCompositor { fn on_touch_cancel(&mut self, identifier: TouchId, point: TypedPoint2D) { // Send the event to script. self.touch_handler.on_touch_cancel(identifier, point); - let dppx = self.page_zoom * self.device_pixels_per_screen_px(); + let dppx = self.page_zoom * self.hidpi_factor(); let translated_point = (point / dppx).to_untyped(); self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Cancel, identifier, @@ -1078,7 +1078,7 @@ impl IOCompositor { pressure: f32, phase: TouchpadPressurePhase) { if let Some(true) = PREFS.get("dom.forcetouch.enabled").as_boolean() { - let dppx = self.page_zoom * self.device_pixels_per_screen_px(); + let dppx = self.page_zoom * self.hidpi_factor(); let translated_point = (point / dppx).to_untyped(); self.send_event_to_root_pipeline(TouchpadPressureEvent(translated_point, pressure, @@ -1291,7 +1291,7 @@ impl IOCompositor { } } - fn device_pixels_per_screen_px(&self) -> ScaleFactor { + fn hidpi_factor(&self) -> ScaleFactor { match opts::get().device_pixels_per_px { Some(device_pixels_per_px) => ScaleFactor::new(device_pixels_per_px), None => match opts::get().output_file { @@ -1302,7 +1302,7 @@ impl IOCompositor { } fn device_pixels_per_page_px(&self) -> ScaleFactor { - self.viewport_zoom * self.page_zoom * self.device_pixels_per_screen_px() + self.viewport_zoom * self.page_zoom * self.hidpi_factor() } fn update_zoom_transform(&mut self) { diff --git a/servo/components/compositing/touch.rs b/servo/components/compositing/touch.rs index a158c1e5c332..12bc365c77f9 100644 --- a/servo/components/compositing/touch.rs +++ b/servo/components/compositing/touch.rs @@ -7,7 +7,7 @@ use euclid::scale_factor::ScaleFactor; use script_traits::{DevicePixel, EventResult, TouchId}; use self::TouchState::*; -/// Minimum number of `ScreenPx` to begin touch scrolling. +/// Minimum number of `DeviceIndependentPixel` to begin touch scrolling. const TOUCH_PAN_MIN_SCREEN_PX: f32 = 20.0; pub struct TouchHandler { @@ -100,7 +100,6 @@ impl TouchHandler { let action = match self.state { Touching => { let delta = point - old_point; - // TODO let delta: TypedPoint2D<_, ScreenPx> = delta / self.device_pixels_per_screen_px(); if delta.x.abs() > TOUCH_PAN_MIN_SCREEN_PX || delta.y.abs() > TOUCH_PAN_MIN_SCREEN_PX diff --git a/servo/components/compositing/windowing.rs b/servo/components/compositing/windowing.rs index 39b117c85b6d..85d73af2f9f1 100644 --- a/servo/components/compositing/windowing.rs +++ b/servo/components/compositing/windowing.rs @@ -12,7 +12,7 @@ use euclid::size::TypedSize2D; use msg::constellation_msg::{Key, KeyModifiers, KeyState}; use net_traits::net_error_list::NetError; use script_traits::{DevicePixel, MouseButton, TouchEventType, TouchId, TouchpadPressurePhase}; -use servo_geometry::ScreenPx; +use servo_geometry::DeviceIndependentPixel; use servo_url::ServoUrl; use std::fmt::{Debug, Error, Formatter}; use style_traits::cursor::Cursor; @@ -109,7 +109,7 @@ pub trait WindowMethods { /// Returns the size of the window in hardware pixels. fn framebuffer_size(&self) -> TypedSize2D; /// Returns the size of the window in density-independent "px" units. - fn size(&self) -> TypedSize2D; + fn size(&self) -> TypedSize2D; /// Presents the window to the screen (perhaps by page flipping). fn present(&self); @@ -137,8 +137,8 @@ pub trait WindowMethods { /// Called when the tag has finished parsing fn head_parsed(&self); - /// Returns the scale factor of the system (device pixels / screen pixels). - fn scale_factor(&self) -> ScaleFactor; + /// Returns the scale factor of the system (device pixels / device independent pixels). + fn hidpi_factor(&self) -> ScaleFactor; /// Creates a channel to the compositor. The dummy parameter is needed because we don't have /// UFCS in Rust yet. diff --git a/servo/components/config/opts.rs b/servo/components/config/opts.rs index df2bb2e041ee..ca0c24f8d096 100644 --- a/servo/components/config/opts.rs +++ b/servo/components/config/opts.rs @@ -10,7 +10,7 @@ use getopts::Options; use num_cpus; use prefs::{self, PrefValue, PREFS}; use resource_files::set_resources_path; -use servo_geometry::ScreenPx; +use servo_geometry::DeviceIndependentPixel; use servo_url::ServoUrl; use std::borrow::Cow; use std::cmp; @@ -143,7 +143,7 @@ pub struct Opts { pub webdriver_port: Option, /// The initial requested size of the window. - pub initial_window_size: TypedSize2D, + pub initial_window_size: TypedSize2D, /// An optional string allowing the user agent to be set for testing. pub user_agent: Cow<'static, str>, diff --git a/servo/components/geometry/lib.rs b/servo/components/geometry/lib.rs index ddca8d719727..55c1a165295b 100644 --- a/servo/components/geometry/lib.rs +++ b/servo/components/geometry/lib.rs @@ -20,17 +20,17 @@ use std::i32; /// should approximate a device-independent reference length. This unit corresponds to Android's /// "density-independent pixel" (dip), Mac OS X's "point", and Windows "device-independent pixel." /// -/// The relationship between DevicePixel and ScreenPx is defined by the OS. On most low-dpi -/// screens, one ScreenPx is equal to one DevicePixel. But on high-density screens it can be -/// some larger number. For example, by default on Apple "retina" displays, one ScreenPx equals -/// two DevicePixels. On Android "MDPI" displays, one ScreenPx equals 1.5 device pixels. +/// The relationship between DevicePixel and DeviceIndependentPixel is defined by the OS. On most low-dpi +/// screens, one DeviceIndependentPixel is equal to one DevicePixel. But on high-density screens it can be +/// some larger number. For example, by default on Apple "retina" displays, one DeviceIndependentPixel equals +/// two DevicePixels. On Android "MDPI" displays, one DeviceIndependentPixel equals 1.5 device pixels. /// -/// The ratio between ScreenPx and DevicePixel for a given display be found by calling +/// The ratio between DeviceIndependentPixel and DevicePixel for a given display be found by calling /// `servo::windowing::WindowMethods::hidpi_factor`. #[derive(Clone, Copy, Debug)] -pub enum ScreenPx {} +pub enum DeviceIndependentPixel {} -known_heap_size!(0, ScreenPx); +known_heap_size!(0, DeviceIndependentPixel); // An Au is an "App Unit" and represents 1/60th of a CSS pixel. It was // originally proposed in 2002 as a standard unit of measure in Gecko. diff --git a/servo/components/servo/lib.rs b/servo/components/servo/lib.rs index 38b9b7b83250..afda35f1582a 100644 --- a/servo/components/servo/lib.rs +++ b/servo/components/servo/lib.rs @@ -149,7 +149,7 @@ impl Browser where Window: WindowMethods + 'static { let (webrender, webrender_api_sender) = { // TODO(gw): Duplicates device_pixels_per_screen_px from compositor. Tidy up! - let scale_factor = window.scale_factor().get(); + let scale_factor = window.hidpi_factor().get(); let device_pixel_ratio = match opts.device_pixels_per_px { Some(device_pixels_per_px) => device_pixels_per_px, None => match opts.output_file { diff --git a/servo/components/style/build_gecko.rs b/servo/components/style/build_gecko.rs index b62ec7e0f0c2..8d530f7e34f9 100644 --- a/servo/components/style/build_gecko.rs +++ b/servo/components/style/build_gecko.rs @@ -232,6 +232,24 @@ mod bindings { File::create(&out_file).unwrap().write_all(&result.into_bytes()).expect("Unable to write output"); } + fn get_arc_types() -> Vec { + // Read the file + let mut list_file = File::open(DISTDIR_PATH.join("include/mozilla/ServoArcTypeList.h")) + .expect("Unable to open ServoArcTypeList.h"); + let mut content = String::new(); + list_file.read_to_string(&mut content).expect("Fail to read ServoArcTypeList.h"); + // Remove comments + let block_comment_re = Regex::new(r#"(?s)/\*.*?\*/"#).unwrap(); + let content = block_comment_re.replace_all(&content, ""); + // Extract the list + let re = Regex::new(r#"^SERVO_ARC_TYPE\(\w+,\s*(\w+)\)$"#).unwrap(); + content.lines().map(|line| line.trim()).filter(|line| !line.is_empty()) + .map(|line| re.captures(&line) + .expect(&format!("Unrecognized line in ServoArcTypeList.h: '{}'", line)) + .get(1).unwrap().as_str().to_string()) + .collect() + } + pub fn generate_structs(build_type: BuildType) { let mut builder = Builder::get_initial_builder(build_type) .enable_cxx_namespaces() @@ -600,15 +618,6 @@ mod bindings { let array_types = [ ArrayType { cpp_type: "uintptr_t", rust_type: "usize" }, ]; - let servo_nullable_arc_types = [ - "ServoComputedValues", - "ServoCssRules", - "RawServoStyleSheet", - "RawServoDeclarationBlock", - "RawServoStyleRule", - "RawServoImportRule", - "RawServoAnimationValue", - ]; struct ServoOwnedType { name: &'static str, opaque: bool, @@ -646,7 +655,7 @@ mod bindings { .raw_line(format!("pub type nsTArrayBorrowed_{}<'a> = &'a mut ::gecko_bindings::structs::nsTArray<{}>;", cpp_type, rust_type)) } - for &ty in servo_nullable_arc_types.iter() { + for ty in get_arc_types().iter() { builder = builder .hide_type(format!("{}Strong", ty)) .raw_line(format!("pub type {0}Strong = ::gecko_bindings::sugar::ownership::Strong<{0}>;", ty)) diff --git a/servo/components/style_traits/lib.rs b/servo/components/style_traits/lib.rs index 4eadd6404026..4ee378833a46 100644 --- a/servo/components/style_traits/lib.rs +++ b/servo/components/style_traits/lib.rs @@ -29,11 +29,11 @@ pub type UnsafeNode = (usize, usize); /// One CSS "px" in the coordinate system of the "initial viewport": /// http://www.w3.org/TR/css-device-adapt/#initial-viewport /// -/// `ViewportPx` is equal to `ScreenPx` times a "page zoom" factor controlled by the user. This is +/// `ViewportPx` is equal to `DeviceIndependentPixel` times a "page zoom" factor controlled by the user. This is /// the desktop-style "full page" zoom that enlarges content but then reflows the layout viewport /// so it still exactly fits the visible area. /// -/// At the default zoom level of 100%, one `PagePx` is equal to one `ScreenPx`. However, if the +/// At the default zoom level of 100%, one `PagePx` is equal to one `DeviceIndependentPixel`. However, if the /// document is zoomed in or out then this scale may be larger or smaller. #[derive(Clone, Copy, Debug)] pub enum ViewportPx {} @@ -50,7 +50,7 @@ pub enum PagePx {} // In summary, the hierarchy of pixel units and the factors to convert from one to the next: // // DevicePixel -// / hidpi_ratio => ScreenPx +// / hidpi_ratio => DeviceIndependentPixel // / desktop_zoom => ViewportPx // / pinch_zoom => PagePx diff --git a/servo/ports/cef/window.rs b/servo/ports/cef/window.rs index 958bc0b22215..4c31a890774e 100644 --- a/servo/ports/cef/window.rs +++ b/servo/ports/cef/window.rs @@ -26,7 +26,7 @@ use gleam::gl; use msg::constellation_msg::{Key, KeyModifiers}; use net_traits::net_error_list::NetError; use script_traits::DevicePixel; -use servo_geometry::ScreenPx; +use servo_geometry::DeviceIndependentPixel; use std::cell::RefCell; use std::ffi::CString; use std::os::raw::{c_char, c_void}; @@ -206,7 +206,7 @@ impl WindowMethods for Window { } } - fn size(&self) -> TypedSize2D { + fn size(&self) -> TypedSize2D { let browser = self.cef_browser.borrow(); match *browser { None => TypedSize2D::new(400.0, 300.0), @@ -250,7 +250,7 @@ impl WindowMethods for Window { } } - fn scale_factor(&self) -> ScaleFactor { + fn hidpi_factor(&self) -> ScaleFactor { if cfg!(target_os="macos") { let browser = self.cef_browser.borrow(); match *browser { diff --git a/servo/ports/glutin/window.rs b/servo/ports/glutin/window.rs index 2ed98b0f6824..338bb056ebd1 100644 --- a/servo/ports/glutin/window.rs +++ b/servo/ports/glutin/window.rs @@ -28,7 +28,7 @@ use script_traits::{DevicePixel, TouchEventType, TouchpadPressurePhase}; use servo_config::opts; use servo_config::prefs::PREFS; use servo_config::resource_files; -use servo_geometry::ScreenPx; +use servo_geometry::DeviceIndependentPixel; use servo_url::ServoUrl; use std::cell::{Cell, RefCell}; #[cfg(any(target_os = "linux", target_os = "macos"))] @@ -193,12 +193,12 @@ pub struct Window { } #[cfg(not(target_os = "windows"))] -fn window_creation_scale_factor() -> ScaleFactor { +fn window_creation_scale_factor() -> ScaleFactor { ScaleFactor::new(1.0) } #[cfg(target_os = "windows")] -fn window_creation_scale_factor() -> ScaleFactor { +fn window_creation_scale_factor() -> ScaleFactor { let hdc = unsafe { user32::GetDC(::std::ptr::null_mut()) }; let ppi = unsafe { gdi32::GetDeviceCaps(hdc, winapi::wingdi::LOGPIXELSY) }; ScaleFactor::new(ppi as f32 / 96.0) @@ -207,7 +207,7 @@ fn window_creation_scale_factor() -> ScaleFactor { impl Window { pub fn new(is_foreground: bool, - window_size: TypedSize2D, + window_size: TypedSize2D, parent: Option) -> Rc { let win_size: TypedSize2D = (window_size.to_f32() * window_creation_scale_factor()) @@ -797,7 +797,7 @@ impl WindowMethods for Window { } } - fn size(&self) -> TypedSize2D { + fn size(&self) -> TypedSize2D { match self.kind { WindowKind::Window(ref window) => { // TODO(ajeffrey): can this fail? @@ -881,7 +881,7 @@ impl WindowMethods for Window { } #[cfg(not(target_os = "windows"))] - fn scale_factor(&self) -> ScaleFactor { + fn hidpi_factor(&self) -> ScaleFactor { match self.kind { WindowKind::Window(ref window) => { ScaleFactor::new(window.hidpi_factor()) @@ -893,7 +893,7 @@ impl WindowMethods for Window { } #[cfg(target_os = "windows")] - fn scale_factor(&self) -> ScaleFactor { + fn hidpi_factor(&self) -> ScaleFactor { let hdc = unsafe { user32::GetDC(::std::ptr::null_mut()) }; let ppi = unsafe { gdi32::GetDeviceCaps(hdc, winapi::wingdi::LOGPIXELSY) }; ScaleFactor::new(ppi as f32 / 96.0) diff --git a/taskcluster/mach_commands.py b/taskcluster/mach_commands.py index 176fda2c83ee..749b684c4dbc 100644 --- a/taskcluster/mach_commands.py +++ b/taskcluster/mach_commands.py @@ -22,8 +22,6 @@ from mach.decorators import ( from mozbuild.base import MachCommandBase -ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}' - class ShowTaskGraphSubCommand(SubCommand): """A SubCommand with TaskGraph-specific arguments""" diff --git a/taskcluster/taskgraph/__init__.py b/taskcluster/taskgraph/__init__.py index e69de29bb2d1..84b524c81a37 100644 --- a/taskcluster/taskgraph/__init__.py +++ b/taskcluster/taskgraph/__init__.py @@ -0,0 +1,7 @@ +# 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/. + +import os + +GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..')) diff --git a/taskcluster/taskgraph/action.py b/taskcluster/taskgraph/action.py index 2f7a0ed46f93..601435d6a3dd 100644 --- a/taskcluster/taskgraph/action.py +++ b/taskcluster/taskgraph/action.py @@ -6,18 +6,17 @@ from __future__ import absolute_import, print_function, unicode_literals -import json import logging import requests -import yaml from .create import create_tasks from .decision import write_artifact from .optimize import optimize_task_graph from .taskgraph import TaskGraph +from .util.taskcluster import get_artifact + logger = logging.getLogger(__name__) -TASKCLUSTER_QUEUE_URL = "https://queue.taskcluster.net/v1/task" TREEHERDER_URL = "https://treeherder.mozilla.org/api" # We set this to 5 for now because this is what SETA sets the @@ -63,15 +62,6 @@ def add_tasks(decision_task_id, task_labels, prefix=''): create_tasks(optimized_graph, label_to_taskid, decision_params) -def get_artifact(task_id, path): - resp = requests.get(url="{}/{}/artifacts/{}".format(TASKCLUSTER_QUEUE_URL, task_id, path)) - if path.endswith('.json'): - artifact = json.loads(resp.text) - elif path.endswith('.yml'): - artifact = yaml.load(resp.text) - return artifact - - def backfill(project, job_id): """ Run the backfill task. This function implements `mach taskgraph backfill-task`, diff --git a/taskcluster/taskgraph/cron/__init__.py b/taskcluster/taskgraph/cron/__init__.py index 9745cea38acc..8c372892a1d1 100644 --- a/taskcluster/taskgraph/cron/__init__.py +++ b/taskcluster/taskgraph/cron/__init__.py @@ -21,6 +21,7 @@ from .util import ( calculate_head_rev ) from ..create import create_task +from .. import GECKO from taskgraph.util.attributes import match_run_on_projects from taskgraph.util.schema import resolve_keyed_by @@ -32,7 +33,6 @@ JOB_TYPES = { 'decision-task': decision.run_decision_task, } -GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..', '..')) logger = logging.getLogger(__name__) _session = None diff --git a/taskcluster/taskgraph/decision.py b/taskcluster/taskgraph/decision.py index b33dcaec7a58..1521dfcee949 100644 --- a/taskcluster/taskgraph/decision.py +++ b/taskcluster/taskgraph/decision.py @@ -18,6 +18,7 @@ from .create import create_tasks from .parameters import Parameters from .taskgraph import TaskGraph from actions import render_actions_json +from . import GECKO from taskgraph.util.templates import Templates from taskgraph.util.time import ( @@ -28,7 +29,6 @@ from taskgraph.util.time import ( logger = logging.getLogger(__name__) ARTIFACTS_DIR = 'artifacts' -GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..')) # For each project, this gives a set of parameters specific to the project. # See `taskcluster/docs/parameters.rst` for information on parameters. diff --git a/taskcluster/taskgraph/docker.py b/taskcluster/taskgraph/docker.py index f053378df83f..76e333ec6296 100644 --- a/taskcluster/taskgraph/docker.py +++ b/taskcluster/taskgraph/docker.py @@ -12,31 +12,32 @@ import sys import subprocess import tarfile import tempfile -import urllib2 import which from subprocess import Popen, PIPE from io import BytesIO from taskgraph.util import docker +from taskgraph.util.taskcluster import ( + find_task_id, + get_artifact_url, +) +from . import GECKO -GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..')) -INDEX_URL = 'https://index.taskcluster.net/v1/task/' + docker.INDEX_PREFIX + '.{}.{}.hash.{}' -ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}' +DOCKER_INDEX = docker.INDEX_PREFIX + '.{}.{}.hash.{}' def load_image_by_name(image_name, tag=None): context_path = os.path.join(GECKO, 'taskcluster', 'docker', image_name) context_hash = docker.generate_context_hash(GECKO, context_path, image_name) - image_index_url = INDEX_URL.format('level-3', image_name, context_hash) - print("Fetching", image_index_url) - task = json.load(urllib2.urlopen(image_index_url)) + index_path = DOCKER_INDEX.format('level-3', image_name, context_hash) + task_id = find_task_id(index_path) - return load_image_by_task_id(task['taskId'], tag) + return load_image_by_task_id(task_id, tag) def load_image_by_task_id(task_id, tag=None): - artifact_url = ARTIFACT_URL.format(task_id, 'public/image.tar.zst') + artifact_url = get_artifact_url(task_id, 'public/image.tar.zst') result = load_image(artifact_url, tag) print("Found docker image: {}:{}".format(result['image'], result['tag'])) if tag: diff --git a/taskcluster/taskgraph/filter_tasks.py b/taskcluster/taskgraph/filter_tasks.py index e24c30d1e33a..db28eb570dd1 100644 --- a/taskcluster/taskgraph/filter_tasks.py +++ b/taskcluster/taskgraph/filter_tasks.py @@ -5,7 +5,6 @@ from __future__ import absolute_import, unicode_literals import logging -import os from . import ( target_tasks, @@ -13,8 +12,6 @@ from . import ( logger = logging.getLogger(__name__) -GECKO = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) - filter_task_functions = {} diff --git a/taskcluster/taskgraph/parameters.py b/taskcluster/taskgraph/parameters.py index f7ee8fab9342..925dc39d3981 100644 --- a/taskcluster/taskgraph/parameters.py +++ b/taskcluster/taskgraph/parameters.py @@ -62,9 +62,7 @@ def load_parameters_file(options): Load parameters from the --parameters option """ import urllib - - url_prefix = "https://queue.taskcluster.net/v1/task/" - url_postfix = "/artifacts/public/parameters.yml" + from taskgraph.util.taskcluster import get_artifact_url filename = options['parameters'] @@ -78,7 +76,7 @@ def load_parameters_file(options): # fetching parameters.yml using task task-id or supplied url if filename.startswith("task-id="): task_id = filename.split("=")[1] - filename = url_prefix + task_id + url_postfix + filename = get_artifact_url(task_id, 'public/parameters.yml') f = urllib.urlopen(filename) if filename.endswith('.yml'): diff --git a/taskcluster/taskgraph/task/base.py b/taskcluster/taskgraph/task/base.py index 084f90ae1fc2..01c28df7c16f 100644 --- a/taskcluster/taskgraph/task/base.py +++ b/taskcluster/taskgraph/task/base.py @@ -5,17 +5,8 @@ from __future__ import absolute_import, print_function, unicode_literals import abc -import json -import os -import urllib2 - - -# if running in a task, prefer to use the taskcluster proxy (http://taskcluster/), -# otherwise hit the services directly -if os.environ.get('TASK_ID'): - INDEX_URL = 'http://taskcluster/index/v1/task/{}' -else: - INDEX_URL = 'https://index.taskcluster.net/v1/task/{}' +import requests +from taskgraph.util.taskcluster import find_task_id class Task(object): @@ -109,11 +100,10 @@ class Task(object): """ for index_path in self.index_paths: try: - url = INDEX_URL.format(index_path) - existing_task = json.load(urllib2.urlopen(url)) + task_id = find_task_id(index_path) - return True, existing_task['taskId'] - except urllib2.HTTPError: + return True, task_id + except requests.exceptions.HTTPError: pass return False, None diff --git a/taskcluster/taskgraph/task/docker_image.py b/taskcluster/taskgraph/task/docker_image.py index 9a72420fa9d8..ce2b622def22 100644 --- a/taskcluster/taskgraph/task/docker_image.py +++ b/taskcluster/taskgraph/task/docker_image.py @@ -9,22 +9,16 @@ import os import urllib2 from . import base +from .. import GECKO from taskgraph.util.docker import ( docker_image, generate_context_hash, INDEX_PREFIX, ) +from taskgraph.util.taskcluster import get_artifact_url from taskgraph.util.templates import Templates logger = logging.getLogger(__name__) -GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..', '..')) - -# if running in a task, prefer to use the taskcluster proxy (http://taskcluster/), -# otherwise hit the services directly -if os.environ.get('TASK_ID'): - ARTIFACT_URL = 'http://taskcluster/queue/v1/task/{}/artifacts/{}' -else: - ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}' class DockerImageTask(base.Task): @@ -94,7 +88,7 @@ class DockerImageTask(base.Task): # Only return the task ID if the artifact exists for the indexed # task. request = urllib2.Request( - ARTIFACT_URL.format(taskId, 'public/image.tar.zst')) + get_artifact_url(taskId, 'public/image.tar.zst')) request.get_method = lambda: 'HEAD' urllib2.urlopen(request) diff --git a/taskcluster/taskgraph/taskgraph.py b/taskcluster/taskgraph/taskgraph.py index 606ea209868a..4722002a3678 100644 --- a/taskcluster/taskgraph/taskgraph.py +++ b/taskcluster/taskgraph/taskgraph.py @@ -4,14 +4,9 @@ from __future__ import absolute_import, print_function, unicode_literals -import os - from .graph import Graph from .util.python_path import find_object -TASKCLUSTER_QUEUE_URL = "https://queue.taskcluster.net/v1/task/" -GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..')) - class TaskGraph(object): """ diff --git a/taskcluster/taskgraph/transforms/job/mozharness_test.py b/taskcluster/taskgraph/transforms/job/mozharness_test.py index ac84242264bc..2fd8f89de32f 100644 --- a/taskcluster/taskgraph/transforms/job/mozharness_test.py +++ b/taskcluster/taskgraph/transforms/job/mozharness_test.py @@ -3,6 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. from voluptuous import Schema, Required +from taskgraph.util.taskcluster import get_artifact_url from taskgraph.transforms.job import run_job_using from taskgraph.transforms.tests import ( test_description_schema, @@ -15,8 +16,6 @@ from taskgraph.transforms.job.common import ( import os import re -ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}' - ARTIFACTS = [ # (artifact name prefix, in-image path) ("public/logs/", "build/upload/logs/"), @@ -59,11 +58,11 @@ def mozharness_test_on_docker(config, job, taskdesc): ("public/test_info/", "/home/worker/workspace/build/blobber_upload_dir/"), ] - installer_url = ARTIFACT_URL.format('', mozharness['build-artifact-name']) - test_packages_url = ARTIFACT_URL.format('', - 'public/build/target.test_packages.json') - mozharness_url = ARTIFACT_URL.format('', - 'public/build/mozharness.zip') + installer_url = get_artifact_url('', mozharness['build-artifact-name']) + test_packages_url = get_artifact_url('', + 'public/build/target.test_packages.json') + mozharness_url = get_artifact_url('', + 'public/build/mozharness.zip') worker['artifacts'] = [{ 'name': prefix, @@ -206,11 +205,11 @@ def mozharness_test_on_windows(config, job, taskdesc): target = 'firefox-{}.en-US.{}'.format(get_firefox_version(), build_platform) - installer_url = ARTIFACT_URL.format( + installer_url = get_artifact_url( '', 'public/build/{}.zip'.format(target)) - test_packages_url = ARTIFACT_URL.format( + test_packages_url = get_artifact_url( '', 'public/build/{}.test_packages.json'.format(target)) - mozharness_url = ARTIFACT_URL.format( + mozharness_url = get_artifact_url( '', 'public/build/mozharness.zip') taskdesc['scopes'].extend( @@ -270,11 +269,11 @@ def mozharness_test_on_mac_osx(config, job, taskdesc): mozharness = test['mozharness'] worker = taskdesc['worker'] - installer_url = ARTIFACT_URL.format('', mozharness['build-artifact-name']) - test_packages_url = ARTIFACT_URL.format('', - 'public/build/target.test_packages.json') - mozharness_url = ARTIFACT_URL.format('', - 'public/build/mozharness.zip') + installer_url = get_artifact_url('', mozharness['build-artifact-name']) + test_packages_url = get_artifact_url('', + 'public/build/target.test_packages.json') + mozharness_url = get_artifact_url('', + 'public/build/mozharness.zip') worker['artifacts'] = [{ 'name': prefix.rstrip('/'), diff --git a/taskcluster/taskgraph/transforms/job/toolchain.py b/taskcluster/taskgraph/transforms/job/toolchain.py index e6581401fc44..c8bb8fad007b 100644 --- a/taskcluster/taskgraph/transforms/job/toolchain.py +++ b/taskcluster/taskgraph/transforms/job/toolchain.py @@ -7,8 +7,6 @@ Support for running toolchain-building jobs via dedicated scripts from __future__ import absolute_import, print_function, unicode_literals -import os - from voluptuous import Schema, Optional, Required, Any from taskgraph.transforms.job import run_job_using @@ -18,9 +16,9 @@ from taskgraph.transforms.job.common import ( docker_worker_support_vcs_checkout, ) from taskgraph.util.hash import hash_paths +from taskgraph import GECKO -GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..', '..', '..')) TOOLCHAIN_INDEX = 'gecko.cache.level-{level}.toolchains.v1.{name}.{digest}' toolchain_run_schema = Schema({ diff --git a/taskcluster/taskgraph/transforms/signing.py b/taskcluster/taskgraph/transforms/signing.py index efcce4259b70..c5fc10f2d047 100644 --- a/taskcluster/taskgraph/transforms/signing.py +++ b/taskcluster/taskgraph/transforms/signing.py @@ -13,9 +13,6 @@ from taskgraph.transforms.task import task_description_schema from voluptuous import Schema, Any, Required, Optional -ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/<{}>/artifacts/{}' - - # Voluptuous uses marker objects as dictionary *keys*, but they are not # comparable, so we cast all of the keys back to regular strings task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()} diff --git a/taskcluster/taskgraph/util/docker.py b/taskcluster/taskgraph/util/docker.py index 09289b6447f5..d7c38002f26f 100644 --- a/taskcluster/taskgraph/util/docker.py +++ b/taskcluster/taskgraph/util/docker.py @@ -14,12 +14,11 @@ import tempfile from mozpack.archive import ( create_tar_gz_from_files, ) +from .. import GECKO -GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..', '..')) IMAGE_DIR = os.path.join(GECKO, 'taskcluster', 'docker') INDEX_PREFIX = 'docker.images.v2' -ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}' def docker_image(name, by_tag=False): diff --git a/taskcluster/taskgraph/util/hash.py b/taskcluster/taskgraph/util/hash.py index 4cf2f75c9557..23f87b09b501 100644 --- a/taskcluster/taskgraph/util/hash.py +++ b/taskcluster/taskgraph/util/hash.py @@ -12,8 +12,7 @@ import hashlib @memoize def _hash_path(path): with open(path) as fh: - return (hashlib.sha256(fh.read()).hexdigest(), - mozpath.normsep(path)) + return hashlib.sha256(fh.read()).hexdigest() def hash_paths(base_path, patterns): diff --git a/taskcluster/taskgraph/util/taskcluster.py b/taskcluster/taskgraph/util/taskcluster.py new file mode 100644 index 000000000000..0dbd35c45968 --- /dev/null +++ b/taskcluster/taskgraph/util/taskcluster.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +# 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/. + +from __future__ import absolute_import, print_function, unicode_literals + +import functools +import os +import yaml +import requests +from mozbuild.util import memoize +from requests.packages.urllib3.util.retry import Retry +from requests.adapters import HTTPAdapter + + +# if running in a task, prefer to use the taskcluster proxy +# (http://taskcluster/), otherwise hit the services directly +if os.environ.get('TASK_ID'): + INDEX_URL = 'http://taskcluster/index/v1/task/{}' + ARTIFACT_URL = 'http://taskcluster/queue/v1/task/{}/artifacts/{}' +else: + INDEX_URL = 'https://index.taskcluster.net/v1/task/{}' + ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}' + + +@memoize +def _get_session(): + session = requests.Session() + retry = Retry(total=5, backoff_factor=0.1, + status_forcelist=[500, 502, 503, 504]) + session.mount('http://', HTTPAdapter(max_retries=retry)) + session.mount('https://', HTTPAdapter(max_retries=retry)) + return session + + +def _do_request(url): + session = _get_session() + return session.get(url, stream=True) + + +def get_artifact_url(task_id, path): + return ARTIFACT_URL.format(task_id, path) + + +def get_artifact(task_id, path): + """ + Returns the artifact with the given path for the given task id. + + If the path ends with ".json" or ".yml", the content is deserialized as, + respectively, json or yaml, and the corresponding python data (usually + dict) is returned. + For other types of content, a file-like object is returned. + """ + response = _do_request(get_artifact_url(task_id, path)) + response.raise_for_status() + if path.endswith('.json'): + return response.json() + if path.endswith('.yml'): + return yaml.load(response.text) + response.raw.read = functools.partial(response.raw.read, + decode_content=True) + return response.raw + + +def list_artifacts(task_id): + response = _do_request(get_artifact_url(task_id, '').rstrip('/')) + response.raise_for_status() + return response.json()['artifacts'] + + +def find_task_id(index_path): + response = _do_request(INDEX_URL.format(index_path)) + response.raise_for_status() + return response.json()['taskId'] diff --git a/toolkit/components/extensions/ExtensionParent.jsm b/toolkit/components/extensions/ExtensionParent.jsm index cb353d8a42ab..a4a7a8b443ab 100644 --- a/toolkit/components/extensions/ExtensionParent.jsm +++ b/toolkit/components/extensions/ExtensionParent.jsm @@ -366,7 +366,10 @@ class ExtensionPageContextParent extends ProxyContextParent { // The window that contains this context. This may change due to moving tabs. get xulWindow() { - return this.xulBrowser.ownerGlobal; + let win = this.xulBrowser.ownerGlobal; + return win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDocShell) + .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow); } get currentWindow() { diff --git a/toolkit/components/extensions/ExtensionStorageSync.jsm b/toolkit/components/extensions/ExtensionStorageSync.jsm index 47bed3283339..c8637b68c49f 100644 --- a/toolkit/components/extensions/ExtensionStorageSync.jsm +++ b/toolkit/components/extensions/ExtensionStorageSync.jsm @@ -30,6 +30,12 @@ const STORAGE_SYNC_CRYPTO_SALT_LENGTH_BYTES = 32; const FXA_OAUTH_OPTIONS = { scope: STORAGE_SYNC_SCOPE, }; +const HISTOGRAM_GET_OPS_SIZE = "STORAGE_SYNC_GET_OPS_SIZE"; +const HISTOGRAM_SET_OPS_SIZE = "STORAGE_SYNC_SET_OPS_SIZE"; +const HISTOGRAM_REMOVE_OPS = "STORAGE_SYNC_REMOVE_OPS"; +const SCALAR_EXTENSIONS_USING = "storage.sync.api.usage.extensions_using"; +const SCALAR_ITEMS_STORED = "storage.sync.api.usage.items_stored"; +const SCALAR_STORAGE_CONSUMED = "storage.sync.api.usage.storage_consumed"; // Default is 5sec, which seems a bit aggressive on the open internet const KINTO_REQUEST_TIMEOUT = 30000; @@ -61,6 +67,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Log", "resource://gre/modules/Log.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Observers", "resource://services-common/observers.js"); +XPCOMUtils.defineLazyModuleGetter(this, "Services", + "resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Sqlite", "resource://gre/modules/Sqlite.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Svc", @@ -718,9 +726,12 @@ class ExtensionStorageSync { /** * @param {FXAccounts} fxaService (Optional) If not * present, trying to sync will fail. + * @param {nsITelemetry} telemetry Telemetry service to use to + * report sync usage. */ - constructor(fxaService) { + constructor(fxaService, telemetry) { this._fxaService = fxaService; + this._telemetry = telemetry; this.cryptoCollection = new CryptoCollection(fxaService); this.listeners = new WeakMap(); } @@ -741,6 +752,15 @@ class ExtensionStorageSync { }); }); await Promise.all(promises); + + // This needs access to an adapter, but any adapter will do + const collection = await this.cryptoCollection.getCollection(); + const storage = await collection.db.calculateStorage(); + this._telemetry.scalarSet(SCALAR_EXTENSIONS_USING, storage.length); + for (let {collectionName, size, numRecords} of storage) { + this._telemetry.keyedScalarSet(SCALAR_ITEMS_STORED, collectionName, numRecords); + this._telemetry.keyedScalarSet(SCALAR_STORAGE_CONSUMED, collectionName, size); + } } async sync(extension, collection) { @@ -1077,11 +1097,13 @@ class ExtensionStorageSync { const coll = await this.getCollection(extension, context); const keys = Object.keys(items); const ids = keys.map(keyToId); + const histogramSize = this._telemetry.getKeyedHistogramById(HISTOGRAM_SET_OPS_SIZE); const changes = await coll.execute(txn => { let changes = {}; for (let [i, key] of keys.entries()) { const id = ids[i]; let item = items[key]; + histogramSize.add(extension.id, JSON.stringify(item).length); let {oldRecord} = txn.upsert({ id, key, @@ -1121,6 +1143,8 @@ class ExtensionStorageSync { if (Object.keys(changes).length > 0) { this.notifyListeners(extension, changes); } + const histogram = this._telemetry.getKeyedHistogramById(HISTOGRAM_REMOVE_OPS); + histogram.add(extension.id, keys.length); } async clear(extension, context) { @@ -1136,11 +1160,13 @@ class ExtensionStorageSync { async get(extension, spec, context) { const coll = await this.getCollection(extension, context); + const histogramSize = this._telemetry.getKeyedHistogramById(HISTOGRAM_GET_OPS_SIZE); let keys, records; if (spec === null) { records = {}; const res = await coll.list(); for (let record of res.data) { + histogramSize.add(extension.id, JSON.stringify(record.data).length); records[record.key] = record.data; } return records; @@ -1159,6 +1185,7 @@ class ExtensionStorageSync { for (let key of keys) { const res = await coll.getAny(keyToId(key)); if (res.data && res.data._status != "deleted") { + histogramSize.add(extension.id, JSON.stringify(res.data.data).length); records[res.data.key] = res.data.data; } } @@ -1194,4 +1221,4 @@ class ExtensionStorageSync { } } this.ExtensionStorageSync = ExtensionStorageSync; -this.extensionStorageSync = new ExtensionStorageSync(_fxaService); +this.extensionStorageSync = new ExtensionStorageSync(_fxaService, Services.telemetry); diff --git a/toolkit/components/extensions/Schemas.jsm b/toolkit/components/extensions/Schemas.jsm index 8013da72f541..80c50f16967a 100644 --- a/toolkit/components/extensions/Schemas.jsm +++ b/toolkit/components/extensions/Schemas.jsm @@ -1617,7 +1617,7 @@ class SubModuleProperty extends Entry { `is not a sub-module`); } } - let subpath = [path, this.name]; + let subpath = [...path, this.name]; let namespace = subpath.join("."); let functions = type.functions; diff --git a/toolkit/components/extensions/ext-privacy.js b/toolkit/components/extensions/ext-privacy.js new file mode 100644 index 000000000000..020bbf656b23 --- /dev/null +++ b/toolkit/components/extensions/ext-privacy.js @@ -0,0 +1,164 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +XPCOMUtils.defineLazyModuleGetter(this, "Preferences", + "resource://gre/modules/Preferences.jsm"); + +Cu.import("resource://gre/modules/ExtensionPreferencesManager.jsm"); +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +const { + ExtensionError, +} = ExtensionUtils; + +function checkScope(scope) { + if (scope && scope !== "regular") { + throw new ExtensionError( + `Firefox does not support the ${scope} settings scope.`); + } +} + +function getAPI(extension, context, name, callback) { + let anythingSet = false; + return { + async get(details) { + return { + levelOfControl: details.incognito ? + "not_controllable" : + await ExtensionPreferencesManager.getLevelOfControl( + extension, name), + value: await callback(), + }; + }, + async set(details) { + checkScope(details.scope); + if (!anythingSet) { + anythingSet = true; + context.callOnClose({ + close: async () => { + if (["ADDON_DISABLE", "ADDON_UNINSTALL"].includes(extension.shutdownReason)) { + await ExtensionPreferencesManager.unsetAll(extension); + anythingSet = false; + } + }, + }); + } + return await ExtensionPreferencesManager.setSetting( + extension, name, details.value); + }, + async clear(details) { + checkScope(details.scope); + return await ExtensionPreferencesManager.unsetSetting( + extension, name); + }, + }; +} + +// Add settings objects for supported APIs to the preferences manager. +ExtensionPreferencesManager.addSetting("network.networkPredictionEnabled", { + prefNames: [ + "network.predictor.enabled", + "network.prefetch-next", + "network.http.speculative-parallel-limit", + "network.dns.disablePrefetch", + ], + + setCallback(value) { + return { + "network.http.speculative-parallel-limit": value ? undefined : 0, + "network.dns.disablePrefetch": !value, + "network.predictor.enabled": value, + "network.prefetch-next": value, + }; + }, +}); + +ExtensionPreferencesManager.addSetting("network.webRTCIPHandlingPolicy", { + prefNames: [ + "media.peerconnection.ice.default_address_only", + "media.peerconnection.ice.no_host", + "media.peerconnection.ice.proxy_only", + ], + + setCallback(value) { + let prefs = {}; + // Start with all prefs being reset. + for (let pref of this.prefNames) { + prefs[pref] = undefined; + } + switch (value) { + case "default": + // All prefs are already set to be reset. + break; + + case "default_public_and_private_interfaces": + prefs["media.peerconnection.ice.default_address_only"] = true; + break; + + case "default_public_interface_only": + prefs["media.peerconnection.ice.default_address_only"] = true; + prefs["media.peerconnection.ice.no_host"] = true; + break; + + case "disable_non_proxied_udp": + prefs["media.peerconnection.ice.proxy_only"] = true; + break; + } + return prefs; + }, +}); + +ExtensionPreferencesManager.addSetting("websites.hyperlinkAuditingEnabled", { + prefNames: [ + "browser.send_pings", + ], + + setCallback(value) { + return {[this.prefNames[0]]: value}; + }, +}); + +extensions.registerSchemaAPI("privacy.network", "addon_parent", context => { + let {extension} = context; + return { + privacy: { + network: { + networkPredictionEnabled: getAPI(extension, context, + "network.networkPredictionEnabled", + () => { + return Preferences.get("network.predictor.enabled") && + Preferences.get("network.prefetch-next") && + Preferences.get("network.http.speculative-parallel-limit") > 0 && + !Preferences.get("network.dns.disablePrefetch"); + }), + webRTCIPHandlingPolicy: getAPI(extension, context, + "network.webRTCIPHandlingPolicy", + () => { + if (Preferences.get("media.peerconnection.ice.proxy_only")) { + return "disable_non_proxied_udp"; + } + + let default_address_only = + Preferences.get("media.peerconnection.ice.default_address_only"); + if (default_address_only) { + if (Preferences.get("media.peerconnection.ice.no_host")) { + return "default_public_interface_only"; + } + return "default_public_and_private_interfaces"; + } + + return "default"; + }), + }, + websites: { + hyperlinkAuditingEnabled: getAPI(extension, context, + "websites.hyperlinkAuditingEnabled", + () => { + return Preferences.get("browser.send_pings"); + }), + }, + }, + }; +}); diff --git a/toolkit/components/extensions/extensions-toolkit.manifest b/toolkit/components/extensions/extensions-toolkit.manifest index 54db978a7729..3aa8f6ad29ee 100644 --- a/toolkit/components/extensions/extensions-toolkit.manifest +++ b/toolkit/components/extensions/extensions-toolkit.manifest @@ -15,6 +15,7 @@ category webextension-scripts runtime chrome://extensions/content/ext-runtime.js category webextension-scripts extension chrome://extensions/content/ext-extension.js category webextension-scripts storage chrome://extensions/content/ext-storage.js category webextension-scripts topSites chrome://extensions/content/ext-topSites.js +category webextension-scripts privacy chrome://extensions/content/ext-privacy.js # scripts specific for content process. category webextension-scripts-content extension chrome://extensions/content/ext-c-extension.js @@ -57,9 +58,11 @@ category webextension-schemas idle chrome://extensions/content/schemas/idle.json category webextension-schemas management chrome://extensions/content/schemas/management.json category webextension-schemas native_host_manifest chrome://extensions/content/schemas/native_host_manifest.json category webextension-schemas notifications chrome://extensions/content/schemas/notifications.json +category webextension-schemas privacy chrome://extensions/content/schemas/privacy.json category webextension-schemas runtime chrome://extensions/content/schemas/runtime.json category webextension-schemas storage chrome://extensions/content/schemas/storage.json category webextension-schemas test chrome://extensions/content/schemas/test.json category webextension-schemas top_sites chrome://extensions/content/schemas/top_sites.json +category webextension-schemas types chrome://extensions/content/schemas/types.json category webextension-schemas web_navigation chrome://extensions/content/schemas/web_navigation.json category webextension-schemas web_request chrome://extensions/content/schemas/web_request.json diff --git a/toolkit/components/extensions/jar.mn b/toolkit/components/extensions/jar.mn index c30fd9c61957..e877ea645731 100644 --- a/toolkit/components/extensions/jar.mn +++ b/toolkit/components/extensions/jar.mn @@ -21,6 +21,7 @@ toolkit.jar: content/extensions/ext-extension.js content/extensions/ext-storage.js content/extensions/ext-topSites.js + content/extensions/ext-privacy.js content/extensions/ext-c-backgroundPage.js content/extensions/ext-c-extension.js #ifndef ANDROID diff --git a/toolkit/components/extensions/schemas/jar.mn b/toolkit/components/extensions/schemas/jar.mn index d8669e082531..3a91c97e53cf 100644 --- a/toolkit/components/extensions/schemas/jar.mn +++ b/toolkit/components/extensions/schemas/jar.mn @@ -21,9 +21,11 @@ toolkit.jar: content/extensions/schemas/manifest.json content/extensions/schemas/native_host_manifest.json content/extensions/schemas/notifications.json + content/extensions/schemas/privacy.json content/extensions/schemas/runtime.json content/extensions/schemas/storage.json content/extensions/schemas/test.json content/extensions/schemas/top_sites.json + content/extensions/schemas/types.json content/extensions/schemas/web_navigation.json content/extensions/schemas/web_request.json diff --git a/toolkit/components/extensions/schemas/privacy.json b/toolkit/components/extensions/schemas/privacy.json new file mode 100644 index 000000000000..24fa7ad444c4 --- /dev/null +++ b/toolkit/components/extensions/schemas/privacy.json @@ -0,0 +1,73 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ + { + "namespace": "manifest", + "types": [ + { + "$extend": "Permission", + "choices": [{ + "type": "string", + "enum": [ + "privacy" + ] + }] + } + ] + }, + { + "namespace": "privacy", + "permissions": ["privacy"] + }, + { + "namespace": "privacy.network", + "description": "Use the browser.privacy API to control usage of the features in the browser that can affect a user's privacy.", + "permissions": ["privacy"], + "types": [ + { + "id": "IPHandlingPolicy", + "type": "string", + "enum": ["default", "default_public_and_private_interfaces", "default_public_interface_only", "disable_non_proxied_udp"], + "description": "The IP handling policy of WebRTC." + } + ], + "properties": { + "networkPredictionEnabled": { + "$ref": "types.Setting", + "description": "If enabled, the browser attempts to speed up your web browsing experience by pre-resolving DNS entries, prerendering sites (<link rel='prefetch' ...>), and preemptively opening TCP and SSL connections to servers. This preference's value is a boolean, defaulting to true." + }, + "webRTCIPHandlingPolicy": { + "$ref": "types.Setting", + "description": "Allow users to specify the media performance/privacy tradeoffs which impacts how WebRTC traffic will be routed and how much local address information is exposed. This preference's value is of type IPHandlingPolicy, defaulting to default." + } + } + }, + { + "namespace": "privacy.websites", + "description": "Use the browser.privacy API to control usage of the features in the browser that can affect a user's privacy.", + "permissions": ["privacy"], + "properties": { + "thirdPartyCookiesAllowed": { + "$ref": "types.Setting", + "description": "If disabled, the browser blocks third-party sites from setting cookies. The value of this preference is of type boolean, and the default value is true.", + "unsupported": true + }, + "hyperlinkAuditingEnabled": { + "$ref": "types.Setting", + "description": "If enabled, the browser sends auditing pings when requested by a website (<a ping>). The value of this preference is of type boolean, and the default value is true." + }, + "referrersEnabled": { + "$ref": "types.Setting", + "description": "If enabled, the browser sends referer headers with your requests. Yes, the name of this preference doesn't match the misspelled header. No, we're not going to change it. The value of this preference is of type boolean, and the default value is true.", + "unsupported": true + }, + "protectedContentEnabled": { + "$ref": "types.Setting", + "description": "Available on Windows and ChromeOS only: If enabled, the browser provides a unique ID to plugins in order to run protected content. The value of this preference is of type boolean, and the default value is true.", + "unsupported": true + } + } + } +] diff --git a/toolkit/components/extensions/schemas/types.json b/toolkit/components/extensions/schemas/types.json new file mode 100644 index 000000000000..7bc745a0ca24 --- /dev/null +++ b/toolkit/components/extensions/schemas/types.json @@ -0,0 +1,163 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ + { + "namespace": "types", + "description": "Contains types used by other schemas.", + "types": [ + { + "id": "SettingScope", + "type": "string", + "enum": ["regular", "regular_only", "incognito_persistent", "incognito_session_only"], + "description": "The scope of the Setting. One of
  • regular: setting for the regular profile (which is inherited by the incognito profile if not overridden elsewhere),
  • regular_only: setting for the regular profile only (not inherited by the incognito profile),
  • incognito_persistent: setting for the incognito profile that survives browser restarts (overrides regular preferences),
  • incognito_session_only: setting for the incognito profile that can only be set during an incognito session and is deleted when the incognito session ends (overrides regular and incognito_persistent preferences).
Only regular is supported by Firefox at this time." + }, + { + "id": "LevelOfControl", + "type": "string", + "enum": ["not_controllable", "controlled_by_other_extensions", "controllable_by_this_extension", "controlled_by_this_extension"], + "description": "One of
  • not_controllable: cannot be controlled by any extension
  • controlled_by_other_extensions: controlled by extensions with higher precedence
  • controllable_by_this_extension: can be controlled by this extension
  • controlled_by_this_extension: controlled by this extension
" + }, + { + "id": "Setting", + "type": "object", + "functions": [ + { + "name": "get", + "type": "function", + "description": "Gets the value of a setting.", + "async": "callback", + "parameters": [ + { + "name": "details", + "type": "object", + "description": "Which setting to consider.", + "properties": { + "incognito": { + "type": "boolean", + "optional": true, + "description": "Whether to return the value that applies to the incognito session (default false)." + } + } + }, + { + "name": "callback", + "type": "function", + "parameters": [ + { + "name": "details", + "type": "object", + "description": "Details of the currently effective value.", + "properties": { + "value": { + "description": "The value of the setting.", + "type": "any" + }, + "levelOfControl": { + "$ref": "LevelOfControl", + "description": "The level of control of the setting." + }, + "incognitoSpecific": { + "description": "Whether the effective value is specific to the incognito session.
This property will only be present if the incognito property in the details parameter of get() was true.", + "type": "boolean", + "optional": true + } + } + } + ] + } + ] + }, + { + "name": "set", + "type": "function", + "description": "Sets the value of a setting.", + "async": "callback", + "parameters": [ + { + "name": "details", + "type": "object", + "description": "Which setting to change.", + "properties": { + "value": { + "description": "The value of the setting.
Note that every setting has a specific value type, which is described together with the setting. An extension should not set a value of a different type.", + "type": "any" + }, + "scope": { + "$ref": "SettingScope", + "optional": true, + "description": "Where to set the setting (default: regular)." + } + } + }, + { + "name": "callback", + "type": "function", + "description": "Called at the completion of the set operation.", + "optional": true, + "parameters": [] + } + ] + }, + { + "name": "clear", + "type": "function", + "description": "Clears the setting, restoring any default value.", + "async": "callback", + "parameters": [ + { + "name": "details", + "type": "object", + "description": "Which setting to clear.", + "properties": { + "scope": { + "$ref": "SettingScope", + "optional": true, + "description": "Where to clear the setting (default: regular)." + } + } + }, + { + "name": "callback", + "type": "function", + "description": "Called at the completion of the clear operation.", + "optional": true, + "parameters": [] + } + ] + } + ], + "events": [ + { + "name": "onChange", + "type": "function", + "description": "Fired after the setting changes.", + "unsupported": true, + "parameters": [ + { + "type": "object", + "name": "details", + "properties": { + "value": { + "description": "The value of the setting after the change.", + "type": "any" + }, + "levelOfControl": { + "$ref": "LevelOfControl", + "description": "The level of control of the setting." + }, + "incognitoSpecific": { + "description": "Whether the value that has changed is specific to the incognito session.
This property will only be present if the user has enabled the extension in incognito mode.", + "type": "boolean", + "optional": true + } + } + } + ] + } + ] + } + ] + } +] diff --git a/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js b/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js index ae43da8a5ea8..48ec9cedc91c 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js +++ b/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js @@ -82,6 +82,8 @@ let expectedBackgroundApis = [ "runtime.openOptionsPage", "runtime.reload", "runtime.setUninstallURL", + "types.LevelOfControl", + "types.SettingScope", ]; function sendAllApis() { diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_privacy.js b/toolkit/components/extensions/test/xpcshell/test_ext_privacy.js new file mode 100644 index 000000000000..5562e391bab4 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_privacy.js @@ -0,0 +1,326 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", + "resource://gre/modules/AddonManager.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "ExtensionPreferencesManager", + "resource://gre/modules/ExtensionPreferencesManager.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Preferences", + "resource://gre/modules/Preferences.jsm"); + +const { + createAppInfo, + promiseShutdownManager, + promiseStartupManager, +} = AddonTestUtils; + +AddonTestUtils.init(this); + +createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42"); + +add_task(async function test_privacy() { + // Create a object to hold the values to which we will initialize the prefs. + const SETTINGS = { + "network.networkPredictionEnabled": { + "network.predictor.enabled": true, + "network.prefetch-next": true, + "network.http.speculative-parallel-limit": 10, + "network.dns.disablePrefetch": false, + }, + "websites.hyperlinkAuditingEnabled": { + "browser.send_pings": true, + }, + }; + + async function background() { + browser.test.onMessage.addListener(async (msg, ...args) => { + let data = args[0]; + // The second argument is the end of the api name, + // e.g., "network.networkPredictionEnabled". + let apiObj = args[1].split(".").reduce((o, i) => o[i], browser.privacy); + let settingData; + switch (msg) { + case "get": + settingData = await apiObj.get(data); + browser.test.sendMessage("gotData", settingData); + break; + + case "set": + await apiObj.set(data); + settingData = await apiObj.get({}); + browser.test.sendMessage("afterSet", settingData); + break; + + case "clear": + await apiObj.clear(data); + settingData = await apiObj.get({}); + browser.test.sendMessage("afterClear", settingData); + break; + } + }); + } + + // Set prefs to our initial values. + for (let setting in SETTINGS) { + for (let pref in SETTINGS[setting]) { + Preferences.set(pref, SETTINGS[setting][pref]); + } + } + + do_register_cleanup(() => { + // Reset the prefs. + for (let setting in SETTINGS) { + for (let pref in SETTINGS[setting]) { + Preferences.reset(pref); + } + } + }); + + // Create an array of extensions to install. + let testExtensions = [ + ExtensionTestUtils.loadExtension({ + background, + manifest: { + permissions: ["privacy"], + }, + useAddonManager: "temporary", + }), + + ExtensionTestUtils.loadExtension({ + background, + manifest: { + permissions: ["privacy"], + }, + useAddonManager: "temporary", + }), + ]; + + await promiseStartupManager(); + + for (let extension of testExtensions) { + await extension.startup(); + } + + for (let setting in SETTINGS) { + testExtensions[0].sendMessage("get", {}, setting); + let data = await testExtensions[0].awaitMessage("gotData"); + ok(data.value, "get returns expected value."); + equal(data.levelOfControl, "controllable_by_this_extension", + "get returns expected levelOfControl."); + + testExtensions[0].sendMessage("get", {incognito: true}, setting); + data = await testExtensions[0].awaitMessage("gotData"); + ok(data.value, "get returns expected value with incognito."); + equal(data.levelOfControl, "not_controllable", + "get returns expected levelOfControl with incognito."); + + // Change the value to false. + testExtensions[0].sendMessage("set", {value: false}, setting); + data = await testExtensions[0].awaitMessage("afterSet"); + ok(!data.value, "get returns expected value after setting."); + equal(data.levelOfControl, "controlled_by_this_extension", + "get returns expected levelOfControl after setting."); + + // Verify the prefs have been set to match the "false" setting. + for (let pref in SETTINGS[setting]) { + let msg = `${pref} set correctly for ${setting}`; + if (pref === "network.http.speculative-parallel-limit") { + equal(Preferences.get(pref), 0, msg); + } else { + equal(Preferences.get(pref), !SETTINGS[setting][pref], msg); + } + } + + // Change the value with a newer extension. + testExtensions[1].sendMessage("set", {value: true}, setting); + data = await testExtensions[1].awaitMessage("afterSet"); + ok(data.value, "get returns expected value after setting via newer extension."); + equal(data.levelOfControl, "controlled_by_this_extension", + "get returns expected levelOfControl after setting."); + + // Verify the prefs have been set to match the "true" setting. + for (let pref in SETTINGS[setting]) { + let msg = `${pref} set correctly for ${setting}`; + if (pref === "network.http.speculative-parallel-limit") { + equal(Preferences.get(pref), ExtensionPreferencesManager.getDefaultValue(pref), msg); + } else { + equal(Preferences.get(pref), SETTINGS[setting][pref], msg); + } + } + + // Change the value with an older extension. + testExtensions[0].sendMessage("set", {value: false}, setting); + data = await testExtensions[0].awaitMessage("afterSet"); + ok(data.value, "Newer extension remains in control."); + equal(data.levelOfControl, "controlled_by_other_extensions", + "get returns expected levelOfControl when controlled by other."); + + // Clear the value of the newer extension. + testExtensions[1].sendMessage("clear", {}, setting); + data = await testExtensions[1].awaitMessage("afterClear"); + ok(!data.value, "Older extension gains control."); + equal(data.levelOfControl, "controllable_by_this_extension", + "Expected levelOfControl returned after clearing."); + + testExtensions[0].sendMessage("get", {}, setting); + data = await testExtensions[0].awaitMessage("gotData"); + ok(!data.value, "Current, older extension has control."); + equal(data.levelOfControl, "controlled_by_this_extension", + "Expected levelOfControl returned after clearing."); + + // Set the value again with the newer extension. + testExtensions[1].sendMessage("set", {value: true}, setting); + data = await testExtensions[1].awaitMessage("afterSet"); + ok(data.value, "get returns expected value after setting via newer extension."); + equal(data.levelOfControl, "controlled_by_this_extension", + "get returns expected levelOfControl after setting."); + + // Unload the newer extension. Expect the older extension to regain control. + await testExtensions[1].unload(); + testExtensions[0].sendMessage("get", {}, setting); + data = await testExtensions[0].awaitMessage("gotData"); + ok(!data.value, "Older extension regained control."); + equal(data.levelOfControl, "controlled_by_this_extension", + "Expected levelOfControl returned after unloading."); + + // Reload the extension for the next iteration of the loop. + testExtensions[1] = ExtensionTestUtils.loadExtension({ + background, + manifest: { + permissions: ["privacy"], + }, + useAddonManager: "temporary", + }); + await testExtensions[1].startup(); + + // Clear the value of the older extension. + testExtensions[0].sendMessage("clear", {}, setting); + data = await testExtensions[0].awaitMessage("afterClear"); + ok(data.value, "Setting returns to original value when all are cleared."); + equal(data.levelOfControl, "controllable_by_this_extension", + "Expected levelOfControl returned after clearing."); + + // Verify that our initial values were restored. + for (let pref in SETTINGS[setting]) { + equal(Preferences.get(pref), SETTINGS[setting][pref], `${pref} was reset to its initial value.`); + } + } + + for (let extension of testExtensions) { + await extension.unload(); + } + + await promiseShutdownManager(); +}); + +add_task(async function test_privacy_webRTCIPHandlingPolicy() { + // Create a object to hold the default values of all the prefs. + const PREF_DEFAULTS = { + "media.peerconnection.ice.default_address_only": null, + "media.peerconnection.ice.no_host": null, + "media.peerconnection.ice.proxy_only": null, + }; + + // Store the default values of each pref. + for (let pref in PREF_DEFAULTS) { + PREF_DEFAULTS[pref] = ExtensionPreferencesManager.getDefaultValue(pref); + } + + do_register_cleanup(() => { + // Reset the prefs. + for (let pref in PREF_DEFAULTS) { + Preferences.reset(pref); + } + }); + + async function background() { + browser.test.onMessage.addListener(async (msg, value) => { + let rtcData; + switch (msg) { + case "set": + await browser.privacy.network.webRTCIPHandlingPolicy.set({value}); + rtcData = await browser.privacy.network.webRTCIPHandlingPolicy.get({}); + browser.test.sendMessage("rtcData", rtcData); + break; + + case "clear": + await browser.privacy.network.webRTCIPHandlingPolicy.clear({}); + rtcData = await browser.privacy.network.webRTCIPHandlingPolicy.get({}); + browser.test.sendMessage("rtcData", rtcData); + break; + + } + }); + } + + let extension = ExtensionTestUtils.loadExtension({ + background, + manifest: { + permissions: ["privacy"], + }, + useAddonManager: "temporary", + }); + + await promiseStartupManager(); + await extension.startup(); + + async function testSetting(value, truePrefs) { + extension.sendMessage("set", value); + let data = await extension.awaitMessage("rtcData"); + equal(data.value, value); + for (let pref in PREF_DEFAULTS) { + let prefValue = Preferences.get(pref); + if (truePrefs.includes(pref)) { + ok(prefValue, `${pref} set correctly for ${value}`); + } else { + equal(prefValue, PREF_DEFAULTS[pref], `${pref} contains default value for ${value}`); + } + } + } + + await testSetting( + "default_public_and_private_interfaces", + ["media.peerconnection.ice.default_address_only"]); + + await testSetting( + "default_public_interface_only", + ["media.peerconnection.ice.default_address_only", "media.peerconnection.ice.no_host"]); + + await testSetting( + "disable_non_proxied_udp", + ["media.peerconnection.ice.proxy_only"]); + + await testSetting("default", []); + + await extension.unload(); + + await promiseShutdownManager(); +}); + +add_task(async function test_exceptions() { + async function background() { + await browser.test.assertRejects( + browser.privacy.network.networkPredictionEnabled.set({value: true, scope: "regular_only"}), + "Firefox does not support the regular_only settings scope.", + "Expected rejection calling set with invalid scope."); + + await browser.test.assertRejects( + browser.privacy.network.networkPredictionEnabled.clear({scope: "incognito_persistent"}), + "Firefox does not support the incognito_persistent settings scope.", + "Expected rejection calling clear with invalid scope."); + + browser.test.notifyPass("exceptionTests"); + } + + let extension = ExtensionTestUtils.loadExtension({ + background, + manifest: { + permissions: ["privacy"], + }, + }); + + await extension.startup(); + await extension.awaitFinish("exceptionTests"); + await extension.unload(); +}); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js b/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js index 7aa4505f7ee2..fe6d24bb5f5b 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js @@ -391,7 +391,29 @@ function* withSignedInUser(user, f) { return Promise.resolve(true); }, }; - let extensionStorageSync = new ExtensionStorageSync(fxaServiceMock); + + let telemetryMock = { + _calls: [], + _histograms: {}, + scalarSet(name, value) { + this._calls.push({method: "scalarSet", name, value}); + }, + keyedScalarSet(name, key, value) { + this._calls.push({method: "keyedScalarSet", name, key, value}); + }, + getKeyedHistogramById(name) { + let self = this; + return { + add(key, value) { + if (!self._histograms[name]) { + self._histograms[name] = []; + } + self._histograms[name].push(value); + }, + }; + }, + }; + let extensionStorageSync = new ExtensionStorageSync(fxaServiceMock, telemetryMock); yield* f(extensionStorageSync, fxaServiceMock); } diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell.ini b/toolkit/components/extensions/test/xpcshell/xpcshell.ini index 5f3048026aeb..225d8f41b8dd 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini @@ -48,6 +48,7 @@ skip-if = release_or_beta [test_ext_manifest_minimum_chrome_version.js] [test_ext_onmessage_removelistener.js] skip-if = true # This test no longer tests what it is meant to test. +[test_ext_privacy.js] [test_ext_runtime_connect_no_receiver.js] [test_ext_runtime_getBrowserInfo.js] [test_ext_runtime_getPlatformInfo.js] diff --git a/toolkit/components/mozintl/MozIntl.cpp b/toolkit/components/mozintl/MozIntl.cpp index 432db62b14ba..04d25ec67194 100644 --- a/toolkit/components/mozintl/MozIntl.cpp +++ b/toolkit/components/mozintl/MozIntl.cpp @@ -82,6 +82,17 @@ MozIntl::AddPluralRulesConstructor(JS::Handle val, JSContext* cx) return NS_OK; } +NS_IMETHODIMP +MozIntl::AddGetLocaleInfo(JS::Handle val, JSContext* cx) +{ + static const JSFunctionSpec funcs[] = { + JS_SELF_HOSTED_FN("getLocaleInfo", "Intl_getLocaleInfo", 1, 0), + JS_FS_END + }; + + return AddFunctions(cx, val, funcs); +} + NS_GENERIC_FACTORY_CONSTRUCTOR(MozIntl) NS_DEFINE_NAMED_CID(MOZ_MOZINTL_CID); diff --git a/toolkit/components/mozintl/mozIMozIntl.idl b/toolkit/components/mozintl/mozIMozIntl.idl index e0d88e12a766..31cbb71eceb3 100644 --- a/toolkit/components/mozintl/mozIMozIntl.idl +++ b/toolkit/components/mozintl/mozIMozIntl.idl @@ -10,6 +10,7 @@ interface mozIMozIntl : nsISupports { [implicit_jscontext] void addGetCalendarInfo(in jsval intlObject); [implicit_jscontext] void addGetDisplayNames(in jsval intlObject); + [implicit_jscontext] void addGetLocaleInfo(in jsval intlObject); /** * Adds a PluralRules constructor to the given object. This function may only diff --git a/toolkit/components/mozintl/test/test_mozintl.js b/toolkit/components/mozintl/test/test_mozintl.js index 8d2720bf0152..2f58a44078b2 100644 --- a/toolkit/components/mozintl/test/test_mozintl.js +++ b/toolkit/components/mozintl/test/test_mozintl.js @@ -35,6 +35,7 @@ function test_cross_global(mozIntl) { function test_methods_presence(mozIntl) { equal(mozIntl.addGetCalendarInfo instanceof Function, true); equal(mozIntl.addGetDisplayNames instanceof Function, true); + equal(mozIntl.addGetLocaleInfo instanceof Function, true); let x = {}; @@ -43,4 +44,7 @@ function test_methods_presence(mozIntl) { mozIntl.addGetDisplayNames(x); equal(x.getDisplayNames instanceof Function, true); + + mozIntl.addGetLocaleInfo(x); + equal(x.getLocaleInfo instanceof Function, true); } diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index a31f9b1eab76..757af7379c30 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -10700,6 +10700,37 @@ "kind": "count", "description": "Tracking the total number of opened Containers." }, + "STORAGE_SYNC_GET_OPS_SIZE": { + "alert_emails": ["eglassercamp@mozilla.com"], + "expires_in_version": "58", + "bug_numbers": [1328974], + "kind": "exponential", + "high": 100000, + "n_buckets": 30, + "keyed": true, + "description": "Track the size of results of get() operations performed this subsession. The key is the addon ID.", + "releaseChannelCollection": "opt-out" + }, + "STORAGE_SYNC_SET_OPS_SIZE": { + "alert_emails": ["eglassercamp@mozilla.com"], + "expires_in_version": "58", + "bug_numbers": [1328974], + "kind": "exponential", + "high": 100000, + "n_buckets": 30, + "keyed": true, + "description": "Track the size of set() operations performed by addons this subsession. The key is the addon ID.", + "releaseChannelCollection": "opt-out" + }, + "STORAGE_SYNC_REMOVE_OPS": { + "alert_emails": ["eglassercamp@mozilla.com"], + "expires_in_version": "58", + "bug_numbers": [1328974], + "kind": "count", + "keyed": true, + "description": "Track the number of remove() operations addons perform this subsession. The key is the addon ID.", + "releaseChannelCollection": "opt-out" + }, "FENNEC_SESSIONSTORE_DAMAGED_SESSION_FILE": { "alert_emails": ["jh+bugzilla@buttercookie.de"], "expires_in_version": "56", diff --git a/toolkit/components/telemetry/Scalars.yaml b/toolkit/components/telemetry/Scalars.yaml index 94fdd8e8b5aa..3550dec3e92d 100644 --- a/toolkit/components/telemetry/Scalars.yaml +++ b/toolkit/components/telemetry/Scalars.yaml @@ -201,6 +201,58 @@ browser.engagement.navigation: record_in_processes: - 'main' +# This section is for probes used to measure use of the Webextensions storage.sync API. +storage.sync.api.usage: + extensions_using: + bug_numbers: + - 1328974 + description: > + The count of webextensions that have data stored in the chrome.storage.sync API. + This includes extensions that have not used the storage.sync API this session. + This includes items that were not stored this session. + This scalar is collected after every sync. + expires: "58" + kind: uint + keyed: false + notification_emails: + - eglassercamp@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + items_stored: + bug_numbers: + - 1328974 + description: > + The count of items in storage.sync storage, broken down by extension ID. + This includes extensions that have not used the storage.sync API this session. + This includes items that were not stored this session. + This scalar is collected after every sync. + expires: "58" + kind: uint + keyed: true + notification_emails: + - eglassercamp@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + storage_consumed: + bug_numbers: + - 1328974 + description: > + The count of bytes used in storage.sync, broken down by extension ID. + This includes extensions that have not used the storage.sync API this session. + This includes items that were not stored this session. + This scalar is collected after every sync. + expires: "58" + kind: uint + keyed: true + notification_emails: + - eglassercamp@mozilla.com + release_channel_collection: opt-out + record_in_processes: + - main + + # The following section is for probes testing the Telemetry system. They will not be # submitted in pings and are only used for testing. telemetry.test: diff --git a/toolkit/components/telemetry/docs/collection/index.rst b/toolkit/components/telemetry/docs/collection/index.rst index 191872a9e5fc..9d85f5e17513 100644 --- a/toolkit/components/telemetry/docs/collection/index.rst +++ b/toolkit/components/telemetry/docs/collection/index.rst @@ -20,6 +20,7 @@ The current data collection possibilities include: * :doc:`measuring elapsed time ` * :doc:`custom pings ` * :doc:`stack capture ` allow recording application call stacks +* :doc:`Use counters ` measure the usage of web platform features .. toctree:: :maxdepth: 2 diff --git a/toolkit/components/telemetry/docs/collection/use-counters.rst b/toolkit/components/telemetry/docs/collection/use-counters.rst new file mode 100644 index 000000000000..14861a2f034d --- /dev/null +++ b/toolkit/components/telemetry/docs/collection/use-counters.rst @@ -0,0 +1,74 @@ +============ +Use Counters +============ + +Use counters are used to report Telemetry statistics on whether individual documents +use a given WebIDL method or attribute (getters and setters are reported separately), CSS +property and deprecated DOM operations. + +The API +======= +The process to add a new use counter is different depending on the type feature that needs +to be measured. In general, for each defined use counter, two separate boolean histograms are generated: + +- one describes the use of the tracked feature for individual documents and has the ``_DOCUMENT`` suffix; +- the other describes the use of the same thing for top-level pages (basically what we think of as a *web page*) and has the ``_PAGE`` suffix. + +Using two histograms is particularly suited to measure how many sites would be affected by +removing the tracked feature. + +Example scenarios: + +- Site *X* triggers use counter *Y*. We report "used" (true) in both the ``_DOCUMENT`` and ``_PAGE`` histograms. +- Site *X* does not trigger use counter *Y*. We report "unused" (false) in both the ``_DOCUMENT`` and ``_PAGE`` histograms. +- Site *X* has an iframe for site *W*. Site *W* triggers use counter *Y*, but site *X* does not. We report one "used" and one "unused" in the individual ``_DOCUMENT`` histogram and one "used" in the top-level ``_PAGE`` histogram. + +Deprecated DOM operations +------------------------- +Use counters for deprecated DOM operations are declared in the `nsDeprecatedOperationList.h `_ file. The counters are +registered through the ``DEPRECATED_OPERATION(DeprecationReference)`` macro. The provided +parameter must have the same value of the deprecation note added to the *IDL* file. + +See this `changeset `_ for a sample +deprecated operation. + +The UseCounters registry +------------------------ +Use counters for WebIDL methods/attributes and CSS properties are registered in the `UseCounters.conf `_ file. The format of this file is very strict. Each line can be: + +1. a blank line +2. a comment, which is a line that begins with ``//`` +3. one of three possible use counter declarations: + + * ``method .`` + * ``attribute .`` + * ``property `` + +CSS properties +~~~~~~~~~~~~~~ +The CSS property method name should be identical to the ``method`` argument of ``CSS_PROP()`` and related macros. The only differences are that all hyphens are removed and CamelCase naming is used. See `nsCSSPropList.h `_ for further details. + +WebIDL methods and attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Additionally to having a new entry added to the `UseCounters.conf `_ file, WebIDL methods and attributes must have a ``[UseCounter]`` extended attribute in the Web IDL file in order for the counters to be incremented. + +Both additions are required because generating things from bindings codegen and ensuring all the dependencies are correct would have been rather difficult, and annotating the WebIDL files does nothing for identifying CSS property usage, which we would also like to track. + +The processor script +==================== +The definition files are processed twice: + +- once to generate two C++ headers files, included by the web platform components (e.g. DOM) that own the features to be tracked; +- the other time by the Telemetry component, to generate the histogram definitions that make the collection system work. + +.. note:: + + The histograms that are generated out of use counters are set to *never* expire and are *opt-in*. + +gen-usecounters.py +------------------ +This script is called by the build system to generate: + +- the ``PropertyUseCounterMap.inc`` C++ header for the CSS properties; +- the ``UseCounterList.h`` header for the WebIDL, out of the definition files. +