Merge m-c to inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2017-05-19 12:25:23 -04:00
commit 9f0c23d7c6
852 changed files with 58886 additions and 25590 deletions

View File

@ -55,12 +55,13 @@ DEFAULT_NO_CONNECTIONS_PREFS = {
'browser.newtab.url' : 'about:blank',
'browser.search.update': False,
'browser.search.suggest.enabled' : False,
'browser.safebrowsing.downloads.remote.url': 'http://localhost/safebrowsing-dummy/downloads',
'browser.safebrowsing.malware.enabled' : False,
'browser.safebrowsing.phishing.enabled' : False,
'browser.safebrowsing.provider.google.updateURL': 'http://localhost/safebrowsing-dummy/update',
'browser.safebrowsing.provider.google.gethashURL': 'http://localhost/safebrowsing-dummy/gethash',
'browser.safebrowsing.provider.google4.updateURL': 'http://localhost/safebrowsing4-dummy/update',
'browser.safebrowsing.provider.google4.gethashURL': 'http://localhost/safebrowsing4-dummy/gethash',
'browser.safebrowsing.malware.reportURL': 'http://localhost/safebrowsing-dummy/malwarereport',
'browser.selfsupport.url': 'https://localhost/selfsupport-dummy',
'browser.safebrowsing.provider.mozilla.gethashURL': 'http://localhost/safebrowsing-dummy/gethash',
'browser.safebrowsing.provider.mozilla.updateURL': 'http://localhost/safebrowsing-dummy/update',
@ -118,6 +119,7 @@ DEFAULT_FIREFOX_PREFS = {
# Make url-classifier updates so rare that they won't affect tests.
'urlclassifier.updateinterval' : 172800,
# Point the url-classifier to a nonexistent local URL for fast failures.
'browser.safebrowsing.downloads.remote.url': 'http://localhost/safebrowsing-dummy/downloads',
'browser.safebrowsing.provider.google.gethashURL' : 'http://localhost/safebrowsing-dummy/gethash',
'browser.safebrowsing.provider.google.updateURL' : 'http://localhost/safebrowsing-dummy/update',
'browser.safebrowsing.provider.google4.gethashURL' : 'http://localhost/safebrowsing4-dummy/gethash',

View File

@ -15,6 +15,8 @@
"browser.newtab.url": "about:blank",
"browser.search.update": false,
"browser.search.suggest.enabled": false,
"browser.safebrowsing.downloads.remote.url": "http://localhost/safebrowsing-dummy/downloads",
"browser.safebrowsing.malware.enabled": false,
"browser.safebrowsing.phishing.enabled": false,
"browser.safebrowsing.provider.google.updateURL": "http://localhost/safebrowsing-dummy/update",
"browser.safebrowsing.provider.google.gethashURL": "http://localhost/safebrowsing-dummy/gethash",

View File

@ -175,6 +175,14 @@ appUpdater.prototype =
if (button) {
if (aChildID == "downloadAndInstall") {
let updateVersion = gAppUpdater.update.displayVersion;
// Include the build ID if this is an "a#" (nightly or aurora) build
if (/a\d+$/.test(updateVersion)) {
let buildID = gAppUpdater.update.buildID;
let year = buildID.slice(0, 4);
let month = buildID.slice(4, 6);
let day = buildID.slice(6, 8);
updateVersion += ` (${year}-${month}-${day})`;
}
button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1);
button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey");
}

View File

@ -482,7 +482,7 @@ var PlacesCommandHook = {
try {
info.title = docInfo.isErrorPage ?
(await PlacesUtils.promisePlaceInfo(aBrowser.currentURI)).title :
(await PlacesUtils.history.fetch(aBrowser.currentURI)).title :
aBrowser.contentTitle;
info.title = info.title || url.href;
description = docInfo.description;

View File

@ -25,9 +25,6 @@ with Files("newtab/**"):
with Files("pageinfo/**"):
BUG_COMPONENT = ("Firefox", "Page Info Window")
with Files("sync/**"):
BUG_COMPONENT = ("Firefox", "Sync")
with Files("test/alerts/**"):
BUG_COMPONENT = ("Toolkit", "Notifications and Alerts")

View File

@ -1,48 +0,0 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- import-globals-from aboutSyncTabs.js -->
<bindings id="tabBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="tab-listing" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content>
<xul:hbox flex="1">
<xul:vbox pack="start">
<xul:image class="tabIcon"
xbl:inherits="src=icon"/>
</xul:vbox>
<xul:vbox pack="start" flex="1">
<xul:label xbl:inherits="value=title,selected"
crop="end" flex="1" class="title"/>
<xul:label xbl:inherits="value=url,selected"
crop="end" flex="1" class="url"/>
</xul:vbox>
</xul:hbox>
</content>
<handlers>
<handler event="dblclick" button="0">
<![CDATA[
RemoteTabViewer.openSelected();
]]>
</handler>
</handlers>
</binding>
<binding id="client-listing" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content>
<xul:hbox pack="start" align="center" onfocus="event.target.blur()" onselect="return false;">
<xul:image/>
<xul:label xbl:inherits="value=clientName"
class="clientName"
crop="center" flex="1"/>
</xul:hbox>
</content>
</binding>
</bindings>

View File

@ -1,11 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
richlistitem[type="tab"] {
-moz-binding: url(chrome://browser/content/sync/aboutSyncTabs-bindings.xml#tab-listing);
}
richlistitem[type="client"] {
-moz-binding: url(chrome://browser/content/sync/aboutSyncTabs-bindings.xml#client-listing);
}

View File

@ -1,305 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from ../utilityOverlay.js */
var Cu = Components.utils;
Cu.import("resource://services-common/utils.js");
Cu.import("resource://services-sync/main.js");
Cu.import("resource:///modules/PlacesUIUtils.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm", this);
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
var RemoteTabViewer = {
_tabsList: null,
init() {
Services.obs.addObserver(this, "weave:service:login:finish");
Services.obs.addObserver(this, "weave:engine:sync:finish");
this._tabsList = document.getElementById("tabsList");
this.buildList(true);
},
uninit() {
Services.obs.removeObserver(this, "weave:service:login:finish");
Services.obs.removeObserver(this, "weave:engine:sync:finish");
},
createItem(attrs) {
let item = document.createElement("richlistitem");
// Copy the attributes from the argument into the item.
for (let attr in attrs) {
item.setAttribute(attr, attrs[attr]);
}
if (attrs["type"] == "tab") {
item.label = attrs.title != "" ? attrs.title : attrs.url;
}
return item;
},
filterTabs(event) {
let val = event.target.value.toLowerCase();
let numTabs = this._tabsList.getRowCount();
let clientTabs = 0;
let currentClient = null;
for (let i = 0; i < numTabs; i++) {
let item = this._tabsList.getItemAtIndex(i);
let hide = false;
if (item.getAttribute("type") == "tab") {
if (!item.getAttribute("url").toLowerCase().includes(val) &&
!item.getAttribute("title").toLowerCase().includes(val)) {
hide = true;
} else {
clientTabs++;
}
} else if (item.getAttribute("type") == "client") {
if (currentClient) {
if (clientTabs == 0) {
currentClient.hidden = true;
}
}
currentClient = item;
clientTabs = 0;
}
item.hidden = hide;
}
if (clientTabs == 0) {
currentClient.hidden = true;
}
},
openSelected() {
let items = this._tabsList.selectedItems;
let urls = [];
for (let i = 0; i < items.length; i++) {
if (items[i].getAttribute("type") == "tab") {
urls.push(items[i].getAttribute("url"));
let index = this._tabsList.getIndexOfItem(items[i]);
this._tabsList.removeItemAt(index);
}
}
if (urls.length) {
getTopWin().gBrowser.loadTabs(urls);
this._tabsList.clearSelection();
}
},
bookmarkSingleTab() {
let item = this._tabsList.selectedItems[0];
let uri = Weave.Utils.makeURI(item.getAttribute("url"));
let title = item.getAttribute("title");
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "bookmark"
, uri
, title
, hiddenRows: [ "description"
, "location"
, "loadInSidebar"
, "keyword" ]
}, window.top);
},
bookmarkSelectedTabs() {
let items = this._tabsList.selectedItems;
let URIs = [];
for (let i = 0; i < items.length; i++) {
if (items[i].getAttribute("type") == "tab") {
let uri = Weave.Utils.makeURI(items[i].getAttribute("url"));
if (!uri) {
continue;
}
URIs.push(uri);
}
}
if (URIs.length) {
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "folder"
, URIList: URIs
, hiddenRows: [ "description" ]
}, window.top);
}
},
getIcon(iconUri, defaultIcon) {
try {
let iconURI = Weave.Utils.makeURI(iconUri);
return PlacesUtils.favicons.getFaviconLinkForIcon(iconURI).spec;
} catch (ex) {
// Do nothing.
}
// Just give the provided default icon or the system's default.
return defaultIcon || PlacesUtils.favicons.defaultFavicon.spec;
},
_waitingForBuildList: false,
_buildListRequested: false,
buildList(forceSync) {
if (this._waitingForBuildList) {
this._buildListRequested = true;
return;
}
this._waitingForBuildList = true;
this._buildListRequested = false;
this._clearTabList();
if (Weave.Service.isLoggedIn) {
this._refetchTabs(forceSync);
this._generateWeaveTabList();
} else {
// XXXzpao We should say something about not being logged in & not having data
// or tell the appropriate condition. (bug 583344)
}
this._waitingForBuildList = false;
if (this._buildListRequested) {
CommonUtils.nextTick(this.buildList, this);
}
},
_clearTabList() {
let list = this._tabsList;
// Clear out existing richlistitems.
let count = list.getRowCount();
if (count > 0) {
for (let i = count - 1; i >= 0; i--) {
list.removeItemAt(i);
}
}
},
_generateWeaveTabList() {
let engine = Weave.Service.engineManager.get("tabs");
let list = this._tabsList;
let seenURLs = new Set();
let localURLs = engine.getOpenURLs();
for (let [, client] of Object.entries(engine.getAllClients())) {
// Create the client node, but don't add it in-case we don't show any tabs
let appendClient = true;
client.tabs.forEach(function({title, urlHistory, icon}) {
let url = urlHistory[0];
if (!url || localURLs.has(url) || seenURLs.has(url)) {
return;
}
seenURLs.add(url);
if (appendClient) {
let attrs = {
type: "client",
clientName: client.clientName,
class: Weave.Service.clientsEngine.isMobile(client.id) ? "mobile" : "desktop"
};
let clientEnt = this.createItem(attrs);
list.appendChild(clientEnt);
appendClient = false;
clientEnt.disabled = true;
}
let attrs = {
type: "tab",
title: title || url,
url,
icon: this.getIcon(icon),
}
let tab = this.createItem(attrs);
list.appendChild(tab);
}, this);
}
},
adjustContextMenu(event) {
let mode = "all";
switch (this._tabsList.selectedItems.length) {
case 0:
break;
case 1:
mode = "single"
break;
default:
mode = "multiple";
break;
}
let menu = document.getElementById("tabListContext");
let el = menu.firstChild;
while (el) {
let showFor = el.getAttribute("showFor");
if (showFor) {
el.hidden = showFor != mode && showFor != "all";
}
el = el.nextSibling;
}
},
_refetchTabs(force) {
if (!force) {
// Don't bother refetching tabs if we already did so recently
let lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch", 0);
let now = Math.floor(Date.now() / 1000);
if (now - lastFetch < 30) {
return false;
}
}
// Ask Sync to just do the tabs engine if it can.
Weave.Service.sync(["tabs"]);
Services.prefs.setIntPref("services.sync.lastTabFetch",
Math.floor(Date.now() / 1000));
return true;
},
observe(subject, topic, data) {
switch (topic) {
case "weave:service:login:finish":
// A login has finished, which means that a Sync is about to start and
// we will eventually get to the "tabs" engine - but try and force the
// tab engine to sync first by passing |true| for the forceSync param.
this.buildList(true);
break;
case "weave:engine:sync:finish":
if (data == "tabs") {
// The tabs engine just finished, so re-build the list without
// forcing a new sync of the tabs engine.
this.buildList(false);
}
break;
}
},
handleClick(event) {
if (event.target.getAttribute("type") != "tab") {
return;
}
if (event.button == 1) {
let url = event.target.getAttribute("url");
openUILink(url, event);
let index = this._tabsList.getIndexOfItem(event.target);
this._tabsList.removeItemAt(index);
}
}
}

View File

@ -1,68 +0,0 @@
<?xml version="1.0" encoding="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/. -->
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/aboutSyncTabs.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/sync/aboutSyncTabs.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % aboutSyncTabsDTD SYSTEM "chrome://browser/locale/aboutSyncTabs.dtd">
%aboutSyncTabsDTD;
]>
<window id="tabs-display"
onload="RemoteTabViewer.init()"
onunload="RemoteTabViewer.uninit()"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="&tabs.otherDevices.label;">
<script type="application/javascript" src="chrome://browser/content/sync/aboutSyncTabs.js"/>
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
<html:head>
<html:link rel="icon" href="chrome://browser/skin/sync-16.png"/>
</html:head>
<popupset id="contextmenus">
<menupopup id="tabListContext">
<menuitem label="&tabs.context.openTab.label;"
accesskey="&tabs.context.openTab.accesskey;"
oncommand="RemoteTabViewer.openSelected()"
showFor="single"/>
<menuitem label="&tabs.context.bookmarkSingleTab.label;"
accesskey="&tabs.context.bookmarkSingleTab.accesskey;"
oncommand="RemoteTabViewer.bookmarkSingleTab(event)"
showFor="single"/>
<menuitem label="&tabs.context.openMultipleTabs.label;"
accesskey="&tabs.context.openMultipleTabs.accesskey;"
oncommand="RemoteTabViewer.openSelected()"
showFor="multiple"/>
<menuitem label="&tabs.context.bookmarkMultipleTabs.label;"
accesskey="&tabs.context.bookmarkMultipleTabs.accesskey;"
oncommand="RemoteTabViewer.bookmarkSelectedTabs()"
showFor="multiple"/>
<menuseparator/>
<menuitem label="&tabs.context.refreshList.label;"
accesskey="&tabs.context.refreshList.accesskey;"
oncommand="RemoteTabViewer.buildList()"
showFor="all"/>
</menupopup>
</popupset>
<richlistbox context="tabListContext" id="tabsList" seltype="multiple"
align="center" flex="1"
onclick="RemoteTabViewer.handleClick(event)"
oncontextmenu="RemoteTabViewer.adjustContextMenu(event)">
<hbox id="headers" align="center">
<label id="tabsListHeading"
value="&tabs.otherDevices.label;"/>
<spacer flex="1"/>
<textbox type="search"
emptytext="&tabs.searchText.label;"
oncommand="RemoteTabViewer.filterTabs(event)"/>
</hbox>
</richlistbox>
</window>

View File

@ -506,8 +506,6 @@ skip-if = os == "linux" || os == "mac" # No tabs in titlebar on linux
[browser_tabkeynavigation.js]
skip-if = (os == "mac" && !e10s) # Bug 1237713 - OSX eats keypresses for some reason
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
[browser_tabopen_reflows.js]
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
[browser_tabs_close_beforeunload.js]
support-files =
close_beforeunload_opens_second_tab.html
@ -584,9 +582,6 @@ skip-if = (os == "win" && !debug)
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
[browser_web_channel.js]
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
[browser_windowopen_reflows.js]
skip-if = os == "mac" # bug 1339317
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
[browser_zbug569342.js]
skip-if = e10s || debug # Bug 1094240 - has findbar-related failures
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.

View File

@ -1,145 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
XPCOMUtils.defineLazyGetter(this, "docShell", () => {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
});
const EXPECTED_REFLOWS = [
// tabbrowser.adjustTabstrip() call after tabopen animation has finished
"adjustTabstrip@chrome://browser/content/tabbrowser.xml|" +
"_handleNewTab@chrome://browser/content/tabbrowser.xml|" +
"onxbltransitionend@chrome://browser/content/tabbrowser.xml|",
// switching focus in updateCurrentBrowser() causes reflows
"_adjustFocusAfterTabSwitch@chrome://browser/content/tabbrowser.xml|" +
"updateCurrentBrowser@chrome://browser/content/tabbrowser.xml|" +
"onselect@chrome://browser/content/browser.xul|",
// switching focus in openLinkIn() causes reflows
"openLinkIn@chrome://browser/content/utilityOverlay.js|" +
"openUILinkIn@chrome://browser/content/utilityOverlay.js|" +
"BrowserOpenTab@chrome://browser/content/browser.js|",
// selection change notification may cause querying the focused editor content
// by IME and that will cause reflow.
"select@chrome://global/content/bindings/textbox.xml|" +
"focusAndSelectUrlBar@chrome://browser/content/browser.js|" +
"openLinkIn@chrome://browser/content/utilityOverlay.js|" +
"openUILinkIn@chrome://browser/content/utilityOverlay.js|" +
"BrowserOpenTab@chrome://browser/content/browser.js|",
];
const PREF_PRELOAD = "browser.newtab.preload";
const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directory.source";
/*
* This test ensures that there are no unexpected
* uninterruptible reflows when opening new tabs.
*/
add_task(async function() {
let DirectoryLinksProvider = Cu.import("resource:///modules/DirectoryLinksProvider.jsm", {}).DirectoryLinksProvider;
let NewTabUtils = Cu.import("resource://gre/modules/NewTabUtils.jsm", {}).NewTabUtils;
let Promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
// resolves promise when directory links are downloaded and written to disk
function watchLinksChangeOnce() {
return new Promise(resolve => {
let observer = {
onManyLinksChanged: () => {
DirectoryLinksProvider.removeObserver(observer);
NewTabUtils.links.populateCache(() => {
NewTabUtils.allPages.update();
resolve();
}, true);
}
};
observer.onDownloadFail = observer.onManyLinksChanged;
DirectoryLinksProvider.addObserver(observer);
});
}
let gOrigDirectorySource = Services.prefs.getCharPref(PREF_NEWTAB_DIRECTORYSOURCE);
registerCleanupFunction(() => {
Services.prefs.clearUserPref(PREF_PRELOAD);
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, gOrigDirectorySource);
return watchLinksChangeOnce();
});
Services.prefs.setBoolPref(PREF_PRELOAD, false);
// set directory source to dummy/empty links
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, 'data:application/json,{"test":1}');
// run tests when directory source change completes
await watchLinksChangeOnce();
// Perform a click in the top left of content to ensure the mouse isn't
// hovering over any of the tiles
let target = gBrowser.selectedBrowser;
let rect = target.getBoundingClientRect();
let left = rect.left + 1;
let top = rect.top + 1;
let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
// Add a reflow observer and open a new tab.
docShell.addWeakReflowObserver(observer);
BrowserOpenTab();
// Wait until the tabopen animation has finished.
await waitForTransitionEnd();
// Remove reflow observer and clean up.
docShell.removeWeakReflowObserver(observer);
gBrowser.removeCurrentTab();
});
var observer = {
reflow(start, end) {
// Gather information about the current code path.
let path = (new Error().stack).split("\n").slice(1).map(line => {
return line.replace(/:\d+:\d+$/, "");
}).join("|");
let pathWithLineNumbers = (new Error().stack).split("\n").slice(1).join("|");
// Stack trace is empty. Reflow was triggered by native code.
if (path === "") {
return;
}
// Check if this is an expected reflow.
for (let stack of EXPECTED_REFLOWS) {
if (path.startsWith(stack)) {
ok(true, "expected uninterruptible reflow '" + stack + "'");
return;
}
}
ok(false, "unexpected uninterruptible reflow '" + pathWithLineNumbers + "'");
},
reflowInterruptible(start, end) {
// We're not interested in interruptible reflows.
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
Ci.nsISupportsWeakReference])
};
function waitForTransitionEnd() {
return new Promise(resolve => {
let tab = gBrowser.selectedTab;
tab.addEventListener("transitionend", function onEnd(event) {
if (event.propertyName === "max-width") {
tab.removeEventListener("transitionend", onEnd);
resolve();
}
});
});
}

View File

@ -1,117 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const EXPECTED_REFLOWS = [
// handleEvent flushes layout to get the tabstrip width after a resize.
"handleEvent@chrome://browser/content/tabbrowser.xml|",
// Loading a tab causes a reflow.
"loadTabs@chrome://browser/content/tabbrowser.xml|" +
"loadOneOrMoreURIs@chrome://browser/content/browser.js|" +
"_delayedStartup@chrome://browser/content/browser.js|",
// Selecting the address bar causes a reflow.
"select@chrome://global/content/bindings/textbox.xml|" +
"focusAndSelectUrlBar@chrome://browser/content/browser.js|" +
"_delayedStartup@chrome://browser/content/browser.js|",
// Focusing the content area causes a reflow.
"_delayedStartup@chrome://browser/content/browser.js|",
];
if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") {
// TabsInTitlebar._update causes a reflow on OS X and Windows trying to do calculations
// since layout info is already dirty. This doesn't seem to happen before
// MozAfterPaint on Linux.
EXPECTED_REFLOWS.push("rect@chrome://browser/content/browser-tabsintitlebar.js|" +
"_update@chrome://browser/content/browser-tabsintitlebar.js|" +
"updateAppearance@chrome://browser/content/browser-tabsintitlebar.js|" +
"handleEvent@chrome://browser/content/tabbrowser.xml|");
}
if (Services.appinfo.OS == "Darwin") {
// _onOverflow causes a reflow getting widths.
EXPECTED_REFLOWS.push("_onOverflow@resource:///modules/CustomizableUI.jsm|" +
"init@resource:///modules/CustomizableUI.jsm|" +
"observe@resource:///modules/CustomizableUI.jsm|" +
"_delayedStartup@chrome://browser/content/browser.js|");
// Same as above since in packaged builds there are no function names and the resource URI includes "app"
EXPECTED_REFLOWS.push("@resource://app/modules/CustomizableUI.jsm|" +
"@resource://app/modules/CustomizableUI.jsm|" +
"@resource://app/modules/CustomizableUI.jsm|" +
"_delayedStartup@chrome://browser/content/browser.js|");
}
/*
* This test ensures that there are no unexpected
* uninterruptible reflows when opening new windows.
*/
function test() {
waitForExplicitFinish();
// Add a reflow observer and open a new window
let win = OpenBrowserWindow();
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
docShell.addWeakReflowObserver(observer);
// Wait until the mozafterpaint event occurs.
waitForMozAfterPaint(win, function paintListener() {
// Remove reflow observer and clean up.
docShell.removeWeakReflowObserver(observer);
win.close();
finish();
});
}
var observer = {
reflow(start, end) {
// Gather information about the current code path.
let stack = new Error().stack;
let path = stack.split("\n").slice(1).map(line => {
return line.replace(/:\d+:\d+$/, "");
}).join("|");
let pathWithLineNumbers = (new Error().stack).split("\n").slice(1).join("|");
// Stack trace is empty. Reflow was triggered by native code.
if (path === "") {
return;
}
// Check if this is an expected reflow.
for (let expectedStack of EXPECTED_REFLOWS) {
if (path.startsWith(expectedStack) ||
// Accept an empty function name for gBrowserInit._delayedStartup or TabsInTitlebar._update to workaround bug 906578.
path.startsWith(expectedStack.replace(/(^|\|)(gBrowserInit\._delayedStartup|TabsInTitlebar\._update)@/, "$1@"))) {
ok(true, "expected uninterruptible reflow '" + expectedStack + "'");
return;
}
}
ok(false, "unexpected uninterruptible reflow '" + pathWithLineNumbers + "'");
},
reflowInterruptible(start, end) {
// We're not interested in interruptible reflows.
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
Ci.nsISupportsWeakReference])
};
function waitForMozAfterPaint(win, callback) {
let dwu = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let lastTransactionId = dwu.lastTransactionId;
win.addEventListener("MozAfterPaint", function onEnd(event) {
if (event.target != win || event.transactionId <= lastTransactionId)
return;
win.removeEventListener("MozAfterPaint", onEnd);
executeSoon(callback);
});
}

View File

@ -0,0 +1,7 @@
[DEFAULT]
support-files =
head.js
[browser_tabclose_reflows.js]
[browser_tabopen_reflows.js]
[browser_toolbariconcolor_restyles.js]
[browser_windowopen_reflows.js]

View File

@ -0,0 +1,71 @@
"use strict";
/**
* WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This
* is a whitelist that should slowly go away as we improve the performance of
* the front-end. Instead of adding more reflows to the whitelist, you should
* be modifying your code to avoid the reflow.
*
* See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
* for tips on how to do that.
*/
const EXPECTED_REFLOWS = [
[
"_adjustFocusAfterTabSwitch@chrome://browser/content/tabbrowser.xml",
],
];
if (gMultiProcessBrowser) {
EXPECTED_REFLOWS.push(
[
"_adjustFocusAfterTabSwitch@chrome://browser/content/tabbrowser.xml",
],
);
}
/*
* This test ensures that there are no unexpected
* uninterruptible reflows when closing new tabs.
*/
add_task(async function() {
// If we've got a preloaded browser, get rid of it so that it
// doesn't interfere with the test if it's loading. We have to
// do this before we disable preloading or changing the new tab
// URL, otherwise _getPreloadedBrowser will return null, despite
// the preloaded browser existing.
let preloaded = gBrowser._getPreloadedBrowser();
if (preloaded) {
preloaded.remove();
}
await SpecialPowers.pushPrefEnv({
set: [["browser.newtab.preload", false]],
});
let aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
.getService(Ci.nsIAboutNewTabService);
aboutNewTabService.newTabURL = "about:blank";
registerCleanupFunction(() => {
aboutNewTabService.resetNewTabURL();
});
// Because the tab strip is a scrollable frame, we can't use the
// default dirtying function from withReflowObserver and reliably
// get reflows for the strip. Instead, we provide a node that's
// already in the scrollable frame to dirty - in this case, the
// original tab.
let origTab = gBrowser.selectedTab;
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
await BrowserTestUtils.waitForCondition(() => tab._fullyOpen);
// Add a reflow observer and open a new tab.
await withReflowObserver(async function() {
let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
gBrowser.removeTab(tab, { animate: true });
await BrowserTestUtils.waitForEvent(tab, "transitionend",
false, e => e.propertyName === "max-width");
await switchDone;
}, EXPECTED_REFLOWS, window, origTab);
});

View File

@ -0,0 +1,108 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This
* is a whitelist that should slowly go away as we improve the performance of
* the front-end. Instead of adding more reflows to the whitelist, you should
* be modifying your code to avoid the reflow.
*
* See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
* for tips on how to do that.
*/
const EXPECTED_REFLOWS = [
// selection change notification may cause querying the focused editor content
// by IME and that will cause reflow.
[
"select@chrome://global/content/bindings/textbox.xml",
"focusAndSelectUrlBar@chrome://browser/content/browser.js",
"openLinkIn@chrome://browser/content/utilityOverlay.js",
"openUILinkIn@chrome://browser/content/utilityOverlay.js",
"BrowserOpenTab@chrome://browser/content/browser.js",
],
// selection change notification may cause querying the focused editor content
// by IME and that will cause reflow.
[
"select@chrome://global/content/bindings/textbox.xml",
"focusAndSelectUrlBar@chrome://browser/content/browser.js",
"openLinkIn@chrome://browser/content/utilityOverlay.js",
"openUILinkIn@chrome://browser/content/utilityOverlay.js",
"BrowserOpenTab@chrome://browser/content/browser.js",
],
[
"select@chrome://global/content/bindings/textbox.xml",
"focusAndSelectUrlBar@chrome://browser/content/browser.js",
"openLinkIn@chrome://browser/content/utilityOverlay.js",
"openUILinkIn@chrome://browser/content/utilityOverlay.js",
"BrowserOpenTab@chrome://browser/content/browser.js",
],
[
"openLinkIn@chrome://browser/content/utilityOverlay.js",
"openUILinkIn@chrome://browser/content/utilityOverlay.js",
"BrowserOpenTab@chrome://browser/content/browser.js",
],
];
if (!gMultiProcessBrowser) {
EXPECTED_REFLOWS.push(
[
"_adjustFocusAfterTabSwitch@chrome://browser/content/tabbrowser.xml",
"updateCurrentBrowser@chrome://browser/content/tabbrowser.xml",
"onselect@chrome://browser/content/browser.xul",
],
);
}
/*
* This test ensures that there are no unexpected
* uninterruptible reflows when opening new tabs.
*/
add_task(async function() {
// If we've got a preloaded browser, get rid of it so that it
// doesn't interfere with the test if it's loading. We have to
// do this before we disable preloading or changing the new tab
// URL, otherwise _getPreloadedBrowser will return null, despite
// the preloaded browser existing.
let preloaded = gBrowser._getPreloadedBrowser();
if (preloaded) {
preloaded.remove();
}
await SpecialPowers.pushPrefEnv({
set: [["browser.newtab.preload", false]],
});
let aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
.getService(Ci.nsIAboutNewTabService);
aboutNewTabService.newTabURL = "about:blank";
registerCleanupFunction(() => {
aboutNewTabService.resetNewTabURL();
});
// Because the tab strip is a scrollable frame, we can't use the
// default dirtying function from withReflowObserver and reliably
// get reflows for the strip. Instead, we provide a node that's
// already in the scrollable frame to dirty - in this case, the
// original tab.
let origTab = gBrowser.selectedTab;
// Add a reflow observer and open a new tab.
await withReflowObserver(async function() {
let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
BrowserOpenTab();
await BrowserTestUtils.waitForEvent(gBrowser.selectedTab, "transitionend",
false, e => e.propertyName === "max-width");
await switchDone;
}, EXPECTED_REFLOWS, window, origTab);
let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
await switchDone;
});

View File

@ -0,0 +1,73 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This
* is a whitelist that should slowly go away as we improve the performance of
* the front-end. Instead of adding more reflows to the whitelist, you should
* be modifying your code to avoid the reflow.
*
* See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
* for tips on how to do that.
*/
const EXPECTED_REFLOWS = [
// Selecting the address bar causes two reflows, unfortunately.
[
"select@chrome://global/content/bindings/textbox.xml",
"focusAndSelectUrlBar@chrome://browser/content/browser.js",
"_delayedStartup@chrome://browser/content/browser.js",
],
// Selecting the address bar causes two reflows, unfortunately.
[
"select@chrome://global/content/bindings/textbox.xml",
"focusAndSelectUrlBar@chrome://browser/content/browser.js",
"_delayedStartup@chrome://browser/content/browser.js",
],
];
if (Services.appinfo.OS == "Darwin") {
// TabsInTitlebar._update causes a reflow on OS X trying to do calculations
// since layout info is already dirty. This doesn't seem to happen before
// MozAfterPaint on Linux.
EXPECTED_REFLOWS.push(
[
"rect@chrome://browser/content/browser-tabsintitlebar.js",
"_update@chrome://browser/content/browser-tabsintitlebar.js",
"updateAppearance@chrome://browser/content/browser-tabsintitlebar.js",
"handleEvent@chrome://browser/content/tabbrowser.xml",
],
);
}
if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") {
EXPECTED_REFLOWS.push(
[
"handleEvent@chrome://browser/content/tabbrowser.xml",
"inferFromText@chrome://browser/content/browser.js",
"handleEvent@chrome://browser/content/browser.js",
],
);
}
/*
* This test ensures that there are no unexpected
* uninterruptible reflows when opening new windows.
*/
add_task(async function() {
let win = OpenBrowserWindow();
await withReflowObserver(async function() {
let resizeEvent = BrowserTestUtils.waitForEvent(win, "resize");
let delayedStartup =
TestUtils.topicObserved("browser-delayed-startup-finished",
subject => subject == win);
await resizeEvent;
await delayedStartup;
}, EXPECTED_REFLOWS, win);
await BrowserTestUtils.closeWindow(win);
});

View File

@ -0,0 +1,155 @@
/**
* Async utility function for ensuring that no unexpected uninterruptible
* reflows occur during some period of time in a window.
*
* The helper works by running a JS function before each event is
* dispatched that attempts to dirty the layout tree - the idea being
* that this puts us in the "worst case scenario" so that any JS
* that attempts to query for layout or style information will cause
* a reflow to fire. We also dirty the layout tree after each reflow
* occurs, for good measure.
*
* This sounds good in theory, but it's trickier in practice due to
* various optimizations in our Layout engine. The default function
* for dirtying the layout tree adds a margin to the first element
* child it finds in the window to a maximum of 3px, and then goes
* back to 0px again and loops.
*
* This is not sufficient for reflows that we expect to happen within
* scrollable frames, as Gecko is able to side-step reflowing the
* contents of a scrollable frame if outer frames are dirtied. Because
* of this, it's currently possible to override the default node to
* dirty with one more appropriate for the test.
*
* It is also theoretically possible for enough events to fire between
* reflows such that the before and after state of the layout tree is
* exactly the same, meaning that no reflow is required, which opens
* us up to missing expected reflows. This seems to be possible in
* theory, but hasn't yet shown up in practice - it's just something
* to be aware of.
*
* Bug 1363361 has been filed for a more reliable way of dirtying layout.
*
* @param testFn (async function)
* The async function that will exercise the browser activity that is
* being tested for reflows.
* @param expectedStacks (Array, optional)
* An Array of Arrays representing stacks.
*
* Example:
*
* [
* // This reflow is caused by lorem ipsum
* [
* "select@chrome://global/content/bindings/textbox.xml",
* "focusAndSelectUrlBar@chrome://browser/content/browser.js",
* "openLinkIn@chrome://browser/content/utilityOverlay.js",
* "openUILinkIn@chrome://browser/content/utilityOverlay.js",
* "BrowserOpenTab@chrome://browser/content/browser.js",
* ],
*
* // This reflow is caused by lorem ipsum
* [
* "get_scrollPosition@chrome://global/content/bindings/scrollbox.xml",
* "_fillTrailingGap@chrome://browser/content/tabbrowser.xml",
* "_handleNewTab@chrome://browser/content/tabbrowser.xml",
* "onxbltransitionend@chrome://browser/content/tabbrowser.xml",
* ],
*
* ]
*
* Note that line numbers are not included in the stacks.
*
* Order of the reflows doesn't matter. Expected reflows that aren't seen
* will cause an assertion failure. When this argument is not passed,
* it defaults to the empty Array, meaning no reflows are expected.
* @param window (browser window, optional)
* The browser window to monitor. Defaults to the current window.
* @param elemToDirty (DOM node, optional)
* Callers can provide a custom DOM node to change some layout style
* on in the event that the action being tested is occurring within
* a scrollable frame. Otherwise, withReflowObserver defaults to dirtying
* the first element child of the window.
*/
async function withReflowObserver(testFn, expectedStacks = [], win = window, elemToDirty) {
if (!elemToDirty) {
elemToDirty = win.document.firstElementChild;
}
let i = 0;
let dirtyFrameFn = (e) => {
elemToDirty.style.margin = (++i % 4) + "px";
};
let els = Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService);
// We're going to remove the stacks one by one as we see them so that
// we can check for expected, unseen reflows, so let's clone the array.
expectedStacks = expectedStacks.slice(0);
let observer = {
reflow(start, end) {
// Gather information about the current code path, slicing out the current
// frame.
let path = (new Error().stack).split("\n").slice(1).map(line => {
return line.replace(/:\d+:\d+$/, "");
}).join("|");
let pathWithLineNumbers = (new Error().stack).split("\n").slice(1);
// Just in case, dirty the frame now that we've reflowed.
dirtyFrameFn();
// Stack trace is empty. Reflow was triggered by native code, which
// we ignore.
if (path === "") {
return;
}
let index = expectedStacks.findIndex(stack => path.startsWith(stack.join("|")));
if (index != -1) {
Assert.ok(true, "expected uninterruptible reflow: '" +
JSON.stringify(pathWithLineNumbers, null, "\t") + "'");
expectedStacks.splice(index, 1);
} else {
Assert.ok(false, "unexpected uninterruptible reflow \n" +
JSON.stringify(pathWithLineNumbers, null, "\t") + "\n");
}
},
reflowInterruptible(start, end) {
// We're not interested in interruptible reflows.
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
Ci.nsISupportsWeakReference])
};
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
docShell.addWeakReflowObserver(observer);
els.addListenerForAllEvents(win, dirtyFrameFn, true);
try {
dirtyFrameFn();
await testFn();
} finally {
for (let remainder of expectedStacks) {
Assert.ok(false,
`Unused expected reflow: ${JSON.stringify(remainder, null, "\t")}.\n` +
"This is probably a good thing - just remove it from the " +
"expected list.");
}
els.removeListenerForAllEvents(win, dirtyFrameFn, true);
docShell.removeWeakReflowObserver(observer);
elemToDirty.style.margin = "";
}
}

View File

@ -1,2 +0,0 @@
[DEFAULT]
[browser_toolbariconcolor_restyles.js]

View File

@ -129,10 +129,6 @@ browser.jar:
content/browser/pageinfo/permissions.js (content/pageinfo/permissions.js)
content/browser/pageinfo/security.js (content/pageinfo/security.js)
content/browser/robot.ico (content/robot.ico)
content/browser/sync/aboutSyncTabs.xul (content/sync/aboutSyncTabs.xul)
content/browser/sync/aboutSyncTabs.js (content/sync/aboutSyncTabs.js)
content/browser/sync/aboutSyncTabs.css (content/sync/aboutSyncTabs.css)
content/browser/sync/aboutSyncTabs-bindings.xml (content/sync/aboutSyncTabs-bindings.xml)
content/browser/safeMode.css (content/safeMode.css)
content/browser/safeMode.js (content/safeMode.js)
content/browser/safeMode.xul (content/safeMode.xul)

View File

@ -22,6 +22,7 @@ BROWSER_CHROME_MANIFESTS += [
'content/test/general/browser.ini',
'content/test/newtab/browser.ini',
'content/test/pageinfo/browser.ini',
'content/test/performance/browser.ini',
'content/test/permissions/browser.ini',
'content/test/plugins/browser.ini',
'content/test/popupNotifications/browser.ini',
@ -38,7 +39,6 @@ BROWSER_CHROME_MANIFESTS += [
'content/test/urlbar/browser.ini',
'content/test/webextensions/browser.ini',
'content/test/webrtc/browser.ini',
'content/test/windows/browser.ini',
]
if CONFIG['MOZ_UPDATER']:

View File

@ -80,8 +80,6 @@ static const RedirEntry kRedirMap[] = {
nsIAboutModule::ALLOW_SCRIPT },
{ "welcomeback", "chrome://browser/content/aboutWelcomeBack.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
{ "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul",
nsIAboutModule::ALLOW_SCRIPT },
// Linkable because of indexeddb use (bug 1228118)
{ "home", "chrome://browser/content/abouthome/aboutHome.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |

View File

@ -97,7 +97,6 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "searchreset", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "sessionrestore", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "welcomeback", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-tabs", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "home", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },

View File

@ -103,7 +103,7 @@ add_task(async function test() {
let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey",
[{
initDataTypes: [aKeyInfo.initDataType],
videoCapabilities: [{contentType: "video/webm"}],
videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}],
sessionTypes: ["persistent-license"],
persistentState: "required",
}]);
@ -156,7 +156,7 @@ add_task(async function test() {
let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey",
[{
initDataTypes: [aKeyInfo.initDataType],
videoCapabilities: [{contentType: "video/webm"}],
videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}],
sessionTypes: ["persistent-license"],
persistentState: "required",
}]);

View File

@ -98,7 +98,7 @@ async function setupEMEKey(browser) {
let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey",
[{
initDataTypes: [aKeyInfo.initDataType],
videoCapabilities: [{contentType: "video/webm"}],
videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}],
sessionTypes: ["persistent-license"],
persistentState: "required",
}]);
@ -153,7 +153,7 @@ async function checkEMEKey(browser, emeSessionId) {
let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey",
[{
initDataTypes: [aKeyInfo.initDataType],
videoCapabilities: [{contentType: "video/webm"}],
videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}],
sessionTypes: ["persistent-license"],
persistentState: "required",
}]);

View File

@ -147,6 +147,13 @@ var gMenuBuilder = {
let menuPopup = element.firstChild;
if (menuPopup && menuPopup.childNodes.length == 1) {
let onlyChild = menuPopup.firstChild;
// Keep single checkbox items in the submenu on Linux since
// the extension icon overlaps the checkbox otherwise.
if (AppConstants.platform === "linux" && onlyChild.getAttribute("type") === "checkbox") {
return element;
}
onlyChild.remove();
return onlyChild;
}

View File

@ -24,26 +24,54 @@ add_task(async function() {
type: "checkbox",
});
browser.contextMenus.create({
type: "separator",
});
browser.test.sendMessage("single-contextmenu-item-added");
browser.contextMenus.create({
title: "Checkbox",
type: "checkbox",
checked: true,
});
browser.test.onMessage.addListener(msg => {
if (msg !== "add-additional-menu-items") {
return;
}
browser.contextMenus.create({
title: "Checkbox",
type: "checkbox",
});
browser.contextMenus.create({
type: "separator",
});
browser.test.notifyPass("contextmenus-checkboxes");
browser.contextMenus.create({
title: "Checkbox",
type: "checkbox",
checked: true,
});
browser.contextMenus.create({
title: "Checkbox",
type: "checkbox",
});
browser.test.notifyPass("contextmenus-checkboxes");
});
},
});
await extension.startup();
await extension.awaitMessage("single-contextmenu-item-added");
async function testSingleCheckboxItem() {
let extensionMenuRoot = await openExtensionContextMenu();
// On Linux, the single menu item should be contained in a submenu.
if (AppConstants.platform === "linux") {
let items = extensionMenuRoot.getElementsByAttribute("type", "checkbox");
is(items.length, 1, "single checkbox should be in the submenu on Linux");
await closeContextMenu();
} else {
is(extensionMenuRoot, null, "there should be no submenu for a single checkbox item");
await closeContextMenu();
}
}
await testSingleCheckboxItem();
extension.sendMessage("add-additional-menu-items");
await extension.awaitFinish("contextmenus-checkboxes");
function confirmCheckboxStates(extensionMenuRoot, expectedStates) {

View File

@ -14,17 +14,12 @@ function genericChecker() {
browser.test.onMessage.addListener((msg, ...args) => {
if (msg == kind + "-check-views") {
let windowId = args[0];
let counts = {
"background": 0,
"tab": 0,
"popup": 0,
"kind": 0,
"window": 0,
background: 0,
tab: 0,
popup: 0,
kind: 0,
};
if (Number.isInteger(windowId)) {
counts.window = browser.extension.getViews({windowId: windowId}).length;
}
if (kind !== "background") {
counts.kind = browser.extension.getViews({type: kind}).length;
}
@ -48,8 +43,13 @@ function genericChecker() {
} else {
browser.test.sendMessage("counts", counts);
}
} else if (msg == kind + "-getViews-with-filter") {
let filter = args[0];
let count = browser.extension.getViews(filter).length;
browser.test.sendMessage("getViews-count", count);
} else if (msg == kind + "-open-tab") {
browser.tabs.create({windowId: args[0], url: browser.runtime.getURL("tab.html")});
browser.tabs.create({windowId: args[0], url: browser.runtime.getURL("tab.html")})
.then((tab) => browser.test.sendMessage("opened-tab", tab.id));
} else if (msg == kind + "-close-tab") {
browser.tabs.query({
windowId: args[0],
@ -112,28 +112,40 @@ add_task(async function() {
async function openTab(winId) {
extension.sendMessage("background-open-tab", winId);
await extension.awaitMessage("tab-ready");
return extension.awaitMessage("opened-tab");
}
async function checkViews(kind, tabCount, popupCount, kindCount, windowId = undefined, windowCount = 0) {
extension.sendMessage(kind + "-check-views", windowId);
async function checkViews(kind, tabCount, popupCount, kindCount) {
extension.sendMessage(kind + "-check-views");
let counts = await extension.awaitMessage("counts");
is(counts.background, 1, "background count correct");
is(counts.tab, tabCount, "tab count correct");
is(counts.popup, popupCount, "popup count correct");
is(counts.kind, kindCount, "count for type correct");
is(counts.window, windowCount, "count for window correct");
}
async function checkViewsWithFilter(filter, expectedCount) {
extension.sendMessage("background-getViews-with-filter", filter);
let count = await extension.awaitMessage("getViews-count");
is(count, expectedCount, `count for ${JSON.stringify(filter)} correct`);
}
await checkViews("background", 0, 0, 0);
await checkViewsWithFilter({windowId: -1}, 1);
await checkViewsWithFilter({tabId: -1}, 1);
await openTab(winId1);
let tabId1 = await openTab(winId1);
await checkViews("background", 1, 0, 0, winId1, 1);
await checkViews("background", 1, 0, 0);
await checkViews("tab", 1, 0, 1);
await checkViewsWithFilter({windowId: winId1}, 1);
await checkViewsWithFilter({tabId: tabId1}, 1);
await openTab(winId2);
let tabId2 = await openTab(winId2);
await checkViews("background", 2, 0, 0, winId2, 1);
await checkViews("background", 2, 0, 0);
await checkViewsWithFilter({windowId: winId2}, 1);
await checkViewsWithFilter({tabId: tabId2}, 1);
async function triggerPopup(win, callback) {
await clickBrowserAction(extension, win);
@ -153,18 +165,24 @@ add_task(async function() {
await new Promise(resolve => win1.setTimeout(resolve, 10));
await triggerPopup(win1, async function() {
await checkViews("background", 2, 1, 0, winId1, 2);
await checkViews("background", 2, 1, 0);
await checkViews("popup", 2, 1, 1);
await checkViewsWithFilter({windowId: winId1}, 2);
await checkViewsWithFilter({type: "popup", tabId: -1}, 1);
});
await triggerPopup(win2, async function() {
await checkViews("background", 2, 1, 0, winId2, 2);
await checkViews("background", 2, 1, 0);
await checkViews("popup", 2, 1, 1);
await checkViewsWithFilter({windowId: winId2}, 2);
await checkViewsWithFilter({type: "popup", tabId: -1}, 1);
});
info("checking counts after popups");
await checkViews("background", 2, 0, 0, winId1, 1);
await checkViews("background", 2, 0, 0);
await checkViewsWithFilter({windowId: winId1}, 1);
await checkViewsWithFilter({tabId: -1}, 1);
info("closing one tab");

View File

@ -170,35 +170,22 @@ class TestFirefoxRefresh(MarionetteTestCase):
self.assertEqual(titleInBookmarks, self._bookmarkText)
def checkHistory(self):
historyResults = self.runAsyncCode("""
let placeInfos = [];
PlacesUtils.asyncHistory.getPlacesInfo(makeURI(arguments[0]), {
handleError(resultCode, place) {
placeInfos = null;
marionetteScriptFinished("Unexpected error in fetching visit: " + resultCode);
},
handleResult(placeInfo) {
placeInfos.push(placeInfo);
},
handleCompletion() {
if (placeInfos) {
if (!placeInfos.length) {
marionetteScriptFinished("No visits found");
} else {
marionetteScriptFinished(placeInfos);
}
}
},
historyResult = self.runAsyncCode("""
PlacesUtils.history.fetch(arguments[0]).then(pageInfo => {
if (!pageInfo) {
marionetteScriptFinished("No visits found");
} else {
marionetteScriptFinished(pageInfo);
}
}).catch(e => {
marionetteScriptFinished("Unexpected error in fetching page: " + e);
});
""", script_args=[self._historyURL])
if type(historyResults) == str:
self.fail(historyResults)
if type(historyResult) == str:
self.fail(historyResult)
return
historyCount = len(historyResults)
self.assertEqual(historyCount, 1, "Should have exactly 1 entry for URI, got %d" % historyCount)
if historyCount == 1:
self.assertEqual(historyResults[0]['title'], self._historyTitle)
self.assertEqual(historyResult['title'], self._historyTitle)
def checkFormHistory(self):
formFieldResults = self.runAsyncCode("""

View File

@ -660,7 +660,7 @@ var BookmarkPropertiesPanel = {
} else if (this._itemType == BOOKMARK_FOLDER) {
itemGuid = await PlacesTransactions.NewFolder(info).transact();
for (let uri of this._URIs) {
let placeInfo = await PlacesUtils.promisePlaceInfo(uri);
let placeInfo = await PlacesUtils.history.fetch(uri);
let title = placeInfo ? placeInfo.title : "";
await PlacesTransactions.transact({ parentGuid: itemGuid, uri, title });
}

View File

@ -1,20 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY tabs.otherDevices.label "Tabs From Other Devices">
<!ENTITY tabs.searchText.label "Type here to find tabs…">
<!-- LOCALIZATION NOTE (tabs.context.openTab.accesskey, tabs.context.openMultipleTabs.accesskey):
Only one of these will show at a time (based on selection), so reusing accesskey is ok. -->
<!ENTITY tabs.context.openTab.label "Open This Tab">
<!ENTITY tabs.context.openTab.accesskey "O">
<!ENTITY tabs.context.openMultipleTabs.label "Open Selected Tabs">
<!ENTITY tabs.context.openMultipleTabs.accesskey "O">
<!ENTITY tabs.context.bookmarkSingleTab.label "Bookmark This Tab…">
<!ENTITY tabs.context.bookmarkSingleTab.accesskey "B">
<!ENTITY tabs.context.bookmarkMultipleTabs.label "Bookmark Selected Tabs…">
<!ENTITY tabs.context.bookmarkMultipleTabs.accesskey "B">
<!ENTITY tabs.context.refreshList.label "Refresh List">
<!ENTITY tabs.context.refreshList.accesskey "R">

View File

@ -20,7 +20,6 @@
locale/browser/aboutSearchReset.dtd (%chrome/browser/aboutSearchReset.dtd)
locale/browser/aboutSessionRestore.dtd (%chrome/browser/aboutSessionRestore.dtd)
locale/browser/aboutTabCrashed.dtd (%chrome/browser/aboutTabCrashed.dtd)
locale/browser/aboutSyncTabs.dtd (%chrome/browser/aboutSyncTabs.dtd)
locale/browser/browser.dtd (%chrome/browser/browser.dtd)
locale/browser/baseMenuOverlay.dtd (%chrome/browser/baseMenuOverlay.dtd)
locale/browser/browser.properties (%chrome/browser/browser.properties)

View File

@ -1,105 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#tabs-display,
#tabsList {
background-color: transparent;
-moz-appearance: none;
margin: 0;
}
#tabsList {
width: 100%;
}
#tabs-display {
background: #fff url(chrome://browser/skin/sync-bg.png) repeat-x center -80px;
}
#headers {
background: url(chrome://browser/skin/sync-32.png) no-repeat;
margin-top: 4px;
width: 45em;
height: 32px;
margin-inline-start: 2em;
margin-inline-end: 2em;
}
#headers:-moz-locale-dir(rtl) {
background-position-x: 100%;
}
#tabsListHeading {
font-size: 140%;
font-weight: bold;
margin-inline-start: 40px;
}
richlistitem {
margin-inline-end: 2em;
}
richlistitem[selected="true"],
richlistitem:focus {
outline-style: none;
}
richlistitem[type="tab"] {
min-height: 3em;
border: #999999 1px solid !important;
padding: 2px 5px;
margin-bottom: 4px;
margin-inline-start: 4em;
border-radius: 6px;
background-color: menu;
width: 44em;
opacity: 0.9;
box-shadow:
inset rgba(255, 255, 255, 0.5) 0 1px 0px,
inset rgba(0, 0, 0, 0.1) 0 -2px 0px,
rgba(0, 0, 0, 0.1) 0px 1px 0px;
}
richlistitem[type="tab"][selected="true"] {
background-color: -moz-MenuHover;
}
richlistitem[type="client"] {
min-height: 2em;
color: #000000;
margin-inline-start: 2em;
margin-top: 2px;
margin-bottom: 3px;
width: 42em;
border-radius: 6px;
background-color: transparent;
-moz-user-focus: ignore !important;
}
richlistitem.mobile[type="client"] {
list-style-image: url("chrome://browser/skin/sync-mobileIcon.svg#icon");
}
richlistitem.desktop[type="client"] {
list-style-image: url("chrome://browser/skin/sync-desktopIcon.svg#icon");
}
.title,
.clientName {
color: #000000;
font-size: 1.1em;
}
.title[selected="true"],
.url[selected="true"] {
color: inherit;
}
.url {
color: -moz-nativehyperlinktext;
font-size: 0.95em;
}
.tabIcon {
padding-inline-start: 2px;
padding-top: 2px;
}

View File

@ -8,7 +8,6 @@ browser.jar:
#include ../shared/jar.inc.mn
skin/classic/browser/sanitizeDialog.css
skin/classic/browser/aboutSessionRestore-window-icon.png
skin/classic/browser/aboutSyncTabs.css
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
* skin/classic/browser/browser.css
* skin/classic/browser/compacttheme.css
@ -99,9 +98,6 @@ browser.jar:
skin/classic/browser/tabbrowser/tab-stroke-start@2x.png (tabbrowser/tab-stroke-start@2x.png)
skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
skin/classic/browser/sync-16.png
skin/classic/browser/sync-32.png
skin/classic/browser/sync-bg.png
skin/classic/browser/sync-desktopIcon.svg (../shared/sync-desktopIcon.svg)
skin/classic/browser/sync-horizontalbar.png
skin/classic/browser/sync-horizontalbar@2x.png

View File

@ -1,105 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#tabs-display,
#tabsList {
background-color: transparent;
-moz-appearance: none;
margin: 0;
}
#tabsList {
width: 100%;
}
#tabs-display {
background: #fff url(chrome://browser/skin/sync-bg.png) repeat-x center -80px;
}
#headers {
background: url(chrome://browser/skin/sync-32.png) no-repeat;
margin-top: 4px;
width: 45em;
height: 32px;
margin-inline-start: 2em;
margin-inline-end: 2em;
}
#headers:-moz-locale-dir(rtl) {
background-position-x: 100%;
}
#tabsListHeading {
font-size: 140%;
font-weight: bold;
margin-inline-start: 40px;
}
richlistitem {
margin-inline-end: 2em;
}
richlistitem[selected="true"],
richlistitem:focus {
outline-style: none;
}
richlistitem[type="tab"] {
min-height: 3em;
border: #999999 1px solid !important;
padding: 2px 5px;
margin-bottom: 4px;
margin-inline-start: 4em;
border-radius: 6px;
background-color: menu;
width: 44em;
opacity: 0.9;
box-shadow:
inset rgba(255, 255, 255, 0.5) 0 1px 0px,
inset rgba(0, 0, 0, 0.1) 0 -2px 0px,
rgba(0, 0, 0, 0.1) 0px 1px 0px;
}
richlistitem[type="tab"][selected="true"] {
background-color: -moz-MenuHover;
}
richlistitem[type="client"] {
min-height: 2em;
color: #000000;
margin-inline-start: 2em;
margin-top: 2px;
margin-bottom: 3px;
width: 42em;
border-radius: 6px;
background-color: transparent;
-moz-user-focus: ignore !important;
}
richlistitem.mobile[type="client"] {
list-style-image: url("chrome://browser/skin/sync-mobileIcon.svg#icon");
}
richlistitem.desktop[type="client"] {
list-style-image: url("chrome://browser/skin/sync-desktopIcon.svg#icon");
}
.title,
.clientName {
color: #000000;
font-size: 1.1em;
}
.title[selected="true"],
.url[selected="true"] {
color: inherit;
}
.url {
color: -moz-nativehyperlinktext;
font-size: 0.95em;
}
.tabIcon {
padding-inline-start: 2px;
padding-top: 2px;
}

View File

@ -7,7 +7,6 @@ browser.jar:
#include ../shared/jar.inc.mn
skin/classic/browser/sanitizeDialog.css
skin/classic/browser/aboutSessionRestore-window-icon.png
skin/classic/browser/aboutSyncTabs.css
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
* skin/classic/browser/browser.css
* skin/classic/browser/compacttheme.css
@ -146,9 +145,6 @@ browser.jar:
skin/classic/browser/tabbrowser/tab-stroke-start@2x.png (tabbrowser/tab-stroke-start@2x.png)
skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
skin/classic/browser/tabbrowser/tabDragIndicator@2x.png (tabbrowser/tabDragIndicator@2x.png)
skin/classic/browser/sync-16.png
skin/classic/browser/sync-32.png
skin/classic/browser/sync-bg.png
skin/classic/browser/sync-desktopIcon.svg (../shared/sync-desktopIcon.svg)
skin/classic/browser/sync-horizontalbar.png
skin/classic/browser/sync-horizontalbar@2x.png

View File

@ -170,6 +170,7 @@
transition: none;
}
#urlbar[pageproxystate="invalid"] > #identity-box > #extension-icon,
#urlbar[pageproxystate="invalid"] > #identity-box > #tracking-protection-icon {
visibility: collapse;
}

View File

@ -13,12 +13,18 @@ toolbar[brighttext] :-moz-any(@primaryToolbarButtons@) {
fill: var(--toolbarbutton-icon-fill-inverted);
}
#back-button {
list-style-image: url("chrome://browser/skin/back-large.svg");
#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
%ifdef MOZ_PHOTON_THEME
#forward-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#reload-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
%endif
#nav-bar-overflow-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#panic-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
transform: scaleX(-1);
}
#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
transform: scaleX(-1);
#back-button {
list-style-image: url("chrome://browser/skin/back-large.svg");
}
#forward-button {
@ -26,10 +32,6 @@ toolbar[brighttext] :-moz-any(@primaryToolbarButtons@) {
}
%ifdef MOZ_PHOTON_THEME
#forward-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
transform: scaleX(-1);
}
#reload-button {
list-style-image: url("chrome://browser/skin/reload.svg");
}
@ -171,10 +173,6 @@ toolbar:not([brighttext]) #bookmarks-menu-button[cui-areatype="toolbar"][starred
list-style-image: url("chrome://browser/skin/chevron.svg");
}
#nav-bar-overflow-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
transform: scaleX(-1);
}
#email-link-button[cui-areatype="toolbar"] {
list-style-image: url("chrome://browser/skin/mail.svg");
}
@ -191,10 +189,6 @@ toolbar:not([brighttext]) #bookmarks-menu-button[cui-areatype="toolbar"][starred
fill: rgb(213, 32, 20);
}
#panic-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
transform: scaleX(-1);
}
#webide-button[cui-areatype="toolbar"] {
list-style-image: url("chrome://browser/skin/webIDE.svg");
}

View File

@ -1,105 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#tabs-display,
#tabsList {
background-color: transparent;
-moz-appearance: none;
margin: 0;
}
#tabsList {
width: 100%;
}
#tabs-display {
background: #fff url(chrome://browser/skin/sync-bg.png) repeat-x center -80px;
}
#headers {
background: url(chrome://browser/skin/sync-32.png) no-repeat;
margin-top: 4px;
width: 45em;
height: 32px;
margin-inline-start: 2em;
margin-inline-end: 2em;
}
#headers:-moz-locale-dir(rtl) {
background-position-x: 100%;
}
#tabsListHeading {
font-size: 140%;
font-weight: bold;
margin-inline-start: 40px;
}
richlistitem {
margin-inline-end: 2em;
}
richlistitem[selected="true"],
richlistitem:focus {
outline-style: none;
}
richlistitem[type="tab"] {
min-height: 3em;
border: #999999 1px solid !important;
padding: 2px 5px;
margin-bottom: 4px;
margin-inline-start: 4em;
border-radius: 6px;
background-color: menu;
width: 44em;
opacity: 0.9;
box-shadow:
inset rgba(255, 255, 255, 0.5) 0 1px 0px,
inset rgba(0, 0, 0, 0.1) 0 -2px 0px,
rgba(0, 0, 0, 0.1) 0px 1px 0px;
}
richlistitem[type="tab"][selected="true"] {
background-color: -moz-MenuHover;
}
richlistitem[type="client"] {
min-height: 2em;
color: #000000;
margin-inline-start: 2em;
margin-top: 2px;
margin-bottom: 3px;
width: 42em;
border-radius: 6px;
background-color: transparent;
-moz-user-focus: ignore !important;
}
richlistitem.mobile[type="client"] {
list-style-image: url("chrome://browser/skin/sync-mobileIcon.svg#icon");
}
richlistitem.desktop[type="client"] {
list-style-image: url("chrome://browser/skin/sync-desktopIcon.svg#icon");
}
.title,
.clientName {
color: #000000;
font-size: 1.1em;
}
.title[selected="true"],
.url[selected="true"] {
color: inherit;
}
.url {
color: -moz-nativehyperlinktext;
font-size: 0.95em;
}
.tabIcon {
padding-inline-start: 2px;
padding-top: 2px;
}

View File

@ -7,7 +7,6 @@ browser.jar:
#include ../shared/jar.inc.mn
skin/classic/browser/sanitizeDialog.css
skin/classic/browser/aboutSessionRestore-window-icon.png
skin/classic/browser/aboutSyncTabs.css
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
* skin/classic/browser/browser.css
* skin/classic/browser/compacttheme.css
@ -125,9 +124,6 @@ browser.jar:
skin/classic/browser/tabbrowser/tab-stroke-start.png (tabbrowser/tab-stroke-start.png)
skin/classic/browser/tabbrowser/tab-stroke-start@2x.png (tabbrowser/tab-stroke-start@2x.png)
skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
skin/classic/browser/sync-16.png
skin/classic/browser/sync-32.png
skin/classic/browser/sync-bg.png
skin/classic/browser/sync-desktopIcon.svg (../shared/sync-desktopIcon.svg)
skin/classic/browser/sync-horizontalbar.png
skin/classic/browser/sync-horizontalbar@2x.png

View File

@ -65,20 +65,19 @@ def check_build_environment(help, dist):
return result
set_config('TOPSRCDIR', delayed_getattr(check_build_environment, 'topsrcdir'))
set_config('TOPOBJDIR', delayed_getattr(check_build_environment, 'topobjdir'))
set_config('MOZ_BUILD_ROOT', delayed_getattr(check_build_environment,
'topobjdir'))
set_config('DIST', delayed_getattr(check_build_environment, 'dist'))
set_config('TOPSRCDIR', check_build_environment.topsrcdir)
set_config('TOPOBJDIR', check_build_environment.topobjdir)
set_config('MOZ_BUILD_ROOT', check_build_environment.topobjdir)
set_config('DIST', check_build_environment.dist)
add_old_configure_assignment(
'_topsrcdir', delayed_getattr(check_build_environment, 'topsrcdir'))
'_topsrcdir', check_build_environment.topsrcdir)
add_old_configure_assignment(
'_objdir', delayed_getattr(check_build_environment, 'topobjdir'))
'_objdir', check_build_environment.topobjdir)
add_old_configure_assignment(
'MOZ_BUILD_ROOT', delayed_getattr(check_build_environment, 'topobjdir'))
'MOZ_BUILD_ROOT', check_build_environment.topobjdir)
add_old_configure_assignment(
'DIST', delayed_getattr(check_build_environment, 'dist'))
'DIST', check_build_environment.dist)
option(env='MOZ_AUTOMATION', help='Enable options for automated builds')
set_config('MOZ_AUTOMATION', depends_if('MOZ_AUTOMATION')(lambda x: True))
@ -558,21 +557,20 @@ def target_variables(target):
INTEL_ARCHITECTURE=target.cpu in ('x86', 'x86_64') or None,
)
set_config('OS_TARGET', delayed_getattr(target_variables, 'OS_TARGET'))
set_config('OS_TARGET', target_variables.OS_TARGET)
add_old_configure_assignment('OS_TARGET',
delayed_getattr(target_variables, 'OS_TARGET'))
set_config('OS_ARCH', delayed_getattr(target_variables, 'OS_ARCH'))
target_variables.OS_TARGET)
set_config('OS_ARCH', target_variables.OS_ARCH)
add_old_configure_assignment('OS_ARCH',
delayed_getattr(target_variables, 'OS_ARCH'))
set_config('OS_TEST', delayed_getattr(target_variables, 'OS_TEST'))
target_variables.OS_ARCH)
set_config('OS_TEST', target_variables.OS_TEST)
add_old_configure_assignment('OS_TEST',
delayed_getattr(target_variables, 'OS_TEST'))
set_config('CPU_ARCH', delayed_getattr(target, 'cpu'))
add_old_configure_assignment('CPU_ARCH', delayed_getattr(target, 'cpu'))
set_config('INTEL_ARCHITECTURE', delayed_getattr(target_variables,
'INTEL_ARCHITECTURE'))
set_config('TARGET_CPU', delayed_getattr(target, 'raw_cpu'))
set_config('TARGET_OS', delayed_getattr(target, 'raw_os'))
target_variables.OS_TEST)
set_config('CPU_ARCH', target.cpu)
add_old_configure_assignment('CPU_ARCH', target.cpu)
set_config('INTEL_ARCHITECTURE', target_variables.INTEL_ARCHITECTURE)
set_config('TARGET_CPU', target.raw_cpu)
set_config('TARGET_OS', target.raw_os)
@depends(host)
@ -585,10 +583,10 @@ def host_variables(host):
HOST_OS_ARCH=os_arch,
)
set_config('HOST_CPU_ARCH', delayed_getattr(host, 'cpu'))
set_config('HOST_OS_ARCH', delayed_getattr(host_variables, 'HOST_OS_ARCH'))
set_config('HOST_CPU_ARCH', host.cpu)
set_config('HOST_OS_ARCH', host_variables.HOST_OS_ARCH)
add_old_configure_assignment('HOST_OS_ARCH',
delayed_getattr(host_variables, 'HOST_OS_ARCH'))
host_variables.HOST_OS_ARCH)
@depends(target)
def target_is_windows(target):
@ -717,19 +715,14 @@ def milestone(build_env, _):
is_nightly=is_nightly,
is_release_or_beta=is_release_or_beta)
@depends(milestone)
def is_nightly(milestone):
return milestone.is_nightly
set_config('GRE_MILESTONE', delayed_getattr(milestone, 'version'))
set_config('NIGHTLY_BUILD', is_nightly)
set_define('NIGHTLY_BUILD', is_nightly)
add_old_configure_assignment('NIGHTLY_BUILD',
is_nightly)
set_config('RELEASE_OR_BETA', delayed_getattr(milestone, 'is_release_or_beta'))
set_define('RELEASE_OR_BETA', delayed_getattr(milestone, 'is_release_or_beta'))
set_config('GRE_MILESTONE', milestone.version)
set_config('NIGHTLY_BUILD', milestone.is_nightly)
set_define('NIGHTLY_BUILD', milestone.is_nightly)
add_old_configure_assignment('NIGHTLY_BUILD', milestone.is_nightly)
set_config('RELEASE_OR_BETA', milestone.is_release_or_beta)
set_define('RELEASE_OR_BETA', milestone.is_release_or_beta)
add_old_configure_assignment('RELEASE_OR_BETA',
delayed_getattr(milestone, 'is_release_or_beta'))
milestone.is_release_or_beta)
# The app update channel is 'default' when not supplied. The value is used in
# the application's confvars.sh (and is made available to a project specific

View File

@ -60,5 +60,5 @@ def id_and_secret_keyfile(desc):
name = desc.upper().replace(' ', '_')
set_config('MOZ_%s_CLIENTID' % name, delayed_getattr(content, 'id'))
set_config('MOZ_%s_KEY' % name, delayed_getattr(content, 'secret'))
set_config('MOZ_%s_CLIENTID' % name, content.id)
set_config('MOZ_%s_KEY' % name, content.secret)

View File

@ -27,9 +27,9 @@ def yasm_version(yasm):
# Until we move all the yasm consumers out of old-configure.
# bug 1257904
add_old_configure_assignment('_YASM_MAJOR_VERSION',
delayed_getattr(yasm_version, 'major'))
yasm_version.major)
add_old_configure_assignment('_YASM_MINOR_VERSION',
delayed_getattr(yasm_version, 'minor'))
yasm_version.minor)
@depends(yasm, target)
def yasm_asflags(yasm, target):
@ -645,7 +645,7 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
# result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let
# old-configure AC_SUBST it (because it's autoconf doing it, not us)
compiler = check_prog('_%s' % var, what=what, progs=default_compilers,
input=delayed_getattr(provided_compiler, 'compiler'),
input=provided_compiler.compiler,
paths=toolchain_search_path)
@depends(compiler, provided_compiler, compiler_wrapper, host_or_target)
@ -791,11 +791,11 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
# old-configure to do some of its still existing checks.
if language == 'C':
set_config(
'%s_TYPE' % var, delayed_getattr(valid_compiler, 'type'))
'%s_TYPE' % var, valid_compiler.type)
add_old_configure_assignment(
'%s_TYPE' % var, delayed_getattr(valid_compiler, 'type'))
'%s_TYPE' % var, valid_compiler.type)
add_old_configure_assignment(
'%s_VERSION' % var, delayed_getattr(valid_compiler, 'version'))
'%s_VERSION' % var, valid_compiler.version)
valid_compiler = compiler_class(valid_compiler)

View File

@ -366,27 +366,6 @@ always = dependable(True)
never = dependable(False)
# Some @depends function return namespaces, and one could want to use one
# specific attribute from such a namespace as a "value" given to functions
# such as `set_config`. But those functions do not take immediate values.
# The `delayed_getattr` function allows access to attributes from the result
# of a @depends function in a non-immediate manner.
# @depends('--option')
# def option(value)
# return namespace(foo=value)
# set_config('FOO', delayed_getattr(option, 'foo')
@template
def delayed_getattr(func, key):
@depends(func)
def result(value):
# The @depends function we're being passed may have returned
# None, or an object that simply doesn't have the wanted key.
# In that case, just return None.
return getattr(value, key, None)
return result
# Like @depends, but the decorated function is only called if one of the
# arguments it would be called with has a positive value (bool(value) is True)
@template

View File

@ -153,7 +153,7 @@ def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version,
add_old_configure_assignment(
'WINDOWSSDKDIR',
delayed_getattr(valid_windows_sdk_dir, 'path'))
valid_windows_sdk_dir.path)
add_old_configure_assignment(
'MOZ_WINSDK_MAXVER',
depends(valid_windows_sdk_dir)(

View File

@ -973,6 +973,7 @@ define CARGO_BUILD
env $(environment_cleaner) $(rustflags_override) \
CARGO_TARGET_DIR=$(CARGO_TARGET_DIR) \
RUSTC=$(RUSTC) \
MOZ_SRC=$(topsrcdir) \
MOZ_DIST=$(ABS_DIST) \
LIBCLANG_PATH=$(MOZ_LIBCLANG_PATH) \
CLANG_PATH=$(MOZ_CLANG_PATH) \

View File

@ -28,6 +28,8 @@ module.exports = {
"setInterval": true,
"setTimeout": true,
"uneval": true,
"TextDecoder": true,
"TextEncoder": true,
"URL": true,
"WebSocket": true,
"XMLHttpRequest": true

View File

@ -83,7 +83,13 @@ SourceMapURLService.prototype.originalPositionFor = async function (url, line, c
await this._sourceMapService.getOriginalURLs(urlInfo);
const location = { sourceId: urlInfo.id, line, column, sourceUrl: url };
let resolvedLocation = await this._sourceMapService.getOriginalLocation(location);
return resolvedLocation === location ? null : resolvedLocation;
if (!resolvedLocation ||
(resolvedLocation.line === location.line &&
resolvedLocation.column === location.column &&
resolvedLocation.sourceUrl === location.sourceUrl)) {
return null;
}
return resolvedLocation;
};
exports.SourceMapURLService = SourceMapURLService;

View File

@ -68,6 +68,7 @@ skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keybo
[browser_inspector_highlighter-02.js]
[browser_inspector_highlighter-03.js]
[browser_inspector_highlighter-04.js]
[browser_inspector_highlighter-05.js]
[browser_inspector_highlighter-by-type.js]
[browser_inspector_highlighter-cancel.js]
[browser_inspector_highlighter-comments.js]

View File

@ -0,0 +1,69 @@
/* 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/. */
"use strict";
// This is testing that the Anonymous Content is properly inserted into the document.
// Usually that is happening during the "interactive" state of the document, to have them
// ready as soon as possible.
// However, in some conditions, that's not possible since we don't have access yet to
// the `CustomContentContainer`, that is used to add the Anonymous Content.
// That can happen if the page has some external resource, as <link>, that takes time
// to load and / or returns the wrong content. This is not happening, for instance, with
// images.
//
// In those case, we want to be sure that if we're not able to insert the Anonymous
// Content at the "interactive" state, we're doing so when the document is loaded.
//
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1365075
const server = createTestHTTPServer();
const filepath = "/slow.css";
const cssuri = `http://localhost:${server.identity.primaryPort}${filepath}`;
// Register a slow css file handler so we can simulate a long loading time.
server.registerContentType("css", "text/css");
server.registerPathHandler(filepath, (metadata, response) => {
info("CSS has been requested");
response.processAsync();
setTimeout(() => {
info("CSS is responding");
response.finish();
}, 2000);
});
const TEST_URL = "data:text/html," + encodeURIComponent(`
<!DOCTYPE html>
<html>
<head>
<link href="${cssuri}" rel="stylesheet" />
</head>
<body>
<p>Slow page</p>
</body>
</html>
`);
add_task(function* () {
info("Open the inspector to a blank page.");
let { inspector, tab, testActor } = yield openInspectorForURL("about:blank");
let pageLoaded = waitForPageLoad(tab);
info("Navigate to the test url and waiting for the page to be loaded.");
yield navigateTo(inspector, TEST_URL);
yield pageLoaded;
info("Shows the box model highligher for the <p> node.");
let divFront = yield getNodeFront("p", inspector);
yield inspector.highlighter.showBoxModel(divFront);
info("Check the node is highlighted.");
is(yield testActor.isHighlighting(), true,
"Box Model highlighter is working as expected.");
});
const waitForPageLoad = (tab) => new Promise(resolve => {
tab.linkedBrowser.addEventListener("load", resolve, {capture: true, once: true});
});

View File

@ -35,13 +35,16 @@ const StackTrace = createClass({
stacktrace: PropTypes.array.isRequired,
onViewSourceInDebugger: PropTypes.func.isRequired,
onViewSourceInScratchpad: PropTypes.func,
// Service to enable the source map feature.
sourceMapService: PropTypes.object,
},
render() {
let {
stacktrace,
onViewSourceInDebugger,
onViewSourceInScratchpad
onViewSourceInScratchpad,
sourceMapService,
} = this.props;
let frames = [];
@ -67,7 +70,8 @@ const StackTrace = createClass({
showFullSourceUrl: true,
onClick: (/^Scratchpad\/\d+$/.test(source))
? onViewSourceInScratchpad
: onViewSourceInDebugger
: onViewSourceInDebugger,
sourceMapService,
}), "\n");
});

View File

@ -11,6 +11,7 @@ support-files =
[test_searchbox-with-autocomplete.html]
[test_sidebar_toggle.html]
[test_stack-trace.html]
[test_stack-trace-source-maps.html]
[test_tabs_accessibility.html]
[test_tabs_menu.html]
[test_tree_01.html]

View File

@ -0,0 +1,105 @@
<!-- 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/. -->
<!DOCTYPE HTML>
<html>
<!--
Test the rendering of a stack trace with source maps
-->
<head>
<meta charset="utf-8">
<title>StackTrace component test</title>
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<script src="head.js"></script>
<script>
/* import-globals-from head.js */
"use strict";
window.onload = function () {
let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
let React = browserRequire("devtools/client/shared/vendor/react");
let StackTrace = React.createFactory(
browserRequire("devtools/client/shared/components/stack-trace")
);
ok(StackTrace, "Got the StackTrace factory");
add_task(function* () {
let stacktrace = [
{
filename: "https://bugzilla.mozilla.org/bundle.js",
lineNumber: 99,
columnNumber: 10
},
{
functionName: "loadFunc",
filename: "https://bugzilla.mozilla.org/bundle.js",
lineNumber: 108,
}
];
let props = {
stacktrace,
onViewSourceInDebugger: () => {},
onViewSourceInScratchpad: () => {},
// A mock source map service.
sourceMapService: {
originalPositionFor: function (url, line, column) {
let newLine = line === 99 ? 1 : 7;
// Return a phony promise-like thing that resolves
// immediately.
return {
then: function (consequence) {
consequence({
sourceId: "whatever",
sourceUrl: "https://bugzilla.mozilla.org/original.js",
line: newLine,
column,
});
},
};
}
},
};
let trace = ReactDOM.render(StackTrace(props), window.document.body);
yield forceRender(trace);
let traceEl = ReactDOM.findDOMNode(trace);
ok(traceEl, "Rendered StackTrace has an element");
// Get the child nodes and filter out the text-only whitespace ones
let frameEls = Array.from(traceEl.childNodes)
.filter(n => n.className && n.className.includes("frame"));
ok(frameEls, "Rendered StackTrace has frames");
is(frameEls.length, 2, "StackTrace has 2 frames");
checkFrameString({
el: frameEls[0],
functionName: "<anonymous>",
source: "https://bugzilla.mozilla.org/original.js",
file: "original.js",
line: 1,
column: 10,
shouldLink: true,
tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:1:10",
});
checkFrameString({
el: frameEls[1],
functionName: "loadFunc",
source: "https://bugzilla.mozilla.org/original.js",
file: "original.js",
line: 7,
column: null,
shouldLink: true,
tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:7",
});
});
};
</script>
</body>
</html>

View File

@ -21,7 +21,7 @@ const EventEmitter = require("devtools/shared/event-emitter");
const {Task} = require("devtools/shared/task");
const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
const {TextDecoder, OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
const {OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
const {
getString,
showFilePicker,

View File

@ -19,12 +19,11 @@ loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", tr
const { extend } = require("sdk/core/heritage");
const XHTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const STRINGS_URI = "devtools/client/locales/webconsole.properties";
const WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
const { getSourceNames } = require("devtools/client/shared/source-utils");
const {Task} = require("devtools/shared/task");
const l10n = new WebConsoleUtils.L10n(STRINGS_URI);
const l10n = require("devtools/client/webconsole/webconsole-l10n");
const nodeConstants = require("devtools/shared/dom-node-constants");
const {PluralForm} = require("devtools/shared/plural-form");
@ -3556,9 +3555,12 @@ Widgets.Stacktrace.prototype = extend(Widgets.BaseWidget.prototype, {
result.className = "stacktrace devtools-monospace";
if (this.stacktrace) {
const target = this.message.output.toolboxTarget;
const toolbox = gDevTools.getToolbox(target);
this.output.owner.ReactDOM.render(this.output.owner.StackTraceView({
stacktrace: this.stacktrace,
onViewSourceInDebugger: frame => this.output.openLocationInDebugger(frame)
onViewSourceInDebugger: frame => this.output.openLocationInDebugger(frame),
sourceMapService: toolbox ? toolbox.sourceMapURLService : null,
}), result);
}

View File

@ -22,8 +22,7 @@ loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main",
loader.lazyRequireGetter(this, "showDoorhanger", "devtools/client/shared/doorhanger", true);
loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source");
const STRINGS_URI = "devtools/client/locales/webconsole.properties";
var l10n = new WebConsoleUtils.L10n(STRINGS_URI);
const l10n = require("devtools/client/webconsole/webconsole-l10n");
const BROWSER_CONSOLE_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";

View File

@ -27,8 +27,7 @@ loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/wi
loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
const STRINGS_URI = "devtools/client/locales/webconsole.properties";
var l10n = new WebConsoleUtils.L10n(STRINGS_URI);
const l10n = require("devtools/client/webconsole/webconsole-l10n");
// Constants used for defining the direction of JSTerm input history navigation.
const HISTORY_BACK = -1;

View File

@ -19,8 +19,9 @@ DevToolsModules(
'panel.js',
'utils.js',
'webconsole-connection-proxy.js',
'webconsole-l10n.js',
'webconsole.js',
)
with Files('**'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Console')
with Files('**'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Console')

View File

@ -38,7 +38,9 @@ var NetInfoBody = React.createClass({
data: PropTypes.shape({
request: PropTypes.object.isRequired,
response: PropTypes.object.isRequired
})
}),
// Service to enable the source map feature.
sourceMapService: PropTypes.object,
},
displayName: "NetInfoBody",
@ -76,7 +78,7 @@ var NetInfoBody = React.createClass({
},
getTabPanels() {
let actions = this.props.actions;
let { actions, sourceMapService } = this.props;
let data = this.state.data;
let {request} = data;
@ -153,7 +155,8 @@ var NetInfoBody = React.createClass({
title: Locale.$STR("netRequest.callstack")},
StackTraceTab({
data: data,
actions: actions
actions: actions,
sourceMapService: sourceMapService,
})
)
);

View File

@ -13,15 +13,17 @@ const StackTraceTab = createClass({
data: PropTypes.object.isRequired,
actions: PropTypes.shape({
onViewSourceInDebugger: PropTypes.func.isRequired
})
}),
// Service to enable the source map feature.
sourceMapService: PropTypes.object,
},
render() {
let { stacktrace } = this.props.data.cause;
let { actions } = this.props;
let { actions, sourceMapService } = this.props;
let onViewSourceInDebugger = actions.onViewSourceInDebugger.bind(actions);
return StackTrace({ stacktrace, onViewSourceInDebugger });
return StackTrace({ stacktrace, onViewSourceInDebugger, sourceMapService });
}
});

View File

@ -163,7 +163,8 @@ NetRequest.prototype = {
// As soon as Redux is in place state and actions will come from
// separate modules.
let body = NetInfoBody({
actions: this
actions: this,
sourceMapService: this.owner.sourceMapURLService,
});
// Render net info body!

View File

@ -30,6 +30,7 @@ const ConsoleOutput = createClass({
serviceContainer: PropTypes.shape({
attachRefToHud: PropTypes.func.isRequired,
openContextMenu: PropTypes.func.isRequired,
sourceMapService: PropTypes.object,
}),
autoscroll: PropTypes.bool.isRequired,
dispatch: PropTypes.func.isRequired,

View File

@ -13,7 +13,6 @@ const { getAllFilters } = require("devtools/client/webconsole/new-console-output
const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
const {
filterTextSet,
filtersClear,
filterBarToggle,
messagesClear
} = require("devtools/client/webconsole/new-console-output/actions/index");
@ -49,10 +48,6 @@ const FilterBar = createClass({
this.props.dispatch(filterBarToggle());
},
onClickFiltersClear: function () {
this.props.dispatch(filtersClear());
},
onSearchInput: function (e) {
this.props.dispatch(filterTextSet(e.target.value));
},

View File

@ -20,6 +20,7 @@ ConsoleCommand.propTypes = {
autoscroll: PropTypes.bool.isRequired,
indent: PropTypes.number.isRequired,
timestampsVisible: PropTypes.bool.isRequired,
serviceContainer: PropTypes.object,
};
ConsoleCommand.defaultProps = {
@ -35,6 +36,7 @@ function ConsoleCommand(props) {
indent,
message,
timestampsVisible,
serviceContainer,
} = props;
const {
@ -44,10 +46,6 @@ function ConsoleCommand(props) {
messageText: messageBody,
} = message;
const {
serviceContainer,
} = props;
return Message({
source,
type,

View File

@ -20,6 +20,7 @@ EvaluationResult.propTypes = {
message: PropTypes.object.isRequired,
indent: PropTypes.number.isRequired,
timestampsVisible: PropTypes.bool.isRequired,
serviceContainer: PropTypes.object,
};
EvaluationResult.defaultProps = {

View File

@ -20,6 +20,7 @@ PageError.propTypes = {
open: PropTypes.bool,
indent: PropTypes.number.isRequired,
timestampsVisible: PropTypes.bool.isRequired,
serviceContainer: PropTypes.object,
};
PageError.defaultProps = {

View File

@ -148,6 +148,7 @@ const Message = createClass({
stacktrace: stacktrace,
onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger,
onViewSourceInScratchpad: serviceContainer.onViewSourceInScratchpad,
sourceMapService: serviceContainer.sourceMapService,
})
);
}

View File

@ -9,6 +9,7 @@ const { l10n } = require("devtools/client/webconsole/new-console-output/utils/me
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const { getLogLimit } = require("devtools/client/webconsole/new-console-output/selectors/prefs");
const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const {
MESSAGE_TYPE,
MESSAGE_SOURCE
@ -131,8 +132,9 @@ function isTextInFrame(text, frame) {
if (!frame) {
return false;
}
return Object.values(frame)
.join(":")
const { short } = getSourceNames(frame.source);
return `${short}:${frame.line}:${frame.column}`
.toLocaleLowerCase()
.includes(text.toLocaleLowerCase());
}

View File

@ -99,8 +99,7 @@ describe("ConsoleAPICall component:", () => {
serviceContainer,
timestampsVisible: true,
}));
const L10n = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
const { timestampString } = new L10n();
const { timestampString } = require("devtools/client/webconsole/webconsole-l10n");
expect(wrapper.find(".timestamp").text()).toBe(timestampString(message.timeStamp));
});

View File

@ -97,8 +97,7 @@ describe("EvaluationResult component:", () => {
message,
timestampsVisible: true,
}));
const L10n = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
const { timestampString } = new L10n();
const { timestampString } = require("devtools/client/webconsole/webconsole-l10n");
expect(wrapper.find(".timestamp").text()).toBe(timestampString(message.timeStamp));
});

View File

@ -29,8 +29,7 @@ describe("NetworkEventMessage component:", () => {
serviceContainer,
timestampsVisible: true,
}));
const L10n = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
const { timestampString } = new L10n();
const { timestampString } = require("devtools/client/webconsole/webconsole-l10n");
expect(wrapper.find(".timestamp").text()).toBe(timestampString(message.timeStamp));
expect(wrapper.find(".message-body .method").text()).toBe("GET");

View File

@ -32,8 +32,7 @@ describe("PageError component:", () => {
serviceContainer,
timestampsVisible: true,
}));
const L10n = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
const { timestampString } = new L10n();
const { timestampString } = require("devtools/client/webconsole/webconsole-l10n");
expect(wrapper.find(".timestamp").text()).toBe(timestampString(message.timeStamp));

View File

@ -14,9 +14,7 @@ Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
var {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils");
const WEBCONSOLE_STRINGS_URI = "devtools/client/locales/webconsole.properties";
var WCUL10n = new WebConsoleUtils.L10n(WEBCONSOLE_STRINGS_URI);
var WCUL10n = require("devtools/client/webconsole/webconsole-l10n");
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
registerCleanupFunction(function* () {

View File

@ -27,8 +27,6 @@ requireHacker.global_hook("default", path => {
// Some modules depend on Chrome APIs which don't work in mocha. When such a module
// is required, replace it with a mock version.
switch (path) {
case "devtools/client/webconsole/utils":
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/WebConsoleUtils")`;
case "devtools/shared/l10n":
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/LocalizationHelper")`;
case "devtools/shared/plural-form":

View File

@ -66,6 +66,18 @@ describe("Searching in grips", () => {
expect(getAllMessages(store.getState()).size).toEqual(1);
});
});
describe("Search in frame", () => {
it("matches on file name", () => {
store.dispatch(actions.filterTextSet("test-console-api.html:1:27"));
expect(getAllMessages(store.getState()).size).toEqual(7);
});
it("do not match on full url", () => {
store.dispatch(actions.filterTextSet("http://example.com/browser/devtools"));
expect(getAllMessages(store.getState()).size).toEqual(0);
});
});
});
function prepareBaseStore() {

View File

@ -6,9 +6,7 @@
"use strict";
const WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
const STRINGS_URI = "devtools/client/locales/webconsole.properties";
const l10n = new WebConsoleUtils.L10n(STRINGS_URI);
const l10n = require("devtools/client/webconsole/webconsole-l10n");
const {
MESSAGE_SOURCE,

View File

@ -37,8 +37,7 @@ const SEVERITY_LOG = 3;
// The indent of a console group in pixels.
const GROUP_INDENT = 12;
const WEBCONSOLE_STRINGS_URI = "devtools/client/locales/webconsole.properties";
var WCUL10n = new WebConsoleUtils.L10n(WEBCONSOLE_STRINGS_URI);
var WCUL10n = require("devtools/client/webconsole/webconsole-l10n");
const DOCS_GA_PARAMS = "?utm_source=mozilla" +
"&utm_medium=firefox-console-errors" +

View File

@ -8,7 +8,6 @@
const {Cc, Ci} = require("chrome");
const Services = require("Services");
const {LocalizationHelper} = require("devtools/shared/l10n");
// Match the function name from the result of toString() or toSource().
//
@ -285,65 +284,3 @@ var WebConsoleUtils = {
exports.Utils = WebConsoleUtils;
// Localization
WebConsoleUtils.L10n = function (bundleURI) {
this._helper = new LocalizationHelper(bundleURI);
};
WebConsoleUtils.L10n.prototype = {
/**
* Generates a formatted timestamp string for displaying in console messages.
*
* @param integer [milliseconds]
* Optional, allows you to specify the timestamp in milliseconds since
* the UNIX epoch.
* @return string
* The timestamp formatted for display.
*/
timestampString: function (milliseconds) {
let d = new Date(milliseconds ? milliseconds : null);
let hours = d.getHours(), minutes = d.getMinutes();
let seconds = d.getSeconds();
milliseconds = d.getMilliseconds();
let parameters = [hours, minutes, seconds, milliseconds];
return this.getFormatStr("timestampFormat", parameters);
},
/**
* Retrieve a localized string.
*
* @param string name
* The string name you want from the Web Console string bundle.
* @return string
* The localized string.
*/
getStr: function (name) {
try {
return this._helper.getStr(name);
} catch (ex) {
console.error("Failed to get string: " + name);
throw ex;
}
},
/**
* Retrieve a localized string formatted with values coming from the given
* array.
*
* @param string name
* The string name you want from the Web Console string bundle.
* @param array array
* The array of values you want in the formatted string.
* @return string
* The formatted local string.
*/
getFormatStr: function (name, array) {
try {
return this._helper.getFormatStr(name, ...array);
} catch (ex) {
console.error("Failed to format string: " + name);
throw ex;
}
},
};

View File

@ -10,8 +10,7 @@ const {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils");
const defer = require("devtools/shared/defer");
const Services = require("Services");
const STRINGS_URI = "devtools/client/locales/webconsole.properties";
var l10n = new WebConsoleUtils.L10n(STRINGS_URI);
const l10n = require("devtools/client/webconsole/webconsole-l10n");
const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
// Web Console connection proxy

View File

@ -0,0 +1,70 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {LocalizationHelper} = require("devtools/shared/l10n");
const helper = new LocalizationHelper("devtools/client/locales/webconsole.properties");
const l10n = {
/**
* Generates a formatted timestamp string for displaying in console messages.
*
* @param integer [milliseconds]
* Optional, allows you to specify the timestamp in milliseconds since
* the UNIX epoch.
* @return string
* The timestamp formatted for display.
*/
timestampString: function (milliseconds) {
let d = new Date(milliseconds ? milliseconds : null);
let hours = d.getHours(), minutes = d.getMinutes();
let seconds = d.getSeconds();
milliseconds = d.getMilliseconds();
let parameters = [hours, minutes, seconds, milliseconds];
return l10n.getFormatStr("timestampFormat", parameters);
},
/**
* Retrieve a localized string.
*
* @param string name
* The string name you want from the Web Console string bundle.
* @return string
* The localized string.
*/
getStr: function (name) {
try {
return helper.getStr(name);
} catch (ex) {
console.error("Failed to get string: " + name);
throw ex;
}
},
/**
* Retrieve a localized string formatted with values coming from the given
* array.
*
* @param string name
* The string name you want from the Web Console string bundle.
* @param array array
* The array of values you want in the formatted string.
* @return string
* The formatted local string.
*/
getFormatStr: function (name, array) {
try {
return helper.getFormatStr(name, ...array);
} catch (ex) {
console.error("Failed to format string: " + name);
throw ex;
}
},
};
module.exports = l10n;

View File

@ -38,8 +38,7 @@ loader.lazyRequireGetter(this, "ZoomKeys", "devtools/client/shared/zoom-keys");
loader.lazyRequireGetter(this, "WebConsoleConnectionProxy", "devtools/client/webconsole/webconsole-connection-proxy", true);
const {PluralForm} = require("devtools/shared/plural-form");
const STRINGS_URI = "devtools/client/locales/webconsole.properties";
var l10n = new WebConsoleUtils.L10n(STRINGS_URI);
const l10n = require("devtools/client/webconsole/webconsole-l10n");
const XHTML_NS = "http://www.w3.org/1999/xhtml";

View File

@ -49,6 +49,7 @@ webpackConfig.resolve = {
"devtools/client/webconsole/utils": path.join(__dirname, "new-console-output/test/fixtures/WebConsoleUtils"),
"devtools/client/webconsole/new-console-output": path.join(__dirname, "new-console-output"),
"devtools/client/webconsole/webconsole-connection-proxy": path.join(__dirname, "webconsole-connection-proxy"),
"devtools/client/webconsole/webconsole-l10n": path.join(__dirname, "webconsole-l10n"),
"react": path.join(__dirname, "node_modules/react"),
"devtools/client/shared/vendor/immutable": "immutable",

View File

@ -9,7 +9,7 @@ const {TargetFactory} = require("devtools/client/framework/target");
const Services = require("Services");
const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
const EventEmitter = require("devtools/shared/event-emitter");
const {TextEncoder, OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
const {OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
const {AppProjects} = require("devtools/client/webide/modules/app-projects");
const TabStore = require("devtools/client/webide/modules/tab-store");
const {AppValidator} = require("devtools/client/webide/modules/app-validator");

View File

@ -6,7 +6,7 @@ const {Cu, Cc, Ci} = require("chrome");
const promise = require("promise");
const { Task } = require("devtools/shared/task");
const { TextDecoder, OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
const { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
const Subprocess = require("sdk/system/child_process/subprocess");
const ProjectBuilding = exports.ProjectBuilding = {

View File

@ -18,7 +18,7 @@
window.onload = function() {
SimpleTest.waitForExplicitFinish();
let {TextDecoder, OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
let {OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
let {ProjectBuilding} = require("devtools/client/webide/modules/build");
Task.spawn(function* () {

View File

@ -18,7 +18,7 @@
window.onload = function() {
SimpleTest.waitForExplicitFinish();
let {TextDecoder, OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
let {OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
Task.spawn(function* () {
let win = yield openWebIDE();

View File

@ -4,7 +4,7 @@
"use strict";
const { Cc, Ci, Cu } = require("chrome");
const { Cc, Ci, Cu, Cr } = require("chrome");
const { getCurrentZoom, getWindowDimensions, getViewportDimensions,
getRootBindingParent, loadSheet } = require("devtools/shared/layout/utils");
const { on, emit } = require("sdk/event/core");
@ -270,7 +270,23 @@ CanvasFrameAnonymousContentHelper.prototype = {
// at least on desktop. Therefore, removing the code that was dealing with
// that scenario, fixes when we're adding anonymous content in a tab that
// is not the active one (see bug 1260043 and bug 1260044)
this._content = doc.insertAnonymousContent(node);
try {
this._content = doc.insertAnonymousContent(node);
} catch (e) {
// If the `insertAnonymousContent` fails throwing a `NS_ERROR_UNEXPECTED`, it means
// we don't have access to a `CustomContentContainer` yet (see bug 1365075).
// At this point, it could only happen on document's interactive state, and we
// need to wait until the `complete` state before inserting the anonymous content
// again.
if (e.result === Cr.NS_ERROR_UNEXPECTED && doc.readyState === "interactive") {
// The next state change will be "complete" since the current is "interactive"
doc.addEventListener("readystatechange", () => {
this._content = doc.insertAnonymousContent(node);
}, { once: true });
} else {
throw e;
}
}
},
_remove() {

View File

@ -363,10 +363,6 @@ DevToolsUtils.defineLazyGetter(this, "OS", () => {
return Cu.import("resource://gre/modules/osfile.jsm", {}).OS;
});
DevToolsUtils.defineLazyGetter(this, "TextDecoder", () => {
return Cu.import("resource://gre/modules/osfile.jsm", {}).TextDecoder;
});
DevToolsUtils.defineLazyGetter(this, "NetworkHelper", () => {
return require("devtools/shared/webconsole/network-helper");
});

View File

@ -20,7 +20,7 @@ const jsmScope = Cu.import("resource://gre/modules/Services.jsm", {});
const { Services } = jsmScope;
// Steal various globals only available in JSM scope (and not Sandbox one)
const { PromiseDebugging, ChromeUtils, ThreadSafeChromeUtils, HeapSnapshot,
atob, btoa } = jsmScope;
atob, btoa, TextEncoder, TextDecoder } = jsmScope;
const { URL } = Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(),
{wantGlobalProperties: ["URL"]});
@ -215,6 +215,8 @@ exports.globals = {
reportError: Cu.reportError,
atob: atob,
btoa: btoa,
TextEncoder: TextEncoder,
TextDecoder: TextDecoder,
URL,
loader: {
lazyGetter: defineLazyGetter,

View File

@ -1234,6 +1234,8 @@ exports.CSS_PROPERTIES = {
"supports": [],
"values": [
"ignore",
"ignore-horizontal",
"ignore-vertical",
"inherit",
"initial",
"stretch-to-fit",
@ -3402,6 +3404,8 @@ exports.CSS_PROPERTIES = {
"icon",
"ideographic",
"ignore",
"ignore-horizontal",
"ignore-vertical",
"inactive",
"infinite",
"inherit",

View File

@ -5,7 +5,7 @@
"use strict";
const { Cc, Ci, Cu } = require("chrome");
const { OS, TextDecoder } = Cu.import("resource://gre/modules/osfile.jsm", {});
const { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
const { Task } = require("devtools/shared/task");
const gcli = require("gcli/index");

View File

@ -228,7 +228,8 @@ AnonymousContent::GetComputedStylePropertyValue(const nsAString& aElementId,
}
RefPtr<nsComputedDOMStyle> cs =
new nsComputedDOMStyle(element, NS_LITERAL_STRING(""), shell);
new nsComputedDOMStyle(element, NS_LITERAL_STRING(""), shell,
nsComputedDOMStyle::eAll);
aRv = cs->GetPropertyValue(aPropertyName, aResult);
}

View File

@ -2299,13 +2299,15 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
bool aNotify,
nsAttrValue& aOldValue,
uint8_t* aModType,
bool* aHasListeners)
bool* aHasListeners,
bool* aOldValueSet)
{
bool modification = false;
*aHasListeners = aNotify &&
nsContentUtils::HasMutationListeners(this,
NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
this);
*aOldValueSet = false;
// If we have no listeners and aNotify is false, we are almost certainly
// coming from the content sink and will almost certainly have no previous
@ -2330,6 +2332,7 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
// We have to serialize the value anyway in order to create the
// mutation event so there's no cost in doing it now.
aOldValue.SetToSerialized(*info.mValue);
*aOldValueSet = true;
}
bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
if (valueMatches && aPrefix == info.mName->GetPrefix()) {
@ -2349,10 +2352,12 @@ Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix,
const nsAttrValueOrString& aValue,
bool aNotify, nsAttrValue& aOldValue,
uint8_t* aModType, bool* aHasListeners)
uint8_t* aModType, bool* aHasListeners,
bool* aOldValueSet)
{
if (!MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, aValue, aNotify,
aOldValue, aModType, aHasListeners)) {
aOldValue, aModType, aHasListeners,
aOldValueSet)) {
return false;
}
@ -2383,9 +2388,10 @@ Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName,
// OnlyNotifySameValueSet call.
nsAttrValueOrString value(aValue);
nsAttrValue oldValue;
bool oldValueSet;
if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
oldValue, &modType, &hasListeners)) {
oldValue, &modType, &hasListeners, &oldValueSet)) {
return NS_OK;
}
@ -2418,7 +2424,8 @@ Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName,
attrValue.SetTo(aValue);
}
return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
oldValueSet ? &oldValue : nullptr,
attrValue, modType, hasListeners, aNotify,
kCallAfterSetAttr, document, updateBatch);
}
@ -2443,9 +2450,10 @@ Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName,
bool hasListeners;
nsAttrValueOrString value(aParsedValue);
nsAttrValue oldValue;
bool oldValueSet;
if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
oldValue, &modType, &hasListeners)) {
oldValue, &modType, &hasListeners, &oldValueSet)) {
return NS_OK;
}
@ -2459,7 +2467,8 @@ Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName,
nsIDocument* document = GetComposedDoc();
mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
oldValueSet ? &oldValue : nullptr,
aParsedValue, modType, hasListeners, aNotify,
kCallAfterSetAttr, document, updateBatch);
}
@ -2468,7 +2477,7 @@ nsresult
Element::SetAttrAndNotify(int32_t aNamespaceID,
nsIAtom* aName,
nsIAtom* aPrefix,
const nsAttrValue& aOldValue,
const nsAttrValue* aOldValue,
nsAttrValue& aParsedValue,
uint8_t aModType,
bool aFireMutation,
@ -2489,6 +2498,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
bool hadValidDir = false;
bool hadDirAuto = false;
bool oldValueSet;
if (aNamespaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::dir) {
@ -2499,8 +2509,8 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
// XXXbz Perhaps we should push up the attribute mapping function
// stuff to Element?
if (!IsAttributeMapped(aName) ||
!SetMappedAttribute(aName, aParsedValue, &rv)) {
rv = mAttrsAndChildren.SetAndSwapAttr(aName, aParsedValue);
!SetAndSwapMappedAttribute(aName, aParsedValue, &oldValueSet, &rv)) {
rv = mAttrsAndChildren.SetAndSwapAttr(aName, aParsedValue, &oldValueSet);
}
}
else {
@ -2509,14 +2519,24 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
aNamespaceID,
nsIDOMNode::ATTRIBUTE_NODE);
rv = mAttrsAndChildren.SetAndSwapAttr(ni, aParsedValue);
rv = mAttrsAndChildren.SetAndSwapAttr(ni, aParsedValue, &oldValueSet);
}
NS_ENSURE_SUCCESS(rv, rv);
// If the old value owns its own data, we know it is OK to keep using it.
const nsAttrValue* oldValue =
aParsedValue.StoresOwnData() ? &aParsedValue : &aOldValue;
NS_ENSURE_SUCCESS(rv, rv);
// oldValue will be null if there was no previously set value
const nsAttrValue* oldValue;
if (aParsedValue.StoresOwnData()) {
if (oldValueSet) {
oldValue = &aParsedValue;
} else {
oldValue = nullptr;
}
} else {
// No need to conditionally assign null here. If there was no previously
// set value for the attribute, aOldValue will already be null.
oldValue = aOldValue;
}
if (aComposedDocument || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
RefPtr<nsXBLBinding> binding = GetXBLBinding();
@ -2527,7 +2547,14 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
nsIDocument* ownerDoc = OwnerDoc();
if (ownerDoc && GetCustomElementData()) {
nsCOMPtr<nsIAtom> oldValueAtom = oldValue->GetAsAtom();
nsCOMPtr<nsIAtom> oldValueAtom;
if (oldValue) {
oldValueAtom = oldValue->GetAsAtom();
} else {
// If there is no old value, get the value of the uninitialized attribute
// that was swapped with aParsedValue.
oldValueAtom = aParsedValue.GetAsAtom();
}
nsCOMPtr<nsIAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom();
LifecycleCallbackArgs args = {
nsDependentAtomString(aName),
@ -2541,7 +2568,8 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
}
if (aCallAfterSetAttr) {
rv = AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, aNotify);
rv = AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, oldValue,
aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
@ -2557,7 +2585,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
// Callers only compute aOldValue under certain conditions which may not
// be triggered by all nsIMutationObservers.
nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType,
oldValue == &aParsedValue ? &aParsedValue : nullptr);
aParsedValue.StoresOwnData() ? &aParsedValue : nullptr);
}
if (aFireMutation) {
@ -2575,7 +2603,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
if (!newValue.IsEmpty()) {
mutation.mNewAttrValue = NS_Atomize(newValue);
}
if (!oldValue->IsEmptyString()) {
if (oldValue && !oldValue->IsEmptyString()) {
mutation.mPrevAttrValue = oldValue->GetAsAtom();
}
mutation.mAttrChange = aModType;
@ -2618,9 +2646,10 @@ Element::ParseAttribute(int32_t aNamespaceID,
}
bool
Element::SetMappedAttribute(nsIAtom* aName,
nsAttrValue& aValue,
nsresult* aRetval)
Element::SetAndSwapMappedAttribute(nsIAtom* aName,
nsAttrValue& aValue,
bool* aValueWasSet,
nsresult* aRetval)
{
*aRetval = NS_OK;
return false;
@ -2775,7 +2804,7 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
ownerDoc, nsIDocument::eAttributeChanged, this, &args);
}
rv = AfterSetAttr(aNameSpaceID, aName, nullptr, aNotify);
rv = AfterSetAttr(aNameSpaceID, aName, nullptr, &oldValue, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
UpdateState(aNotify);

View File

@ -631,25 +631,49 @@ public:
* values will not actually be compared if we aren't notifying and we don't
* have mutation listeners (in which case it's cheap to just return false
* and let the caller go ahead and set the value).
* @param aOldValue Set to the old value of the attribute, but only if there
* are event listeners. If set, the type of aOldValue will be either
* @param aOldValue [out] Set to the old value of the attribute, but only if
* there are event listeners. If set, the type of aOldValue will be either
* nsAttrValue::eString or nsAttrValue::eAtom.
* @param aModType Set to nsIDOMMutationEvent::MODIFICATION or to
* @param aModType [out] Set to nsIDOMMutationEvent::MODIFICATION or to
* nsIDOMMutationEvent::ADDITION, but only if this helper returns true
* @param aHasListeners Set to true if there are mutation event listeners
* listening for NS_EVENT_BITS_MUTATION_ATTRMODIFIED
* @param aHasListeners [out] Set to true if there are mutation event
* listeners listening for NS_EVENT_BITS_MUTATION_ATTRMODIFIED
* @param aOldValueSet [out] Indicates whether an old attribute value has been
* stored in aOldValue. The bool will be set to true if a value was stored.
*/
bool MaybeCheckSameAttrVal(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix,
const nsAttrValueOrString& aValue,
bool aNotify, nsAttrValue& aOldValue,
uint8_t* aModType, bool* aHasListeners);
uint8_t* aModType, bool* aHasListeners,
bool* aOldValueSet);
/**
* Notifies mutation listeners if aNotify is true, there are mutation
* listeners, and the attribute value is changing.
*
* @param aNamespaceID The namespace of the attribute
* @param aName The local name of the attribute
* @param aPrefix The prefix of the attribute
* @param aValue The value that the attribute is being changed to
* @param aNotify If true, mutation listeners will be notified if they exist
* and the attribute value is changing
* @param aOldValue [out] Set to the old value of the attribute, but only if
* there are event listeners. If set, the type of aOldValue will be either
* nsAttrValue::eString or nsAttrValue::eAtom.
* @param aModType [out] Set to nsIDOMMutationEvent::MODIFICATION or to
* nsIDOMMutationEvent::ADDITION, but only if this helper returns true
* @param aHasListeners [out] Set to true if there are mutation event
* listeners listening for NS_EVENT_BITS_MUTATION_ATTRMODIFIED
* @param aOldValueSet [out] Indicates whether an old attribute value has been
* stored in aOldValue. The bool will be set to true if a value was stored.
*/
bool OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix,
const nsAttrValueOrString& aValue,
bool aNotify, nsAttrValue& aOldValue,
uint8_t* aModType, bool* aHasListeners);
uint8_t* aModType, bool* aHasListeners,
bool* aOldValueSet);
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix,
const nsAString& aValue, bool aNotify) override;
@ -1352,7 +1376,10 @@ protected:
* its current value) is !StoresOwnData() --- in which
* case the current value is probably already useless.
* If the current value is StoresOwnData() (or absent),
* aOldValue will not be used.
* aOldValue will not be used. aOldValue will only be set
* in certain circumstances (there are mutation
* listeners, element is a custom element, attribute was
* not previously unset). Otherwise it will be null.
* @param aParsedValue parsed new value of attribute. Replaced by the
* old value of the attribute. This old value is only
* useful if either it or the new value is StoresOwnData.
@ -1366,7 +1393,7 @@ protected:
nsresult SetAttrAndNotify(int32_t aNamespaceID,
nsIAtom* aName,
nsIAtom* aPrefix,
const nsAttrValue& aOldValue,
const nsAttrValue* aOldValue,
nsAttrValue& aParsedValue,
uint8_t aModType,
bool aFireMutation,
@ -1410,13 +1437,20 @@ protected:
* returns true (the value of aRetval does not matter for that purpose).
*
* @param aName the name of the attribute
* @param aValue the nsAttrValue to set
* @param aValue the nsAttrValue to set. Will be swapped with the existing
* value of the attribute if the attribute already exists.
* @param [out] aValueWasSet If the attribute was not set previously,
* aValue will be swapped with an empty attribute
* and aValueWasSet will be set to false. Otherwise,
* aValueWasSet will be set to true and aValue will
* contain the previous value set.
* @param [out] aRetval the nsresult status of the operation, if any.
* @return true if the setting was attempted, false otherwise.
*/
virtual bool SetMappedAttribute(nsIAtom* aName,
nsAttrValue& aValue,
nsresult* aRetval);
virtual bool SetAndSwapMappedAttribute(nsIAtom* aName,
nsAttrValue& aValue,
bool* aValueWasSet,
nsresult* aRetval);
/**
* Hook that is called by Element::SetAttr to allow subclasses to
@ -1444,19 +1478,24 @@ protected:
/**
* Hook that is called by Element::SetAttr to allow subclasses to
* deal with attribute sets. This will only be called after we have called
* SetAndTakeAttr and AttributeChanged (that is, after we have actually set
* the attr). It will always be called under a scriptblocker.
* SetAndSwapAttr (that is, after we have actually set the attr). It will
* always be called under a scriptblocker.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aValue the value it's being set to. If null, the attr is being
* removed.
* @param aOldValue the value that the attribute had previously. If null,
* the attr was not previously set. This argument may not have the
* correct value for SVG elements, or other cases in which the
* attribute value doesn't store its own data
* @param aNotify Whether we plan to notify document observers.
*/
// Note that this is inlined so that when subclasses call it it gets
// inlined. Those calls don't go through a vtable.
virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify)
const nsAttrValue* aValue,
const nsAttrValue* aOldValue, bool aNotify)
{
return NS_OK;
}

View File

@ -389,12 +389,15 @@ nsAttrAndChildArray::AttrAt(uint32_t aPos) const
}
nsresult
nsAttrAndChildArray::SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue)
nsAttrAndChildArray::SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue,
bool* aHadValue)
{
*aHadValue = false;
uint32_t i, slotCount = AttrSlotCount();
for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
if (ATTRS(mImpl)[i].mName.Equals(aLocalName)) {
ATTRS(mImpl)[i].mValue.SwapValueWith(aValue);
*aHadValue = true;
return NS_OK;
}
}
@ -414,21 +417,22 @@ nsAttrAndChildArray::SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue)
}
nsresult
nsAttrAndChildArray::SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue)
nsAttrAndChildArray::SetAndSwapAttr(mozilla::dom::NodeInfo* aName,
nsAttrValue& aValue, bool* aHadValue)
{
int32_t namespaceID = aName->NamespaceID();
nsIAtom* localName = aName->NameAtom();
if (namespaceID == kNameSpaceID_None) {
return SetAndSwapAttr(localName, aValue);
return SetAndSwapAttr(localName, aValue, aHadValue);
}
*aHadValue = false;
uint32_t i, slotCount = AttrSlotCount();
for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
if (ATTRS(mImpl)[i].mName.Equals(localName, namespaceID)) {
ATTRS(mImpl)[i].mName.SetTo(aName);
ATTRS(mImpl)[i].mValue.Reset();
ATTRS(mImpl)[i].mValue.SwapValueWith(aValue);
*aHadValue = true;
return NS_OK;
}
}
@ -583,10 +587,11 @@ nsAttrAndChildArray::IndexOfAttr(nsIAtom* aLocalName, int32_t aNamespaceID) cons
}
nsresult
nsAttrAndChildArray::SetAndTakeMappedAttr(nsIAtom* aLocalName,
nsAttrAndChildArray::SetAndSwapMappedAttr(nsIAtom* aLocalName,
nsAttrValue& aValue,
nsMappedAttributeElement* aContent,
nsHTMLStyleSheet* aSheet)
nsHTMLStyleSheet* aSheet,
bool* aHadValue)
{
bool willAdd = true;
if (mImpl && mImpl->mMappedAttrs) {
@ -596,7 +601,7 @@ nsAttrAndChildArray::SetAndTakeMappedAttr(nsIAtom* aLocalName,
RefPtr<nsMappedAttributes> mapped =
GetModifiableMapped(aContent, aSheet, willAdd);
mapped->SetAndTakeAttr(aLocalName, aValue);
mapped->SetAndSwapAttr(aLocalName, aValue, aHadValue);
return MakeMappedUnique(mapped);
}

View File

@ -91,8 +91,13 @@ public:
nsCaseTreatment aCaseSensitive) const;
const nsAttrValue* AttrAt(uint32_t aPos) const;
// SetAndSwapAttr swaps the current attribute value with aValue.
nsresult SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue);
nsresult SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue);
// If the attribute was unset, an empty value will be swapped into aValue
// and aHadValue will be set to false. Otherwise, aHadValue will be set to
// true.
nsresult SetAndSwapAttr(nsIAtom* aLocalName, nsAttrValue& aValue,
bool* aHadValue);
nsresult SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue,
bool* aHadValue);
// Remove the attr at position aPos. The value of the attr is placed in
// aValue; any value that was already in aValue is destroyed.
@ -110,9 +115,14 @@ public:
const nsAttrName* GetExistingAttrNameFromQName(const nsAString& aName) const;
int32_t IndexOfAttr(nsIAtom* aLocalName, int32_t aNamespaceID = kNameSpaceID_None) const;
nsresult SetAndTakeMappedAttr(nsIAtom* aLocalName, nsAttrValue& aValue,
// SetAndSwapMappedAttr swaps the current attribute value with aValue.
// If the attribute was unset, an empty value will be swapped into aValue
// and aHadValue will be set to false. Otherwise, aHadValue will be set to
// true.
nsresult SetAndSwapMappedAttr(nsIAtom* aLocalName, nsAttrValue& aValue,
nsMappedAttributeElement* aContent,
nsHTMLStyleSheet* aSheet);
nsHTMLStyleSheet* aSheet,
bool* aHadValue);
nsresult SetMappedAttrStyleSheet(nsHTMLStyleSheet* aSheet) {
if (!mImpl || !mImpl->mMappedAttrs) {
return NS_OK;

View File

@ -21,13 +21,6 @@
0x45f27d10, 0x987b, 0x11d2, \
{0xbd, 0x40, 0x00, 0x10, 0x5a, 0xa4, 0x5e, 0x89} }
//The dom cannot provide the crypto or pkcs11 classes that
//were used in older days, so if someone wants to provide
//the service they must implement an object and give it
//this class ID
#define NS_CRYPTO_CONTRACTID "@mozilla.org/security/crypto;1"
#define NS_PKCS11_CONTRACTID "@mozilla.org/security/pkcs11;1"
#define NS_XPATH_EVALUATOR_CONTRACTID "@mozilla.org/dom/xpath-evaluator;1"
#endif /* nsDOMCID_h__ */

View File

@ -2807,6 +2807,7 @@ nsDOMWindowUtils::GetUnanimatedComputedStyle(nsIDOMElement* aElement,
RefPtr<nsComputedDOMStyle> computedStyle =
NS_NewComputedDOMStyle(
element, aPseudoElement, shell,
nsComputedDOMStyle::StyleType::eAll,
nsComputedDOMStyle::AnimationFlag::eWithoutAnimation);
computedStyle->GetPropertyValue(propertyID, aResult);
return NS_OK;

View File

@ -11079,13 +11079,46 @@ nsGlobalWindow::GetComputedStyle(Element& aElt, const nsAString& aPseudoElt,
ErrorResult& aError)
{
MOZ_ASSERT(IsInnerWindow());
FORWARD_TO_OUTER_OR_THROW(GetComputedStyleOuter,
(aElt, aPseudoElt), aError, nullptr);
return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
}
already_AddRefed<nsICSSDeclaration>
nsGlobalWindow::GetComputedStyleOuter(Element& aElt,
const nsAString& aPseudoElt)
nsGlobalWindow::GetDefaultComputedStyle(Element& aElt,
const nsAString& aPseudoElt,
ErrorResult& aError)
{
MOZ_ASSERT(IsInnerWindow());
return GetComputedStyleHelper(aElt, aPseudoElt, true, aError);
}
nsresult
nsGlobalWindow::GetComputedStyleHelper(nsIDOMElement* aElt,
const nsAString& aPseudoElt,
bool aDefaultStylesOnly,
nsIDOMCSSStyleDeclaration** aReturn)
{
MOZ_ASSERT(IsInnerWindow());
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = nullptr;
nsCOMPtr<dom::Element> element = do_QueryInterface(aElt);
if (!element) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
ErrorResult rv;
nsCOMPtr<nsIDOMCSSStyleDeclaration> declaration =
GetComputedStyleHelper(*element, aPseudoElt, aDefaultStylesOnly, rv);
declaration.forget(aReturn);
return rv.StealNSResult();
}
already_AddRefed<nsICSSDeclaration>
nsGlobalWindow::GetComputedStyleHelperOuter(Element& aElt,
const nsAString& aPseudoElt,
bool aDefaultStylesOnly)
{
MOZ_RELEASE_ASSERT(IsOuterWindow());
@ -11117,11 +11150,24 @@ nsGlobalWindow::GetComputedStyleOuter(Element& aElt,
}
RefPtr<nsComputedDOMStyle> compStyle =
NS_NewComputedDOMStyle(&aElt, aPseudoElt, presShell);
NS_NewComputedDOMStyle(&aElt, aPseudoElt, presShell,
aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly :
nsComputedDOMStyle::eAll);
return compStyle.forget();
}
already_AddRefed<nsICSSDeclaration>
nsGlobalWindow::GetComputedStyleHelper(Element& aElt,
const nsAString& aPseudoElt,
bool aDefaultStylesOnly,
ErrorResult& aError)
{
FORWARD_TO_OUTER_OR_THROW(GetComputedStyleHelperOuter,
(aElt, aPseudoElt, aDefaultStylesOnly),
aError, nullptr);
}
Storage*
nsGlobalWindow::GetSessionStorage(ErrorResult& aError)
{

Some files were not shown because too many files have changed in this diff Show More