merge autoland to mozilla-central. r=merge a=merge
merge autoland to mozilla-central. r=merge a=merge
MozReview-Commit-ID: BY4c5BIOF81
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,56 +1,61 @@
# Always ignore node_modules.
**/node_modules/**/*.*
+# Always ignore crashtests - specially crafted files that originally caused a
+# crash.
+**/crashtests/**
+
# Exclude expected objdirs.
obj*/**
# We ignore all these directories by default, until we get them enabled.
# If you are enabling a directory, please add directory specific exclusions
# below.
chrome/**
docshell/**
editor/**
-embedding/**
extensions/cookie/**
extensions/spellcheck/**
extensions/universalchardet/**
gfx/**
image/**
intl/**
layout/**
media/**
memory/**
modules/**
netwerk/**
parser/**
-python/**
rdf/**
-servo/**
tools/update-packaging/**
uriloader/**
-view/**
widget/**
# We currently have no js files in these directories, so we ignore them by
# default to aid ESLint's performance.
build/**
config/**
db/**
+embedding/**
gradle/**
hal/**
mfbt/**
mozglue/**
nsprpub/**
other-licenses/**
probes/**
startupcache/**
xpfe/**
+# These directories only contain crashtests, but we still skip the whole
+# directory to aid performance.
+view/**
+
# browser/ exclusions
browser/app/**
browser/branding/**/firefox-branding.js
# Gzipped test file.
browser/base/content/test/general/gZipOfflineChild.html
browser/base/content/test/urlbar/file_blank_but_not_blank.html
# New tab is likely to be replaced soon.
browser/base/content/newtab/**
@@ -277,28 +282,34 @@ mobile/android/chrome/content/about.js
# Not much JS to lint and non-standard at that
mobile/android/installer/
mobile/android/locales/
# Non-standard `(catch ex if ...)`
mobile/android/chrome/content/browser.js
mobile/android/components/Snippets.js
+# Only contains non-standard test files.
+python/**
+
# security/ exclusions (pref files).
security/manager/ssl/security-prefs.js
# NSS / taskcluster only.
security/nss/**
# services/ exclusions
# Uses `#filter substitution`
services/sync/modules/constants.js
services/sync/services-sync.js
+# Servo is imported.
+servo/**
+
# Remote protocol exclusions
testing/marionette/test_*.js
testing/marionette/atom.js
testing/marionette/legacyaction.js
testing/marionette/client
testing/marionette/doc
testing/marionette/harness
--- a/accessible/tests/mochitest/hittest/a11y.ini
+++ b/accessible/tests/mochitest/hittest/a11y.ini
@@ -4,12 +4,11 @@ support-files = zoom_tree.xul
!/accessible/tests/mochitest/letters.gif
[test_browser.html]
[test_canvas_hitregion.html]
skip-if = (os == "android" || appname == "b2g")
[test_general.html]
[test_menu.xul]
[test_shadowroot.html]
-skip-if = stylo # Stylo doesn't support shadow DOM yet, bug 1293844
[test_zoom.html]
[test_zoom_text.html]
[test_zoom_tree.xul]
--- a/browser/base/content/browser-pageActions.js
+++ b/browser/base/content/browser-pageActions.js
@@ -268,18 +268,22 @@ var BrowserPageActions = {
* Returns the node in the urlbar to which popups for the given action should
* be anchored. If the action is null, a sensible anchor is returned.
*
* @param action (PageActions.Action, optional)
* The action you want to anchor.
* @return (DOM node, nonnull) The node to which the action should be
* anchored.
*/
- panelAnchorNodeForAction(action) {
+ panelAnchorNodeForAction(action, event) {
// Try each of the following nodes in order, using the first that's visible.
+ if (event && event.target.closest("panel")) {
+ return this.mainButtonNode;
+ }
+
let potentialAnchorNodeIDs = [
action && action.anchorIDOverride,
action && this._urlbarButtonNodeIDForActionID(action.id),
this.mainButtonNode.id,
"identity-icon",
];
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
@@ -748,17 +752,17 @@ var BrowserPageActionFeedback = {
delete this.feedbackLabel;
return this.feedbackLabel = document.getElementById("pageActionFeedbackMessage");
},
show(action, event) {
this.feedbackLabel.textContent = this.panelNode.getAttribute(action.id + "Feedback");
this.panelNode.hidden = false;
- let anchor = BrowserPageActions.panelAnchorNodeForAction(action);
+ let anchor = BrowserPageActions.panelAnchorNodeForAction(action, event);
this.panelNode.openPopup(anchor, {
position: "bottomcenter topright",
triggerEvent: event,
});
this.panelNode.addEventListener("popupshown", () => {
this.feedbackAnimationBox.setAttribute("animate", "true");
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -134,16 +134,19 @@ if (AppConstants.MOZ_CRASHREPORTER) {
XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
"@mozilla.org/xre/app-info;1",
"nsICrashReporter");
}
XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
return Services.strings.createBundle("chrome://browser/locale/browser.properties");
});
+XPCOMUtils.defineLazyGetter(this, "gTabBrowserBundle", function() {
+ return Services.strings.createBundle("chrome://browser/locale/tabbrowser.properties");
+});
XPCOMUtils.defineLazyGetter(this, "gCustomizeMode", function() {
let scope = {};
Cu.import("resource:///modules/CustomizeMode.jsm", scope);
return new scope.CustomizeMode(window);
});
XPCOMUtils.defineLazyGetter(this, "gPrefService", function() {
@@ -4071,18 +4074,17 @@ function FillHistoryMenu(aParent) {
item.setAttribute("index", j);
// Cache this so that gotoHistoryIndex doesn't need the original index
item.setAttribute("historyindex", j - index);
if (j != index) {
// Use list-style-image rather than the image attribute in order to
// allow CSS to override this.
- item.style.listStyleImage =
- "url(" + PlacesUtils.urlWithSizeRef(window, "page-icon:" + uri, 16) + ")";
+ item.style.listStyleImage = `url(page-icon:${uri})`;
}
if (j < index) {
item.className = "unified-nav-back menuitem-iconic menuitem-with-favicon";
item.setAttribute("tooltiptext", tooltipBack);
} else if (j == index) {
item.setAttribute("type", "radio");
item.setAttribute("checked", "true");
@@ -8948,18 +8950,18 @@ TabModalPromptBox.prototype = {
try {
hostForAllowFocusCheckbox = principalToAllowFocusFor.URI.host;
} catch (ex) { /* Ignore exceptions for host-less URIs */ }
if (hostForAllowFocusCheckbox) {
let allowFocusRow = document.createElementNS(XUL_NS, "row");
allowFocusCheckbox = document.createElementNS(XUL_NS, "checkbox");
let spacer = document.createElementNS(XUL_NS, "spacer");
allowFocusRow.appendChild(spacer);
- let label = gBrowser.mStringBundle.getFormattedString("tabs.allowTabFocusByPromptForSite",
- [hostForAllowFocusCheckbox]);
+ let label = gTabBrowserBundle.formatStringFromName("tabs.allowTabFocusByPromptForSite",
+ [hostForAllowFocusCheckbox], 1);
allowFocusCheckbox.setAttribute("label", label);
allowFocusRow.appendChild(allowFocusCheckbox);
newPrompt.appendChild(allowFocusRow);
}
let tab = gBrowser.getTabForBrowser(browser);
let closeCB = this._promptCloseCallback.bind(null, onCloseCallback, principalToAllowFocusFor,
allowFocusCheckbox);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -12,17 +12,16 @@
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="tabbrowser">
<resources>
<stylesheet src="chrome://browser/content/tabbrowser.css"/>
</resources>
<content>
- <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
<xul:tabbox anonid="tabbox" class="tabbrowser-tabbox"
flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown,tabcontainer"
onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
<xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
<xul:notificationbox flex="1" notificationside="top">
<xul:hbox flex="1" class="browserSidebarContainer">
<xul:vbox flex="1" class="browserContainer">
<xul:stack flex="1" class="browserStack" anonid="browserStack">
@@ -71,19 +70,16 @@
.getService(Components.interfaces.mozIPlacesAutoComplete);
</field>
<field name="mTabBox" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
</field>
<field name="mPanelContainer" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
</field>
- <field name="mStringBundle">
- document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
- </field>
<field name="mCurrentTab">
null
</field>
<field name="_lastRelatedTabMap">
new WeakMap();
</field>
<field name="mCurrentBrowser">
null
@@ -1627,17 +1623,17 @@
try {
var characterSet = browser.characterSet;
const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
.getService(Components.interfaces.nsITextToSubURI);
title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
} catch (ex) { /* Do nothing. */ }
} else {
// Still no title? Fall back to our untitled string.
- title = this.mStringBundle.getString("tabs.emptyTabTitle");
+ title = gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle");
}
}
return this._setTabLabel(aTab, title, { isContentTitle });
]]>
</body>
</method>
@@ -2640,17 +2636,17 @@
lazyBrowserURI = aURIObject;
aURI = "about:blank";
}
var uriIsAboutBlank = aURI == "about:blank";
if (!aNoInitialLabel) {
if (isBlankPageURL(aURI)) {
- t.setAttribute("label", this.mStringBundle.getString("tabs.emptyTabTitle"));
+ t.setAttribute("label", gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle"));
} else {
// Set URL as label so that the tab isn't empty initially.
this.setInitialTabTitle(t, aURI, { beforeTabOpen: true });
}
}
if (aIsPrerendered) {
t.setAttribute("hidden", "true");
@@ -2908,37 +2904,36 @@
var shouldPrompt = Services.prefs.getBoolPref(pref);
if (!shouldPrompt)
return true;
var ps = Services.prompt;
// default to true: if it were false, we wouldn't get this far
var warnOnClose = { value: true };
- var bundle = this.mStringBundle;
// focus the window before prompting.
// this will raise any minimized window, which will
// make it obvious which window the prompt is for and will
// solve the problem of windows "obscuring" the prompt.
// see bug #350299 for more details
window.focus();
var warningMessage =
- PluralForm.get(tabsToClose, bundle.getString("tabs.closeWarningMultiple"))
+ PluralForm.get(tabsToClose, gTabBrowserBundle.GetStringFromName("tabs.closeWarningMultiple"))
.replace("#1", tabsToClose);
var buttonPressed =
ps.confirmEx(window,
- bundle.getString("tabs.closeWarningTitle"),
+ gTabBrowserBundle.GetStringFromName("tabs.closeWarningTitle"),
warningMessage,
(ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0)
+ (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
- bundle.getString("tabs.closeButtonMultiple"),
+ gTabBrowserBundle.GetStringFromName("tabs.closeButtonMultiple"),
null, null,
aCloseTabs == this.closingTabsEnum.ALL ?
- bundle.getString("tabs.closeWarningPromptMe") : null,
+ gTabBrowserBundle.GetStringFromName("tabs.closeWarningPromptMe") : null,
warnOnClose);
var reallyClose = (buttonPressed == 0);
// don't set the pref unless they press OK and it's false
if (aCloseTabs == this.closingTabsEnum.ALL && reallyClose && !warnOnClose.value)
Services.prefs.setBoolPref(pref, false);
return reallyClose;
@@ -5511,55 +5506,55 @@
if (tab.localName != "tab") {
event.preventDefault();
return;
}
let stringWithShortcut = (stringId, keyElemId) => {
let keyElem = document.getElementById(keyElemId);
let shortcut = ShortcutUtils.prettifyShortcut(keyElem);
- return this.mStringBundle.getFormattedString(stringId, [shortcut]);
+ return gTabBrowserBundle.formatStringFromName(stringId, [shortcut], 1);
};
var label;
if (tab.mOverCloseButton) {
label = tab.selected ?
stringWithShortcut("tabs.closeSelectedTab.tooltip", "key_close") :
- this.mStringBundle.getString("tabs.closeTab.tooltip");
+ gTabBrowserBundle.GetStringFromName("tabs.closeTab.tooltip");
} else if (tab._overPlayingIcon) {
let stringID;
if (tab.selected) {
stringID = tab.linkedBrowser.audioMuted ?
"tabs.unmuteAudio.tooltip" :
"tabs.muteAudio.tooltip";
label = stringWithShortcut(stringID, "key_toggleMute");
} else {
if (tab.hasAttribute("activemedia-blocked")) {
stringID = "tabs.unblockAudio.tooltip";
} else {
stringID = tab.linkedBrowser.audioMuted ?
"tabs.unmuteAudio.background.tooltip" :
"tabs.muteAudio.background.tooltip";
}
- label = this.mStringBundle.getString(stringID);
+ label = gTabBrowserBundle.GetStringFromName(stringID);
}
} else {
label = tab._fullLabel || tab.getAttribute("label");
if (AppConstants.E10S_TESTING_ONLY &&
tab.linkedBrowser &&
tab.linkedBrowser.isRemoteBrowser) {
label += " - e10s";
if (tab.linkedBrowser.frameLoader &&
Services.appinfo.maxWebProcessCount > 1) {
label += " (" + tab.linkedBrowser.frameLoader.tabParent.osPid + ")";
}
}
if (tab.userContextId) {
- label = this.mStringBundle.getFormattedString("tabs.containers.tooltip", [label, ContextualIdentityService.getUserContextLabel(tab.userContextId)]);
+ label = gTabBrowserBundle.formatStringFromName("tabs.containers.tooltip", [label, ContextualIdentityService.getUserContextLabel(tab.userContextId)], 2);
}
}
event.target.setAttribute("label", label);
]]></body>
</method>
<method name="handleEvent">
@@ -6367,20 +6362,20 @@
</content>
<implementation implements="nsIDOMEventListener, nsIObserver">
<constructor>
<![CDATA[
this.mTabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");
let { restoreTabsButton } = this;
- restoreTabsButton.setAttribute("label", this.tabbrowser.mStringBundle.getString("tabs.restoreLastTabs"));
+ restoreTabsButton.setAttribute("label", gTabBrowserBundle.GetStringFromName("tabs.restoreLastTabs"));
var tab = this.firstChild;
- tab.label = this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle");
+ tab.label = gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle");
tab.setAttribute("onerror", "this.removeAttribute('image');");
window.addEventListener("resize", this);
window.addEventListener("load", this);
Services.prefs.addObserver("privacy.userContext", this);
this.observe(null, "nsPref:changed", "privacy.userContext.enabled");
@@ -6611,17 +6606,17 @@
</property>
<method name="_propagateVisibility">
<body><![CDATA[
let visible = this.visible;
document.getElementById("menu_closeWindow").hidden = !visible;
document.getElementById("menu_close").setAttribute("label",
- this.tabbrowser.mStringBundle.getString(visible ? "tabs.closeTab" : "tabs.close"));
+ gTabBrowserBundle.GetStringFromName(visible ? "tabs.closeTab" : "tabs.close"));
TabsInTitlebar.allowedBy("tabs-visible", visible);
]]></body>
</method>
<method name="updateVisibility">
<body><![CDATA[
if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1)
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -100,17 +100,16 @@ const startupPhases = {
"resource://gre/modules/CrashSubmit.jsm",
"resource://gre/modules/FxAccounts.jsm",
"resource://gre/modules/FxAccountsStorage.jsm",
"resource://gre/modules/PlacesSyncUtils.jsm",
"resource://gre/modules/Sqlite.jsm",
]),
services: new Set([
"@mozilla.org/browser/annotation-service;1",
- "@mozilla.org/browser/favicon-service;1",
"@mozilla.org/browser/nav-bookmarks-service;1",
])
}},
// Things that are expected to be completely out of the startup path
// and loaded lazily when used for the first time by the user should
// be blacklisted here.
"before becoming idle": {blacklist: {
--- a/browser/base/content/test/urlbar/browser_page_action_menu.js
+++ b/browser/base/content/test/urlbar/browser_page_action_menu.js
@@ -121,16 +121,63 @@ add_task(async function emailLink() {
let hiddenPromise = promisePageActionPanelHidden();
EventUtils.synthesizeMouseAtCenter(emailLinkButton, {});
await hiddenPromise;
Assert.ok(fnCalled);
});
});
+add_task(async function copyURLFromPanel() {
+ // Open an actionable page so that the main page action button appears. (It
+ // does not appear on about:blank for example.)
+ let url = "http://example.com/";
+ await BrowserTestUtils.withNewTab(url, async () => {
+ // Open the panel and click Copy URL.
+ await promisePageActionPanelOpen();
+ Assert.ok(true, "page action panel opened");
+
+ let copyURLButton =
+ document.getElementById("pageAction-panel-copyURL");
+ let hiddenPromise = promisePageActionPanelHidden();
+ EventUtils.synthesizeMouseAtCenter(copyURLButton, {});
+ await hiddenPromise;
+
+ let feedbackPanel = document.getElementById("pageActionFeedback");
+ let feedbackShownPromise = BrowserTestUtils.waitForEvent(feedbackPanel, "popupshown");
+ await feedbackShownPromise;
+ Assert.equal(feedbackPanel.anchorNode.id, "pageActionButton", "Feedback menu should be anchored on the main Page Action button");
+ let feedbackHiddenPromise = promisePanelHidden("pageActionFeedback");
+ await feedbackHiddenPromise;
+ });
+});
+
+add_task(async function copyURLFromURLBar() {
+ // Open an actionable page so that the main page action button appears. (It
+ // does not appear on about:blank for example.)
+ let url = "http://example.com/";
+ await BrowserTestUtils.withNewTab(url, async () => {
+ // Add action to URL bar.
+ let action = PageActions._builtInActions.find(a => a.id == "copyURL");
+ action.shownInUrlbar = true;
+ registerCleanupFunction(() => action.shownInUrlbar = false);
+
+ let copyURLButton =
+ document.getElementById("pageAction-urlbar-copyURL");
+ let feedbackShownPromise = promisePanelShown("pageActionFeedback");
+ EventUtils.synthesizeMouseAtCenter(copyURLButton, {});
+
+ await feedbackShownPromise;
+ let panel = document.getElementById("pageActionFeedback");
+ Assert.equal(panel.anchorNode.id, "pageAction-urlbar-copyURL", "Feedback menu should be anchored on the main URL bar button");
+ let feedbackHiddenPromise = promisePanelHidden("pageActionFeedback");
+ await feedbackHiddenPromise;
+ });
+});
+
add_task(async function sendToDevice_nonSendable() {
// Open a tab that's not sendable. An about: page like about:home is
// convenient.
await BrowserTestUtils.withNewTab("about:home", async () => {
// ... but the page actions should be hidden on about:home, including the
// main button. (It's not easy to load a page that's both actionable and
// not sendable.) So first check that that's the case, and then unhide the
// main button so that this test can continue.
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -1,13 +1,14 @@
[DEFAULT]
support-files =
head.js
head_pageAction.js
head_sessions.js
+ head_webNavigation.js
profilerSymbols.sjs
context.html
context_frame.html
ctxmenu-image.png
context_tabs_onUpdated_page.html
context_tabs_onUpdated_iframe.html
file_clearplugindata.html
file_find_frames.html
@@ -171,16 +172,18 @@ skip-if = os == "linux" && debug && bits
[browser_ext_themes_validation.js]
[browser_ext_url_overrides_newtab.js]
[browser_ext_user_events.js]
[browser_ext_webRequest.js]
[browser_ext_webNavigation_frameId0.js]
[browser_ext_webNavigation_getFrames.js]
[browser_ext_webNavigation_onCreatedNavigationTarget.js]
[browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js]
+[browser_ext_webNavigation_onCreatedNavigationTarget_named_window.js]
+[browser_ext_webNavigation_onCreatedNavigationTarget_subframe_window_open.js]
[browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js]
[browser_ext_webNavigation_urlbar_transitions.js]
[browser_ext_windows.js]
[browser_ext_windows_create.js]
tags = fullscreen
[browser_ext_windows_create_params.js]
[browser_ext_windows_create_tabId.js]
[browser_ext_windows_create_url.js]
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
@@ -1,15 +1,14 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
-const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
-const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+Services.scriptloader.loadSubScript(new URL("head_webNavigation.js", gTestPath).href,
+ this);
async function background() {
const tabs = await browser.tabs.query({active: true, currentWindow: true});
const sourceTabId = tabs[0].id;
const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
@@ -29,83 +28,65 @@ async function background() {
browser.test.sendMessage("tabsOnCreated", tab.id);
});
browser.test.sendMessage("expectedSourceTab", {
sourceTabId, sourceTabFrames,
});
}
-async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
- await openNavTarget();
-
- const webNavMsg = await extension.awaitMessage("webNavOnCreated");
- const createdTabId = await extension.awaitMessage("tabsOnCreated");
- const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
-
- let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
-
- is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
- is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
- is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
- is(webNavMsg.url, url, "Got the expected url property");
-
- is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
- is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
-}
-
add_task(async function test_on_created_navigation_target_from_mouse_click() {
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
const extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
permissions: ["webNavigation"],
},
});
await extension.startup();
const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
info("Open link in a new tab using Ctrl-click");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
BrowserTestUtils.synthesizeMouseAtCenter("#test-create-new-tab-from-mouse-click",
{ctrlKey: true, metaKey: true},
tab.linkedBrowser);
},
expectedWebNavProps: {
sourceTabId: expectedSourceTab.sourceTabId,
sourceFrameId: 0,
url: `${OPENED_PAGE}#new-tab-from-mouse-click`,
},
});
info("Open link in a new window using Shift-click");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
BrowserTestUtils.synthesizeMouseAtCenter("#test-create-new-window-from-mouse-click",
{shiftKey: true},
tab.linkedBrowser);
},
expectedWebNavProps: {
sourceTabId: expectedSourceTab.sourceTabId,
sourceFrameId: 0,
url: `${OPENED_PAGE}#new-window-from-mouse-click`,
},
});
info("Open link with target=\"_blank\" in a new tab using click");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
BrowserTestUtils.synthesizeMouseAtCenter("#test-create-new-tab-from-targetblank-click",
{},
tab.linkedBrowser);
},
expectedWebNavProps: {
sourceTabId: expectedSourceTab.sourceTabId,
@@ -130,17 +111,17 @@ add_task(async function test_on_created_
});
await extension.startup();
const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
info("Open a subframe link in a new tab using Ctrl-click");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
BrowserTestUtils.synthesizeMouseAtCenter(function() {
// This code runs as a framescript in the child process and it returns the
// target link in the subframe.
return this.content.frames[0].document // eslint-disable-line mozilla/no-cpows-in-tests
.querySelector("#test-create-new-tab-from-mouse-click-subframe");
}, {ctrlKey: true, metaKey: true}, tab.linkedBrowser);
@@ -149,17 +130,17 @@ add_task(async function test_on_created_
sourceTabId: expectedSourceTab.sourceTabId,
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
url: `${OPENED_PAGE}#new-tab-from-mouse-click-subframe`,
},
});
info("Open a subframe link in a new window using Shift-click");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
BrowserTestUtils.synthesizeMouseAtCenter(function() {
// This code runs as a framescript in the child process and it returns the
// target link in the subframe.
return this.content.frames[0].document // eslint-disable-line mozilla/no-cpows-in-tests
.querySelector("#test-create-new-window-from-mouse-click-subframe");
}, {shiftKey: true}, tab.linkedBrowser);
@@ -168,17 +149,17 @@ add_task(async function test_on_created_
sourceTabId: expectedSourceTab.sourceTabId,
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
url: `${OPENED_PAGE}#new-window-from-mouse-click-subframe`,
},
});
info("Open a subframe link with target=\"_blank\" in a new tab using click");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
BrowserTestUtils.synthesizeMouseAtCenter(function() {
// This code runs as a framescript in the child process and it returns the
// target link in the subframe.
return this.content.frames[0].document // eslint-disable-line mozilla/no-cpows-in-tests
.querySelector("#test-create-new-tab-from-targetblank-click-subframe");
}, {}, tab.linkedBrowser);
@@ -189,9 +170,8 @@ add_task(async function test_on_created_
url: `${OPENED_PAGE}#new-tab-from-targetblank-click-subframe`,
},
});
await BrowserTestUtils.removeTab(tab);
await extension.unload();
});
-
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js
@@ -1,15 +1,22 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
-const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
-const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+Services.scriptloader.loadSubScript(new URL("head_webNavigation.js", gTestPath).href,
+ this);
+
+async function clickContextMenuItem({pageElementSelector, contextMenuItemLabel}) {
+ const contentAreaContextMenu = await openContextMenu(pageElementSelector);
+ const item = contentAreaContextMenu.getElementsByAttribute("label", contextMenuItemLabel);
+ is(item.length, 1, `found contextMenu item for "${contextMenuItemLabel}"`);
+ item[0].click();
+ await closeContextMenu();
+}
async function background() {
const tabs = await browser.tabs.query({active: true, currentWindow: true});
const sourceTabId = tabs[0].id;
const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
@@ -29,76 +36,50 @@ async function background() {
browser.test.sendMessage("tabsOnCreated", tab.id);
});
browser.test.sendMessage("expectedSourceTab", {
sourceTabId, sourceTabFrames,
});
}
-async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
- await openNavTarget();
-
- const webNavMsg = await extension.awaitMessage("webNavOnCreated");
- const createdTabId = await extension.awaitMessage("tabsOnCreated");
- const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
-
- let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
-
- is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
- is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
- is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
- is(webNavMsg.url, url, "Got the expected url property");
-
- is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
- is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
-}
-
-async function clickContextMenuItem({pageElementSelector, contextMenuItemLabel}) {
- const contentAreaContextMenu = await openContextMenu(pageElementSelector);
- const item = contentAreaContextMenu.getElementsByAttribute("label", contextMenuItemLabel);
- is(item.length, 1, `found contextMenu item for "${contextMenuItemLabel}"`);
- item[0].click();
- await closeContextMenu();
-}
-
add_task(async function test_on_created_navigation_target_from_context_menu() {
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
const extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
permissions: ["webNavigation"],
},
});
await extension.startup();
const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
info("Open link in a new tab from the context menu");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
async openNavTarget() {
await clickContextMenuItem({
pageElementSelector: "#test-create-new-tab-from-context-menu",
contextMenuItemLabel: "Open Link in New Tab",
});
},
expectedWebNavProps: {
sourceTabId: expectedSourceTab.sourceTabId,
sourceFrameId: 0,
url: `${OPENED_PAGE}#new-tab-from-context-menu`,
},
});
info("Open link in a new window from the context menu");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
async openNavTarget() {
await clickContextMenuItem({
pageElementSelector: "#test-create-new-window-from-context-menu",
contextMenuItemLabel: "Open Link in New Window",
});
},
expectedWebNavProps: {
@@ -124,17 +105,17 @@ add_task(async function test_on_created_
});
await extension.startup();
const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
info("Open a subframe link in a new tab from the context menu");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
async openNavTarget() {
await clickContextMenuItem({
pageElementSelector: function() {
// This code runs as a framescript in the child process and it returns the
// target link in the subframe.
return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
.document.querySelector("#test-create-new-tab-from-context-menu-subframe");
@@ -146,17 +127,17 @@ add_task(async function test_on_created_
sourceTabId: expectedSourceTab.sourceTabId,
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
url: `${OPENED_PAGE}#new-tab-from-context-menu-subframe`,
},
});
info("Open a subframe link in a new window from the context menu");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
async openNavTarget() {
await clickContextMenuItem({
pageElementSelector: function() {
// This code runs as a framescript in the child process and it returns the
// target link in the subframe.
return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
.document.querySelector("#test-create-new-window-from-context-menu-subframe");
copy from browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
copy to browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_named_window.js
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_named_window.js
@@ -1,15 +1,14 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
-const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
-const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+Services.scriptloader.loadSubScript(new URL("head_webNavigation.js", gTestPath).href,
+ this);
async function background() {
const tabs = await browser.tabs.query({active: true, currentWindow: true});
const sourceTabId = tabs[0].id;
const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
@@ -35,135 +34,64 @@ async function background() {
}
});
browser.test.sendMessage("expectedSourceTab", {
sourceTabId, sourceTabFrames,
});
}
-async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
- await openNavTarget();
-
- const webNavMsg = await extension.awaitMessage("webNavOnCreated");
- const createdTabId = await extension.awaitMessage("tabsOnCreated");
- const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
-
- let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
-
- is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
- is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
- is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
- is(webNavMsg.url, url, "Got the expected url property");
-
- is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
- is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
-}
-
-add_task(async function test_on_created_navigation_target_from_window_open() {
+add_task(async function test_window_open_in_named_win() {
const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
gBrowser.selectedTab = tab1;
const extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
permissions: ["webNavigation", "tabs", "<all_urls>"],
},
});
await extension.startup();
const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
- info("open an url in a new tab from a window.open call");
+ info("open a url in a new named window from a window.open call");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
extension.sendMessage({
type: "execute-contentscript",
- code: `window.open("${OPENED_PAGE}#new-tab-from-window-open"); true;`,
+ code: `window.open("${OPENED_PAGE}#new-named-window-open", "TestWinName"); true;`,
});
},
expectedWebNavProps: {
sourceTabId: expectedSourceTab.sourceTabId,
sourceFrameId: 0,
- url: `${OPENED_PAGE}#new-tab-from-window-open`,
+ url: `${OPENED_PAGE}#new-named-window-open`,
},
});
- info("open an url in a new window from a window.open call");
+ info("open a url in an existent named window from a window.open call");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
extension.sendMessage({
type: "execute-contentscript",
- code: `window.open("${OPENED_PAGE}#new-win-from-window-open", "_blank", "toolbar=0"); true;`,
+ code: `window.open("${OPENED_PAGE}#existent-named-window-open", "TestWinName"); true;`,
});
},
expectedWebNavProps: {
sourceTabId: expectedSourceTab.sourceTabId,
sourceFrameId: 0,
- url: `${OPENED_PAGE}#new-win-from-window-open`,
+ url: `${OPENED_PAGE}#existent-named-window-open`,
},
});
+ assertNoPendingCreatedNavigationTargetData();
+
await BrowserTestUtils.removeTab(tab1);
await extension.unload();
});
-
-add_task(async function test_on_created_navigation_target_from_window_open_subframe() {
- const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
-
- gBrowser.selectedTab = tab1;
-
- const extension = ExtensionTestUtils.loadExtension({
- background,
- manifest: {
- permissions: ["webNavigation", "tabs", "<all_urls>"],
- },
- });
-
- await extension.startup();
-
- const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
-
- info("open an url in a new tab from subframe window.open call");
-
- await runTestCase({
- extension,
- openNavTarget() {
- extension.sendMessage({
- type: "execute-contentscript",
- code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-tab-from-window-open-subframe"); true;`,
- });
- },
- expectedWebNavProps: {
- sourceTabId: expectedSourceTab.sourceTabId,
- sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
- url: `${OPENED_PAGE}#new-tab-from-window-open-subframe`,
- },
- });
-
- info("open an url in a new window from subframe window.open call");
-
- await runTestCase({
- extension,
- openNavTarget() {
- extension.sendMessage({
- type: "execute-contentscript",
- code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-win-from-window-open-subframe", "_blank", "toolbar=0"); true;`,
- });
- },
- expectedWebNavProps: {
- sourceTabId: expectedSourceTab.sourceTabId,
- sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
- url: `${OPENED_PAGE}#new-win-from-window-open-subframe`,
- },
- });
-
- await BrowserTestUtils.removeTab(tab1);
-
- await extension.unload();
-});
copy from browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
copy to browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_subframe_window_open.js
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_subframe_window_open.js
@@ -1,15 +1,14 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
-const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
-const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+Services.scriptloader.loadSubScript(new URL("head_webNavigation.js", gTestPath).href,
+ this);
async function background() {
const tabs = await browser.tabs.query({active: true, currentWindow: true});
const sourceTabId = tabs[0].id;
const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
@@ -35,135 +34,130 @@ async function background() {
}
});
browser.test.sendMessage("expectedSourceTab", {
sourceTabId, sourceTabFrames,
});
}
-async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
- await openNavTarget();
-
- const webNavMsg = await extension.awaitMessage("webNavOnCreated");
- const createdTabId = await extension.awaitMessage("tabsOnCreated");
- const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
-
- let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
-
- is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
- is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
- is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
- is(webNavMsg.url, url, "Got the expected url property");
-
- is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
- is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
-}
-
-add_task(async function test_on_created_navigation_target_from_window_open() {
+add_task(async function test_window_open_from_subframe() {
const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
gBrowser.selectedTab = tab1;
const extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
permissions: ["webNavigation", "tabs", "<all_urls>"],
},
});
await extension.startup();
const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
- info("open an url in a new tab from a window.open call");
+ info("open a url in a new tab from subframe window.open call");
+
+ await runCreatedNavigationTargetTest({
+ extension,
+ openNavTarget() {
+ extension.sendMessage({
+ type: "execute-contentscript",
+ code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-tab-from-window-open-subframe"); true;`,
+ });
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+ url: `${OPENED_PAGE}#new-tab-from-window-open-subframe`,
+ },
+ });
+
+ info("open a url in a new window from subframe window.open call");
+
+ await runCreatedNavigationTargetTest({
+ extension,
+ openNavTarget() {
+ extension.sendMessage({
+ type: "execute-contentscript",
+ code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-win-from-window-open-subframe", "_blank", "toolbar=0"); true;`,
+ });
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+ url: `${OPENED_PAGE}#new-win-from-window-open-subframe`,
+ },
+ });
+
+ assertNoPendingCreatedNavigationTargetData();
+
+ await BrowserTestUtils.removeTab(tab1);
+
+ await extension.unload();
+});
- await runTestCase({
+add_task(async function test_window_open_close_from_browserAction_popup() {
+ const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+
+ gBrowser.selectedTab = tab1;
+
+ function popup() {
+ window.open("", "_self").close();
+
+ browser.test.sendMessage("browserAction_popup_executed");
+ }
+
+ const extension = ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ browser_action: {
+ default_popup: "popup.html",
+ },
+ permissions: ["webNavigation", "tabs", "<all_urls>"],
+ },
+ files: {
+ "popup.html": `<!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <script src="popup.js"></script>
+ </body>
+ </html>
+ `,
+ "popup.js": popup,
+ },
+ });
+
+ await extension.startup();
+
+ const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
+
+ clickBrowserAction(extension);
+
+ await extension.awaitMessage("browserAction_popup_executed");
+
+ info("open a url in a new tab from a window.open call");
+
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
extension.sendMessage({
type: "execute-contentscript",
code: `window.open("${OPENED_PAGE}#new-tab-from-window-open"); true;`,
});
},
expectedWebNavProps: {
sourceTabId: expectedSourceTab.sourceTabId,
sourceFrameId: 0,
url: `${OPENED_PAGE}#new-tab-from-window-open`,
},
});
- info("open an url in a new window from a window.open call");
-
- await runTestCase({
- extension,
- openNavTarget() {
- extension.sendMessage({
- type: "execute-contentscript",
- code: `window.open("${OPENED_PAGE}#new-win-from-window-open", "_blank", "toolbar=0"); true;`,
- });
- },
- expectedWebNavProps: {
- sourceTabId: expectedSourceTab.sourceTabId,
- sourceFrameId: 0,
- url: `${OPENED_PAGE}#new-win-from-window-open`,
- },
- });
+ assertNoPendingCreatedNavigationTargetData();
await BrowserTestUtils.removeTab(tab1);
await extension.unload();
});
-
-add_task(async function test_on_created_navigation_target_from_window_open_subframe() {
- const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
-
- gBrowser.selectedTab = tab1;
-
- const extension = ExtensionTestUtils.loadExtension({
- background,
- manifest: {
- permissions: ["webNavigation", "tabs", "<all_urls>"],
- },
- });
-
- await extension.startup();
-
- const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
-
- info("open an url in a new tab from subframe window.open call");
-
- await runTestCase({
- extension,
- openNavTarget() {
- extension.sendMessage({
- type: "execute-contentscript",
- code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-tab-from-window-open-subframe"); true;`,
- });
- },
- expectedWebNavProps: {
- sourceTabId: expectedSourceTab.sourceTabId,
- sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
- url: `${OPENED_PAGE}#new-tab-from-window-open-subframe`,
- },
- });
-
- info("open an url in a new window from subframe window.open call");
-
- await runTestCase({
- extension,
- openNavTarget() {
- extension.sendMessage({
- type: "execute-contentscript",
- code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-win-from-window-open-subframe", "_blank", "toolbar=0"); true;`,
- });
- },
- expectedWebNavProps: {
- sourceTabId: expectedSourceTab.sourceTabId,
- sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
- url: `${OPENED_PAGE}#new-win-from-window-open-subframe`,
- },
- });
-
- await BrowserTestUtils.removeTab(tab1);
-
- await extension.unload();
-});
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
@@ -1,15 +1,14 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
-const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
-const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+Services.scriptloader.loadSubScript(new URL("head_webNavigation.js", gTestPath).href,
+ this);
async function background() {
const tabs = await browser.tabs.query({active: true, currentWindow: true});
const sourceTabId = tabs[0].id;
const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
@@ -35,135 +34,130 @@ async function background() {
}
});
browser.test.sendMessage("expectedSourceTab", {
sourceTabId, sourceTabFrames,
});
}
-async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
- await openNavTarget();
-
- const webNavMsg = await extension.awaitMessage("webNavOnCreated");
- const createdTabId = await extension.awaitMessage("tabsOnCreated");
- const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
-
- let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
-
- is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
- is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
- is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
- is(webNavMsg.url, url, "Got the expected url property");
-
- is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
- is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
-}
-
-add_task(async function test_on_created_navigation_target_from_window_open() {
+add_task(async function test_window_open() {
const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
gBrowser.selectedTab = tab1;
const extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
permissions: ["webNavigation", "tabs", "<all_urls>"],
},
});
await extension.startup();
const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
- info("open an url in a new tab from a window.open call");
+ info("open a url in a new tab from a window.open call");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
extension.sendMessage({
type: "execute-contentscript",
code: `window.open("${OPENED_PAGE}#new-tab-from-window-open"); true;`,
});
},
expectedWebNavProps: {
sourceTabId: expectedSourceTab.sourceTabId,
sourceFrameId: 0,
url: `${OPENED_PAGE}#new-tab-from-window-open`,
},
});
- info("open an url in a new window from a window.open call");
+ info("open a url in a new window from a window.open call");
- await runTestCase({
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
extension.sendMessage({
type: "execute-contentscript",
code: `window.open("${OPENED_PAGE}#new-win-from-window-open", "_blank", "toolbar=0"); true;`,
});
},
expectedWebNavProps: {
sourceTabId: expectedSourceTab.sourceTabId,
sourceFrameId: 0,
url: `${OPENED_PAGE}#new-win-from-window-open`,
},
});
+ assertNoPendingCreatedNavigationTargetData();
+
await BrowserTestUtils.removeTab(tab1);
await extension.unload();
});
-add_task(async function test_on_created_navigation_target_from_window_open_subframe() {
+add_task(async function test_window_open_close_from_browserAction_popup() {
const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
gBrowser.selectedTab = tab1;
+ function popup() {
+ window.open("", "_self").close();
+
+ browser.test.sendMessage("browserAction_popup_executed");
+ }
+
const extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
+ browser_action: {
+ default_popup: "popup.html",
+ },
permissions: ["webNavigation", "tabs", "<all_urls>"],
},
+ files: {
+ "popup.html": `<!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <script src="popup.js"></script>
+ </body>
+ </html>
+ `,
+ "popup.js": popup,
+ },
});
await extension.startup();
const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
- info("open an url in a new tab from subframe window.open call");
+ clickBrowserAction(extension);
+
+ await extension.awaitMessage("browserAction_popup_executed");
- await runTestCase({
+ info("open a url in a new tab from a window.open call");
+
+ await runCreatedNavigationTargetTest({
extension,
openNavTarget() {
extension.sendMessage({
type: "execute-contentscript",
- code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-tab-from-window-open-subframe"); true;`,
+ code: `window.open("${OPENED_PAGE}#new-tab-from-window-open"); true;`,
});
},
expectedWebNavProps: {
sourceTabId: expectedSourceTab.sourceTabId,
- sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
- url: `${OPENED_PAGE}#new-tab-from-window-open-subframe`,
+ sourceFrameId: 0,
+ url: `${OPENED_PAGE}#new-tab-from-window-open`,
},
});
- info("open an url in a new window from subframe window.open call");
-
- await runTestCase({
- extension,
- openNavTarget() {
- extension.sendMessage({
- type: "execute-contentscript",
- code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-win-from-window-open-subframe", "_blank", "toolbar=0"); true;`,
- });
- },
- expectedWebNavProps: {
- sourceTabId: expectedSourceTab.sourceTabId,
- sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
- url: `${OPENED_PAGE}#new-win-from-window-open-subframe`,
- },
- });
+ assertNoPendingCreatedNavigationTargetData();
await BrowserTestUtils.removeTab(tab1);
await extension.unload();
});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/head_webNavigation.js
@@ -0,0 +1,39 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+/* exported BASE_URL, SOURCE_PAGE, OPENED_PAGE,
+ runCreatedNavigationTargetTest, assertNoPendingCreatedNavigationTargetData */
+
+const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
+const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
+const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+
+async function runCreatedNavigationTargetTest({extension, openNavTarget, expectedWebNavProps}) {
+ await openNavTarget();
+
+ const webNavMsg = await extension.awaitMessage("webNavOnCreated");
+ const createdTabId = await extension.awaitMessage("tabsOnCreated");
+ const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
+
+ let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
+
+ is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
+ is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
+ is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
+ is(webNavMsg.url, url, "Got the expected url property");
+
+ is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
+ is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
+}
+
+
+// Test that there are no pending createdNavigationTarget messages still tracked
+// in WebNavigation.jsm (to be called before the extension is unloaded, because
+// once the last extension which have subscribed a webNavigation event is unloaded
+// all the pending created navigation target data is completely cleared).
+function assertNoPendingCreatedNavigationTargetData() {
+ const {Manager} = Cu.import("resource://gre/modules/WebNavigation.jsm", {});
+ Assert.equal(Manager.createdNavigationTargetByOuterWindowId.size, 0,
+ "There should be no pending createdNavigationTarget messages in WebNavigation");
+}
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -957,16 +957,19 @@ BrowserGlue.prototype = {
DirectoryLinksProvider.init();
NewTabUtils.init();
NewTabUtils.links.addProvider(DirectoryLinksProvider);
PageActions.init();
this._firstWindowTelemetry(aWindow);
this._firstWindowLoaded();
+
+ // Set the default favicon size for UI views that use the page-icon protocol.
+ PlacesUtils.favicons.setDefaultIconURIPreferredSize(16 * aWindow.devicePixelRatio);
},
_sendMediaTelemetry() {
let win = Services.appShell.hiddenDOMWindow;
let v = win.document.createElementNS("http://www.w3.org/1999/xhtml", "video");
v.reportCanPlayTelemetry();
},
--- a/browser/components/resistfingerprinting/test/browser/browser.ini
+++ b/browser/components/resistfingerprinting/test/browser/browser.ini
@@ -13,17 +13,18 @@ support-files =
[browser_netInfo.js]
[browser_performanceAPI.js]
[browser_roundedWindow_dialogWindow.js]
[browser_roundedWindow_newWindow.js]
[browser_roundedWindow_open_max_inner.js]
[browser_roundedWindow_open_max_outer.js]
[browser_roundedWindow_open_mid_inner.js]
[browser_roundedWindow_open_mid_outer.js]
-[browser_roundedWindow_open_min.js]
+[browser_roundedWindow_open_min_inner.js]
+[browser_roundedWindow_open_min_outer.js]
[browser_roundedWindow_windowSetting_max_inner.js]
[browser_roundedWindow_windowSetting_max_outer.js]
[browser_roundedWindow_windowSetting_mid_inner.js]
[browser_roundedWindow_windowSetting_mid_outer.js]
[browser_roundedWindow_windowSetting_min_inner.js]
[browser_roundedWindow_windowSetting_min_outer.js]
[browser_timezone.js]
[browser_bug1369357_site_specific_zoom_level.js]
deleted file mode 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_min.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Bug 1330882 - A test case for opening new windows through window.open() as
- * rounded size when fingerprinting resistance is enabled. This test is for
- * minimum values.
- */
-
-OpenTest.run([
- {settingWidth: 199, settingHeight: 99, targetWidth: 200, targetHeight: 100},
- {settingWidth: 10, settingHeight: 10, targetWidth: 200, targetHeight: 100}
-]);
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_min_inner.js
@@ -0,0 +1,10 @@
+/*
+ * Bug 1330882 - A test case for opening new windows through window.open() as
+ * rounded size when fingerprinting resistance is enabled. This test is for
+ * minimum values.
+ */
+
+OpenTest.run([
+ {settingWidth: 199, settingHeight: 99, targetWidth: 200, targetHeight: 100},
+ {settingWidth: 10, settingHeight: 10, targetWidth: 200, targetHeight: 100}
+], false);
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_min_outer.js
@@ -0,0 +1,10 @@
+/*
+ * Bug 1330882 - A test case for opening new windows through window.open() as
+ * rounded size when fingerprinting resistance is enabled. This test is for
+ * minimum values.
+ */
+
+OpenTest.run([
+ {settingWidth: 199, settingHeight: 99, targetWidth: 200, targetHeight: 100},
+ {settingWidth: 10, settingHeight: 10, targetWidth: 200, targetHeight: 100}
+], true);
--- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
@@ -2758,18 +2758,18 @@ class PreferencesPane extends React.Pure
key: nestedPref.name,
prefName: nestedPref.name,
disabled: !enabled,
value: prefs[nestedPref.name],
onChange: this.handlePrefChange,
titleString: nestedPref.titleString,
labelClassName: `icon ${nestedPref.icon}` }))
)),
- React.createElement("hr", null),
- React.createElement(PreferencesInput, { className: "showSnippets", prefName: "feeds.snippets",
+ !prefs.disableSnippets && React.createElement("hr", null),
+ !prefs.disableSnippets && React.createElement(PreferencesInput, { className: "showSnippets", prefName: "feeds.snippets",
value: prefs["feeds.snippets"], onChange: this.handlePrefChange,
titleString: { id: "settings_pane_snippets_header" },
descString: { id: "settings_pane_snippets_body" } })
),
React.createElement(
"section",
{ className: "actions" },
React.createElement(
@@ -3883,23 +3883,23 @@ function addSnippetsSubscriber(store) {
let initializing = false;
store.subscribe(async () => {
const state = store.getState();
// state.Prefs.values["feeds.snippets"]: Should snippets be shown?
// state.Snippets.initialized Is the snippets data initialized?
// snippets.initialized: Is SnippetsProvider currently initialised?
- if (state.Prefs.values["feeds.snippets"] && state.Snippets.initialized && !snippets.initialized &&
+ if (state.Prefs.values["feeds.snippets"] && !state.Prefs.values.disableSnippets && state.Snippets.initialized && !snippets.initialized &&
// Don't call init multiple times
!initializing) {
initializing = true;
await snippets.init({ appData: state.Snippets });
initializing = false;
- } else if (state.Prefs.values["feeds.snippets"] === false && snippets.initialized) {
+ } else if ((state.Prefs.values["feeds.snippets"] === false || state.Prefs.values.disableSnippets === true) && snippets.initialized) {
snippets.uninit();
}
});
// These values are returned for testing purposes
return snippets;
}
@@ -3907,9 +3907,9 @@ module.exports = {
addSnippetsSubscriber,
SnippetsMap,
SnippetsProvider,
SNIPPETS_UPDATE_INTERVAL_MS
};
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
/***/ })
-/******/ ]);
\ No newline at end of file
+/******/ ]);
--- a/browser/extensions/activity-stream/lib/ActivityStream.jsm
+++ b/browser/extensions/activity-stream/lib/ActivityStream.jsm
@@ -92,16 +92,20 @@ const PREFS_CONFIG = new Map([
["prerender", {
title: "Use the prerendered version of activity-stream.html. This is set automatically by PrefsFeed.jsm.",
value: true
}],
["showSearch", {
title: "Show the Search bar",
value: true
}],
+ ["disableSnippets", {
+ title: "Disable snippets on activity stream",
+ value: false
+ }],
["showTopSites", {
title: "Show the Top Sites section",
value: true
}],
["collapseTopSites", {
title: "Collapse the Top Sites section",
value: false
}],
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -346,21 +346,58 @@ FormAutofillHandler.prototype = {
}
// Delete the field so the phishing hint won't treat it as a "also fill"
// field.
delete profile[fieldName];
}
}
},
+ _creditCardExpDateTransformer(profile) {
+ if (!profile["cc-exp"]) {
+ return;
+ }
+
+ let detail = this.getFieldDetailByName("cc-exp");
+ if (!detail) {
+ return;
+ }
+
+ let element = detail.elementWeakRef.get();
+ if (element.tagName != "INPUT" || !element.placeholder) {
+ return;
+ }
+
+ let result,
+ ccExpMonth = profile["cc-exp-month"],
+ ccExpYear = profile["cc-exp-year"],
+ placeholder = element.placeholder;
+
+ result = /(?:[^m]|\b)(m{1,2})\s*([-/\\]*)\s*(y{2,4})(?!y)/i.exec(placeholder);
+ if (result) {
+ profile["cc-exp"] = String(ccExpMonth).padStart(result[1].length, "0") +
+ result[2] +
+ String(ccExpYear).substr(-1 * result[3].length);
+ return;
+ }
+
+ result = /(?:[^y]|\b)(y{2,4})\s*([-/\\]*)\s*(m{1,2})(?!m)/i.exec(placeholder);
+ if (result) {
+ profile["cc-exp"] = String(ccExpYear).substr(-1 * result[1].length) +
+ result[2] +
+ String(ccExpMonth).padStart(result[3].length, "0");
+ }
+ },
+
getAdaptedProfiles(originalProfiles) {
for (let profile of originalProfiles) {
this._addressTransformer(profile);
this._telTransformer(profile);
this._matchSelectOptions(profile);
+ this._creditCardExpDateTransformer(profile);
}
return originalProfiles;
},
/**
* Processes form fields that can be autofilled, and populates them with the
* profile provided by backend.
*
--- a/browser/extensions/formautofill/content/FormAutofillFrameScript.js
+++ b/browser/extensions/formautofill/content/FormAutofillFrameScript.js
@@ -31,16 +31,19 @@ var FormAutofillFrameScript = {
return;
}
this._hasPendingTask = true;
setTimeout(() => {
FormAutofillContent.identifyAutofillFields(this._nextHandleElement);
this._hasPendingTask = false;
this._nextHandleElement = null;
+ // This is for testing purpose only which sends a message to indicate that the
+ // form has been identified, and ready to open popup.
+ sendAsyncMessage("FormAutofill:FieldsIdentified");
});
},
init() {
addEventListener("focusin", this);
addMessageListener("FormAutofill:PreviewProfile", this);
addMessageListener("FormAutoComplete:PopupClosed", this);
addMessageListener("FormAutoComplete:PopupOpened", this);
--- a/browser/extensions/formautofill/test/browser/head.js
+++ b/browser/extensions/formautofill/test/browser/head.js
@@ -93,16 +93,54 @@ function getDisplayedPopupItems(browser,
return [...listItemElems].filter(item => item.getAttribute("collapsed") != "true");
}
async function sleep(ms = 500) {
await new Promise(resolve => setTimeout(resolve, ms));
}
+async function focusAndWaitForFieldsIdentified(browser, selector) {
+ /* eslint no-shadow: ["error", { "allow": ["selector", "previouslyFocused", "previouslyIdentified"] }] */
+ const {previouslyFocused, previouslyIdentified} = await ContentTask.spawn(browser, {selector}, async function({selector}) {
+ Components.utils.import("resource://gre/modules/FormLikeFactory.jsm");
+ const input = content.document.querySelector(selector);
+ const rootElement = FormLikeFactory.findRootForField(input);
+ const previouslyFocused = content.document.activeElement == input;
+ const previouslyIdentified = rootElement.hasAttribute("test-formautofill-identified");
+
+ input.focus();
+
+ return {previouslyFocused, previouslyIdentified};
+ });
+
+ if (previouslyIdentified) {
+ return;
+ }
+
+ // Once the input is previously focused, no more FormAutofill:FieldsIdentified will be
+ // sent as the message goes along with focus event.
+ if (!previouslyFocused) {
+ await new Promise(resolve => {
+ Services.mm.addMessageListener("FormAutofill:FieldsIdentified", function onIdentified() {
+ Services.mm.removeMessageListener("FormAutofill:FieldsIdentified", onIdentified);
+ resolve();
+ });
+ });
+ }
+ // Wait 500ms to ensure that "markAsAutofillField" is completely finished.
+ await sleep();
+ await ContentTask.spawn(browser, {}, async function() {
+ Components.utils.import("resource://gre/modules/FormLikeFactory.jsm");
+ FormLikeFactory
+ .findRootForField(content.document.activeElement)
+ .setAttribute("test-formautofill-identified", "true");
+ });
+}
+
async function expectPopupOpen(browser) {
const {autoCompletePopup} = browser;
const listItemElems = getDisplayedPopupItems(browser);
await BrowserTestUtils.waitForCondition(() => autoCompletePopup.popupOpen,
"popup should be open");
await BrowserTestUtils.waitForCondition(() => {
return [...listItemElems].every(item => {
@@ -111,33 +149,17 @@ async function expectPopupOpen(browser)
item.getAttribute("originaltype") == "autofill-footer") &&
item.hasAttribute("formautofillattached");
});
}, "The popup should be a form autofill one");
}
async function openPopupOn(browser, selector) {
await SimpleTest.promiseFocus(browser);
- /* eslint no-shadow: ["error", { "allow": ["selector"] }] */
- const identified = await ContentTask.spawn(browser, {selector}, async function({selector}) {
- const input = content.document.querySelector(selector);
- const forms = content.document.getElementsByTagName("form");
- const rootElement = [...forms].find(form => form.contains(input)) || content.document.body;
-
- input.focus();
- if (rootElement.hasAttribute("test-formautofill-identified")) {
- return true;
- }
- rootElement.setAttribute("test-formautofill-identified", "true");
- return false;
- });
- // Wait 2 seconds for identifyAutofillFields if the form hasn't been identified yet.
- if (!identified) {
- await sleep(2000);
- }
+ await focusAndWaitForFieldsIdentified(browser, selector);
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectPopupOpen(browser);
}
async function expectPopupClose(browser) {
await BrowserTestUtils.waitForCondition(() => !browser.autoCompletePopup.popupOpen,
"popup should have closed");
}
--- a/browser/extensions/formautofill/test/mochitest/formautofill_common.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
@@ -9,28 +9,48 @@ let expectingPopup = null;
const {FormAutofillUtils} = SpecialPowers.Cu.import("resource://formautofill/FormAutofillUtils.jsm");
async function sleep(ms = 500, reason = "Intentionally wait for UI ready") {
SimpleTest.requestFlakyTimeout(reason);
await new Promise(resolve => setTimeout(resolve, ms));
}
-async function setInput(selector, value) {
- let input = document.querySelector("input" + selector);
- input.value = value;
+async function focusAndWaitForFieldsIdentified(input) {
+ const rootElement = input.form || input.ownerDocument.documentElement;
+ const previouslyFocused = input != document.activeElement;
+
input.focus();
- // "identifyAutofillFields" is invoked asynchronously in "focusin" event. We
- // should make sure fields are ready for popup before doing tests.
- //
- // TODO: "sleep" is used here temporarily because there's no event to
- // notify us of the state of "identifyAutofillFields" for now. We should
- // figure out a better way after the heuristics land.
- await sleep(500, "Guarantee asynchronous identifyAutofillFields is invoked");
+ if (rootElement.hasAttribute("test-formautofill-identified")) {
+ return;
+ }
+ if (!previouslyFocused) {
+ await new Promise(resolve => {
+ formFillChromeScript.addMessageListener("FormAutofillTest:FieldsIdentified", function onIdentified() {
+ formFillChromeScript.removeMessageListener("FormAutofillTest:FieldsIdentified", onIdentified);
+ resolve();
+ });
+ });
+ }
+ // In order to ensure that "markAsAutofillField" is fully executed, a short period
+ // of timeout is still required.
+ await sleep(300, "Guarantee asynchronous identifyAutofillFields is invoked");
+ rootElement.setAttribute("test-formautofill-identified", "true");
+}
+
+async function setInput(selector, value, userInput = false) {
+ const input = document.querySelector("input" + selector);
+ if (userInput) {
+ SpecialPowers.wrap(input).setUserInput(value);
+ } else {
+ input.value = value;
+ }
+ await focusAndWaitForFieldsIdentified(input);
+
return input;
}
function clickOnElement(selector) {
let element = document.querySelector(selector);
if (!element) {
throw new Error("Can not find the element");
--- a/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
@@ -164,16 +164,20 @@ var ParentUtils = {
observe(subject, topic, data) {
assert.ok(topic === "formautofill-storage-changed");
sendAsyncMessage("formautofill-storage-changed", {subject: null, topic, data});
},
};
Services.obs.addObserver(ParentUtils, "formautofill-storage-changed");
+Services.mm.addMessageListener("FormAutofill:FieldsIdentified", () => {
+ sendAsyncMessage("FormAutofillTest:FieldsIdentified");
+});
+
addMessageListener("FormAutofillTest:AddAddress", (msg) => {
ParentUtils.operateAddress("add", msg, "FormAutofillTest:AddressAdded");
});
addMessageListener("FormAutofillTest:RemoveAddress", (msg) => {
ParentUtils.operateAddress("remove", msg, "FormAutofillTest:AddressRemoved");
});
--- a/browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
+++ b/browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
@@ -729,16 +729,90 @@ const TESTCASES = [
"cc-exp-month": 1,
})],
expectedResult: [{
"guid": "123",
"cc-exp-month": 1,
}],
expectedOptionElements: [],
},
+ {
+ description: "Use placeholder to adjust cc-exp format [mm/yy].",
+ document: `<form><input placeholder="mm/yy" autocomplete="cc-exp"></form>`,
+ profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+ "cc-exp": "01/25",
+ })],
+ },
+ {
+ description: "Use placeholder to adjust cc-exp format [mm / yy].",
+ document: `<form><input placeholder="mm / yy" autocomplete="cc-exp"></form>`,
+ profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+ "cc-exp": "01/25",
+ })],
+ },
+ {
+ description: "Use placeholder to adjust cc-exp format [MM / YY].",
+ document: `<form><input placeholder="MM / YY" autocomplete="cc-exp"></form>`,
+ profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+ "cc-exp": "01/25",
+ })],
+ },
+ {
+ description: "Use placeholder to adjust cc-exp format [mm / yyyy].",
+ document: `<form><input placeholder="mm / yyyy" autocomplete="cc-exp"></form>`,
+ profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+ "cc-exp": "01/2025",
+ })],
+ },
+ {
+ description: "Use placeholder to adjust cc-exp format [mm - yyyy].",
+ document: `<form><input placeholder="mm - yyyy" autocomplete="cc-exp"></form>`,
+ profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+ "cc-exp": "01-2025",
+ })],
+ },
+ {
+ description: "Use placeholder to adjust cc-exp format [yyyy-mm].",
+ document: `<form><input placeholder="yyyy-mm" autocomplete="cc-exp"></form>`,
+ profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+ "cc-exp": "2025-01",
+ })],
+ },
+ {
+ description: "Use placeholder to adjust cc-exp format [yyy-mm].",
+ document: `<form><input placeholder="yyy-mm" autocomplete="cc-exp"></form>`,
+ profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+ "cc-exp": "025-01",
+ })],
+ },
+ {
+ description: "Use placeholder to adjust cc-exp format [mmm yyyy].",
+ document: `<form><input placeholder="mmm yyyy" autocomplete="cc-exp"></form>`,
+ profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ },
+ {
+ description: "Use placeholder to adjust cc-exp format [mm foo yyyy].",
+ document: `<form><input placeholder="mm foo yyyy" autocomplete="cc-exp"></form>`,
+ profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ },
+ {
+ description: "Use placeholder to adjust cc-exp format [mm - - yyyy].",
+ document: `<form><input placeholder="mm - - yyyy" autocomplete="cc-exp"></form>`,
+ profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+ },
];
for (let testcase of TESTCASES) {
add_task(async function() {
do_print("Starting testcase: " + testcase.description);
let doc = MockDocument.createTestDocument("http://localhost:8080/test/",
testcase.document);
--- a/browser/locales/search/list.json
+++ b/browser/locales/search/list.json
@@ -1,12 +1,12 @@
{
"default": {
"visibleDefaultEngines": [
- "google", "yahoo", "amazondotcom", "bing", "ddg", "twitter", "wikipedia"
+ "google", "yahoo", "amazondotcom", "bing", "ddg", "ebay", "twitter", "wikipedia"
]
},
"regionOverrides": {
"US": {
"google": "google-nocodes"
},
"CA": {
"google": "google-nocodes",
@@ -129,17 +129,17 @@
"visibleDefaultEngines": [
"yandex-by", "google", "ddg", "wikipedia-be", "wikipedia-be-tarask"
]
}
},
"bg": {
"default": {
"visibleDefaultEngines": [
- "google", "diribg", "amazondotcom", "ddg", "portalbgdict", "wikipedia-bg"
+ "google", "amazondotcom", "ddg", "portalbgdict", "wikipedia-bg"
]
}
},
"bn-BD": {
"default": {
"visibleDefaultEngines": [
"google", "yahoo", "bing", "ddg", "wikipedia-bn"
]
deleted file mode 100644
--- a/browser/locales/searchplugins/diribg.xml
+++ /dev/null
@@ -1,17 +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/. -->
-
-<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
-<ShortName>Дири.бг</ShortName>
-<Description>Търсачка Дири на dir.bg</Description>
-<InputEncoding>UTF-8</InputEncoding>
-<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAUAAAAAAAAAAAAAAAEAAAABAAD///8A7NLCAM+OZgDFd0cAyoNXAOK8pACySgoA+/TxANCPZwC0TAsAwm45AOzTwwDPiFkAuVENAMJmKgDpyLMAynxJAO7UwwDnwKYAvlYPAMp1OwD79PAA0opZAO/VxADXjlwAxFsRAPDWwwD46uEA8NbEAMlfEwDWh00A8tfFAM5kFgDRbiUA2opQAPPZxQDZfDUA02kYAPns4gDWciYA9NrGAOy3jQDZbhoA56VwAO6/mgD228YA/fbxAOB8KgDecxwA7LCAAPHCnAD33McA+ubVAOWALADjeB4A6ppWAOeJOgD43scA/O7jAO+scgDleh8A7aNkAOybVwD53sgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDAwQFAAMGAQAAAAAHCAkJCQkJCQoKCQsAAAAADA0ODwAADxANDQ0RAAAAEhMUFQAAAAAAFhMTFwAAABgZGgAAAAAAABsZGRwAAAAdHQAAAAAAAAAAHh0fAAAAICEAAAAAAAAAACIgIwAAACQlJgAAAAAAAAAnJSgAAAApKisAAAAAAAAsKiotAAAALi8wMS4AAAAyMDAwMwAAAAA0NTY1Nzc4NjU1NjkAAAAAADo7PDw8PD00Pjw/AAAAAAAAAAA5OQAAAD48PwAAAAAAAAAAAAAAAAA+PD8AAAAAAAAAAAAAAAAAPjw/AAAAAAAAAAAAAAAAAD48PwAAAAAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8=</Image>
-<Url type="text/html" method="GET" template="http://www.diri.bg/search.php" resultdomain="diri.bg">
- <Param name="came" value="s" />
- <Param name="u" value="1" />
- <Param name="browser" value="ff" />
- <Param name="textfield" value="{searchTerms}" />
-</Url>
-<SearchForm>http://diri.bg/</SearchForm>
-</SearchPlugin>
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -538,23 +538,31 @@ tabbrowser {
.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([pinned]):not([selected="true"]):-moz-locale-dir(rtl) {
background-position-x: right 11px;
}
.tab-label[attention]:not([selected="true"]) {
font-weight: bold;
}
-/* Tab separators */
+/* Drag space */
.titlebar-placeholder[type="pre-tabs"],
.titlebar-placeholder[type="post-tabs"] {
width: 40px;
}
+@media (max-width: 500px) {
+ .titlebar-placeholder[type="post-tabs"] {
+ display: none;
+ }
+}
+
+/* Tab separators */
+
.titlebar-placeholder[type="pre-tabs"] {
border-inline-end: 1px solid;
opacity: 0.2;
}
.tabbrowser-tab::after,
.tabbrowser-tab::before {
border-left: 1px solid;
--- a/browser/themes/windows/compacttheme.css
+++ b/browser/themes/windows/compacttheme.css
@@ -41,16 +41,26 @@
#TabsToolbar {
color: hsl(240,9%,98%);
}
/* Keep showing the correct color inside the tabs. */
.tabbrowser-tab {
color: var(--chrome-color) !important;
}
+
+ /* Because we're forcing the tabs toolbar to be [brighttext] to
+ * get white toolbar button icons, we need to manually set the
+ * correct color for the tab hover state for the light theme. */
+ .tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]):-moz-lwtheme-darktext {
+ background-color: rgba(0,0,0,.1) !important;
+ }
+ .tabbrowser-tab:hover > .tab-stack > .tab-background > .tab-line:not([selected=true]):-moz-lwtheme-darktext {
+ background-color: rgba(0,0,0,.2) !important;
+ }
}
}
@media (-moz-windows-glass) {
/* Set to full fill-opacity to improve visibility of toolbar buttons on aero glass. */
#TabsToolbar {
--toolbarbutton-icon-fill-opacity: 1;
}
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -914,17 +914,19 @@ rust_unlock_unstable =
ifdef MOZ_RUST_SIMD
rust_unlock_unstable += RUSTC_BOOTSTRAP=1
endif
ifdef MOZ_USING_SCCACHE
sccache_wrap := RUSTC_WRAPPER='$(CCACHE)'
endif
+ifdef MOZ_DEBUG_SYMBOLS
default_rustflags += -C debuginfo=2
+endif
# We use the + prefix to pass down the jobserver fds to cargo, but we
# don't use the prefix when make -n is used, so that cargo doesn't run
# in that case)
define RUN_CARGO
$(if $(findstring n,$(filter-out --%, $(MAKEFLAGS))),,+)env $(environment_cleaner) $(rust_unlock_unstable) $(rustflags_override) $(sccache_wrap) \
CARGO_TARGET_DIR=$(CARGO_TARGET_DIR) \
RUSTC=$(RUSTC) \
--- a/devtools/client/inspector/rules/test/browser_rules_original-source-link.js
+++ b/devtools/client/inspector/rules/test/browser_rules_original-source-link.js
@@ -73,11 +73,11 @@ function editorSelected(editor) {
is(line, 3, "cursor is at correct line number in original source");
}
function verifyLinkText(text, view) {
info("Verifying that the rule-view stylesheet link is " + text);
let label = getRuleViewLinkByIndex(view, 1)
.querySelector(".ruleview-rule-source-label");
return waitForSuccess(function* () {
- return label.textContent == text;
+ return label.textContent == text && label.getAttribute("title") === URL_ROOT + text;
}, "Link text changed to display correct location: " + text);
}
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -304,27 +304,30 @@ RuleEditor.prototype = {
}
this._currentLocation = {
url,
line,
column
};
- let title = CssLogic.shortSource({href: displayURL});
+ let sourceTextContent = CssLogic.shortSource({href: displayURL});
+ let title = displayURL ? displayURL : sourceTextContent;
if (line > 0) {
+ sourceTextContent += ":" + line;
title += ":" + line;
}
if (this.rule.mediaText) {
+ sourceTextContent += " @" + this.rule.mediaText;
title += " @" + this.rule.mediaText;
}
let sourceLabel = this.element.querySelector(".ruleview-rule-source-label");
sourceLabel.setAttribute("title", title);
- sourceLabel.textContent = title;
+ sourceLabel.textContent = sourceTextContent;
},
updateSourceLink: function () {
if (this.rule.isSystem) {
let sourceLabel = this.element.querySelector(".ruleview-rule-source-label");
let title = this.rule.title;
let sourceHref = (this.rule.sheet && this.rule.sheet.href) ?
this.rule.sheet.href : title;
--- a/devtools/client/netmonitor/src/components/MonitorPanel.js
+++ b/devtools/client/netmonitor/src/components/MonitorPanel.js
@@ -9,17 +9,17 @@ const {
createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
const Actions = require("../actions/index");
-const { getFormDataSections } = require("../utils/request-utils");
+const { updateFormDataSections } = require("../utils/request-utils");
const { getSelectedRequest } = require("../selectors/index");
// Components
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));
const NetworkDetailsPanel = createFactory(require("./NetworkDetailsPanel"));
const RequestList = createFactory(require("./RequestList"));
const Toolbar = createFactory(require("./Toolbar"));
const { div } = DOM;
@@ -49,42 +49,17 @@ const MonitorPanel = createClass({
};
},
componentDidMount() {
MediaQueryList.addListener(this.onLayoutChange);
},
componentWillReceiveProps(nextProps) {
- let {
- request = {},
- updateRequest,
- } = nextProps;
- let {
- formDataSections,
- requestHeaders,
- requestHeadersFromUploadStream,
- requestPostData,
- } = request;
-
- if (!formDataSections && requestHeaders &&
- requestHeadersFromUploadStream && requestPostData) {
- getFormDataSections(
- requestHeaders,
- requestHeadersFromUploadStream,
- requestPostData,
- this.props.connector.getLongString,
- ).then((newFormDataSections) => {
- updateRequest(
- request.id,
- { formDataSections: newFormDataSections },
- true,
- );
- });
- }
+ updateFormDataSections(nextProps);
},
componentWillUnmount() {
MediaQueryList.removeListener(this.onLayoutChange);
let { clientWidth, clientHeight } = findDOMNode(this.refs.endPanel) || {};
if (this.state.isVerticalSpliter && clientWidth) {
@@ -103,40 +78,41 @@ const MonitorPanel = createClass({
});
},
render() {
let {
connector,
isEmpty,
networkDetailsOpen,
+ openLink,
sourceMapService,
- openLink
} = this.props;
let initialWidth = Services.prefs.getIntPref(
"devtools.netmonitor.panes-network-details-width");
let initialHeight = Services.prefs.getIntPref(
"devtools.netmonitor.panes-network-details-height");
+
return (
div({ className: "monitor-panel" },
Toolbar(),
SplitBox({
className: "devtools-responsive-container",
initialWidth: `${initialWidth}px`,
initialHeight: `${initialHeight}px`,
minSize: "50px",
maxSize: "80%",
splitterSize: "1px",
startPanel: RequestList({ isEmpty, connector }),
endPanel: networkDetailsOpen && NetworkDetailsPanel({
ref: "endPanel",
connector,
+ openLink,
sourceMapService,
- openLink,
}),
endPanelCollapsed: !networkDetailsOpen,
endPanelControl: true,
vert: this.state.isVerticalSpliter,
}),
)
);
}
--- a/devtools/client/netmonitor/src/components/ParamsPanel.js
+++ b/devtools/client/netmonitor/src/components/ParamsPanel.js
@@ -1,22 +1,26 @@
/* 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 {
+ createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
const { L10N } = require("../utils/l10n");
const { getUrlQuery, parseQueryString, parseFormData } = require("../utils/request-utils");
const { sortObjectKeys } = require("../utils/sort-utils");
+const { updateFormDataSections } = require("../utils/request-utils");
+const Actions = require("../actions/index");
// Components
const PropertiesView = createFactory(require("./PropertiesView"));
const { div } = DOM;
const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
const PARAMS_EMPTY_TEXT = L10N.getStr("paramsEmptyText");
@@ -26,93 +30,106 @@ const PARAMS_POST_PAYLOAD = L10N.getStr(
const PARAMS_QUERY_STRING = L10N.getStr("paramsQueryString");
const SECTION_NAMES = [
JSON_SCOPE_NAME,
PARAMS_FORM_DATA,
PARAMS_POST_PAYLOAD,
PARAMS_QUERY_STRING,
];
-/*
+/**
* Params panel component
* Displays the GET parameters and POST data of a request
*/
-function ParamsPanel({
- openLink,
- request,
-}) {
- let {
- formDataSections,
- mimeType,
- requestPostData,
- url,
- } = request;
- let postData = requestPostData ? requestPostData.postData.text : null;
- let query = getUrlQuery(url);
+const ParamsPanel = createClass({
+ displayName: "ParamsPanel",
+
+ propTypes: {
+ connector: PropTypes.object.isRequired,
+ openLink: PropTypes.func,
+ request: PropTypes.object.isRequired,
+ updateRequest: PropTypes.func.isRequired,
+ },
+
+ componentDidMount() {
+ updateFormDataSections(this.props);
+ },
+
+ componentWillReceiveProps(nextProps) {
+ updateFormDataSections(nextProps);
+ },
+
+ render() {
+ let {
+ openLink,
+ request
+ } = this.props;
+ let {
+ formDataSections,
+ mimeType,
+ requestPostData,
+ url,
+ } = request;
+ let postData = requestPostData ? requestPostData.postData.text : null;
+ let query = getUrlQuery(url);
+
+ if (!formDataSections && !postData && !query) {
+ return div({ className: "empty-notice" },
+ PARAMS_EMPTY_TEXT
+ );
+ }
+
+ let object = {};
+ let json;
- if (!formDataSections && !postData && !query) {
- return div({ className: "empty-notice" },
- PARAMS_EMPTY_TEXT
+ // Query String section
+ if (query) {
+ object[PARAMS_QUERY_STRING] = getProperties(parseQueryString(query));
+ }
+
+ // Form Data section
+ if (formDataSections && formDataSections.length > 0) {
+ let sections = formDataSections.filter((str) => /\S/.test(str)).join("&");
+ object[PARAMS_FORM_DATA] = getProperties(parseFormData(sections));
+ }
+
+ // Request payload section
+ if (formDataSections && formDataSections.length === 0 && postData) {
+ try {
+ json = JSON.parse(postData);
+ } catch (error) {
+ // Continue regardless of parsing error
+ }
+
+ if (json) {
+ object[JSON_SCOPE_NAME] = sortObjectKeys(json);
+ } else {
+ object[PARAMS_POST_PAYLOAD] = {
+ EDITOR_CONFIG: {
+ text: postData,
+ mode: mimeType.replace(/;.+/, ""),
+ },
+ };
+ }
+ } else {
+ postData = "";
+ }
+
+ return (
+ div({ className: "panel-container" },
+ PropertiesView({
+ object,
+ filterPlaceHolder: PARAMS_FILTER_TEXT,
+ sectionNames: SECTION_NAMES,
+ openLink,
+ })
+ )
);
}
-
- let object = {};
- let json;
-
- // Query String section
- if (query) {
- object[PARAMS_QUERY_STRING] = getProperties(parseQueryString(query));
- }
-
- // Form Data section
- if (formDataSections && formDataSections.length > 0) {
- let sections = formDataSections.filter((str) => /\S/.test(str)).join("&");
- object[PARAMS_FORM_DATA] = getProperties(parseFormData(sections));
- }
-
- // Request payload section
- if (formDataSections && formDataSections.length === 0 && postData) {
- try {
- json = JSON.parse(postData);
- } catch (error) {
- // Continue regardless of parsing error
- }
-
- if (json) {
- object[JSON_SCOPE_NAME] = sortObjectKeys(json);
- } else {
- object[PARAMS_POST_PAYLOAD] = {
- EDITOR_CONFIG: {
- text: postData,
- mode: mimeType.replace(/;.+/, ""),
- },
- };
- }
- } else {
- postData = "";
- }
-
- return (
- div({ className: "panel-container" },
- PropertiesView({
- object,
- filterPlaceHolder: PARAMS_FILTER_TEXT,
- sectionNames: SECTION_NAMES,
- openLink,
- })
- )
- );
-}
-
-ParamsPanel.displayName = "ParamsPanel";
-
-ParamsPanel.propTypes = {
- request: PropTypes.object.isRequired,
- openLink: PropTypes.func,
-};
+});
/**
* Mapping array to dict for TreeView usage.
* Since TreeView only support Object(dict) format.
* This function also deal with duplicate key case
* (for multiple selection and query params with same keys)
*
* @param {Object[]} arr - key-value pair array like query or form params
@@ -128,9 +145,13 @@ function getProperties(arr) {
map[obj.name].push(obj.value);
} else {
map[obj.name] = obj.value;
}
return map;
}, {}));
}
-module.exports = ParamsPanel;
+module.exports = connect(null,
+ (dispatch) => ({
+ updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)),
+ }),
+)(ParamsPanel);
--- a/devtools/client/netmonitor/src/components/TabboxPanel.js
+++ b/devtools/client/netmonitor/src/components/TabboxPanel.js
@@ -67,17 +67,17 @@ function TabboxPanel({
title: COOKIES_TITLE,
},
CookiesPanel({ request, openLink }),
),
TabPanel({
id: PANELS.PARAMS,
title: PARAMS_TITLE,
},
- ParamsPanel({ request, openLink }),
+ ParamsPanel({ connector, openLink, request }),
),
TabPanel({
id: PANELS.RESPONSE,
title: RESPONSE_TITLE,
},
ResponsePanel({ request, openLink }),
),
TabPanel({
@@ -86,17 +86,17 @@ function TabboxPanel({
},
TimingsPanel({ request }),
),
request.cause && request.cause.stacktrace && request.cause.stacktrace.length > 0 &&
TabPanel({
id: PANELS.STACK_TRACE,
title: STACK_TRACE_TITLE,
},
- StackTracePanel({ request, sourceMapService, openLink, connector }),
+ StackTracePanel({ connector, openLink, request, sourceMapService }),
),
request.securityState && request.securityState !== "insecure" &&
TabPanel({
id: PANELS.SECURITY,
title: SECURITY_TITLE,
},
SecurityPanel({ request, openLink }),
),
--- a/devtools/client/netmonitor/src/reducers/requests.js
+++ b/devtools/client/netmonitor/src/reducers/requests.js
@@ -1,27 +1,29 @@
/* 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 I = require("devtools/client/shared/vendor/immutable");
-const { getUrlDetails } = require("../utils/request-utils");
+const {
+ getUrlDetails,
+ processNetworkUpdates,
+} = require("../utils/request-utils");
const {
ADD_REQUEST,
CLEAR_REQUESTS,
CLONE_SELECTED_REQUEST,
OPEN_NETWORK_DETAILS,
REMOVE_SELECTED_CUSTOM_REQUEST,
SELECT_REQUEST,
SEND_CUSTOM_REQUEST,
TOGGLE_RECORDING,
UPDATE_REQUEST,
- UPDATE_PROPS,
} = require("../constants");
const Request = I.Record({
id: null,
// Set to true in case of a request that's being edited as part of "edit and resend"
isCustom: false,
// Request properties - at the beginning, they are unknown and are gradually filled in
startedMillis: undefined,
@@ -185,40 +187,18 @@ function requestsReducer(state = new Req
let { requests, lastEndedMillis } = state;
let updatedRequest = requests.get(action.id);
if (!updatedRequest) {
return state;
}
updatedRequest = updatedRequest.withMutations(request => {
- for (let [key, value] of Object.entries(action.data)) {
- if (!UPDATE_PROPS.includes(key)) {
- continue;
- }
-
- request[key] = value;
-
- switch (key) {
- case "url":
- // Compute the additional URL details
- request.urlDetails = getUrlDetails(value);
- break;
- case "totalTime":
- request.endedMillis = request.startedMillis + value;
- lastEndedMillis = Math.max(lastEndedMillis, request.endedMillis);
- break;
- case "requestPostData":
- request.requestHeadersFromUploadStream = {
- headers: [],
- headersSize: 0,
- };
- break;
- }
- }
+ let values = processNetworkUpdates(action.data);
+ request = Object.assign(request, values);
});
return state.withMutations(st => {
st.requests = requests.set(updatedRequest.id, updatedRequest);
st.lastEndedMillis = lastEndedMillis;
});
}
--- a/devtools/client/netmonitor/src/utils/request-utils.js
+++ b/devtools/client/netmonitor/src/utils/request-utils.js
@@ -1,16 +1,20 @@
/* 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/. */
/* eslint-disable mozilla/reject-some-requires */
"use strict";
+const {
+ UPDATE_PROPS,
+} = require("devtools/client/netmonitor/src/constants");
+
const CONTENT_MIME_TYPE_ABBREVIATIONS = {
"ecmascript": "js",
"javascript": "js",
"x-javascript": "js"
};
/**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
@@ -384,16 +388,79 @@ function getResponseHeader(item, header)
for (let responseHeader of responseHeaders.headers) {
if (responseHeader.name.toLowerCase() == header) {
return responseHeader.value;
}
}
return null;
}
+/**
+ * Extracts any urlencoded form data sections from a POST request.
+ */
+function updateFormDataSections(props) {
+ let {
+ connector,
+ request = {},
+ updateRequest,
+ } = props;
+ let {
+ formDataSections,
+ requestHeaders,
+ requestHeadersFromUploadStream,
+ requestPostData,
+ } = request;
+
+ if (!formDataSections && requestHeaders &&
+ requestHeadersFromUploadStream && requestPostData) {
+ getFormDataSections(
+ requestHeaders,
+ requestHeadersFromUploadStream,
+ requestPostData,
+ connector.getLongString,
+ ).then((newFormDataSections) => {
+ updateRequest(
+ request.id,
+ { formDataSections: newFormDataSections },
+ true,
+ );
+ });
+ }
+}
+
+/**
+ * This helper function is used for additional processing of
+ * incoming network update packets. It's used by Network and
+ * Console panel reducers.
+ */
+function processNetworkUpdates(request) {
+ let result = {};
+ for (let [key, value] of Object.entries(request)) {
+ if (UPDATE_PROPS.includes(key)) {
+ result[key] = value;
+
+ switch (key) {
+ case "securityInfo":
+ result.securityState = value.state;
+ break;
+ case "totalTime":
+ result.totalTime = request.totalTime;
+ break;
+ case "requestPostData":
+ result.requestHeadersFromUploadStream = {
+ headers: [],
+ headersSize: 0,
+ };
+ break;
+ }
+ }
+ }
+ return result;
+}
+
module.exports = {
decodeUnicodeBase64,
getFormDataSections,
fetchHeaders,
formDataURI,
writeHeaderText,
decodeUnicodeUrl,
getAbbreviatedMimeType,
@@ -406,11 +473,13 @@ module.exports = {
getUrlBaseNameWithQuery,
getUrlDetails,
getUrlHost,
getUrlHostName,
getUrlQuery,
getUrlScheme,
parseQueryString,
parseFormData,
+ updateFormDataSections,
+ processNetworkUpdates,
propertiesEqual,
ipToLong,
};
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -441,17 +441,17 @@ html .jsterm-stack-node {
textarea.jsterm-input-node,
textarea.jsterm-complete-node {
width: 100%;
border: none;
margin: 0;
background-color: transparent;
resize: none;
- font-size: var(--theme-toolbar-font-size);
+ font-size: inherit;
line-height: 16px;
overflow-x: hidden;
/* Set padding for console input on textarea to make sure it is included in
scrollHeight that is used when resizing JSTerminal's input. */
padding: 4px 0;
padding-inline-start: 20px;
}
@@ -1128,18 +1128,18 @@ a.learn-more-link.webconsole-learn-more-
flex: 1;
direction: ltr;
overflow: auto;
-moz-user-select: text;
position: relative;
}
html,
-body,
-#app-wrapper {
+body {
+ display: block !important; /* Until Bug 1409413 removes the accidental display: flex */
height: 100%;
margin: 0;
padding: 0;
}
body {
overflow: hidden;
}
--- a/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.js
@@ -103,17 +103,19 @@ function NetworkEventMessage({
// API consumed by Net monitor UI components. Most of the method
// are not needed in context of the Console panel (atm) and thus
// let's just provide empty implementation.
// Individual methods might be implemented step by step as needed.
let connector = {
viewSourceInDebugger: (url, line) => {
serviceContainer.onViewSourceInDebugger({url, line});
},
- getLongString: () => {},
+ getLongString: (grip) => {
+ return serviceContainer.getLongString(grip);
+ },
getTabTarget: () => {},
getNetworkRequest: () => {},
sendHTTPRequest: () => {},
setPreferences: () => {},
triggerActivity: () => {},
};
// Only render the attachment if the network-event is
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
+++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
@@ -69,32 +69,37 @@ NewConsoleOutputWrapper.prototype = {
let selection = this.document.defaultView.getSelection();
if (selection && !selection.isCollapsed) {
return;
}
this.jsterm.focus();
});
+ let { hud } = this.jsterm;
+
const serviceContainer = {
attachRefToHud,
emitNewMessage: (node, messageId, timeStamp) => {
this.jsterm.hud.emit("new-messages", new Set([{
node,
messageId,
timeStamp,
}]));
},
- hudProxy: this.jsterm.hud.proxy,
+ hudProxy: hud.proxy,
openLink: url => {
- this.jsterm.hud.owner.openLink(url);
+ hud.owner.openLink(url);
},
createElement: nodename => {
return this.document.createElement(nodename);
},
+ getLongString: (grip) => {
+ return hud.proxy.webConsoleClient.getString(grip);
+ },
};
// Set `openContextMenu` this way so, `serviceContainer` variable
// is available in the current scope and we can pass it into
// `createContextMenu` method.
serviceContainer.openContextMenu = (e, message) => {
let { screenX, screenY, target } = e;
@@ -228,18 +233,25 @@ NewConsoleOutputWrapper.prototype = {
dispatchTimestampsToggle: function (enabled) {
store.dispatch(actions.timestampsToggle(enabled));
},
dispatchMessageUpdate: function (message, res) {
// network-message-updated will emit when all the update message arrives.
// Since we can't ensure the order of the network update, we check
// that networkInfo.updates has all we need.
+ // Note that 'requestPostData' is sent only for POST requests, so we need
+ // to count with that.
const NUMBER_OF_NETWORK_UPDATE = 8;
- if (res.networkInfo.updates.length === NUMBER_OF_NETWORK_UPDATE) {
+ let expectedLength = NUMBER_OF_NETWORK_UPDATE;
+ if (res.networkInfo.updates.indexOf("requestPostData") != -1) {
+ expectedLength++;
+ }
+
+ if (res.networkInfo.updates.length === expectedLength) {
this.batchedMessageUpdates({ res, message });
}
},
dispatchRequestUpdate: function (id, data) {
this.batchedRequestUpdates({ id, data });
},
--- a/devtools/client/webconsole/new-console-output/reducers/messages.js
+++ b/devtools/client/webconsole/new-console-output/reducers/messages.js
@@ -18,19 +18,23 @@ const {
FILTERS,
MESSAGE_TYPE,
MESSAGE_SOURCE,
} = constants;
const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const {
- UPDATE_PROPS
+ UPDATE_REQUEST,
} = require("devtools/client/netmonitor/src/constants");
+const {
+ processNetworkUpdates,
+} = require("devtools/client/netmonitor/src/utils/request-utils");
+
const MessageState = Immutable.Record({
// List of all the messages added to the console.
messagesById: Immutable.OrderedMap(),
// Array of the visible messages.
visibleMessages: [],
// Object for the filtered messages.
filteredMessagesCount: getDefaultFiltersCounter(),
// List of the message ids which are opened.
@@ -269,44 +273,24 @@ function messages(state = new MessageSta
case constants.NETWORK_MESSAGE_UPDATE:
return state.set(
"networkMessagesUpdateById",
Object.assign({}, networkMessagesUpdateById, {
[action.message.id]: action.message
})
);
+ case UPDATE_REQUEST:
case constants.NETWORK_UPDATE_REQUEST: {
let request = networkMessagesUpdateById[action.id];
if (!request) {
return state;
}
- let values = {};
- for (let [key, value] of Object.entries(action.data)) {
- if (UPDATE_PROPS.includes(key)) {
- values[key] = value;
-
- switch (key) {
- case "securityInfo":
- values.securityState = value.state;
- break;
- case "totalTime":
- values.totalTime = request.totalTime;
- break;
- case "requestPostData":
- values.requestHeadersFromUploadStream = {
- headers: [],
- headersSize: 0,
- };
- break;
- }
- }
- }
-
+ let values = processNetworkUpdates(action.data);
newState = state.set(
"networkMessagesUpdateById",
Object.assign({}, networkMessagesUpdateById, {
[action.id]: Object.assign({}, request, values)
})
);
return newState;
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -190,39 +190,35 @@ skip-if = true # Bug 1406060
skip-if = true # Bug 1406060
[browser_console_webconsole_ctrlw_close_tab.js]
skip-if = true # Bug 1406060
[browser_console_webconsole_iframe_messages.js]
skip-if = true # Bug 1406060
[browser_console_webconsole_private_browsing.js]
skip-if = true # Bug 1403188
# old console skip-if = e10s # Bug 1042253 - webconsole e10s tests
+[browser_jsterm_add_edited_input_to_history.js]
+[browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js]
[browser_jsterm_copy_command.js]
skip-if = true
subsuite = clipboard
# old console skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
[browser_jsterm_dollar.js]
[browser_jsterm_history_persist.js]
[browser_jsterm_inspect.js]
[browser_jsterm_no_autocompletion_on_defined_variables.js]
[browser_jsterm_no_input_and_tab_key_pressed.js]
[browser_jsterm_no_input_change_and_tab_key_pressed.js]
[browser_netmonitor_shows_reqs_in_webconsole.js]
-[browser_webconsole.js]
-skip-if = true # Bug 1404829
-[browser_webconsole_add_edited_input_to_history.js]
-skip-if = true # Bug 1408915
[browser_webconsole_allow_mixedcontent_securityerrors.js]
tags = mcb
skip-if = true # Bug 1403452
# old console skip-if = (os == 'win' && bits == 64) # Bug 1390001
[browser_webconsole_assert.js]
skip-if = true # Bug 1403458
-[browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js]
-skip-if = true # Bug 1408916
[browser_webconsole_autocomplete_JSTerm_helpers.js]
skip-if = true # Bug 1408917
[browser_webconsole_autocomplete_accessibility.js]
skip-if = true # Bug 1408918
[browser_webconsole_autocomplete_and_selfxss.js]
subsuite = clipboard
skip-if = true # Bug 1404850
# old console skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
rename from devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_add_edited_input_to_history.js
rename to devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_add_edited_input_to_history.js
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_add_edited_input_to_history.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_add_edited_input_to_history.js
@@ -7,26 +7,27 @@
// lost after navigating in history.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=817834
"use strict";
const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 817834";
add_task(function* () {
- yield loadTab(TEST_URI);
-
- let hud = yield openConsole();
-
+ let hud = yield openNewTabAndConsole(TEST_URI);
+ // Clearing history that might have been set in previous tests.
+ yield hud.jsterm.clearHistory();
testEditedInputHistory(hud);
+ yield hud.jsterm.clearHistory();
});
-function testEditedInputHistory(HUD) {
- let jsterm = HUD.jsterm;
+function testEditedInputHistory(hud) {
+ let jsterm = hud.jsterm;
let inputNode = jsterm.inputNode;
+
ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
is(inputNode.selectionStart, 0);
is(inputNode.selectionEnd, 0);
jsterm.setInputValue('"first item"');
EventUtils.synthesizeKey("VK_UP", {});
is(jsterm.getInputValue(), '"first item"', "null test history up");
EventUtils.synthesizeKey("VK_DOWN", {});
rename from devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js
rename to devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js
@@ -2,46 +2,38 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that properties starting with underscores or dollars can be
// autocompleted (bug 967468).
-
-add_task(function* () {
- const TEST_URI = "data:text/html;charset=utf8,test autocompletion with " +
- "$ or _";
- yield loadTab(TEST_URI);
-
- function* autocomplete(term) {
- let deferred = defer();
+const TEST_URI = "data:text/html;charset=utf8,test autocompletion with $ or _";
- jsterm.setInputValue(term);
- jsterm.complete(jsterm.COMPLETE_HINT_ONLY, deferred.resolve);
-
- yield deferred.promise;
+add_task(async function () {
+ let { jsterm } = await openNewTabAndConsole(TEST_URI);
- ok(popup.itemCount > 0,
- "There's " + popup.itemCount + " suggestions for '" + term + "'");
- }
-
- let { jsterm } = yield openConsole();
- let popup = jsterm.autocompletePopup;
-
- yield jsterm.execute("var testObject = {$$aaab: '', $$aaac: ''}");
+ await jsterm.execute("var testObject = {$$aaab: '', $$aaac: ''}");
// Should work with bug 967468.
- yield autocomplete("Object.__d");
- yield autocomplete("testObject.$$a");
+ await testAutocomplete(jsterm, "Object.__d");
+ await testAutocomplete(jsterm, "testObject.$$a");
// Here's when things go wrong in bug 967468.
- yield autocomplete("Object.__de");
- yield autocomplete("testObject.$$aa");
+ await testAutocomplete(jsterm, "Object.__de");
+ await testAutocomplete(jsterm, "testObject.$$aa");
// Should work with bug 1207868.
- yield jsterm.execute("let foobar = {a: ''}; const blargh = {a: 1};");
- yield autocomplete("foobar");
- yield autocomplete("blargh");
- yield autocomplete("foobar.a");
- yield autocomplete("blargh.a");
+ await jsterm.execute("let foobar = {a: ''}; const blargh = {a: 1};");
+ await testAutocomplete(jsterm, "foobar");
+ await testAutocomplete(jsterm, "blargh");
+ await testAutocomplete(jsterm, "foobar.a");
+ await testAutocomplete(jsterm, "blargh.a");
});
+
+async function testAutocomplete(jsterm, inputString) {
+ jsterm.setInputValue(inputString);
+ await new Promise(resolve => jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve));
+
+ let popup = jsterm.autocompletePopup;
+ ok(popup.itemCount > 0, `There's ${popup.itemCount} suggestions for '${inputString}'`);
+}
deleted file mode 100644
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// See Bug 611795.
-
-const TEST_URI = 'data:text/html;charset=utf-8,<div style="-moz-opacity:0;">' +
- 'test repeated css warnings</div><p style="-moz-opacity:0">' +
- "hi</p>";
-var hud;
-
-/**
- * Unit test for bug 611795:
- * Repeated CSS messages get collapsed into one.
- */
-
-add_task(function* () {
- yield loadTab(TEST_URI);
-
- hud = yield openConsole();
- hud.jsterm.clearOutput(true);
-
- BrowserReload();
- yield loadBrowser(gBrowser.selectedBrowser);
-
- yield onContentLoaded();
- yield testConsoleLogRepeats();
-
- hud = null;
-});
-
-function onContentLoaded() {
- let cssWarning = "Unknown property \u2018-moz-opacity\u2019. Declaration dropped.";
-
- return waitForMessages({
- webconsole: hud,
- messages: [{
- text: cssWarning,
- category: CATEGORY_CSS,
- severity: SEVERITY_WARNING,
- repeats: 2,
- }],
- });
-}
-
-function testConsoleLogRepeats() {
- let jsterm = hud.jsterm;
-
- jsterm.clearOutput();
-
- jsterm.setInputValue("for (let i = 0; i < 10; ++i) console.log('this is a " +
- "line of reasonably long text that I will use to " +
- "verify that the repeated text node is of an " +
- "appropriate size.');");
- jsterm.execute();
-
- return waitForMessages({
- webconsole: hud,
- messages: [{
- text: "this is a line of reasonably long text",
- category: CATEGORY_WEBDEV,
- severity: SEVERITY_LOG,
- repeats: 10,
- }],
- });
-}
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_network_messages_expand.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_network_messages_expand.js
@@ -1,45 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
-const TEST_URI = "data:text/html;charset=utf8,Test that clicking on a network message " +
- "in the console toggles the HTTP inspection.";
-
const TEST_FILE = "test-network-request.html";
const TEST_PATH = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/";
+const TEST_URI = TEST_PATH + TEST_FILE;
const NET_PREF = "devtools.webconsole.filter.net";
const XHR_PREF = "devtools.webconsole.filter.netxhr";
-Services.prefs.setBoolPref(NET_PREF, true);
+Services.prefs.setBoolPref(NET_PREF, false);
Services.prefs.setBoolPref(XHR_PREF, true);
registerCleanupFunction(() => {
Services.prefs.clearUserPref(NET_PREF);
Services.prefs.clearUserPref(XHR_PREF);
});
add_task(async function task() {
const hud = await openNewTabAndConsole(TEST_URI);
const currentTab = gBrowser.selectedTab;
let target = TargetFactory.forTab(currentTab);
let toolbox = gDevTools.getToolbox(target);
- const documentUrl = TEST_PATH + TEST_FILE;
- await loadDocument(documentUrl);
- info("Document loaded.");
+ // Fire an XHR POST request.
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function () {
+ content.wrappedJSObject.testXhrPost();
+ });
- let messageNode = await waitFor(() => findMessage(hud, documentUrl));
+ info("XHR executed");
+
+ await waitForRequestUpdates(toolbox);
+
+ let xhrUrl = TEST_PATH + "test-data.json";
+ let messageNode = await waitFor(() => findMessage(hud, xhrUrl));
let urlNode = messageNode.querySelector(".url");
info("Network message found.");
- let updates = waitForNetworkUpdates(toolbox);
+ let updates = waitForPayloadReady(toolbox);
// Expand network log
urlNode.click();
await updates;
await testNetworkMessage(messageNode);
});
@@ -56,44 +60,61 @@ async function testNetworkMessage(messag
ok(responseTab, "Response tab is available");
ok(timingsTab, "Timings tab is available");
// Headers tab should be selected by default, so just check its content.
let headersContent = messageNode.querySelector(
"#headers-panel .headers-overview");
ok(headersContent, "Headers content is available");
+ // Select Params tab and check the content. CodeMirror initialization
+ // is delayed to prevent UI freeze, so wait for a little while.
+ paramsTab.click();
+ let paramsPanel = messageNode.querySelector("#params-panel");
+ await waitForSourceEditor(paramsPanel);
+ let paramsContent = messageNode.querySelector(
+ "#params-panel .panel-container .CodeMirror");
+ ok(paramsContent, "Params content is available");
+ ok(paramsContent.textContent.includes("Hello world!"), "Post body is correct");
+
// Select Response tab and check the content. CodeMirror initialization
- // is delayed to prevent UI freeze, so wait for a little while.
+ // is delayed, so again wait for a little while.
responseTab.click();
- await waitForSourceEditor(messageNode);
+ let responsePanel = messageNode.querySelector("#response-panel");
+ await waitForSourceEditor(responsePanel);
let responseContent = messageNode.querySelector(
"#response-panel .editor-row-container .CodeMirror");
ok(responseContent, "Response content is available");
ok(responseContent.textContent, "Response text is available");
// Select Timings tab and check the content.
timingsTab.click();
let timingsContent = messageNode.querySelector(
"#timings-panel .timings-container .timings-label");
ok(timingsContent, "Timings content is available");
ok(timingsContent.textContent, "Timings text is available");
}
-async function waitForNetworkUpdates(toolbox) {
- let panel = toolbox.getCurrentPanel();
- let hud = panel.hud;
- let ui = hud.ui;
-
+async function waitForPayloadReady(toolbox) {
+ let {ui} = toolbox.getCurrentPanel().hud;
return new Promise(resolve => {
ui.jsterm.hud.on("network-request-payload-ready", () => {
info("network-request-payload-ready received");
resolve();
});
});
}
-async function waitForSourceEditor(messageNode) {
+async function waitForSourceEditor(panel) {
return waitUntil(() => {
- return !!messageNode.querySelector(
- "#response-panel .editor-row-container .CodeMirror");
+ return !!panel.querySelector(".CodeMirror");
});
}
+
+async function waitForRequestUpdates(toolbox) {
+ let {ui} = toolbox.getCurrentPanel().hud;
+ return new Promise(resolve => {
+ ui.jsterm.hud.on("network-message-updated", () => {
+ info("network-message-updated received");
+ resolve();
+ });
+ });
+}
--- a/devtools/client/webconsole/new-console-output/test/store/messages.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/messages.test.js
@@ -40,17 +40,17 @@ describe("Message reducer:", () => {
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
dispatch(actions.messageAdd(packet));
const messages = getAllMessagesById(getState());
expect(messages.first()).toEqual(message);
});
- it("increments repeat on a repeating message", () => {
+ it("increments repeat on a repeating log message", () => {
const key1 = "console.log('foobar', 'test')";
const { dispatch, getState } = setupStore([key1, key1]);
const packet = clonePacket(stubPackets.get(key1));
// Repeat ID must be the same even if the timestamp is different.
packet.message.timeStamp = 1;
dispatch(actions.messageAdd(packet));
@@ -60,16 +60,56 @@ describe("Message reducer:", () => {
const messages = getAllMessagesById(getState());
expect(messages.size).toBe(1);
const repeat = getAllRepeatById(getState());
expect(repeat[messages.first().id]).toBe(4);
});
+ it("increments repeat on a repeating css message", () => {
+ const key1 = "Unknown property ‘such-unknown-property’. Declaration dropped.";
+ const { dispatch, getState } = setupStore([key1, key1]);
+
+ const packet = clonePacket(stubPackets.get(key1));
+
+ // Repeat ID must be the same even if the timestamp is different.
+ packet.pageError.timeStamp = 1;
+ dispatch(actions.messageAdd(packet));
+ packet.pageError.timeStamp = 2;
+ dispatch(actions.messageAdd(packet));
+
+ const messages = getAllMessagesById(getState());
+
+ expect(messages.size).toBe(1);
+
+ const repeat = getAllRepeatById(getState());
+ expect(repeat[messages.first().id]).toBe(4);
+ });
+
+ it("increments repeat on a repeating error message", () => {
+ const key1 = "ReferenceError: asdf is not defined";
+ const { dispatch, getState } = setupStore([key1, key1]);
+
+ const packet = clonePacket(stubPackets.get(key1));
+
+ // Repeat ID must be the same even if the timestamp is different.
+ packet.pageError.timeStamp = 1;
+ dispatch(actions.messageAdd(packet));
+ packet.pageError.timeStamp = 2;
+ dispatch(actions.messageAdd(packet));
+
+ const messages = getAllMessagesById(getState());
+
+ expect(messages.size).toBe(1);
+
+ const repeat = getAllRepeatById(getState());
+ expect(repeat[messages.first().id]).toBe(4);
+ });
+
it("does not increment repeat after closing a group", () => {
const logKey = "console.log('foobar', 'test')";
const { getState } = setupStore([
logKey,
logKey,
"console.group('bar')",
logKey,
logKey,
--- a/devtools/client/webconsole/test/browser_webconsole_view_source.js
+++ b/devtools/client/webconsole/test/browser_webconsole_view_source.js
@@ -9,26 +9,16 @@
// order to have it opened in the standard View Source window.
"use strict";
const TEST_URI = "https://example.com/browser/devtools/client/webconsole/" +
"test/test-mixedcontent-securityerrors.html";
add_task(function* () {
- yield actuallyTest();
-});
-
-add_task(function* () {
- Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
- yield actuallyTest();
- Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
-});
-
-var actuallyTest = Task.async(function*() {
yield loadTab(TEST_URI);
let hud = yield openConsole(null);
info("console opened");
let [result] = yield waitForMessages({
webconsole: hud,
messages: [{
text: "Blocked loading mixed active content",
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -74,20 +74,20 @@ ShadowRoot::ShadowRoot(Element* aElement
// Add the ShadowRoot as a mutation observer on the host to watch
// for mutations because the insertion points in this ShadowRoot
// may need to be updated when the host children are modified.
GetHost()->AddMutationObserver(this);
}
ShadowRoot::~ShadowRoot()
{
- if (GetHost()) {
- // mPoolHost may have been unlinked or a new ShadowRoot may have been
- // creating, making this one obsolete.
- GetHost()->RemoveMutationObserver(this);
+ if (auto* host = GetHost()) {
+ // mHost may have been unlinked or a new ShadowRoot may have been
+ // created, making this one obsolete.
+ host->RemoveMutationObserver(this);
}
UnsetFlags(NODE_IS_IN_SHADOW_TREE);
// nsINode destructor expects mSubtreeRoot == this.
SetSubtreeRootPointer(this);
}
@@ -109,18 +109,17 @@ ShadowRoot::FromNode(nsINode* aNode)
return nullptr;
}
void
ShadowRoot::StyleSheetChanged()
{
mProtoBinding->FlushSkinSheets();
- nsIPresShell* shell = OwnerDoc()->GetShell();
- if (shell) {
+ if (nsIPresShell* shell = OwnerDoc()->GetShell()) {
OwnerDoc()->BeginUpdate(UPDATE_STYLE);
shell->RecordShadowStyleChange(this);
OwnerDoc()->EndUpdate(UPDATE_STYLE);
}
}
void
ShadowRoot::InsertSheet(StyleSheet* aSheet,
@@ -234,162 +233,178 @@ ShadowRoot::RemoveInsertionPoint(HTMLCon
mInsertionPoints.RemoveElement(aInsertionPoint);
}
void
ShadowRoot::RemoveDestInsertionPoint(nsIContent* aInsertionPoint,
nsTArray<nsIContent*>& aDestInsertionPoints)
{
// Remove the insertion point from the destination insertion points.
- // Also remove all succeeding insertion points because it is no longer
- // possible for the content to be distributed into deeper node trees.
+ //
+ // Note that while it sounds tempting to just remove all the insertion points
+ // after it too, since they're usually after in tree position, it may not be
+ // the case when we're redistributing after new insertion points have been
+ // bound to the tree before aInsertionPoint, see bug 1409088.
int32_t index = aDestInsertionPoints.IndexOf(aInsertionPoint);
// It's possible that we already removed the insertion point while processing
- // other insertion point removals.
+ // other insertion point removals / fallback content redistribution (which
+ // does DestInsertionPoints().Clear()).
if (index >= 0) {
- aDestInsertionPoints.SetLength(index);
+ aDestInsertionPoints.RemoveElementAt(index);
}
}
void
+ShadowRoot::DistributionChanged()
+{
+ // FIXME(emilio): We could be more granular in a bunch of cases.
+ auto* host = GetHost();
+ if (!host || !host->IsInComposedDoc()) {
+ return;
+ }
+
+ auto* shell = OwnerDoc()->GetShell();
+ if (!shell) {
+ return;
+ }
+
+ // FIXME(emilio): Rename this to DestroyFramesForAndRestyle?
+ shell->DestroyFramesFor(host);
+}
+
+const HTMLContentElement*
ShadowRoot::DistributeSingleNode(nsIContent* aContent)
{
// Find the insertion point to which the content belongs.
- HTMLContentElement* insertionPoint = nullptr;
- for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
- if (mInsertionPoints[i]->Match(aContent)) {
- if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) {
+ HTMLContentElement* foundInsertionPoint = nullptr;
+ for (HTMLContentElement* insertionPoint : mInsertionPoints) {
+ if (insertionPoint->Match(aContent)) {
+ if (insertionPoint->MatchedNodes().Contains(aContent)) {
// Node is already matched into the insertion point. We are done.
- return;
+ return insertionPoint;
}
// Matching may cause the insertion point to drop fallback content.
- if (mInsertionPoints[i]->MatchedNodes().IsEmpty() &&
- static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) {
+ if (insertionPoint->MatchedNodes().IsEmpty() &&
+ insertionPoint->HasChildren()) {
// This match will cause the insertion point to drop all fallback
// content and used matched nodes instead. Give up on the optimization
// and just distribute all nodes.
DistributeAllNodes();
- return;
+ MOZ_ASSERT(insertionPoint->MatchedNodes().Contains(aContent));
+ return insertionPoint;
}
- insertionPoint = mInsertionPoints[i];
+ foundInsertionPoint = insertionPoint;
break;
}
}
+ if (!foundInsertionPoint) {
+ return nullptr;
+ }
+
// Find the index into the insertion point.
- if (insertionPoint) {
- nsCOMArray<nsIContent>& matchedNodes = insertionPoint->MatchedNodes();
- // Find the appropriate position in the matched node list for the
- // newly distributed content.
- bool isIndexFound = false;
- ExplicitChildIterator childIterator(GetHost());
- for (uint32_t i = 0; i < matchedNodes.Length(); i++) {
- // Seek through the host's explicit children until the inserted content
- // is found or when the current matched node is reached.
- if (childIterator.Seek(aContent, matchedNodes[i])) {
- // aContent was found before the current matched node.
- insertionPoint->InsertMatchedNode(i, aContent);
- isIndexFound = true;
- break;
- }
- }
-
- if (!isIndexFound) {
- // We have still not found an index in the insertion point,
- // thus it must be at the end.
- MOZ_ASSERT(childIterator.Seek(aContent, nullptr),
- "Trying to match a node that is not a candidate to be matched");
- insertionPoint->AppendMatchedNode(aContent);
- }
-
- // Handle the case where the parent of the insertion point has a ShadowRoot.
- // The node distributed into the insertion point must be reprojected to the
- // insertion points of the parent's ShadowRoot.
- ShadowRoot* parentShadow = insertionPoint->GetParent()->GetShadowRoot();
- if (parentShadow) {
- parentShadow->DistributeSingleNode(aContent);
+ nsCOMArray<nsIContent>& matchedNodes = foundInsertionPoint->MatchedNodes();
+ // Find the appropriate position in the matched node list for the
+ // newly distributed content.
+ bool isIndexFound = false;
+ ExplicitChildIterator childIterator(GetHost());
+ for (uint32_t i = 0; i < matchedNodes.Length(); i++) {
+ // Seek through the host's explicit children until the inserted content
+ // is found or when the current matched node is reached.
+ if (childIterator.Seek(aContent, matchedNodes[i])) {
+ // aContent was found before the current matched node.
+ foundInsertionPoint->InsertMatchedNode(i, aContent);
+ isIndexFound = true;
+ break;
}
}
+
+ if (!isIndexFound) {
+ // We have still not found an index in the insertion point,
+ // thus it must be at the end.
+ MOZ_ASSERT(childIterator.Seek(aContent, nullptr),
+ "Trying to match a node that is not a candidate to be matched");
+ foundInsertionPoint->AppendMatchedNode(aContent);
+ }
+
+ return foundInsertionPoint;
}
-void
+const HTMLContentElement*
ShadowRoot::RemoveDistributedNode(nsIContent* aContent)
{
// Find insertion point containing the content and remove the node.
- for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
- if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) {
- // Removing the matched node may cause the insertion point to use
- // fallback content.
- if (mInsertionPoints[i]->MatchedNodes().Length() == 1 &&
- static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) {
- // Removing the matched node will cause fallback content to be
- // used instead. Give up optimization and distribute all nodes.
- DistributeAllNodes();
- return;
- }
+ for (HTMLContentElement* insertionPoint : mInsertionPoints) {
+ if (!insertionPoint->MatchedNodes().Contains(aContent)) {
+ continue;
+ }
- mInsertionPoints[i]->RemoveMatchedNode(aContent);
+ // Removing the matched node may cause the insertion point to use
+ // fallback content.
+ if (insertionPoint->MatchedNodes().Length() == 1 &&
+ insertionPoint->HasChildren()) {
+ // Removing the matched node will cause fallback content to be
+ // used instead. Give up optimization and distribute all nodes.
+ DistributeAllNodes();
+ return insertionPoint;
+ }
- // Handle the case where the parent of the insertion point has a ShadowRoot.
- // The removed node needs to be removed from the insertion points of the
- // parent's ShadowRoot.
- ShadowRoot* parentShadow = mInsertionPoints[i]->GetParent()->GetShadowRoot();
- if (parentShadow) {
- parentShadow->RemoveDistributedNode(aContent);
- }
+ insertionPoint->RemoveMatchedNode(aContent);
+ return insertionPoint;
+ }
- break;
- }
- }
+ return nullptr;
}
void
ShadowRoot::DistributeAllNodes()
{
// Create node pool.
nsTArray<nsIContent*> nodePool;
ExplicitChildIterator childIterator(GetHost());
for (nsIContent* content = childIterator.GetNextChild(); content;
content = childIterator.GetNextChild()) {
nodePool.AppendElement(content);
}
nsTArray<ShadowRoot*> shadowsToUpdate;
- for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
- mInsertionPoints[i]->ClearMatchedNodes();
+ for (HTMLContentElement* insertionPoint : mInsertionPoints) {
+ insertionPoint->ClearMatchedNodes();
// Assign matching nodes from node pool.
for (uint32_t j = 0; j < nodePool.Length(); j++) {
- if (mInsertionPoints[i]->Match(nodePool[j])) {
- mInsertionPoints[i]->AppendMatchedNode(nodePool[j]);
+ if (insertionPoint->Match(nodePool[j])) {
+ insertionPoint->AppendMatchedNode(nodePool[j]);
nodePool.RemoveElementAt(j--);
}
}
// Keep track of instances where the content insertion point is distributed
// (parent of insertion point has a ShadowRoot).
- nsIContent* insertionParent = mInsertionPoints[i]->GetParent();
+ nsIContent* insertionParent = insertionPoint->GetParent();
MOZ_ASSERT(insertionParent, "The only way for an insertion point to be in the"
"mInsertionPoints array is to be a descendant of a"
"ShadowRoot, in which case, it should have a parent");
// If the parent of the insertion point has a ShadowRoot, the nodes distributed
// to the insertion point must be reprojected to the insertion points of the
// parent's ShadowRoot.
ShadowRoot* parentShadow = insertionParent->GetShadowRoot();
if (parentShadow && !shadowsToUpdate.Contains(parentShadow)) {
shadowsToUpdate.AppendElement(parentShadow);
}
}
- for (uint32_t i = 0; i < shadowsToUpdate.Length(); i++) {
- shadowsToUpdate[i]->DistributeAllNodes();
+ for (ShadowRoot* shadow : shadowsToUpdate) {
+ shadow->DistributeAllNodes();
}
+
+ DistributionChanged();
}
void
ShadowRoot::GetInnerHTML(nsAString& aInnerHTML)
{
GetMarkup(false, aInnerHTML);
}
@@ -440,115 +455,163 @@ ShadowRoot::StyleSheets()
/**
* Returns whether the web components pool population algorithm
* on the host would contain |aContent|. This function ignores
* insertion points in the pool, thus should only be used to
* test nodes that have not yet been distributed.
*/
bool
-ShadowRoot::IsPooledNode(nsIContent* aContent, nsIContent* aContainer,
- nsIContent* aHost)
+ShadowRoot::IsPooledNode(nsIContent* aContent) const
{
if (nsContentUtils::IsContentInsertionPoint(aContent)) {
// Insertion points never end up in the pool.
return false;
}
- if (aContainer == aHost &&
- nsContentUtils::IsInSameAnonymousTree(aContainer, aContent)) {
+ auto* host = GetHost();
+ auto* container = aContent->GetParent();
+ if (container == host && !aContent->IsRootOfAnonymousSubtree()) {
// Children of the host will end up in the pool. We check to ensure
// that the content is in the same anonymous tree as the container
// because anonymous content may report its container as the host
// but it may not be in the host's child list.
return true;
}
- if (aContainer) {
+ if (auto* content = HTMLContentElement::FromContentOrNull(container)) {
// Fallback content will end up in pool if its parent is a child of the host.
- HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
- return content && content->IsInsertionPoint() &&
+ return content->IsInsertionPoint() &&
content->MatchedNodes().IsEmpty() &&
- aContainer->GetParentNode() == aHost;
+ container->GetParentNode() == host;
}
return false;
}
void
ShadowRoot::AttributeChanged(nsIDocument* aDocument,
Element* aElement,
int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue)
{
- if (!IsPooledNode(aElement, aElement->GetParent(), GetHost())) {
+ if (!IsPooledNode(aElement)) {
return;
}
// Attributes may change insertion point matching, find its new distribution.
- RemoveDistributedNode(aElement);
- DistributeSingleNode(aElement);
+ //
+ // FIXME(emilio): What about state changes?
+ if (!RedistributeElement(aElement)) {
+ return;
+ }
+
+ if (!aElement->IsInComposedDoc()) {
+ return;
+ }
+
+ auto* shell = OwnerDoc()->GetShell();
+ if (!shell) {
+ return;
+ }
+
+ shell->DestroyFramesFor(aElement);
+}
+
+bool
+ShadowRoot::RedistributeElement(Element* aElement)
+{
+ auto* oldInsertionPoint = RemoveDistributedNode(aElement);
+ auto* newInsertionPoint = DistributeSingleNode(aElement);
+
+ if (oldInsertionPoint == newInsertionPoint) {
+ if (oldInsertionPoint) {
+ if (auto* shadow = oldInsertionPoint->GetParent()->GetShadowRoot()) {
+ return shadow->RedistributeElement(aElement);
+ }
+ }
+
+ return false;
+ }
+
+ while (oldInsertionPoint) {
+ // Handle the case where the parent of the insertion point has a ShadowRoot.
+ // The node distributed into the insertion point must be reprojected to the
+ // insertion points of the parent's ShadowRoot.
+ auto* shadow = oldInsertionPoint->GetParent()->GetShadowRoot();
+ if (!shadow) {
+ break;
+ }
+
+ oldInsertionPoint = shadow->RemoveDistributedNode(aElement);
+ }
+
+ while (newInsertionPoint) {
+ // Handle the case where the parent of the insertion point has a ShadowRoot.
+ // The node distributed into the insertion point must be reprojected to the
+ // insertion points of the parent's ShadowRoot.
+ auto* shadow = newInsertionPoint->GetParent()->GetShadowRoot();
+ if (!shadow) {
+ break;
+ }
+
+ newInsertionPoint = shadow->DistributeSingleNode(aElement);
+ }
+
+ return true;
}
void
ShadowRoot::ContentAppended(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aFirstNewContent)
{
- if (mInsertionPointChanged) {
- DistributeAllNodes();
- mInsertionPointChanged = false;
- return;
- }
-
- // Watch for new nodes added to the pool because the node
- // may need to be added to an insertion point.
- nsIContent* currentChild = aFirstNewContent;
- while (currentChild) {
- // Add insertion point to destination insertion points of fallback content.
- if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
- HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
- if (content && content->MatchedNodes().IsEmpty()) {
- currentChild->DestInsertionPoints().AppendElement(aContainer);
- }
- }
-
- if (IsPooledNode(currentChild, aContainer, GetHost())) {
- DistributeSingleNode(currentChild);
- }
-
- currentChild = currentChild->GetNextSibling();
+ for (nsIContent* content = aFirstNewContent;
+ content;
+ content = content->GetNextSibling()) {
+ ContentInserted(aDocument, aContainer, aFirstNewContent);
}
}
void
ShadowRoot::ContentInserted(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild)
{
if (mInsertionPointChanged) {
DistributeAllNodes();
mInsertionPointChanged = false;
return;
}
+ // Add insertion point to destination insertion points of fallback content.
+ if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
+ HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
+ if (content && content->MatchedNodes().IsEmpty()) {
+ aChild->DestInsertionPoints().AppendElement(aContainer);
+ }
+ }
+
// Watch for new nodes added to the pool because the node
// may need to be added to an insertion point.
- if (IsPooledNode(aChild, aContainer, GetHost())) {
- // Add insertion point to destination insertion points of fallback content.
- if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
- HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
- if (content && content->MatchedNodes().IsEmpty()) {
- aChild->DestInsertionPoints().AppendElement(aContainer);
+ if (IsPooledNode(aChild)) {
+ auto* insertionPoint = DistributeSingleNode(aChild);
+ while (insertionPoint) {
+ // Handle the case where the parent of the insertion point has a ShadowRoot.
+ // The node distributed into the insertion point must be reprojected to the
+ // insertion points of the parent's ShadowRoot.
+ auto* parentShadow = insertionPoint->GetParent()->GetShadowRoot();
+ if (!parentShadow) {
+ break;
}
+
+ insertionPoint = parentShadow->DistributeSingleNode(aChild);
}
-
- DistributeSingleNode(aChild);
}
}
void
ShadowRoot::ContentRemoved(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
nsIContent* aPreviousSibling)
@@ -565,18 +628,31 @@ ShadowRoot::ContentRemoved(nsIDocument*
HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
if (content && content->MatchedNodes().IsEmpty()) {
aChild->DestInsertionPoints().Clear();
}
}
// Watch for node that is removed from the pool because
// it may need to be removed from an insertion point.
- if (IsPooledNode(aChild, aContainer, GetHost())) {
- RemoveDistributedNode(aChild);
+ if (IsPooledNode(aChild)) {
+ auto* insertionPoint = RemoveDistributedNode(aChild);
+ while (insertionPoint) {
+ // Handle the case where the parent of the insertion point has a
+ // ShadowRoot.
+ //
+ // The removed node needs to be removed from the insertion points of the
+ // parent's ShadowRoot.
+ auto* parentShadow = insertionPoint->GetParent()->GetShadowRoot();
+ if (!parentShadow) {
+ break;
+ }
+
+ insertionPoint = parentShadow->RemoveDistributedNode(aChild);
+ }
}
}
nsresult
ShadowRoot::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
bool aPreallocateChildren) const
{
*aResult = nullptr;
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -49,44 +49,67 @@ public:
void RemoveFromIdTable(Element* aElement, nsAtom* aId);
void InsertSheet(StyleSheet* aSheet, nsIContent* aLinkingContent);
void RemoveSheet(StyleSheet* aSheet);
bool ApplyAuthorStyles();
void SetApplyAuthorStyles(bool aApplyAuthorStyles);
StyleSheetList* StyleSheets();
/**
- * Distributes a single explicit child of the pool host to the content
- * insertion points in this ShadowRoot.
- */
- void DistributeSingleNode(nsIContent* aContent);
-
- /**
- * Removes a single explicit child of the pool host from the content
- * insertion points in this ShadowRoot.
- */
- void RemoveDistributedNode(nsIContent* aContent);
-
- /**
* Distributes all the explicit children of the pool host to the content
* insertion points in this ShadowRoot.
*/
void DistributeAllNodes();
+private:
+ /**
+ * Distributes a single explicit child of the pool host to the content
+ * insertion points in this ShadowRoot.
+ *
+ * Returns the insertion point the element is distributed to after this call.
+ *
+ * Note that this doesn't handle distributing the node in the insertion point
+ * parent's shadow root.
+ */
+ const HTMLContentElement* DistributeSingleNode(nsIContent* aContent);
+
+ /**
+ * Removes a single explicit child of the pool host from the content
+ * insertion points in this ShadowRoot.
+ *
+ * Returns the old insertion point, if any.
+ *
+ * Note that this doesn't handle removing the node in the returned insertion
+ * point parent's shadow root.
+ */
+ const HTMLContentElement* RemoveDistributedNode(nsIContent* aContent);
+
+ /**
+ * Redistributes a node of the pool, and returns whether the distribution
+ * changed.
+ */
+ bool RedistributeElement(Element*);
+
+ /**
+ * Called when we redistribute content after insertion points have changed.
+ */
+ void DistributionChanged();
+
+ bool IsPooledNode(nsIContent* aChild) const;
+
+public:
void AddInsertionPoint(HTMLContentElement* aInsertionPoint);
void RemoveInsertionPoint(HTMLContentElement* aInsertionPoint);
void SetInsertionPointChanged() { mInsertionPointChanged = true; }
void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; }
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
- static bool IsPooledNode(nsIContent* aChild, nsIContent* aContainer,
- nsIContent* aHost);
static ShadowRoot* FromNode(nsINode* aNode);
static void RemoveDestInsertionPoint(nsIContent* aInsertionPoint,
nsTArray<nsIContent*>& aDestInsertionPoints);
// WebIDL methods.
Element* GetElementById(const nsAString& aElementId);
already_AddRefed<nsContentList>
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -7830,17 +7830,17 @@ fail-if = (os == 'mac' && os_version ==
[generated/test_conformance__glsl__misc__non-ascii-comments.vert.html]
[generated/test_conformance__glsl__misc__non-ascii.vert.html]
[generated/test_conformance__glsl__misc__re-compile-re-link.html]
fail-if = (os == 'android' && android_version == '10')
[generated/test_conformance__glsl__misc__sequence-operator-returns-constant.html]
[generated/test_conformance__glsl__misc__shader-precision-format-obeyed.html]
[generated/test_conformance__glsl__misc__shader-struct-scope.html]
[generated/test_conformance__glsl__misc__shader-uniform-packing-restrictions.html]
-skip-if = (os == 'android')
+skip-if = (os == 'android') || (os == 'win' && os_version == '6.1' && debug)
[generated/test_conformance__glsl__misc__shader-varying-packing-restrictions.html]
[generated/test_conformance__glsl__misc__shader-with-256-character-define.html]
[generated/test_conformance__glsl__misc__shader-with-256-character-identifier.frag.html]
[generated/test_conformance__glsl__misc__shader-with-257-character-define.html]
[generated/test_conformance__glsl__misc__shader-with-257-character-identifier.frag.html]
[generated/test_conformance__glsl__misc__shader-with-_webgl-identifier.vert.html]
[generated/test_conformance__glsl__misc__shader-with-arbitrary-indexing.frag.html]
[generated/test_conformance__glsl__misc__shader-with-arbitrary-indexing.vert.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -528,17 +528,18 @@ fail-if = (os == 'android' && android_ve
skip-if = (os == 'android')
####################
# Timeouts
[generated/test_conformance__context__context-release-upon-reload.html]
skip-if = (os == 'android')
[generated/test_conformance__context__context-release-with-workers.html]
skip-if = (os == 'android')
[generated/test_conformance__glsl__misc__shader-uniform-packing-restrictions.html]
-skip-if = (os == 'android')
+# Frequent timeout on win7 debug.
+skip-if = (os == 'android') || (os == 'win' && os_version == '6.1' && debug)
[generated/test_conformance__glsl__bugs__complex-glsl-does-not-crash.html]
skip-if = (os == 'android')
[generated/test_conformance__glsl__misc__shader-with-non-reserved-words.html]
fail-if = (os == 'android')
# (TODO) Generates results after calling finish()
skip-if = 1
--- a/dom/html/HTMLContentElement.cpp
+++ b/dom/html/HTMLContentElement.cpp
@@ -242,28 +242,26 @@ HTMLContentElement::AfterSetAttr(int32_t
mValidSelector = false;
mSelectorList = nullptr;
break;
}
selectors = selectors->mNext;
}
}
- ShadowRoot* containingShadow = GetContainingShadow();
- if (containingShadow) {
+ if (ShadowRoot* containingShadow = GetContainingShadow()) {
containingShadow->DistributeAllNodes();
}
} else {
// The select attribute was removed. This insertion point becomes
// a universal selector.
mValidSelector = true;
mSelectorList = nullptr;
- ShadowRoot* containingShadow = GetContainingShadow();
- if (containingShadow) {
+ if (ShadowRoot* containingShadow = GetContainingShadow()) {
containingShadow->DistributeAllNodes();
}
}
}
return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
aOldValue, aSubjectPrincipal, aNotify);
}
--- a/dom/html/HTMLContentElement.h
+++ b/dom/html/HTMLContentElement.h
@@ -24,24 +24,17 @@ public:
explicit HTMLContentElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLContentElement,
nsGenericHTMLElement)
- static HTMLContentElement* FromContent(nsIContent* aContent)
- {
- if (aContent->IsHTMLContentElement()) {
- return static_cast<HTMLContentElement*>(aContent);
- }
-
- return nullptr;
- }
+ NS_IMPL_FROMCONTENT_HELPER(HTMLContentElement, IsHTMLContentElement())
virtual bool IsHTMLContentElement() const override { return true; }
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
bool aPreallocateChildren) const override;
virtual nsIDOMNode* AsDOMNode() override { return this; }
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1394,16 +1394,19 @@ public:
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("error"));
if (mOwner->ReadyState() == HAVE_NOTHING &&
aErrorCode == MEDIA_ERR_ABORTED) {
// https://html.spec.whatwg.org/multipage/embedded-content.html#media-data-processing-steps-list
// "If the media data fetching process is aborted by the user"
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("abort"));
mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
+ if (mOwner->mDecoder) {
+ mOwner->ShutdownDecoder();
+ }
} else if (aErrorCode == MEDIA_ERR_SRC_NOT_SUPPORTED) {
mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
} else {
mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
}
}
}
--- a/dom/html/test/test_fullscreen-api.html
+++ b/dom/html/test/test_fullscreen-api.html
@@ -27,17 +27,16 @@ SimpleTest.requestFlakyTimeout("untriage
// run in an iframe, which by default will not have the allowfullscreen
// attribute set, so full-screen won't work.
var gTestWindows = [
"file_fullscreen-multiple.html",
"file_fullscreen-rollback.html",
"file_fullscreen-esc-exit.html",
"file_fullscreen-denied.html",
"file_fullscreen-api.html",
- "file_fullscreen-plugins.html",
"file_fullscreen-hidden.html",
"file_fullscreen-svg-element.html",
"file_fullscreen-navigation.html",
"file_fullscreen-scrollbar.html",
"file_fullscreen-selector.html",
"file_fullscreen-top-layer.html",
"file_fullscreen-backdrop.html",
"file_fullscreen-nested.html",
@@ -63,16 +62,21 @@ function nextTest() {
var gLinuxE10sSkipList = [
{ "test": "file_fullscreen-plugins.html", "reason": "bug 1330553" },
{ "test": "file_fullscreen-api.html", "reason": "bug 1332040" },
{ "test": "file_fullscreen-scrollbar.html", "reason": "bug 1350875" }
];
function shouldSkipTest(test) {
+ if (SpecialPowers.Cc["@mozilla.org/gfx/info;1"].getService(SpecialPowers.Ci.nsIGfxInfo).isHeadless &&
+ test == "file_fullscreen-plugins.html") {
+ todo(false, `${test} skipped due to bug 1409805`);
+ return true;
+ }
if (!SpecialPowers.isMainProcess() &&
navigator.platform.indexOf('Linux') >= 0) {
for (let item of gLinuxE10sSkipList) {
if (item.test == test) {
todo(false, `${test} skipped due to ${item.reason}`);
return true;
}
}
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -250,19 +250,16 @@ var gPlayTests = [
{ name:"bug1377278.webm", type:"video/webm", duration:4.0 },
// Test playback of a WebM file with non-zero start time.
{ name:"split.webm", type:"video/webm", duration:1.967 },
// Test playback of a WebM file with resolution changes.
{ name:"resolution-change.webm", type:"video/webm", duration:6.533 },
- // Test playback of a raw file
- { name:"seek.yuv", type:"video/x-raw-yuv", duration:1.833 },
-
// A really short, low sample rate, single channel file. This tests whether
// we can handle playing files when only push very little audio data to the
// hardware.
{ name:"spacestorm-1000Hz-100ms.ogg", type:"audio/ogg", duration:0.099 },
// Opus data in an ogg container
{ name:"detodos-short.opus", type:"audio/ogg; codecs=opus", duration:0.22 },
// Opus data in a webm container
@@ -347,18 +344,16 @@ var gSeekToNextFrameTests = [
{ name:"multiple-bos.ogg", type:"video/ogg", duration:0.431 },
// Test playback/metadata work after a redirect
{ name:"redirect.sjs?domain=mochi.test:8888&file=320x240.ogv",
type:"video/ogg", duration:0.266 },
// Test playback of a webm file
{ name:"seek-short.webm", type:"video/webm", duration:0.23 },
// Test playback of a WebM file with non-zero start time.
{ name:"split.webm", type:"video/webm", duration:1.967 },
- // Test playback of a raw file
- { name:"seek.yuv", type:"video/x-raw-yuv", duration:1.833 },
{ name:"gizmo-short.mp4", type:"video/mp4", duration:0.27 },
// Test playback of a MP4 file with a non-zero start time (and audio starting
// a second later).
{ name:"bipbop-lateaudio.mp4", type:"video/mp4" },
];
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -539,17 +539,16 @@ support-files =
seek.ogv
seek.ogv^headers^
seek-short.ogv
seek-short.ogv^headers^
seek.webm
seek.webm^headers^
seek-short.webm
seek-short.webm^headers^
- seek.yuv
seek_support.js
seekLies.sjs
seek_with_sound.ogg^headers^
sequential.vtt
short-cenc.mp4
sine.webm
sine.webm^headers^
sintel-short-clearkey-subsample-encrypted-audio.webm
deleted file mode 100644
index 69485e8e1bd15c06f7e93ae389e393238c266e63..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -46,12 +46,12 @@ skip-if = true # disabled - See bug 1390
[test_template.html]
[test_template_xhtml.html]
[test_template_custom_elements.html]
[test_shadowroot.html]
[test_shadowroot_inert_element.html]
[test_shadowroot_style.html]
[test_shadowroot_style_order.html]
[test_style_fallback_content.html]
-skip-if = stylo # bug 1409088
+skip-if = stylo # Bug 1410170
[test_unresolved_pseudo_class.html]
[test_link_prefetch.html]
[test_bug1269155.html]
--- a/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
@@ -8,16 +8,21 @@ https://bugzilla.mozilla.org/show_bug.cg
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<div class="tall" id="bodydiv"></div>
<div id="container"></div>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
<script>
+
+if (!SpecialPowers.DOMWindowUtils.isStyledByServo) {
+ SimpleTest.expectAssertions(3, 3); // GeckoRestyleManager stuff.
+}
+
// Create ShadowRoot.
var container = document.getElementById("container");
var elem = document.createElement("div");
container.appendChild(elem); // Put ShadowRoot host in document.
var root = elem.createShadowRoot();
// A style element that will be appended into the ShadowRoot.
var shadowStyle = document.createElement("style");
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -208,24 +208,25 @@ WebRenderCommandBuilder::CreateWebRender
// If we're going to create a new layer data for this item, stash the
// ASR so that if we recurse into a sublist they will know where to stop
// walking up their ASR chain when building scroll metadata.
if (forceNewLayerData) {
mAsrStack.push_back(asr);
}
}
- // ensure the scope of ScrollingLayersHelper is maintained
- ScrollingLayersHelper clip(item, aBuilder, aSc, mClipIdCache, apzEnabled);
+ { // ensure the scope of ScrollingLayersHelper is maintained
+ ScrollingLayersHelper clip(item, aBuilder, aSc, mClipIdCache, apzEnabled);
- // Note: this call to CreateWebRenderCommands can recurse back into
- // this function if the |item| is a wrapper for a sublist.
- if (!item->CreateWebRenderCommands(aBuilder, aResources, aSc, mManager,
- aDisplayListBuilder)) {
- PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
+ // Note: this call to CreateWebRenderCommands can recurse back into
+ // this function if the |item| is a wrapper for a sublist.
+ if (!item->CreateWebRenderCommands(aBuilder, aResources, aSc, mManager,
+ aDisplayListBuilder)) {
+ PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
+ }
}
if (apzEnabled) {
if (forceNewLayerData) {
// Pop the thing we pushed before the recursion, so the topmost item on
// the stack is enclosing display item's ASR (or the stack is empty)
mAsrStack.pop_back();
const ActiveScrolledRoot* stopAtAsr =
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -247,19 +247,21 @@ WebRenderLayerManager::EndTransactionWit
nsDisplayListBuilder* aDisplayListBuilder)
{
MOZ_ASSERT(aDisplayList && aDisplayListBuilder);
WrBridge()->RemoveExpiredFontKeys();
AUTO_PROFILER_TRACING("Paint", "RenderLayers");
mTransactionIncomplete = false;
- if (gfxPrefs::LayersDump()) {
- this->Dump();
- }
+#if 0
+ // Useful for debugging, it dumps the display list *before* we try to build
+ // WR commands from it
+ nsFrame::PrintDisplayList(aDisplayListBuilder, *aDisplayList);
+#endif
// Since we don't do repeat transactions right now, just set the time
mAnimationReadyTime = TimeStamp::Now();
WrBridge()->BeginTransaction();
DiscardCompositorAnimations();
LayoutDeviceIntSize size = mWidget->GetClientSize();
--- a/ipc/chromium/src/base/process_util_mac.mm
+++ b/ipc/chromium/src/base/process_util_mac.mm
@@ -8,16 +8,17 @@
#include <fcntl.h>
#include <spawn.h>
#include <sys/wait.h>
#include <string>
#include "base/eintr_wrapper.h"
#include "base/logging.h"
+#include "mozilla/ScopeExit.h"
namespace {
static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
} // namespace
namespace base {
@@ -37,42 +38,40 @@ bool LaunchApp(const std::vector<std::st
bool retval = true;
char* argv_copy[argv.size() + 1];
for (size_t i = 0; i < argv.size(); i++) {
argv_copy[i] = const_cast<char*>(argv[i].c_str());
}
argv_copy[argv.size()] = NULL;
- // Make sure we don't leak any FDs to the child process by marking all FDs
- // as close-on-exec.
- SetAllFDsToCloseOnExec();
-
EnvironmentArray vars = BuildEnvironmentArray(env_vars_to_set);
posix_spawn_file_actions_t file_actions;
if (posix_spawn_file_actions_init(&file_actions) != 0) {
return false;
}
+ auto file_actions_guard = mozilla::MakeScopeExit([&file_actions] {
+ posix_spawn_file_actions_destroy(&file_actions);
+ });
// Turn fds_to_remap array into a set of dup2 calls.
for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
it != fds_to_remap.end();
++it) {
int src_fd = it->first;
int dest_fd = it->second;
if (src_fd == dest_fd) {
int flags = fcntl(src_fd, F_GETFD);
if (flags != -1) {
fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC);
}
} else {
if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) {
- posix_spawn_file_actions_destroy(&file_actions);
return false;
}
}
}
// Set up the CPU preference array.
cpu_type_t cpu_types[1];
switch (arch) {
@@ -90,38 +89,50 @@ bool LaunchApp(const std::vector<std::st
break;
}
// Initialize spawn attributes.
posix_spawnattr_t spawnattr;
if (posix_spawnattr_init(&spawnattr) != 0) {
return false;
}
+ auto spawnattr_guard = mozilla::MakeScopeExit([&spawnattr] {
+ posix_spawnattr_destroy(&spawnattr);
+ });
// Set spawn attributes.
size_t attr_count = 1;
size_t attr_ocount = 0;
if (posix_spawnattr_setbinpref_np(&spawnattr, attr_count, cpu_types, &attr_ocount) != 0 ||
attr_ocount != attr_count) {
- posix_spawnattr_destroy(&spawnattr);
+ return false;
+ }
+
+ // Prevent the child process from inheriting any file descriptors
+ // that aren't named in `file_actions`. (This is an Apple-specific
+ // extension to posix_spawn.)
+ if (posix_spawnattr_setflags(&spawnattr, POSIX_SPAWN_CLOEXEC_DEFAULT) != 0) {
return false;
}
+ // Exempt std{in,out,err} from being closed by POSIX_SPAWN_CLOEXEC_DEFAULT.
+ for (int fd = 0; fd <= STDERR_FILENO; ++fd) {
+ if (posix_spawn_file_actions_addinherit_np(&file_actions, fd) != 0) {
+ return false;
+ }
+ }
+
int pid = 0;
int spawn_succeeded = (posix_spawnp(&pid,
argv_copy[0],
&file_actions,
&spawnattr,
argv_copy,
vars.get()) == 0);
- posix_spawn_file_actions_destroy(&file_actions);
-
- posix_spawnattr_destroy(&spawnattr);
-
bool process_handle_valid = pid > 0;
if (!spawn_succeeded || !process_handle_valid) {
retval = false;
} else {
gProcessLog.print("==> process %d launched child process %d\n",
GetCurrentProcId(), pid);
if (wait)
HANDLE_EINTR(waitpid(pid, 0, 0));
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -1202,19 +1202,40 @@ ServoRestyleManager::DoProcessPendingRes
styleSet->MaybeGCRuleTree();
// Note: We are in the scope of |animationsWithDestroyedFrame|, so
// |mAnimationsWithDestroyedFrame| is still valid.
MOZ_ASSERT(mAnimationsWithDestroyedFrame);
mAnimationsWithDestroyedFrame->StopAnimationsForElementsWithoutFrames();
}
+#ifdef DEBUG
+static void
+VerifyFlatTree(const nsIContent& aContent)
+{
+ StyleChildrenIterator iter(&aContent);
+
+ for (auto* content = iter.GetNextChild();
+ content;
+ content = iter.GetNextChild()) {
+ MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle() == &aContent);
+ VerifyFlatTree(*content);
+ }
+}
+#endif
+
void
ServoRestyleManager::ProcessPendingRestyles()
{
+#ifdef DEBUG
+ if (auto* root = mPresContext->Document()->GetRootElement()) {
+ VerifyFlatTree(*root);
+ }
+#endif
+
DoProcessPendingRestyles(ServoTraversalFlags::Empty);
}
void
ServoRestyleManager::ProcessAllPendingAttributeAndStateInvalidations()
{
if (mSnapshots.IsEmpty()) {
return;
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1404789-1.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<script>
+ // Test for content redistribution outside of the document.
+ // Passes if it doesn't assert.
+ let host = document.createElement('div');
+ host.innerHTML = "<div id='foo'></div>";
+
+ let shadowRoot = host.createShadowRoot();
+ shadowRoot.innerHTML = "<content select='#foo'></content>";
+
+ host.firstElementChild.removeAttribute('id');
+
+ // Move to the document, do the same.
+ document.documentElement.appendChild(host);
+ host.firstElementChild.setAttribute('id', 'foo');
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1404789-2.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<iframe style="display: none" src="1404789-1.html"></iframe>
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1409088.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<div></div>
+<script>
+let host = document.querySelector('div');
+let shadow = host.createShadowRoot();
+
+// Append two divs and three spans into host.
+host.innerHTML = '<div></div><span></span><div></div><span></span><span></span>';
+shadow.innerHTML = '<content select="div" id="divpoint"></content><content select="div, span" id="allpoint"></content>';
+
+let divPoint = shadow.getElementById("divpoint");
+let allPoint = shadow.getElementById("allpoint");
+
+shadow.removeChild(allPoint);
+shadow.insertBefore(allPoint, divPoint);
+</script>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -502,10 +502,13 @@ load 1397398-1.html
load 1397398-2.html
load 1397398-3.html
load 1398500.html
load 1400438-1.html
load 1400599-1.html
load 1401739.html
load 1401840.html
load 1402476.html
+load 1404789-1.html
+load 1404789-2.html
load 1406562.html
+load 1409088.html
load 1409147.html
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -7262,16 +7262,23 @@ nsCSSFrameConstructor::CheckBitsForLazyF
"constructed lazily should have frames");
NS_ASSERTION(!needsFrameBitSet, "Ancestors of nodes with frames to be "
"constructed lazily should not have NEEDS_FRAME bit set");
}
#endif
// For inserts aChild should be valid, for appends it should be null.
// Returns true if this operation can be lazy, false if not.
+//
+// FIXME(emilio, bug 1410020): This function assumes that the flattened tree
+// parent of all the appended children is the same, which, afaict, is not
+// necessarily true.
+//
+// But we disable lazy frame construction for shadow trees... We should fix
+// that, too.
bool
nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation,
nsIContent* aContainer,
nsIContent* aChild)
{
// XXXmats no lazy frames for display:contents direct descendants yet
// (bug 979782).
if (mPresShell->GetPresContext()->IsChrome() || !aContainer ||
@@ -7296,16 +7303,22 @@ nsCSSFrameConstructor::MaybeConstructLaz
if (child->IsXULElement()) {
return false;
}
}
}
// We can construct lazily; just need to set suitable bits in the content
// tree.
+ nsIContent* parent = aChild->GetFlattenedTreeParent();
+ if (!parent) {
+ // Not part of the flat tree, nothing to do.
+ return true;
+ }
+
// Set NODE_NEEDS_FRAME on the new nodes.
if (aOperation == CONTENTINSERT) {
NS_ASSERTION(!aChild->GetPrimaryFrame() ||
aChild->GetPrimaryFrame()->GetContent() != aChild,
//XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
// check is needed due to bug 135040. Remove it once that's
// fixed.
@@ -7320,17 +7333,16 @@ nsCSSFrameConstructor::MaybeConstructLaz
// fixed.
"setting NEEDS_FRAME on a node that already has a frame?");
child->SetFlags(NODE_NEEDS_FRAME);
}
}
// Walk up the tree setting the NODE_DESCENDANTS_NEED_FRAMES bit as we go.
// We need different handling for servo given the scoped restyle roots.
- nsIContent* parent = aChild->GetFlattenedTreeParent();
CheckBitsForLazyFrameConstruction(parent);
if (mozilla::GeckoRestyleManager* geckoRM = RestyleManager()->GetAsGecko()) {
while (parent && !parent->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
parent->SetFlags(NODE_DESCENDANTS_NEED_FRAMES);
parent = parent->GetFlattenedTreeParent();
}
geckoRM->PostRestyleEventForLazyConstruction();
@@ -7639,64 +7651,45 @@ nsCSSFrameConstructor::ContentAppended(n
// Just ignore tree tags, anyway we don't create any frames for them.
if (tag == nsGkAtoms::treechildren ||
tag == nsGkAtoms::treeitem ||
tag == nsGkAtoms::treerow)
return;
}
#endif // MOZ_XUL
- bool isNewShadowTreeContent =
- aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE) &&
- !aContainer->IsInNativeAnonymousSubtree() &&
- !aFirstNewContent->IsInNativeAnonymousSubtree();
-
- if (!isNewShadowTreeContent) {
- // See comment in ContentRangeInserted for why this is necessary.
- if (!GetContentInsertionFrameFor(aContainer) &&
- !aContainer->IsActiveChildrenElement()) {
- // We're punting on frame construction because there's no container frame.
- // The Servo-backed style system handles this case like the lazy frame
- // construction case, except when we're already constructing frames, in
- // which case we shouldn't need to do anything else.
- if (aContainer->IsStyledByServo() &&
- aInsertionKind == InsertionKind::Async) {
- LazilyStyleNewChildRange(aFirstNewContent, nullptr);
- }
- return;
- }
-
- if (aInsertionKind == InsertionKind::Async &&
- MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
- if (aContainer->IsStyledByServo()) {
- LazilyStyleNewChildRange(aFirstNewContent, nullptr);
- }
- return;
- }
+ // See comment in ContentRangeInserted for why this is necessary.
+ if (!GetContentInsertionFrameFor(aContainer) &&
+ !aContainer->IsActiveChildrenElement()) {
+ // We're punting on frame construction because there's no container frame.
+ // The Servo-backed style system handles this case like the lazy frame
+ // construction case, except when we're already constructing frames, in
+ // which case we shouldn't need to do anything else.
+ if (aContainer->IsStyledByServo() &&
+ aInsertionKind == InsertionKind::Async) {
+ LazilyStyleNewChildRange(aFirstNewContent, nullptr);
+ }
+ return;
+ }
+
+ if (aInsertionKind == InsertionKind::Async &&
+ MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
+ if (aContainer->IsStyledByServo()) {
+ LazilyStyleNewChildRange(aFirstNewContent, nullptr);
+ }
+ return;
}
// We couldn't construct lazily. Make Servo eagerly traverse the new content
// if needed (when aInsertionKind == InsertionKind::Sync, we know that the
// styles are up-to-date already).
if (aInsertionKind == InsertionKind::Async && aContainer->IsStyledByServo()) {
StyleNewChildRange(aFirstNewContent, nullptr);
}
- if (isNewShadowTreeContent) {
- // Recreate frames if content is appended into a ShadowRoot
- // because children of ShadowRoot are rendered in place of children
- // of the host.
- //XXXsmaug This is super unefficient!
- nsIContent* bindingParent = aContainer->GetBindingParent();
- LAYOUT_PHASE_TEMP_EXIT();
- RecreateFramesForContent(bindingParent, aInsertionKind);
- LAYOUT_PHASE_TEMP_REENTER();
- return;
- }
-
LAYOUT_PHASE_TEMP_EXIT();
InsertionPoint insertion =
GetRangeInsertionPoint(aContainer, aFirstNewContent, nullptr,
aInsertionKind);
nsContainerFrame*& parentFrame = insertion.mParentFrame;
LAYOUT_PHASE_TEMP_REENTER();
if (!parentFrame) {
return;
@@ -7787,18 +7780,20 @@ nsCSSFrameConstructor::ContentAppended(n
}
// Create some new frames
//
// We use the provided tree match context, or create a new one on the fly
// otherwise.
Maybe<TreeMatchContext> matchContext;
if (!aProvidedTreeMatchContext && !aContainer->IsStyledByServo()) {
+ // We use GetParentElementCrossingShadowRoot to handle the case where
+ // aContainer is a ShadowRoot.
matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
- matchContext->InitAncestors(aContainer->AsElement());
+ matchContext->InitAncestors(aFirstNewContent->GetParentElementCrossingShadowRoot());
}
nsFrameConstructorState state(mPresShell,
matchContext.ptrOr(aProvidedTreeMatchContext),
GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
GetAbsoluteContainingBlock(parentFrame, ABS_POS),
containingBlock);
LayoutFrameType frameType = parentFrame->Type();
@@ -8124,71 +8119,55 @@ nsCSSFrameConstructor::ContentRangeInser
accService->ContentRangeInserted(mPresShell, aContainer,
aStartChild, aEndChild);
}
#endif
return;
}
- bool isNewShadowTreeContent =
- aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE) &&
- !aContainer->IsInNativeAnonymousSubtree() &&
- (!aStartChild || !aStartChild->IsInNativeAnonymousSubtree()) &&
- (!aEndChild || !aEndChild->IsInNativeAnonymousSubtree());
-
- if (!isNewShadowTreeContent) {
- nsContainerFrame* parentFrame = GetContentInsertionFrameFor(aContainer);
- // The xbl:children element won't have a frame, but default content can have the children as
- // a parent. While its uncommon to change the structure of the default content itself, a label,
- // for example, can be reframed by having its value attribute set or removed.
- if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
- // We're punting on frame construction because there's no container frame.
- // The Servo-backed style system handles this case like the lazy frame
- // construction case, except when we're already constructing frames, in
- // which case we shouldn't need to do anything else.
- if (aContainer->IsStyledByServo() &&
- aInsertionKind == InsertionKind::Async) {
- LazilyStyleNewChildRange(aStartChild, aEndChild);
- }
- return;
- }
-
- // Otherwise, we've got parent content. Find its frame.
- NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer ||
- GetDisplayContentsStyleFor(aContainer), "New XBL code is possibly wrong!");
-
- if (aInsertionKind == InsertionKind::Async &&
- MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
- if (aContainer->IsStyledByServo()) {
- LazilyStyleNewChildRange(aStartChild, aEndChild);
- }
- return;
- }
+ nsContainerFrame* parentFrame = GetContentInsertionFrameFor(aContainer);
+ // The xbl:children element won't have a frame, but default content can have the children as
+ // a parent. While its uncommon to change the structure of the default content itself, a label,
+ // for example, can be reframed by having its value attribute set or removed.
+ if (!parentFrame &&
+ !(aContainer->IsActiveChildrenElement() ||
+ ShadowRoot::FromNode(aContainer))) {
+ // We're punting on frame construction because there's no container frame.
+ // The Servo-backed style system handles this case like the lazy frame
+ // construction case, except when we're already constructing frames, in
+ // which case we shouldn't need to do anything else.
+ if (aContainer->IsStyledByServo() &&
+ aInsertionKind == InsertionKind::Async) {
+ LazilyStyleNewChildRange(aStartChild, aEndChild);
+ }
+ return;
+ }
+
+ MOZ_ASSERT_IF(ShadowRoot::FromNode(aContainer), !parentFrame);
+
+ // Otherwise, we've got parent content. Find its frame.
+ NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer ||
+ GetDisplayContentsStyleFor(aContainer), "New XBL code is possibly wrong!");
+
+ if (aInsertionKind == InsertionKind::Async &&
+ MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
+ if (aContainer->IsStyledByServo()) {
+ LazilyStyleNewChildRange(aStartChild, aEndChild);
+ }
+ return;
}
// We couldn't construct lazily. Make Servo eagerly traverse the new content
// if needed (when aInsertionKind == InsertionKind::Sync, we know that the
// styles are up-to-date already).
if (aInsertionKind == InsertionKind::Async && aContainer->IsStyledByServo()) {
StyleNewChildRange(aStartChild, aEndChild);
}
- if (isNewShadowTreeContent) {
- // Recreate frames if content is inserted into a ShadowRoot
- // because children of ShadowRoot are rendered in place of
- // the children of the host.
- //XXXsmaug This is super unefficient!
- nsIContent* bindingParent = aContainer->GetBindingParent();
- LAYOUT_PHASE_TEMP_EXIT();
- RecreateFramesForContent(bindingParent, aInsertionKind);
- LAYOUT_PHASE_TEMP_REENTER();
- return;
- }
-
InsertionPoint insertion;
if (isSingleInsert) {
// See if we have an XBL insertion point. If so, then that's our
// real parent frame; if not, then the frame hasn't been built yet
// and we just bail.
insertion = GetInsertionPoint(aContainer, aStartChild);
} else {
// Get our insertion point. If we need to issue single ContentInserted's
@@ -8273,18 +8252,20 @@ nsCSSFrameConstructor::ContentRangeInser
RecreateFramesForContent(insertion.mParentFrame->GetContent(),
aInsertionKind);
LAYOUT_PHASE_TEMP_REENTER();
return;
}
Maybe<TreeMatchContext> matchContext;
if (!aProvidedTreeMatchContext && !aContainer->IsStyledByServo()) {
+ // We use GetParentElementCrossingShadowRoot to handle the case where
+ // aContainer is a ShadowRoot.
matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
- matchContext->InitAncestors(aContainer ? aContainer->AsElement() : nullptr);
+ matchContext->InitAncestors(aStartChild->GetParentElementCrossingShadowRoot());
}
nsFrameConstructorState state(mPresShell,
matchContext.ptrOr(aProvidedTreeMatchContext),
GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
GetFloatContainingBlock(insertion.mParentFrame),
do_AddRef(aFrameState));
@@ -8743,30 +8724,16 @@ nsCSSFrameConstructor::ContentRemoved(ns
if (firstChild && firstChild->GetContent() == aChild) {
isRoot = true;
childFrame = firstChild;
NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
}
}
}
- if (aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE) &&
- !aContainer->IsInNativeAnonymousSubtree() &&
- !aChild->IsInNativeAnonymousSubtree()) {
- // Recreate frames if content is removed from a ShadowRoot because it may
- // contain an insertion point which can change how the host is rendered.
- //
- // XXXsmaug This is super unefficient!
- nsIContent* bindingParent = aContainer->GetBindingParent();
- LAYOUT_PHASE_TEMP_EXIT();
- RecreateFramesForContent(bindingParent, InsertionKind::Async);
- LAYOUT_PHASE_TEMP_REENTER();
- return true;
- }
-
if (childFrame) {
InvalidateCanvasIfNeeded(mPresShell, aChild);
// See whether we need to remove more than just childFrame
LAYOUT_PHASE_TEMP_EXIT();
if (MaybeRecreateContainerForFrameRemoval(childFrame)) {
LAYOUT_PHASE_TEMP_REENTER();
return true;
@@ -9572,18 +9539,21 @@ nsCSSFrameConstructor::GetInsertionPoint
// We've got an explicit insertion child. Check to see if it's
// anonymous.
if (aChild->GetBindingParent() == aContainer) {
// This child content is anonymous. Don't use the insertion
// point, since that's only for the explicit kids.
return InsertionPoint(GetContentInsertionFrameFor(aContainer), aContainer);
}
- if (nsContentUtils::HasDistributedChildren(aContainer)) {
- // The container distributes nodes, use the frame of the flattened tree parent.
+ if (nsContentUtils::HasDistributedChildren(aContainer) ||
+ ShadowRoot::FromNode(aContainer)) {
+ // The container distributes nodes or is a shadow root, use the frame of
+ // the flattened tree parent.
+ //
// It may be the case that the node is distributed but not matched to any
// insertion points, so there is no flattened parent.
nsIContent* flattenedParent = aChild->GetFlattenedTreeParent();
if (flattenedParent) {
return InsertionPoint(GetContentInsertionFrameFor(flattenedParent),
flattenedParent);
}
return InsertionPoint();
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -219,17 +219,17 @@ public:
, mText(text)
, mFontMetrics(fm)
, mPoint(point)
, mListStyleType(listStyleType)
{
MOZ_ASSERT(IsTextType());
}
- void
+ bool
CreateWebRenderCommands(nsDisplayItem* aItem,
wr::DisplayListBuilder& aBuilder,
wr::IpcResourceUpdateQueue& aResources,
const layers::StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder);
DrawResult
@@ -264,37 +264,42 @@ public:
mListStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN &&
mListStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED &&
!mText.IsEmpty();
}
bool
BuildGlyphForText(nsDisplayItem* aItem, bool disableSubpixelAA);
+ void
+ PaintTextToContext(nsIFrame* aFrame,
+ gfxContext* aCtx,
+ bool aDisableSubpixelAA);
+
bool
IsImageContainerAvailable(layers::LayerManager* aManager, uint32_t aFlags);
private:
- void
+ bool
CreateWebRenderCommandsForImage(nsDisplayItem* aItem,
wr::DisplayListBuilder& aBuilder,
wr::IpcResourceUpdateQueue& aResources,
const layers::StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder);
- void
+ bool
CreateWebRenderCommandsForPath(nsDisplayItem* aItem,
wr::DisplayListBuilder& aBuilder,
wr::IpcResourceUpdateQueue& aResources,
const layers::StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder);
- void
+ bool
CreateWebRenderCommandsForText(nsDisplayItem* aItem,
wr::DisplayListBuilder& aBuilder,
wr::IpcResourceUpdateQueue& aResources,
const layers::StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder);
private:
@@ -317,34 +322,34 @@ private:
nsPoint mPoint;
RefPtr<ScaledFont> mFont;
nsTArray<layers::GlyphArray> mGlyphs;
// Store the type of list-style-type.
int32_t mListStyleType;
};
-void
+bool
BulletRenderer::CreateWebRenderCommands(nsDisplayItem* aItem,
wr::DisplayListBuilder& aBuilder,
wr::IpcResourceUpdateQueue& aResources,
const layers::StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder)
{
if (IsImageType()) {
- CreateWebRenderCommandsForImage(aItem, aBuilder, aResources,
+ return CreateWebRenderCommandsForImage(aItem, aBuilder, aResources,
aSc, aManager, aDisplayListBuilder);
} else if (IsPathType()) {
- CreateWebRenderCommandsForPath(aItem, aBuilder, aResources,
+ return CreateWebRenderCommandsForPath(aItem, aBuilder, aResources,
aSc, aManager, aDisplayListBuilder);
} else {
MOZ_ASSERT(IsTextType());
- CreateWebRenderCommandsForText(aItem, aBuilder, aResources, aSc,
- aManager, aDisplayListBuilder);
+ return CreateWebRenderCommandsForText(aItem, aBuilder, aResources, aSc,
+ aManager, aDisplayListBuilder);
}
}
DrawResult
BulletRenderer::Paint(gfxContext& aRenderingContext, nsPoint aPt,
const nsRect& aDirtyRect, uint32_t aFlags,
bool aDisableSubpixelAA, nsIFrame* aFrame)
{
@@ -372,28 +377,17 @@ BulletRenderer::Paint(gfxContext& aRende
drawTarget->Fill(mPath, ColorPattern(ToDeviceColor(mColor)));
break;
default:
MOZ_CRASH("unreachable");
}
}
if (IsTextType()) {
- DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
- DrawTargetAutoDisableSubpixelAntialiasing
- disable(drawTarget, aDisableSubpixelAA);
-
- aRenderingContext.SetColor(Color::FromABGR(mColor));
-
- nsPresContext* presContext = aFrame->PresContext();
- if (!presContext->BidiEnabled() && HasRTLChars(mText)) {
- presContext->SetBidiEnabled();
- }
- nsLayoutUtils::DrawString(aFrame, *mFontMetrics, &aRenderingContext,
- mText.get(), mText.Length(), mPoint);
+ PaintTextToContext(aFrame, &aRenderingContext, aDisableSubpixelAA);
}
return DrawResult::SUCCESS;
}
bool
BulletRenderer::BuildGlyphForText(nsDisplayItem* aItem, bool disableSubpixelAA)
{
@@ -402,31 +396,17 @@ BulletRenderer::BuildGlyphForText(nsDisp
RefPtr<DrawTarget> screenTarget = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
RefPtr<DrawTargetCapture> capture =
Factory::CreateCaptureDrawTarget(screenTarget->GetBackendType(),
IntSize(),
screenTarget->GetFormat());
RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(capture);
- {
- DrawTargetAutoDisableSubpixelAntialiasing
- disable(capture, disableSubpixelAA);
-
- captureCtx->SetColor(
- Color::FromABGR(mColor));
-
- nsPresContext* presContext = aItem->Frame()->PresContext();
- if (!presContext->BidiEnabled() && HasRTLChars(mText)) {
- presContext->SetBidiEnabled();
- }
-
- nsLayoutUtils::DrawString(aItem->Frame(), *mFontMetrics, captureCtx,
- mText.get(), mText.Length(), mPoint);
- }
+ PaintTextToContext(aItem->Frame(), captureCtx, disableSubpixelAA);
layers::GlyphArray* g = mGlyphs.AppendElement();
std::vector<Glyph> glyphs;
Color color;
if (!capture->ContainsOnlyColoredGlyphs(mFont, color, glyphs)) {
mFont = nullptr;
mGlyphs.Clear();
return false;
@@ -434,116 +414,132 @@ BulletRenderer::BuildGlyphForText(nsDisp
g->glyphs().SetLength(glyphs.size());
PodCopy(g->glyphs().Elements(), glyphs.data(), glyphs.size());
g->color() = color;
return true;
}
+void
+BulletRenderer::PaintTextToContext(nsIFrame* aFrame,
+ gfxContext* aCtx,
+ bool aDisableSubpixelAA)
+{
+ MOZ_ASSERT(IsTextType());
+
+ DrawTarget* drawTarget = aCtx->GetDrawTarget();
+ DrawTargetAutoDisableSubpixelAntialiasing
+ disable(drawTarget, aDisableSubpixelAA);
+
+ aCtx->SetColor(Color::FromABGR(mColor));
+
+ nsPresContext* presContext = aFrame->PresContext();
+ if (!presContext->BidiEnabled() && HasRTLChars(mText)) {
+ presContext->SetBidiEnabled();
+ }
+ nsLayoutUtils::DrawString(aFrame, *mFontMetrics, aCtx,
+ mText.get(), mText.Length(), mPoint);
+}
+
bool
BulletRenderer::IsImageContainerAvailable(layers::LayerManager* aManager, uint32_t aFlags)
{
MOZ_ASSERT(IsImageType());
return mImage->IsImageContainerAvailable(aManager, aFlags);
}
-void
+bool
BulletRenderer::CreateWebRenderCommandsForImage(nsDisplayItem* aItem,
wr::DisplayListBuilder& aBuilder,
wr::IpcResourceUpdateQueue& aResources,
const layers::StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder)
{
- MOZ_ASSERT(IsImageType());
-
- if (!mImage) {
- return;
- }
+ MOZ_RELEASE_ASSERT(IsImageType());
+ MOZ_RELEASE_ASSERT(mImage);
uint32_t flags = aDisplayListBuilder->ShouldSyncDecodeImages() ?
imgIContainer::FLAG_SYNC_DECODE :
imgIContainer::FLAG_NONE;
+ // FIXME: is this check always redundant with the next one?
+ if (IsImageContainerAvailable(aManager, flags)) {
+ return false;
+ }
+
RefPtr<layers::ImageContainer> container =
mImage->GetImageContainer(aManager, flags);
if (!container) {
- return;
+ return false;
}
gfx::IntSize size;
Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(aItem, container, aBuilder, aResources,
aSc, size, Nothing());
if (key.isNothing()) {
- return;
+ return true; // Nothing to do
}
const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(mDest, appUnitsPerDevPixel);
wr::LayoutRect dest = aSc.ToRelativeLayoutRect(destRect);
aBuilder.PushImage(dest,
dest,
!aItem->BackfaceIsHidden(),
wr::ImageRendering::Auto,
key.value());
+
+ return true;
}
-void
+bool
BulletRenderer::CreateWebRenderCommandsForPath(nsDisplayItem* aItem,
wr::DisplayListBuilder& aBuilder,
wr::IpcResourceUpdateQueue& aResources,
const layers::StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder)
{
MOZ_ASSERT(IsPathType());
if (!aManager->CommandBuilder().PushItemAsImage(aItem, aBuilder, aResources, aSc, aDisplayListBuilder)) {
NS_WARNING("Fail to create WebRender commands for Bullet path.");
+ return false;
}
+
+ return true;
}
-void
+bool
BulletRenderer::CreateWebRenderCommandsForText(nsDisplayItem* aItem,
wr::DisplayListBuilder& aBuilder,
wr::IpcResourceUpdateQueue& aResources,
const layers::StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder)
{
MOZ_ASSERT(IsTextType());
- MOZ_ASSERT(mFont);
- MOZ_ASSERT(!mGlyphs.IsEmpty());
- const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
bool dummy;
- LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(
- aItem->GetBounds(aDisplayListBuilder, &dummy), appUnitsPerDevPixel);
- wr::LayoutRect wrDestRect = aSc.ToRelativeLayoutRect(destRect);
-
- nsTArray<wr::GlyphInstance> wrGlyphs;
+ nsRect bounds = aItem->GetBounds(aDisplayListBuilder, &dummy);
- for (layers::GlyphArray& glyphArray : mGlyphs) {
- const auto& glyphs = glyphArray.glyphs();
- wrGlyphs.SetLength(glyphs.Length());
+ if (bounds.IsEmpty()) {
+ return true;
+ }
- for (size_t j = 0; j < glyphs.Length(); j++) {
- wrGlyphs[j].index = glyphs[j].mIndex;
- wrGlyphs[j].point = aSc.ToRelativeLayoutPoint(
- LayoutDevicePoint::FromUnknownPoint(glyphs[j].mPosition));
- }
+ RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget(aBuilder, aSc, aManager, aItem, bounds);
+ RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer);
+ PaintTextToContext(aItem->Frame(), captureCtx, aItem);
+ textDrawer->TerminateShadows();
- aManager->WrBridge()->PushGlyphs(aBuilder, wrGlyphs, mFont,
- wr::ToColorF(glyphArray.color().value()),
- aSc, wrDestRect, wrDestRect,
- !aItem->BackfaceIsHidden());
- }
+ return !textDrawer->HasUnsupportedFeatures();
}
class nsDisplayBullet final : public nsDisplayItem {
public:
nsDisplayBullet(nsDisplayListBuilder* aBuilder, nsBulletFrame* aFrame)
: nsDisplayItem(aBuilder, aFrame)
{
MOZ_COUNT_CTOR(nsDisplayBullet);
@@ -674,27 +670,29 @@ nsDisplayBullet::BuildLayer(nsDisplayLis
bool
nsDisplayBullet::CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder)
{
- ContainerLayerParameters parameter;
- if (GetLayerState(aDisplayListBuilder, aManager, parameter) != LAYER_ACTIVE) {
+ // FIXME: avoid needing to make this target if we're drawing text
+ // (non-trivial refactor of all this code)
+ RefPtr<gfxContext> screenRefCtx = gfxContext::CreateOrNull(
+ gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget().get());
+ Maybe<BulletRenderer> br = static_cast<nsBulletFrame*>(mFrame)->
+ CreateBulletRenderer(*screenRefCtx, ToReferenceFrame());
+
+ if (!br) {
return false;
}
- if (!mBulletRenderer)
- return false;
-
- mBulletRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc,
- aManager, aDisplayListBuilder);
- return true;
+ return br->CreateWebRenderCommands(this, aBuilder, aResources, aSc,
+ aManager, aDisplayListBuilder);
}
void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx)
{
uint32_t flags = imgIContainer::FLAG_NONE;
if (aBuilder->ShouldSyncDecodeImages()) {
flags |= imgIContainer::FLAG_SYNC_DECODE;
--- a/layout/reftests/counter-style/reftest.list
+++ b/layout/reftests/counter-style/reftest.list
@@ -1,35 +1,35 @@
== system-cyclic.html system-cyclic-ref.html
-fails-if(webrender) == system-fixed.html system-fixed-ref.html
-fails-if(webrender) == system-symbolic.html system-symbolic-ref.html
+== system-fixed.html system-fixed-ref.html
+== system-symbolic.html system-symbolic-ref.html
== system-alphabetic.html system-alphabetic-ref.html
== system-numeric.html system-numeric-ref.html
-fails-if(webrender) == system-additive.html system-additive-ref.html
+== system-additive.html system-additive-ref.html
== system-extends.html system-extends-ref.html
== system-cyclic-invalid.html system-common-invalid-ref.html
== system-fixed-invalid.html system-common-invalid2-ref.html
== system-symbolic-invalid.html system-common-invalid-ref.html
== system-alphabetic-invalid.html system-common-invalid2-ref.html
== system-numeric-invalid.html system-common-invalid2-ref.html
== system-additive-invalid.html system-common-invalid-ref.html
== system-extends-invalid.html system-extends-invalid-ref.html
== descriptor-negative.html descriptor-negative-ref.html
== descriptor-prefix.html descriptor-prefix-ref.html
-fails-if(webrender) == descriptor-suffix.html descriptor-suffix-ref.html
+== descriptor-suffix.html descriptor-suffix-ref.html
== descriptor-range.html descriptor-range-ref.html
== descriptor-pad.html descriptor-pad-ref.html
== descriptor-fallback.html descriptor-fallback-ref.html
== descriptor-symbols.html descriptor-symbols-ref.html
== descriptor-negative-invalid.html descriptor-negative-invalid-ref.html
== descriptor-prefix-invalid.html descriptor-prefix-invalid-ref.html
== descriptor-suffix-invalid.html descriptor-suffix-invalid-ref.html
== descriptor-range-invalid.html descriptor-range-invalid-ref.html
== descriptor-pad-invalid.html descriptor-pad-invalid-ref.html
== descriptor-fallback.html descriptor-fallback-ref.html
== descriptor-symbols-invalid.html descriptor-symbols-invalid-ref.html
== name-case-sensitivity.html name-case-sensitivity-ref.html
-fails-if(webrender) == dependent-builtin.html dependent-builtin-ref.html
+== dependent-builtin.html dependent-builtin-ref.html
== redefine-builtin.html redefine-builtin-ref.html
== redefine-attr-mapping.html redefine-attr-mapping-ref.html
== disclosure-styles.html disclosure-styles-ref.html
== symbols-function.html symbols-function-ref.html
== symbols-function-invalid.html symbols-function-invalid-ref.html
--- a/layout/reftests/counters/reftest.list
+++ b/layout/reftests/counters/reftest.list
@@ -63,17 +63,17 @@
fails-if(xulRuntime.XPCOMABI.match(/arm/)) == counter-reset-integer-range.html counter-reset-integer-range-ref.html # bug 989718
== counter-ua-limits-00.html counter-ua-limits-00-ref.html
== counter-ua-limits-01.html counter-ua-limits-01-ref.html
fails-if(xulRuntime.XPCOMABI.match(/arm/)) == counter-ua-limits-02.html counter-ua-limits-02-ref.html # bug 989718
== counter-ua-limits-03.html counter-ua-limits-03-ref.html
== counter-ua-limits-list-00.html counter-ua-limits-list-00-ref.html
== counter-ua-limits-list-01.html counter-ua-limits-list-01-ref.html
== multiple-thai-counters.html multiple-thai-counters-ref.html
-fails-if(webrender) == counter-suffix.html counter-suffix-ref.html
+== counter-suffix.html counter-suffix-ref.html
== counter-cjk-decimal.html counter-cjk-decimal-ref.html
== counter-japanese-informal.html counter-japanese-informal-ref.html
== counter-japanese-formal.html counter-japanese-formal-ref.html
== counter-korean-hangul-formal.html counter-korean-hangul-formal-ref.html
== counter-korean-hanja-informal.html counter-korean-hanja-informal-ref.html
== counter-korean-hanja-formal.html counter-korean-hanja-formal-ref.html
== counter-simp-chinese-informal.html counter-simp-chinese-informal-ref.html
== counter-simp-chinese-formal.html counter-simp-chinese-formal-ref.html
--- a/layout/reftests/css-display/display-contents-shadow-dom-1-ref.html
+++ b/layout/reftests/css-display/display-contents-shadow-dom-1-ref.html
@@ -37,14 +37,12 @@ span { color:blue; }
<div>A<span>b</span>c</div>
<div><span>b c</span>d</div>
<div><span>a </span>b</div>
<div><b>One</b><i>Two</i></div>
<div><b>One</b><i>Two</i></div>
<div><b>One</b><i>Two</i></div>
<div><b>One</b><i>Two</i></div>
<b style="color:blue">One</b><i style="color:blue">Two</i>Three
- <span style="color:green">R</span>
<div></div>
<b style="color:green">V</b>
- <!-- <b style="color:green">Y</b> -->
</body>
</html>
--- a/layout/reftests/css-display/display-contents-shadow-dom-1.html
+++ b/layout/reftests/css-display/display-contents-shadow-dom-1.html
@@ -41,22 +41,20 @@ div.after::after {content: " Y";}
<div id="hostJ"></div>
<span id="hostK"></span>
<div id="hostL"></div>
<div id="hostM"><i>Two</i><b>One</b></div>
<div id="hostN"><i class="c">Two</i><b>One</b></div>
<div id="hostO"><i>Two</i><b class="c">One</b></div>
<div id="hostP"><i class="c">Two</i><b class="c">One</b></div>
<div id="hostQ" class="c" style="color:blue"><i class="c">Two</i><b class="c">One</b></div>Three
- <span id="hostR"><style scoped>:scope{color:green}</style></span>
<div id="hostS" class="c"><span class="c">S</span></div>
<div id="hostT" class="c">T</div>
<div id="hostU"><span class="c">U</span></div>
<div id="hostV" class="c" style="color:red"><b class="c" style="color:inherit">V</b></div>
- <!-- TODO(bug 1021572?) <div id="hostY" class="c" style="color:red"><b>Y</b></div> -->
<script>
function shadow(id) {
return document.getElementById(id).createShadowRoot();
}
function span(s) {
var e = document.createElement("span");
var t = document.createTextNode(s);
@@ -98,18 +96,16 @@ div.after::after {content: " Y";}
shadow("hostJ").innerHTML = 'A<content select=".b"></content>';
shadow("hostK").innerHTML = '<content select=".b"></content>';
shadow("hostL").innerHTML = '<content select=".b"></content>';
shadow("hostM").innerHTML = '<content select="b"></content><content select="i"></content>';
shadow("hostN").innerHTML = '<content select="b"></content><content select="i"></content>';
shadow("hostO").innerHTML = '<content select="b"></content><content select="i"></content>';
shadow("hostP").innerHTML = '<content select="b"></content><content select="i"></content>';
shadow("hostQ").innerHTML = '<content select="b"></content><content select="i"></content>';
- shadow("hostR").innerHTML = '<content select="span"></content>';
- // TODO(bug 1021572?) shadow("hostY").innerHTML = '<content select="b"><style scoped>:scope{color:green}</style></content>';
}
function tweak() {
document.body.offsetHeight;
host1.appendChild(span("1"));
host2.appendChild(text("2"));
host3.appendChild(span("3"));
@@ -207,18 +203,16 @@ div.after::after {content: " Y";}
inner.className = "before";
var e = contents(inner);
e.className = "b";
hostL.appendChild(e);
var e = span("b");
e.className = "b";
hostL.appendChild(e);
- hostR.appendChild(span("R"));
-
document.body.offsetHeight;
setTimeout(function() {
shadow("hostS");
shadow("hostT");
shadow("hostU");
shadow("hostV").innerHTML = '<z style="color:green"><content select="b"></content></z>';
document.body.offsetHeight;
--- a/layout/reftests/css-display/reftest.list
+++ b/layout/reftests/css-display/reftest.list
@@ -12,17 +12,17 @@ fails-if(styloVsGecko||stylo) pref(layou
fuzzy-if(winWidget,12,100) skip-if(styloVsGecko||stylo) pref(layout.css.scoped-style.enabled,true) == display-contents-style-inheritance-1-dom-mutations.html display-contents-style-inheritance-1-ref.html
== display-contents-tables.xhtml display-contents-tables-ref.xhtml
== display-contents-tables-2.xhtml display-contents-tables-ref.xhtml
== display-contents-tables-3.xhtml display-contents-tables-3-ref.xhtml
== display-contents-visibility-hidden.html display-contents-visibility-hidden-ref.html
== display-contents-visibility-hidden-2.html display-contents-visibility-hidden-ref.html
== display-contents-495385-2d.html display-contents-495385-2d-ref.html
fuzzy-if(Android,7,3935) == display-contents-xbl.xhtml display-contents-xbl-ref.html
-fuzzy-if(Android,7,1186) skip-if(stylo||styloVsGecko) pref(dom.webcomponents.enabled,true) pref(layout.css.scoped-style.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1-ref.html # stylo: bug 1409086
+fuzzy-if(Android,7,1186) pref(dom.webcomponents.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1-ref.html
fails-if(styloVsGecko) == display-contents-xbl-2.xul display-contents-xbl-2-ref.xul # bug 1408235
fails-if(styloVsGecko) == display-contents-xbl-3.xul display-contents-xbl-3-ref.xul # bug 1408235
skip == display-contents-xbl-4.xul display-contents-xbl-4-ref.xul # fails (not just asserts) due to bug 1089223
asserts(0-1) fuzzy-if(Android,8,3216) == display-contents-fieldset.html display-contents-fieldset-ref.html # bug 1089223
fails-if(styloVsGecko) == display-contents-xbl-5.xul display-contents-xbl-3-ref.xul # bug 1408235
fails-if(!stylo) == display-contents-xbl-6.xhtml display-contents-xbl-6-ref.html # bug 1345809
== display-contents-xbl-7.xhtml display-contents-xbl-7-ref.html
== display-contents-list-item-child.html display-contents-list-item-child-ref.html
--- a/layout/reftests/forms/legend/reftest.list
+++ b/layout/reftests/forms/legend/reftest.list
@@ -1,4 +1,4 @@
== legend.html legend-ref.html
-fuzzy-if(skiaContent,1,7) pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == shadow-dom.html shadow-dom-ref.html # stylo: bug 1409086
+fuzzy-if(skiaContent,1,7) pref(dom.webcomponents.enabled,true) == shadow-dom.html shadow-dom-ref.html
== 1273433.html 1273433-ref.html
fails-if(styloVsGecko||stylo) == 1339287.html 1339287-ref.html
--- a/layout/reftests/forms/legend/shadow-dom.html
+++ b/layout/reftests/forms/legend/shadow-dom.html
@@ -69,32 +69,39 @@ div.after::after {content: " Y";}
host2.appendChild(legend("2"));
host3.appendChild(legend("3"));
host4.appendChild(legend("4"));
var e = legend("5");
e.style.display = "contents";
host5.appendChild(e);
+ document.body.offsetHeight;
+
host6.appendChild(legend("6"));
var e = legend("L");
e.className = "b";
host7.appendChild(e);
var e = legend("7");
e.className = "c";
+
+ document.body.offsetHeight;
+
host7.appendChild(e);
var e = legend("L");
e.className = "b after";
host8.appendChild(e);
var e = legend("8");
e.className = "c before";
host8.appendChild(e);
+ document.body.offsetHeight;
+
var e = legend("L2");
e.className = "b before after";
host9.appendChild(e);
var e = contents(legend(" L3"));
e.className = "b before after";
host9.appendChild(e);
var e = legend("9");
e.className = "c before";
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/reframe-shadow-child-1.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<template id="tmpl">
+ <div style="display: table">
+ Some text
+ <span style="display: table-cell">something</span>
+ More text
+ </div>
+</template>
+<div id="host"></div>
+<script>
+ let shadowRoot = document.getElementById("host").createShadowRoot();
+ let tmpl = document.getElementById("tmpl");
+ shadowRoot.appendChild(document.importNode(tmpl.content, true));
+ document.body.offsetTop;
+ shadowRoot.firstElementChild.querySelector("span").remove();
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/reframe-shadow-child-2.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<template id="tmpl">
+ <div style="display: block">
+ Some text
+ More text
+ </div>
+</template>
+<div id="host"></div>
+<script>
+ let shadowRoot = document.getElementById("host").createShadowRoot();
+ let tmpl = document.getElementById("tmpl");
+ shadowRoot.appendChild(document.importNode(tmpl.content, true));
+ document.body.offsetTop;
+ shadowRoot.firstElementChild.style.display = "table";
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/reframe-shadow-child-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<div style="display: table">
+ Some text
+ More text
+</div>
--- a/layout/reftests/webcomponents/reftest.list
+++ b/layout/reftests/webcomponents/reftest.list
@@ -1,15 +1,17 @@
pref(dom.webcomponents.enabled,true) == cross-tree-selection-1.html cross-tree-selection-1-ref.html
pref(dom.webcomponents.enabled,true) == basic-shadow-1.html basic-shadow-1-ref.html
pref(dom.webcomponents.enabled,true) == basic-shadow-2.html basic-shadow-2-ref.html
pref(dom.webcomponents.enabled,true) == basic-shadow-3.html basic-shadow-3-ref.html
pref(dom.webcomponents.enabled,true) == basic-shadow-4.html basic-shadow-4-ref.html
pref(dom.webcomponents.enabled,true) == basic-insertion-point-1.html basic-insertion-point-1-ref.html
pref(dom.webcomponents.enabled,true) == basic-insertion-point-2.html basic-insertion-point-2-ref.html
-pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == fallback-content-1.html fallback-content-1-ref.html # stylo: bug 1409088
+pref(dom.webcomponents.enabled,true) == fallback-content-1.html fallback-content-1-ref.html
pref(dom.webcomponents.enabled,true) == remove-insertion-point-1.html remove-insertion-point-1-ref.html
pref(dom.webcomponents.enabled,true) == nested-insertion-point-1.html nested-insertion-point-1-ref.html
-pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html
+pref(dom.webcomponents.enabled,true) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html
pref(dom.webcomponents.enabled,true) fuzzy-if(Android,2,7) == input-transition-1.html input-transition-1-ref.html
pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html
-pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html # bug 1409136
+pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html
pref(dom.webcomponents.enabled,true) == remove-append-shadow-host-1.html remove-append-shadow-host-1-ref.html
+pref(dom.webcomponents.enabled,true) == reframe-shadow-child-1.html reframe-shadow-child-ref.html
+pref(dom.webcomponents.enabled,true) == reframe-shadow-child-2.html reframe-shadow-child-ref.html
--- a/layout/reftests/writing-mode/reftest.list
+++ b/layout/reftests/writing-mode/reftest.list
@@ -26,17 +26,17 @@ fuzzy-if(azureSkia,255,2700) == 1090168-
== 1096224-1b.html 1096224-1-ref.html
fails == 1102175-1a.html 1102175-1-ref.html
== 1102175-1b.html 1102175-1-ref.html
== 1103613-1.html 1103613-1-ref.html
== 1105268-1-min-max-dimensions.html 1105268-1-min-max-dimensions-ref.html
== 1105268-2-min-max-dimensions.html 1105268-2-min-max-dimensions-ref.html
== 1106669-1-intrinsic-for-container.html 1106669-1-intrinsic-for-container-ref.html
== 1108923-1-percentage-margins.html 1108923-1-percentage-margins-ref.html
-fails-if(webrender) == 1111944-1-list-marker.html 1111944-1-list-marker-ref.html
+== 1111944-1-list-marker.html 1111944-1-list-marker-ref.html
fuzzy(116,94) fuzzy-if(winWidget,135,124) HTTP(..) == 1115916-1-vertical-metrics.html 1115916-1-vertical-metrics-ref.html
== 1117210-1-vertical-baseline-snap.html 1117210-1-vertical-baseline-snap-ref.html
random-if(webrender) == 1117227-1-text-overflow.html 1117227-1-text-overflow-ref.html
== 1122366-1-margin-collapse.html 1122366-1-margin-collapse-ref.html
== 1124636-1-fieldset-max-height.html 1124636-1-fieldset-max-height-ref.html
== 1124636-2-fieldset-min-height.html 1124636-2-fieldset-min-height-ref.html
== ua-style-sheet-margin-1.html ua-style-sheet-margin-1-ref.html
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -105,17 +105,17 @@ load 894245-1.html
load 915440.html
load 927734-1.html
load 930270-1.html
load 930270-2.html
load 945048-1.html
load 972199-1.html
load 989965-1.html
load 992333-1.html
-pref(dom.webcomponents.enabled,true) skip-if(stylo) load 1017798-1.html # bug 1409086
+pref(dom.webcomponents.enabled,true) load 1017798-1.html # bug 1409086
load 1028514-1.html
load 1066089-1.html
load 1074651-1.html
load 1135534.html
pref(dom.webcomponents.enabled,true) load 1089463-1.html
pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1136010-1.html
pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1146101-1.html
load 1153693-1.html
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
@@ -634,19 +634,24 @@ ClearKeySessionManager::Decrypt(const In
Buffer* buffer = mHost->Allocate(aBuffer.data_size);
assert(buffer != nullptr);
assert(buffer->Data() != nullptr);
assert(buffer->Capacity() >= aBuffer.data_size);
memcpy(buffer->Data(), aBuffer.data, aBuffer.data_size);
- Status status = mDecryptionManager->Decrypt(buffer->Data(),
- buffer->Size(),
- CryptoMetaData(&aBuffer));
+ Status status = Status::kSuccess;
+ // According to the comment `If |iv_size| = 0, the data is unencrypted.`
+ // Use iv_size to determine if the sample is encrypted.
+ if (aBuffer.iv_size != 0) {
+ status = mDecryptionManager->Decrypt(buffer->Data(),
+ buffer->Size(),
+ CryptoMetaData(&aBuffer));
+ }
aDecryptedBlock->SetDecryptedBuffer(buffer);
aDecryptedBlock->SetTimestamp(aBuffer.timestamp);
return status;
}
void
--- a/media/mtransport/transportlayerice.cpp
+++ b/media/mtransport/transportlayerice.cpp
@@ -94,16 +94,27 @@ TransportLayerIce::TransportLayerIce(con
TransportLayerIce::~TransportLayerIce() {
// No need to do anything here, since we use smart pointers
}
void TransportLayerIce::SetParameters(RefPtr<NrIceCtx> ctx,
RefPtr<NrIceMediaStream> stream,
int component) {
+ // Stream could be null in the case of some badly written js that causes
+ // us to be in an ICE restart case, but not have valid streams due to
+ // not calling PeerConnectionMedia::EnsureTransports if
+ // PeerConnectionImpl::SetSignalingState_m thinks the conditions were
+ // not correct. We also solved a case where an incoming answer was
+ // incorrectly beginning an ICE restart when the offer did not indicate one.
+ if (!stream) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
// If SetParameters is called and we already have a stream_, this means
// we're handling an ICE restart. We need to hold the old stream until
// we know the new stream is working.
if (stream_ && !old_stream_ && (stream_ != stream)) {
// Here we leave the old stream's signals connected until we don't need
// it anymore. They will be disconnected if ice restart is successful.
old_stream_ = stream_;
MOZ_MTLOG(ML_INFO, LAYER_INFO << "SetParameters save old stream("
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -722,16 +722,17 @@ JsepSessionImpl::GetRemoteIds(const Sdp&
return rv;
}
nsresult
JsepSessionImpl::CreateOffer(const JsepOfferOptions& options,
std::string* offer)
{
mLastError.clear();
+ mLocalIceIsRestarting = options.mIceRestart.isSome() && *(options.mIceRestart);
if (mState != kJsepStateStable) {
JSEP_SET_ERROR("Cannot create offer in state " << GetStateStr(mState));
return NS_ERROR_UNEXPECTED;
}
// Undo track assignments from a previous call to CreateOffer
// (ie; if the track has not been negotiated yet, it doesn't necessarily need
@@ -1989,16 +1990,25 @@ JsepSessionImpl::ValidateRemoteDescripti
if (oldMsection.GetMediaType() != newMsection.GetMediaType()) {
JSEP_SET_ERROR("Remote description changes the media type of m-line "
<< i);
return NS_ERROR_INVALID_ARG;
}
bool differ = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
+
+ // Detect bad answer ICE restart when offer doesn't request ICE restart
+ if (mIsOfferer && differ && !mLocalIceIsRestarting) {
+ JSEP_SET_ERROR("Remote description indicates ICE restart but offer did not "
+ "request ICE restart (new remote description changes either "
+ "the ice-ufrag or ice-pwd)");
+ return NS_ERROR_INVALID_ARG;
+ }
+
// Detect whether all the creds are the same or all are different
if (!iceCredsDiffer.isSome()) {
// for the first msection capture whether creds are different or same
iceCredsDiffer = mozilla::Some(differ);
} else if (iceCredsDiffer.isSome() && *iceCredsDiffer != differ) {
// subsequent msections must match the first sections
JSEP_SET_ERROR("Partial ICE restart is unsupported at this time "
"(new remote description changes either the ice-ufrag "
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
@@ -29,16 +29,17 @@ public:
class JsepSessionImpl : public JsepSession
{
public:
JsepSessionImpl(const std::string& name, UniquePtr<JsepUuidGenerator> uuidgen)
: JsepSession(name),
mIsOfferer(false),
mWasOffererLastTime(false),
mIceControlling(false),
+ mLocalIceIsRestarting(false),
mRemoteIsIceLite(false),
mRemoteIceIsRestarting(false),
mBundlePolicy(kBundleBalanced),
mSessionId(0),
mSessionVersion(0),
mUuidGen(Move(uuidgen)),
mSdpHelper(&mLastError)
{
@@ -317,16 +318,17 @@ private:
std::vector<RefPtr<JsepTransport> > mOldTransports;
std::vector<JsepTrackPair> mNegotiatedTrackPairs;
bool mIsOfferer;
bool mWasOffererLastTime;
bool mIceControlling;
std::string mIceUfrag;
std::string mIcePwd;
+ bool mLocalIceIsRestarting;
bool mRemoteIsIceLite;
bool mRemoteIceIsRestarting;
std::vector<std::string> mIceOptions;
JsepBundlePolicy mBundlePolicy;
std::vector<JsepDtlsFingerprint> mDtlsFingerprints;
uint64_t mSessionId;
uint64_t mSessionVersion;
std::vector<SdpExtmapAttributeList::Extmap> mAudioRtpExtensions;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -269,16 +269,23 @@ PeerConnectionMedia::StunAddrsHandler::O
{
CSFLogInfo(LOGTAG, "%s: receiving (%d) stun addrs", __FUNCTION__,
(int)addrs.Length());
if (pcm_) {
pcm_->mStunAddrs = addrs;
pcm_->mLocalAddrsCompleted = true;
pcm_->mStunAddrsRequest = nullptr;
pcm_->FlushIceCtxOperationQueueIfReady();
+ // If parent process returns 0 STUN addresses, change ICE connection
+ // state to failed.
+ if (!pcm_->mStunAddrs.Length()) {
+ pcm_->SignalIceConnectionStateChange(pcm_->mIceCtxHdlr->ctx().get(),
+ NrIceCtx::ICE_CTX_FAILED);
+ }
+
pcm_ = nullptr;
}
}
PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl *parent)
: mParent(parent),
mParentHandle(parent->GetHandle()),
mParentName(parent->GetName()),
@@ -951,16 +958,28 @@ PeerConnectionMedia::EnsureIceGathering_
if (mProxyServer) {
mIceCtxHdlr->ctx()->SetProxyServer(*mProxyServer);
} else if (aProxyOnly) {
IceGatheringStateChange_s(mIceCtxHdlr->ctx().get(),
NrIceCtx::ICE_CTX_GATHER_COMPLETE);
return;
}
+ // Make sure we don't call NrIceCtx::StartGathering if we're in e10s mode
+ // and we received no STUN addresses from the parent process. In the
+ // absence of previously provided STUN addresses, StartGathering will
+ // attempt to gather them (as in non-e10s mode), and this will cause a
+ // sandboxing exception in e10s mode.
+ if (!mStunAddrs.Length() && XRE_IsContentProcess()) {
+ CSFLogInfo(LOGTAG,
+ "%s: No STUN addresses returned from parent process",
+ __FUNCTION__);
+ return;
+ }
+
// Belt and suspenders - in e10s mode, the call below to SetStunAddrs
// needs to have the proper flags set on ice ctx. For non-e10s,
// setting those flags happens in StartGathering. We could probably
// just set them here, and only do it here.
mIceCtxHdlr->ctx()->SetCtxFlags(aDefaultRouteOnly, aProxyOnly);
if (mStunAddrs.Length()) {
mIceCtxHdlr->ctx()->SetStunAddrs(mStunAddrs);
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/SyncPreference.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/SyncPreference.java
@@ -59,26 +59,32 @@ class SyncPreference extends Preference
// http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/preference/Preference.java#562
setIcon(null);
setIcon(R.drawable.sync_avatar_default);
}
});
return;
}
- // Update title from account email.
+ final ExtendedJSONObject profileJSON = fxAccount.getProfileJSON();
+ final String displayName = profileJSON != null ? profileJSON.getString(FxAccountConstants.KEY_PROFILE_JSON_USERNAME) : null;
+
+ // Update title from account email/display name.
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
- setTitle(fxAccount.getEmail());
- setSummary("");
+ if (!TextUtils.isEmpty(displayName)) {
+ setTitle(displayName);
+ } else {
+ setTitle(R.string.pref_sync_default_title);
+ }
+ setSummary(fxAccount.getEmail());
}
});
- final ExtendedJSONObject profileJSON = fxAccount.getProfileJSON();
if (profileJSON == null) {
return;
}
// Avatar URI empty, return early.
final String avatarURI = profileJSON.getString(FxAccountConstants.KEY_PROFILE_JSON_AVATAR);
if (TextUtils.isEmpty(avatarURI)) {
return;
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -358,16 +358,17 @@
<!ENTITY pref_zoom_force_enabled "Always enable zoom">
<!ENTITY pref_zoom_force_enabled_summary "Force override so you can zoom any page">
<!ENTITY pref_voice_input "Voice input">
<!ENTITY pref_voice_input_summary2 "Allow voice dictation in the URL bar">
<!ENTITY pref_qrcode_enabled "QR code reader">
<!ENTITY pref_qrcode_enabled_summary2 "Allow QR scanner in the URL bar">
<!ENTITY pref_use_master_password "Use master password">
+<!ENTITY pref_sync_default_title "Firefox Account">
<!ENTITY pref_sync2 "Sign in">
<!ENTITY pref_sync_summary2 "Sync your tabs, bookmarks, logins, history">
<!ENTITY pref_search_suggestions "Show search suggestions">
<!ENTITY pref_history_search_suggestions "Show search history">
<!ENTITY pref_import_options "Import options">
<!ENTITY pref_import_android_summary "Import bookmarks and history from the native browser">
<!-- Localization note (pref_private_data_openTabs): Open tabs is an option in
the Clear Private Data dialog and refers to currently open tabs. -->
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -255,16 +255,17 @@
<string name="pref_zoom_force_enabled_summary">&pref_zoom_force_enabled_summary;</string>
<string name="pref_voice_input">&pref_voice_input;</string>
<string name="pref_voice_input_summary">&pref_voice_input_summary2;</string>
<string name="pref_qrcode_enabled">&pref_qrcode_enabled;</string>
<string name="pref_qrcode_enabled_summary">&pref_qrcode_enabled_summary2;</string>
<string name="pref_restore">&pref_restore_tabs;</string>
<string name="pref_restore_always">&pref_restore_always;</string>
<string name="pref_restore_quit">&pref_restore_quit;</string>
+ <string name="pref_sync_default_title">&pref_sync_default_title;</string>
<string name="pref_sync">&pref_sync2;</string>
<string name="pref_sync_summary">&pref_sync_summary2;</string>
<string name="pref_search_suggestions">&pref_search_suggestions;</string>
<string name="pref_history_search_suggestions">&pref_history_search_suggestions;</string>
<string name="pref_private_data_openTabs">&pref_private_data_openTabs;</string>
<string name="pref_private_data_history2">&pref_private_data_history2;</string>
<string name="pref_private_data_searchHistory">&pref_private_data_searchHistory;</string>
<string name="pref_private_data_formdata2">&pref_private_data_formdata2;</string>
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/ConfigurableServer15Repository.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/ConfigurableServer15Repository.java
@@ -23,43 +23,46 @@ import org.mozilla.gecko.sync.stage.Serv
*
*/
public class ConfigurableServer15Repository extends Server15Repository {
private final String sortOrder;
private final long batchLimit;
private final ServerSyncStage.MultipleBatches multipleBatches;
private final ServerSyncStage.HighWaterMark highWaterMark;
private final boolean forceFullFetch;
+ private final boolean abortOnStoreFailure;
public ConfigurableServer15Repository(
String collection,
long syncDeadline,
String storageURL,
AuthHeaderProvider authHeaderProvider,
InfoCollections infoCollections,
InfoConfiguration infoConfiguration,
long batchLimit,
String sort,
ServerSyncStage.MultipleBatches multipleBatches,
ServerSyncStage.HighWaterMark highWaterMark,
RepositoryStateProvider stateProvider,
- boolean forceFullFetch) throws URISyntaxException {
+ boolean forceFullFetch,
+ boolean abortOnStoreFailure) throws URISyntaxException {
super(
collection,
syncDeadline,
storageURL,
authHeaderProvider,
infoCollections,
infoConfiguration,
stateProvider
);
this.batchLimit = batchLimit;
this.sortOrder = sort;
this.multipleBatches = multipleBatches;
this.highWaterMark = highWaterMark;
this.forceFullFetch = forceFullFetch;
+ this.abortOnStoreFailure = abortOnStoreFailure;
// Sanity check: let's ensure we're configured correctly. At this point in time, it doesn't make
// sense to use H.W.M. with a non-persistent state provider. This might change if we start retrying
// during a download in case of 412s.
if (!stateProvider.isPersistent() && highWaterMark.equals(ServerSyncStage.HighWaterMark.Enabled)) {
throw new IllegalArgumentException("Can not use H.W.M. with NonPersistentRepositoryStateProvider");
}
}
@@ -84,9 +87,14 @@ public class ConfigurableServer15Reposit
return highWaterMark.equals(ServerSyncStage.HighWaterMark.Enabled);
}
@Override
public boolean getFullFetchForced() {
return forceFullFetch;
}
+ @Override
+ public boolean getAbortOnStoreFailure() {
+ return abortOnStoreFailure;
+ }
+
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/Server15Repository.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/Server15Repository.java
@@ -110,16 +110,20 @@ public class Server15Repository extends
public boolean getAllowHighWaterMark() {
return false;
}
public boolean getFullFetchForced() {
return false;
}
+ public boolean getAbortOnStoreFailure() {
+ return false;
+ }
+
/**
* A point in time by which this repository's session must complete fetch and store operations.
* Particularly pertinent for batching downloads performed by the session (should we fetch
* another batch?) and buffered repositories (do we have enough time to merge what we've downloaded?).
*/
public long getSyncDeadline() {
return syncDeadlineMillis;
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/Server15RepositorySession.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/Server15RepositorySession.java
@@ -38,17 +38,17 @@ public class Server15RepositorySession e
@Override
public void setStoreDelegate(RepositorySessionStoreDelegate storeDelegate) {
super.setStoreDelegate(storeDelegate);
// Now that we have the delegate, we can initialize our uploader.
this.uploader = new BatchingUploader(
this, storeWorkQueue, storeDelegate, Uri.parse(serverRepository.collectionURI.toString()),
serverRepository.getCollectionLastModified(), serverRepository.getInfoConfiguration(),
- serverRepository.authHeaderProvider);
+ serverRepository.authHeaderProvider, serverRepository.getAbortOnStoreFailure());
}
private void fetchSince(long timestamp, RepositorySessionFetchRecordsDelegate delegate) {
BatchingDownloaderController.resumeFetchSinceIfPossible(
this.downloader,
this.serverRepository.stateProvider,
delegate,
timestamp,
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/uploaders/BatchingUploader.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/uploaders/BatchingUploader.java
@@ -100,25 +100,33 @@ public class BatchingUploader {
// string containing the data of the record."
// Sync Storage servers place a hard limit on how large a payload _field_ might be, and so we
// maintain this limit for a single sanity check.
private final long maxPayloadFieldBytes;
// Set if this channel should ignore further calls to process.
private volatile boolean aborted = false;
+ // Whether or not we should set aborted if there are any issues with the record.
+ // This is used to prevent corruption with bookmark records, as uploading
+ // only a subset of the bookmarks is very likely to cause corruption, (e.g.
+ // uploading a parent without its children or vice versa).
+ @VisibleForTesting
+ protected final boolean shouldFailBatchOnFailure;
+
public BatchingUploader(
final RepositorySession repositorySession, final ExecutorService workQueue,
final RepositorySessionStoreDelegate sessionStoreDelegate, final Uri baseCollectionUri,
final Long localCollectionLastModified, final InfoConfiguration infoConfiguration,
- final AuthHeaderProvider authHeaderProvider) {
+ final AuthHeaderProvider authHeaderProvider, final boolean shouldAbortOnFailure) {
this.repositorySession = repositorySession;
this.sessionStoreDelegate = sessionStoreDelegate;
this.collectionUri = baseCollectionUri;
this.authHeaderProvider = authHeaderProvider;
+ this.shouldFailBatchOnFailure = shouldAbortOnFailure;
this.uploaderMeta = new UploaderMeta(
payloadLock, infoConfiguration.maxTotalBytes, infoConfiguration.maxTotalRecords);
this.payload = new Payload(
payloadLock, infoConfiguration.maxPostBytes, infoConfiguration.maxPostRecords);
this.payloadDispatcher = createPayloadDispatcher(workQueue, localCollectionLastModified);
@@ -228,42 +236,36 @@ public class BatchingUploader {
payloadDispatcher.finalizeQueue(uploaderMeta.needToCommit(), new Runnable() {
@Override
public void run() {
flush(true, true);
}
});
}
- // We fail the batch for bookmark records because uploading only a subset of bookmark records is
- // very likely to cause corruption (e.g. uploading a parent without its children or vice versa).
- @VisibleForTesting
- /* package-local */ boolean shouldFailBatchOnFailure(Record record) {
- return record instanceof BookmarkRecord;
- }
/* package-local */ void setLastStoreTimestamp(AtomicLong lastModifiedTimestamp) {
repositorySession.setLastStoreTimestamp(lastModifiedTimestamp.get());
}
/* package-local */ void finished() {
sessionStoreDelegate.deferredStoreDelegate(executor).onStoreCompleted();
}
// Common handling for marking a record failure and calling our delegate's onRecordStoreFailed.
private void failRecordStore(final Exception e, final Record record, boolean sizeOverflow) {
// There are three cases we're handling here. See bug 1362206 for some rationale here.
- // 1. If `record` is not a bookmark and it failed sanity checks for reasons other than
+ // 1. shouldFailBatchOnFailure is false, and it failed sanity checks for reasons other than
// "it's too large" (say, `record`'s json is 0 bytes),
// - Then mark record's store as 'failed' and continue uploading
- // 2. If `record` is not a bookmark and it failed sanity checks because it's too large,
+ // 2. shouldFailBatchOnFailure is false, and it failed sanity checks because it's too large,
// - Continue uploading, and don't fail synchronization because of this one.
- // 3. If `record` *is* a bookmark, and it failed for any reason
+ // 3. shouldFailBatchOnFailure is true, and it failed for any reason
// - Stop uploading.
- if (shouldFailBatchOnFailure(record)) {
+ if (shouldFailBatchOnFailure) {
// case 3
Logger.debug(LOG_TAG, "Batch failed with exception: " + e.toString());
// Start ignoring records, and send off to our delegate that we failed.
aborted = true;
executor.execute(new PayloadDispatcher.NonPayloadContextRunnable() {
@Override
public void run() {
sessionStoreDelegate.onRecordStoreFailed(e, record.guid);
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/BookmarksServerSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/BookmarksServerSyncStage.java
@@ -77,17 +77,18 @@ public class BookmarksServerSyncStage ex
session.getAuthHeaderProvider(),
session.config.infoCollections,
session.config.infoConfiguration,
BOOKMARKS_BATCH_LIMIT,
BOOKMARKS_SORT,
getAllowedMultipleBatches(),
getAllowedToUseHighWaterMark(),
getRepositoryStateProvider(),
- false
+ false,
+ true
);
}
@Override
protected Repository getLocalRepository() {
return new BufferingMiddlewareRepository(
session.getSyncDeadline(),
new MemoryBufferStorage(),
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/FormHistoryServerSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/FormHistoryServerSyncStage.java
@@ -71,16 +71,17 @@ public class FormHistoryServerSyncStage
session.getAuthHeaderProvider(),
session.config.infoCollections,
session.config.infoConfiguration,
FORM_HISTORY_BATCH_LIMIT,
FORM_HISTORY_SORT,
getAllowedMultipleBatches(),
getAllowedToUseHighWaterMark(),
getRepositoryStateProvider(),
+ false,
false
);
}
@Override
protected Repository getLocalRepository() {
return new BufferingMiddlewareRepository(
session.getSyncDeadline(),
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/HistoryServerSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/HistoryServerSyncStage.java
@@ -89,16 +89,17 @@ public class HistoryServerSyncStage exte
session.getAuthHeaderProvider(),
session.config.infoCollections,
session.config.infoConfiguration,
HISTORY_BATCH_LIMIT,
HISTORY_SORT,
getAllowedMultipleBatches(),
getAllowedToUseHighWaterMark(),
getRepositoryStateProvider(),
+ false,
false
);
}
@Override
protected RecordFactory getRecordFactory() {
return new HistoryRecordFactory();
}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/RecentHistoryServerSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/RecentHistoryServerSyncStage.java
@@ -90,17 +90,19 @@ public class RecentHistoryServerSyncStag
session.getAuthHeaderProvider(),
session.config.infoCollections,
session.config.infoConfiguration,
HISTORY_BATCH_LIMIT,
HISTORY_SORT,
getAllowedMultipleBatches(),
getAllowedToUseHighWaterMark(),
getRepositoryStateProvider(),
- false);
+ false,
+ false
+ );
}
/**
* This stage is only enabled if full history session is enabled and did not complete a sync yet.
*/
@Override
public boolean isEnabled() throws MetaGlobalException {
final boolean historyStageEnabled = super.isEnabled();
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/ValidateBookmarksSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/ValidateBookmarksSyncStage.java
@@ -111,17 +111,18 @@ public class ValidateBookmarksSyncStage
session.getAuthHeaderProvider(),
session.config.infoCollections,
session.config.infoConfiguration,
BOOKMARKS_BATCH_LIMIT,
BOOKMARKS_SORT,
getAllowedMultipleBatches(),
getAllowedToUseHighWaterMark(),
getRepositoryStateProvider(),
- true
+ true,
+ false // We never do any storing, so this is irrelevant
);
}
@Override
protected Repository getLocalRepository() {
TelemetryStageCollector bookmarkCollector =
this.telemetryStageCollector.getSyncCollector().collectorFor("bookmarks");
return new BookmarksValidationRepository(session.getClientsDelegate(), bookmarkCollector);
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/uploaders/BatchingUploaderTest.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/uploaders/BatchingUploaderTest.java
@@ -606,35 +606,28 @@ public class BatchingUploaderTest {
return new Runnable() {
@Override
public void run() {}
};
}
}
class MockUploader extends BatchingUploader {
- boolean abortOnRecordFail;
MockUploader(final RepositorySession repositorySession, final ExecutorService workQueue,
final RepositorySessionStoreDelegate sessionStoreDelegate, final Uri baseCollectionUri,
final Long localCollectionLastModified, final InfoConfiguration infoConfiguration,
final AuthHeaderProvider authHeaderProvider, final boolean abortOnRecordFail) {
super(repositorySession, workQueue, sessionStoreDelegate, baseCollectionUri,
- localCollectionLastModified, infoConfiguration, authHeaderProvider);
- this.abortOnRecordFail = abortOnRecordFail;
+ localCollectionLastModified, infoConfiguration, authHeaderProvider, abortOnRecordFail);
}
@Override
PayloadDispatcher createPayloadDispatcher(ExecutorService workQueue, Long localCollectionLastModified) {
return new MockPayloadDispatcher(workQueue, this, localCollectionLastModified);
}
-
- @Override
- boolean shouldFailBatchOnFailure(Record r) {
- return abortOnRecordFail;
- }
}
private Server15Repository makeConstrainedRepository(boolean firstSync) {
InfoCollections infoCollections;
if (firstSync) {
infoCollections = new InfoCollections() {
@Override
public Long getTimestamp(String collection) {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/uploaders/PayloadUploadDelegateTest.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/uploaders/PayloadUploadDelegateTest.java
@@ -137,17 +137,18 @@ public class PayloadUploadDelegateTest {
null,
new BatchingUploader(
repositorySession,
null,
sessionStoreDelegate,
Uri.parse("https://example.com"),
0L,
mock(InfoConfiguration.class),
- mock(AuthHeaderProvider.class)
+ mock(AuthHeaderProvider.class),
+ false
)
);
authHeaderProvider = mock(AuthHeaderProvider.class);
}
@Test
public void testHandleRequestSuccessNonSuccess() {
--- a/security/apps/moz.build
+++ b/security/apps/moz.build
@@ -32,17 +32,17 @@ if CONFIG['GNU_CXX']:
# Gecko headers aren't warning-free enough for us to enable these warnings.
CXXFLAGS += [
'-Wno-unused-parameter',
]
test_ssl_path = '/security/manager/ssl/tests/unit'
headers_arrays_certs = [
- ('xpcshell.inc', 'xpcshellRoot', test_ssl_path + '/test_signed_apps/trusted_ca1.der'),
+ ('xpcshell.inc', 'xpcshellRoot', test_ssl_path + '/test_signed_apps/xpcshellTestRoot.der'),
('addons-public.inc', 'addonsPublicRoot', 'addons-public.crt'),
('addons-stage.inc', 'addonsStageRoot', 'addons-stage.crt'),
('privileged-package-root.inc', 'privilegedPackageRoot', 'privileged-package-root.der'),
]
for header, array_name, cert in headers_arrays_certs:
GENERATED_FILES += [header]
h = GENERATED_FILES[header]
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -838,35 +838,16 @@ CertVerifier::VerifyCert(CERTCertificate
EndEntityOrCA::MustBeEndEntity,
KeyUsage::keyAgreement, // ECDH/DH
KeyPurposeId::id_kp_emailProtection,
CertPolicyId::anyPolicy, stapledOCSPResponse);
}
break;
}
- case certificateUsageObjectSigner: {
- NSSCertDBTrustDomain trustDomain(trustObjectSigning, defaultOCSPFetching,
- mOCSPCache, pinArg, ocspGETConfig,
- mOCSPTimeoutSoft, mOCSPTimeoutHard,
- mCertShortLifetimeInDays,
- pinningDisabled, MIN_RSA_BITS_WEAK,
- ValidityCheckingMode::CheckingOff,
- SHA1Mode::Allowed,
- NetscapeStepUpPolicy::NeverMatch,
- originAttributes, builtChain, nullptr,
- nullptr);
- rv = BuildCertChain(trustDomain, certDER, time,
- EndEntityOrCA::MustBeEndEntity,
- KeyUsage::digitalSignature,
- KeyPurposeId::id_kp_codeSigning,
- CertPolicyId::anyPolicy, stapledOCSPResponse);
- break;
- }
-
default:
rv = Result::FATAL_ERROR_INVALID_ARGS;
}
if (rv != Success) {
return rv;
}
--- a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
+++ b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
@@ -41,17 +41,16 @@ Fips140SlotDescription=FIPS 140 Cryptogr
# 32
InternalToken=Software Security Device
# End of size restriction.
VerifySSLClient=SSL Client Certificate
VerifySSLServer=SSL Server Certificate
VerifySSLCA=SSL Certificate Authority
VerifyEmailSigner=Email Signer Certificate
VerifyEmailRecip=Email Recipient Certificate
-VerifyObjSign=Object Signer
HighGrade=High Grade
MediumGrade=Medium Grade
# LOCALIZATION NOTE (nick_template): $1s is the common name from a cert (e.g. "Mozilla"), $2s is the CA name (e.g. VeriSign)
nick_template=%1$s’s %2$s ID
#These are the strings set for the ASN1 objects in a certificate.
CertDumpCertificate=Certificate
CertDumpVersion=Version
# LOCALIZATION NOTE (CertDumpVersionValue): %S is a version number (e.g. "3" in "Version 3")
--- a/security/manager/pki/resources/content/certViewer.js
+++ b/security/manager/pki/resources/content/certViewer.js
@@ -87,38 +87,35 @@ function setWindowName() {
}
// Certificate usages we care about in the certificate viewer.
const certificateUsageSSLClient = 0x0001;
const certificateUsageSSLServer = 0x0002;
const certificateUsageSSLCA = 0x0008;
const certificateUsageEmailSigner = 0x0010;
const certificateUsageEmailRecipient = 0x0020;
-const certificateUsageObjectSigner = 0x0040;
// A map from the name of a certificate usage to the value of the usage.
// Useful for printing debugging information and for enumerating all supported
// usages.
const certificateUsages = {
certificateUsageSSLClient,
certificateUsageSSLServer,
certificateUsageSSLCA,
certificateUsageEmailSigner,
certificateUsageEmailRecipient,
- certificateUsageObjectSigner,
};
// Map of certificate usage name to localization identifier.
const certificateUsageToStringBundleName = {
certificateUsageSSLClient: "VerifySSLClient",
certificateUsageSSLServer: "VerifySSLServer",
certificateUsageSSLCA: "VerifySSLCA",
certificateUsageEmailSigner: "VerifyEmailSigner",
certificateUsageEmailRecipient: "VerifyEmailRecip",
- certificateUsageObjectSigner: "VerifyObjSign",
};
const PRErrorCodeSuccess = 0;
const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
const SEC_ERROR_EXPIRED_CERTIFICATE = SEC_ERROR_BASE + 11;
const SEC_ERROR_REVOKED_CERTIFICATE = SEC_ERROR_BASE + 12;
const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13;
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -637,93 +637,87 @@ nsNSSCertificate::GetOrganizationalUnit(
UniquePORTString orgunit(CERT_GetOrgUnitName(&mCert->subject));
if (orgunit) {
aOrganizationalUnit = NS_ConvertUTF8toUTF16(orgunit.get());
}
}
return NS_OK;
}
+static nsresult
+UniqueCERTCertListToMutableArray(/*in*/ UniqueCERTCertList& nssChain,
+ /*out*/ nsIArray** x509CertArray)
+{
+ if (!x509CertArray) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
+ if (!array) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CERTCertListNode* node;
+ for (node = CERT_LIST_HEAD(nssChain.get());
+ !CERT_LIST_END(node, nssChain.get());
+ node = CERT_LIST_NEXT(node)) {
+ nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::Create(node->cert);
+ nsresult rv = array->AppendElement(cert, false);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ array.forget(x509CertArray);
+ return NS_OK;
+}
+
NS_IMETHODIMP
nsNSSCertificate::GetChain(nsIArray** _rvChain)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
NS_ENSURE_ARG(_rvChain);
mozilla::pkix::Time now(mozilla::pkix::Now());
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
UniqueCERTCertList nssChain;
- // We want to test all usages, but we start with server because most of the
- // time Firefox users care about server certs.
- if (certVerifier->VerifyCert(mCert.get(), certificateUsageSSLServer, now,
- nullptr, /*XXX fixme*/
- nullptr, /* hostname */
- nssChain,
- CertVerifier::FLAG_LOCAL_ONLY)
- != mozilla::pkix::Success) {
- nssChain = nullptr;
- // keep going
- }
-
- // This is the whitelist of all non-SSLServer usages that are supported by
- // verifycert.
- const int otherUsagesToTest = certificateUsageSSLClient |
- certificateUsageSSLCA |
- certificateUsageEmailSigner |
- certificateUsageEmailRecipient |
- certificateUsageObjectSigner;
- for (int usage = certificateUsageSSLClient;
- usage < certificateUsageAnyCA && !nssChain;
- usage = usage << 1) {
- if ((usage & otherUsagesToTest) == 0) {
- continue;
- }
+ // We want to test all usages supported by the certificate verifier, but we
+ // start with TLS server because most of the time Firefox users care about
+ // server certs.
+ const int usagesToTest[] = { certificateUsageSSLServer,
+ certificateUsageSSLClient,
+ certificateUsageSSLCA,
+ certificateUsageEmailSigner,
+ certificateUsageEmailRecipient };
+ for (auto usage : usagesToTest) {
if (certVerifier->VerifyCert(mCert.get(), usage, now,
nullptr, /*XXX fixme*/
nullptr, /*hostname*/
nssChain,
CertVerifier::FLAG_LOCAL_ONLY)
- != mozilla::pkix::Success) {
- nssChain = nullptr;
- // keep going
+ == mozilla::pkix::Success) {
+ return UniqueCERTCertListToMutableArray(nssChain, _rvChain);
}
}
- if (!nssChain) {
- // There is not verified path for the chain, however we still want to
- // present to the user as much of a possible chain as possible, in the case
- // where there was a problem with the cert or the issuers.
- nssChain = UniqueCERTCertList(
- CERT_GetCertChainFromCert(mCert.get(), PR_Now(), certUsageSSLClient));
- }
+ // There is no verified path for the chain, however we still want to
+ // present to the user as much of a possible chain as possible, in the case
+ // where there was a problem with the cert or the issuers.
+ nssChain = UniqueCERTCertList(
+ CERT_GetCertChainFromCert(mCert.get(), PR_Now(), certUsageSSLClient));
if (!nssChain) {
return NS_ERROR_FAILURE;
}
-
- // enumerate the chain for scripting purposes
- nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
- if (!array) {
- return NS_ERROR_FAILURE;
- }
- CERTCertListNode* node;
- for (node = CERT_LIST_HEAD(nssChain.get());
- !CERT_LIST_END(node, nssChain.get());
- node = CERT_LIST_NEXT(node)) {
- nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::Create(node->cert);
- array->AppendElement(cert, false);
- }
- *_rvChain = array;
- NS_IF_ADDREF(*_rvChain);
- return NS_OK;
+ return UniqueCERTCertListToMutableArray(nssChain, _rvChain);
}
NS_IMETHODIMP
nsNSSCertificate::GetSubjectName(nsAString& _subjectName)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
return NS_ERROR_NOT_AVAILABLE;
--- a/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js
+++ b/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js
@@ -34,34 +34,34 @@ add_task(async function testEmailEndEnti
let win = await displayCertificate(cert);
checkUsages(win, ["Email Recipient Certificate", "Email Signer Certificate"]);
await BrowserTestUtils.closeWindow(win);
});
add_task(async function testCodeSignEndEntity() {
let cert = await readCertificate("code-ee.pem", ",,");
let win = await displayCertificate(cert);
- checkUsages(win, ["Object Signer"]);
+ checkError(win, "Could not verify this certificate for unknown reasons.");
await BrowserTestUtils.closeWindow(win);
});
add_task(async function testExpired() {
let cert = await readCertificate("expired-ca.pem", ",,");
let win = await displayCertificate(cert);
checkError(win, "Could not verify this certificate because it has expired.");
await BrowserTestUtils.closeWindow(win);
-});
-add_task(async function testIssuerExpired() {
- let cert = await readCertificate("ee-from-expired-ca.pem", ",,");
- let win = await displayCertificate(cert);
- checkError(win,
+ // These tasks may run in any order, so we run this additional testcase in the
+ // same task.
+ let eeCert = await readCertificate("ee-from-expired-ca.pem", ",,");
+ let eeWin = await displayCertificate(eeCert);
+ checkError(eeWin,
"Could not verify this certificate because the CA certificate " +
"is invalid.");
- await BrowserTestUtils.closeWindow(win);
+ await BrowserTestUtils.closeWindow(eeWin);
});
add_task(async function testUnknownIssuer() {
let cert = await readCertificate("unknown-issuer.pem", ",,");
let win = await displayCertificate(cert);
checkError(win,
"Could not verify this certificate because the issuer is " +
"unknown.");
@@ -79,25 +79,25 @@ add_task(async function testInsecureAlgo
});
add_task(async function testUntrusted() {
let cert = await readCertificate("untrusted-ca.pem", "p,p,p");
let win = await displayCertificate(cert);
checkError(win,
"Could not verify this certificate because it is not trusted.");
await BrowserTestUtils.closeWindow(win);
-});
-add_task(async function testUntrustedIssuer() {
- let cert = await readCertificate("ee-from-untrusted-ca.pem", ",,");
- let win = await displayCertificate(cert);
- checkError(win,
+ // These tasks may run in any order, so we run this additional testcase in the
+ // same task.
+ let eeCert = await readCertificate("ee-from-untrusted-ca.pem", ",,");
+ let eeWin = await displayCertificate(eeCert);
+ checkError(eeWin,
"Could not verify this certificate because the issuer is not " +
"trusted.");
- await BrowserTestUtils.closeWindow(win);
+ await BrowserTestUtils.closeWindow(eeWin);
});
add_task(async function testRevoked() {
// Note that there's currently no way to un-do this. This should only be a
// problem if another test re-uses a certificate with this same key (perhaps
// likely) and subject (less likely).
let certBlocklist = Cc["@mozilla.org/security/certblocklist;1"]
.getService(Ci.nsICertBlocklist);
@@ -105,17 +105,17 @@ add_task(async function testRevoked() {
"MBIxEDAOBgNVBAMMB3Jldm9rZWQ=", // CN=revoked
"VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8="); // hash of the shared key
let cert = await readCertificate("revoked.pem", ",,");
let win = await displayCertificate(cert);
// As of bug 1312827, OneCRL only applies to TLS web server certificates, so
// this certificate will actually verify successfully for every end-entity
// usage except TLS web server.
checkUsages(win, ["Email Recipient Certificate", "Email Signer Certificate",
- "Object Signer", "SSL Client Certificate"]);
+ "SSL Client Certificate"]);
await BrowserTestUtils.closeWindow(win);
});
add_task(async function testInvalid() {
// This certificate has a keyUsage extension asserting cRLSign and
// keyCertSign, but it doesn't have a basicConstraints extension. This
// shouldn't be valid for any usage. Sadly, we give a pretty lame error
// message in this case.
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -85,28 +85,26 @@ const MOZILLA_PKIX_ERROR_REQUIRED_TLS_FE
const MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME = MOZILLA_PKIX_ERROR_BASE + 12;
// Supported Certificate Usages
const certificateUsageSSLClient = 0x0001;
const certificateUsageSSLServer = 0x0002;
const certificateUsageSSLCA = 0x0008;
const certificateUsageEmailSigner = 0x0010;
const certificateUsageEmailRecipient = 0x0020;
-const certificateUsageObjectSigner = 0x0040;
// A map from the name of a certificate usage to the value of the usage.
// Useful for printing debugging information and for enumerating all supported
// usages.
const allCertificateUsages = {
certificateUsageSSLClient,
certificateUsageSSLServer,
certificateUsageSSLCA,
certificateUsageEmailSigner,
certificateUsageEmailRecipient,
- certificateUsageObjectSigner,
};
const NO_FLAGS = 0;
// Commonly certificates are represented as PEM. The format is roughly as
// follows:
//
// -----BEGIN CERTIFICATE-----
--- a/security/manager/ssl/tests/unit/moz.build
+++ b/security/manager/ssl/tests/unit/moz.build
@@ -29,11 +29,12 @@ TEST_DIRS += [
'test_keysize',
'test_keysize_ev',
'test_missing_intermediate',
'test_name_constraints',
'test_ocsp_fetch_method',
'test_ocsp_url',
'test_onecrl',
'test_pinning_dynamic',
+ 'test_signed_apps',
'test_startcom_wosign',
'test_validity',
]
new file mode 100755
--- /dev/null
+++ b/security/manager/ssl/tests/unit/pycms.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env python
+#
+# 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/.
+
+"""
+Reads a specification from stdin and outputs a PKCS7 (CMS) message with
+the desired properties.
+
+The specification format is as follows:
+
+hash:<hex string>
+signer:
+<pycert specification>
+
+hash is the value that will be put in the messageDigest attribute in
+each SignerInfo of the signerInfos field of the SignedData.
+The certificate specification must come last.
+Currently only SHA-1 is supported.
+"""
+
+from pyasn1.codec.der import decoder
+from pyasn1.codec.der import encoder
+from pyasn1.type import tag, univ
+from pyasn1_modules import rfc2315, rfc2459
+import StringIO
+import base64
+import pycert
+import pykey
+import sys
+
+class Error(Exception):
+ """Base class for exceptions in this module."""
+ pass
+
+
+class UnknownDirectiveError(Error):
+ """Helper exception type to handle unknown specification
+ directives."""
+
+ def __init__(self, directive):
+ super(UnknownDirectiveError, self).__init__()
+ self.directive = directive
+
+ def __str__(self):
+ return 'Unknown directive %s' % repr(self.directive)
+
+
+class CMS(object):
+ """Utility class for reading a CMS specification and
+ generating a CMS message"""
+
+ def __init__(self, paramStream):
+ self.hash = ''
+ signerSpecification = StringIO.StringIO()
+ readingSignerSpecification = False
+ for line in paramStream.readlines():
+ if readingSignerSpecification:
+ print >>signerSpecification, line.strip()
+ elif line.strip() == 'signer:':
+ readingSignerSpecification = True
+ elif line.startswith('hash:'):
+ self.hash = line.strip()[len('hash:'):]
+ else:
+ raise UnknownDirectiveError(line.strip())
+ signerSpecification.seek(0)
+ self.signer = pycert.Certificate(signerSpecification)
+ self.signingKey = pykey.keyFromSpecification('default')
+
+ def buildAuthenticatedAttributes(self, value, implicitTag=None):
+ """Utility function to build a pyasn1 AuthenticatedAttributes
+ object. Useful because when building a SignerInfo, the
+ authenticatedAttributes needs to be tagged implicitly, but when
+ signing an AuthenticatedAttributes, it needs the explicit SET
+ tag."""
+ if implicitTag:
+ authenticatedAttributes = rfc2315.Attributes().subtype(implicitTag=implicitTag)
+ else:
+ authenticatedAttributes = rfc2315.Attributes()
+ contentTypeAttribute = rfc2315.Attribute()
+ # PKCS#9 contentType
+ contentTypeAttribute['type'] = univ.ObjectIdentifier('1.2.840.113549.1.9.3')
+ contentTypeAttribute['values'] = univ.SetOf(rfc2459.AttributeValue())
+ # PKCS#7 data
+ contentTypeAttribute['values'][0] = univ.ObjectIdentifier('1.2.840.113549.1.7.1')
+ authenticatedAttributes[0] = contentTypeAttribute
+ hashAttribute = rfc2315.Attribute()
+ # PKCS#9 messageDigest
+ hashAttribute['type'] = univ.ObjectIdentifier('1.2.840.113549.1.9.4')
+ hashAttribute['values'] = univ.SetOf(rfc2459.AttributeValue())
+ hashAttribute['values'][0] = univ.OctetString(hexValue=value)
+ authenticatedAttributes[1] = hashAttribute
+ return authenticatedAttributes
+
+ def pykeyHashToDigestAlgorithm(self, pykeyHash):
+ """Given a pykey hash algorithm identifier, builds an
+ AlgorithmIdentifier for use with pyasn1."""
+ if pykeyHash == pykey.HASH_SHA1:
+ algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+ algorithmIdentifier['algorithm'] = univ.ObjectIdentifier('1.3.14.3.2.26')
+ algorithmIdentifier['parameters'] = univ.Null()
+ return algorithmIdentifier
+ raise pykey.UnknownHashAlgorithmError(pykeyHash)
+
+ def buildSignerInfo(self, certificate, pykeyHash, digestValue):
+ """Given a pyasn1 certificate, a pykey hash identifier
+ and a hash value, creates a SignerInfo with the
+ appropriate values."""
+ signerInfo = rfc2315.SignerInfo()
+ signerInfo['version'] = 1
+ issuerAndSerialNumber = rfc2315.IssuerAndSerialNumber()
+ issuerAndSerialNumber['issuer'] = self.signer.getIssuer()
+ issuerAndSerialNumber['serialNumber'] = certificate['tbsCertificate']['serialNumber']
+ signerInfo['issuerAndSerialNumber'] = issuerAndSerialNumber
+ signerInfo['digestAlgorithm'] = self.pykeyHashToDigestAlgorithm(pykeyHash)
+ rsa = rfc2459.AlgorithmIdentifier()
+ rsa['algorithm'] = rfc2459.rsaEncryption
+ rsa['parameters'] = univ.Null()
+ authenticatedAttributes = self.buildAuthenticatedAttributes(digestValue,
+ implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))
+ authenticatedAttributesTBS = self.buildAuthenticatedAttributes(digestValue)
+ signerInfo['authenticatedAttributes'] = authenticatedAttributes
+ signerInfo['digestEncryptionAlgorithm'] = rsa
+ authenticatedAttributesEncoded = encoder.encode(authenticatedAttributesTBS)
+ signature = self.signingKey.sign(authenticatedAttributesEncoded, pykeyHash)
+ # signature will be a hexified bit string of the form
+ # "'<hex bytes>'H". For some reason that's what BitString wants,
+ # but since this is an OCTET STRING, we have to strip off the
+ # quotation marks and trailing "H".
+ signerInfo['encryptedDigest'] = univ.OctetString(hexValue=signature[1:-2])
+ return signerInfo
+
+ def toDER(self):
+ contentInfo = rfc2315.ContentInfo()
+ contentInfo['contentType'] = rfc2315.signedData
+
+ signedData = rfc2315.SignedData()
+ signedData['version'] = rfc2315.Version(1)
+
+ digestAlgorithms = rfc2315.DigestAlgorithmIdentifiers()
+ digestAlgorithms[0] = self.pykeyHashToDigestAlgorithm(pykey.HASH_SHA1)
+ signedData['digestAlgorithms'] = digestAlgorithms
+
+ dataContentInfo = rfc2315.ContentInfo()
+ dataContentInfo['contentType'] = rfc2315.data
+ signedData['contentInfo'] = dataContentInfo
+
+ certificates = rfc2315.ExtendedCertificatesAndCertificates().subtype(
+ implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))
+ extendedCertificateOrCertificate = rfc2315.ExtendedCertificateOrCertificate()
+ certificate = decoder.decode(self.signer.toDER(),
+ asn1Spec=rfc2459.Certificate())[0]
+ extendedCertificateOrCertificate['certificate'] = certificate
+ certificates[0] = extendedCertificateOrCertificate
+ signedData['certificates'] = certificates
+
+ signerInfos = rfc2315.SignerInfos()
+
+ signerInfos[0] = self.buildSignerInfo(certificate, pykey.HASH_SHA1, self.hash)
+ signedData['signerInfos'] = signerInfos
+
+ encoded = encoder.encode(signedData)
+ anyTag = univ.Any(encoded).subtype(
+ explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))
+
+ contentInfo['content'] = anyTag
+ return encoder.encode(contentInfo)
+
+ def toPEM(self):
+ output = '-----BEGIN PKCS7-----'
+ der = self.toDER()
+ b64 = base64.b64encode(der)
+ while b64:
+ output += '\n' + b64[:64]
+ b64 = b64[64:]
+ output += '\n-----END PKCS7-----'
+ return output
+
+
+# When run as a standalone program, this will read a specification from
+# stdin and output the certificate as PEM to stdout.
+if __name__ == '__main__':
+ print CMS(sys.stdin).toPEM()
old mode 100644
new mode 100755
rename from security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/sign_b2g_app.py
rename to security/manager/ssl/tests/unit/sign_app.py
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/sign_b2g_app.py
+++ b/security/manager/ssl/tests/unit/sign_app.py
@@ -1,176 +1,85 @@
-# flake8: noqa
+#!/usr/bin/env python
+#
+# 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 argparse
+"""
+Given a directory of files, packages them up and signs the
+resulting zip file. Mainly for creating test inputs to the
+nsIX509CertDB.openSignedAppFileAsync API.
+"""
from base64 import b64encode
from hashlib import sha1
-import sys
+import StringIO
+import argparse
+import os
+import pycms
+import re
import zipfile
-import ctypes
-
-import nss_ctypes
-
-# Change the limits in JarSignatureVerification.cpp when you change the limits
-# here.
-max_entry_uncompressed_len = 100 * 1024 * 1024
-max_total_uncompressed_len = 500 * 1024 * 1024
-max_entry_count = 100 * 1000
-max_entry_filename_len = 1024
-max_mf_len = max_entry_count * 50
-max_sf_len = 1024
-
-
-
-def nss_load_cert(nss_db_dir, nss_password, cert_nickname):
- nss_ctypes.NSS_Init(nss_db_dir)
- try:
- wincx = nss_ctypes.SetPasswordContext(nss_password)
- cert = nss_ctypes.PK11_FindCertFromNickname(cert_nickname, wincx)
- return (wincx, cert)
- except:
- nss_ctypes.NSS_Shutdown()
- raise
-def nss_create_detached_signature(cert, dataToSign, wincx):
- certdb = nss_ctypes.CERT_GetDefaultCertDB()
- p7 = nss_ctypes.SEC_PKCS7CreateSignedData(cert,
- nss_ctypes.certUsageObjectSigner,
- certdb,
- nss_ctypes.SEC_OID_SHA1,
- sha1(dataToSign).digest(),
- wincx )
- try:
- nss_ctypes.SEC_PKCS7AddSigningTime(p7)
- nss_ctypes.SEC_PKCS7IncludeCertChain(p7, wincx)
- return nss_ctypes.SEC_PKCS7Encode(p7, None, wincx)
- finally:
- nss_ctypes.SEC_PKCS7DestroyContentInfo(p7)
-
-# We receive a ids_json string for the toBeSigned app
-def sign_zip(in_zipfile_name, out_zipfile_name, cert, wincx, ids_json):
- mf_entries = []
- seen_entries = set()
+def walkDirectory(directory):
+ """Given a relative path to a directory, enumerates the
+ files in the tree rooted at that location. Returns a list
+ of pairs of paths to those files. The first in each pair
+ is the full path to the file. The second in each pair is
+ the path to the file relative to the directory itself."""
+ paths = []
+ for path, dirs, files in os.walk(directory):
+ for f in files:
+ fullPath = os.path.join(path, f)
+ internalPath = re.sub(r'^/', '', fullPath.replace(directory, ''))
+ paths.append((fullPath, internalPath))
+ return paths
- total_uncompressed_len = 0
- entry_count = 0
- with zipfile.ZipFile(out_zipfile_name, 'w') as out_zip:
- with zipfile.ZipFile(in_zipfile_name, 'r') as in_zip:
- for entry_info in in_zip.infolist():
- name = entry_info.filename
+def signZip(appDirectory, outputFile, issuerName, doSign):
+ """Given a directory containing the files to package up,
+ an output filename to write to, the name of the issuer of
+ the signing certificate, and whether or not to actually
+ sign the resulting package, packages up the files in the
+ directory and creates the output as appropriate."""
+ mfEntries = []
- # Check for reserved and/or insane (potentially malicious) names
- if name.endswith("/"):
- pass
- # Do nothing; we don't copy directory entries since they are just a
- # waste of space.
- elif name.lower().startswith("meta-inf/"):
- # META-INF/* is reserved for our use
- raise ValueError("META-INF entries are not allowed: %s" % (name))
- elif len(name) > max_entry_filename_len:
- raise ValueError("Entry's filename is too long: %s" % (name))
- # TODO: elif name has invalid characters...
- elif name in seen_entries:
- # It is possible for a zipfile to have duplicate entries (with the exact
- # same filenames). Python's zipfile module accepts them, but our zip
- # reader in Gecko cannot do anything useful with them, and there's no
- # sane reason for duplicate entries to exist, so reject them.
- raise ValueError("Duplicate entry in input file: %s" % (name))
- else:
- entry_count += 1
- if entry_count > max_entry_count:
- raise ValueError("Too many entries in input archive")
-
- seen_entries.add(name)
+ with zipfile.ZipFile(outputFile, 'w') as outZip:
+ for (fullPath, internalPath) in walkDirectory(appDirectory):
+ with open(fullPath) as inputFile:
+ contents = inputFile.read()
+ outZip.writestr(internalPath, contents)
- # Read in the input entry, but be careful to avoid going over the
- # various limits we have, to minimize the likelihood that we'll run
- # out of memory. Note that we can't use the length from entry_info
- # because that might not be accurate if the input zip file is
- # maliciously crafted to contain misleading metadata.
- with in_zip.open(name, 'r') as entry_file:
- contents = entry_file.read(max_entry_uncompressed_len + 1)
- if len(contents) > max_entry_uncompressed_len:
- raise ValueError("Entry is too large: %s" % (name))
- total_uncompressed_len += len(contents)
- if total_uncompressed_len > max_total_uncompressed_len:
- raise ValueError("Input archive is too large")
-
- # Copy the entry, using the same compression as used in the input file
- out_zip.writestr(entry_info, contents)
+ # Add the entry to the manifest we're building
+ base64hash = b64encode(sha1(contents).digest())
+ mfEntries.append(
+ 'Name: %s\nSHA1-Digest: %s\n' % (internalPath, base64hash))
- # Add the entry to the manifest we're building
- mf_entries.append('Name: %s\nSHA1-Digest: %s\n'
- % (name, b64encode(sha1(contents).digest())))
- if (ids_json):
- mf_entries.append('Name: %s\nSHA1-Digest: %s\n'
- % ("META-INF/ids.json", b64encode(sha1(ids_json).digest())))
-
- mf_contents = 'Manifest-Version: 1.0\n\n' + '\n'.join(mf_entries)
- if len(mf_contents) > max_mf_len:
- raise ValueError("Generated MANIFEST.MF is too large: %d" % (len(mf_contents)))
-
- sf_contents = ('Signature-Version: 1.0\nSHA1-Digest-Manifest: %s\n'
- % (b64encode(sha1(mf_contents).digest())))
- if len(sf_contents) > max_sf_len:
- raise ValueError("Generated SIGNATURE.SF is too large: %d"
- % (len(mf_contents)))
-
- p7 = nss_create_detached_signature(cert, sf_contents, wincx)
-
- # write the signature, SF, and MF
- out_zip.writestr("META-INF/A.RSA", p7, zipfile.ZIP_DEFLATED)
- out_zip.writestr("META-INF/A.SF", sf_contents, zipfile.ZIP_DEFLATED)
- out_zip.writestr("META-INF/MANIFEST.MF", mf_contents, zipfile.ZIP_DEFLATED)
- if (ids_json):
- out_zip.writestr("META-INF/ids.json", ids_json, zipfile.ZIP_DEFLATED)
+ # Just exit early if we're not actually signing.
+ if not doSign:
+ return
-def main():
- parser = argparse.ArgumentParser(description='Sign a B2G app.')
- parser.add_argument('-d', action='store',
- required=True, help='NSS database directory')
- parser.add_argument('-f', action='store',
- type=argparse.FileType('rb'),
- required=True, help='password file')
- parser.add_argument('-k', action='store',
- required=True, help="nickname of signing cert.")
- parser.add_argument('-i', action='store', type=argparse.FileType('rb'),
- required=True, help="input JAR file (unsigned)")
- parser.add_argument('-o', action='store', type=argparse.FileType('wb'),
- required=True, help="output JAR file (signed)")
- parser.add_argument('-I', '--ids-file', action='store', type=argparse.FileType('rb'),
- help="Path to the ids.json file", dest='I')
- parser.add_argument('-S', '--storeId', action='store',
- help="Store Id for the package", dest='S')
- parser.add_argument('-V', '--storeVersion', action='store', type=int,
- help="Package Version", dest='V')
- args = parser.parse_args()
-
- # Sadly nested groups and neccesarily inclusive groups (http://bugs.python.org/issue11588)
- # are not implemented. Note that this means the automatic help is slighty incorrect
- if not((not args.I and args.V and args.S) or (args.I and not args.V and not args.S)):
- raise ValueError("Either -I or -S and -V must be specified")
+ mfContents = 'Manifest-Version: 1.0\n\n' + '\n'.join(mfEntries)
+ base64hash = b64encode(sha1(mfContents).digest())
+ sfContents = 'Signature-Version: 1.0\nSHA1-Digest-Manifest: %s\n' % base64hash
+ cmsSpecification = 'hash:%s\nsigner:\n' % sha1(sfContents).hexdigest() + \
+ 'issuer:%s\n' % issuerName + \
+ 'subject:xpcshell signed app test signer\n' + \
+ 'extension:keyUsage:digitalSignature'
+ cmsSpecificationStream = StringIO.StringIO()
+ print >>cmsSpecificationStream, cmsSpecification
+ cmsSpecificationStream.seek(0)
+ cms = pycms.CMS(cmsSpecificationStream)
+ p7 = cms.toDER()
+ outZip.writestr('META-INF/A.RSA', p7)
+ outZip.writestr('META-INF/A.SF', sfContents)
+ outZip.writestr('META-INF/MANIFEST.MF', mfContents)
- if (args.I):
- ids_contents = args.I.read(max_entry_uncompressed_len+1)
- else:
- ids_contents = '''{
- "id": "%(id)s",
- "version": %(version)d
-}
-''' % {"id": args.S, "version": args.V}
- if len(ids_contents) > max_entry_uncompressed_len:
- raise ValueError("Entry is too large: %s" % (name))
-
- db_dir = args.d
- password = args.f.readline().strip()
- cert_nickname = args.k
-
- (wincx, cert) = nss_load_cert(db_dir, password, cert_nickname)
- try:
- sign_zip(args.i, args.o, cert, wincx, ids_contents)
- return 0
- finally:
- nss_ctypes.CERT_DestroyCertificate(cert)
- nss_ctypes.NSS_Shutdown()
-
-if __name__ == "__main__":
- sys.exit(main())
+def main(outputFile, appPath, *args):
+ """Main entrypoint. Given an already-opened file-like
+ object, a path to the app directory to sign, and some
+ optional arguments, signs the contents of the directory and
+ writes the resulting package to the 'file'."""
+ parser = argparse.ArgumentParser(description='Sign an app.')
+ parser.add_argument('-n', '--no-sign', action='store_true',
+ help='Don\'t actually sign - only create zip')
+ parser.add_argument('-i', '--issuer', action='store', help='Issuer name',
+ default='xpcshell signed apps test root')
+ parsed = parser.parse_args(args)
+ signZip(appPath, outputFile, parsed.issuer, not parsed.no_sign)
--- a/security/manager/ssl/tests/unit/test_cert_keyUsage.js
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage.js
@@ -11,18 +11,18 @@ var certdb = Cc["@mozilla.org/security/x
const caList = [ "ca-no-keyUsage-extension", "ca-missing-keyCertSign",
"ca-all-usages" ];
const eeList = [ "ee-no-keyUsage-extension", "ee-keyCertSign-only",
"ee-keyEncipherment-only", "ee-keyCertSign-and-keyEncipherment" ];
const caUsage = [ certificateUsageSSLCA ];
const allEEUsages = [ certificateUsageSSLClient, certificateUsageSSLServer,
- certificateUsageEmailSigner, certificateUsageEmailRecipient,
- certificateUsageObjectSigner ];
+ certificateUsageEmailSigner,
+ certificateUsageEmailRecipient ];
const serverEEUsages = [ certificateUsageSSLServer,
certificateUsageEmailRecipient ];
const expectedUsagesMap = {
"ca-no-keyUsage-extension": caUsage,
"ca-missing-keyCertSign": [],
"ca-all-usages": caUsage,
--- a/security/manager/ssl/tests/unit/test_cert_trust.js
+++ b/security/manager/ssl/tests/unit/test_cert_trust.js
@@ -31,34 +31,30 @@ function test_ca_distrust(ee_cert, cert_
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageSSLClient);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
certificateUsageSSLCA);
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageEmailSigner);
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageEmailRecipient);
- checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
- certificateUsageObjectSigner);
// Test of active distrust. No usage should pass.
setCertTrust(cert_to_modify_trust, "p,p,p");
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
certificateUsageSSLServer);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
certificateUsageSSLClient);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
certificateUsageSSLCA);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
certificateUsageEmailSigner);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
certificateUsageEmailRecipient);
- checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
- certificateUsageObjectSigner);
// Trust set to T - trusted CA to issue client certs, where client cert is
// usageSSLClient.
setCertTrust(cert_to_modify_trust, "T,T,T");
checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
: PRErrorCodeSuccess,
certificateUsageSSLServer);
@@ -71,69 +67,60 @@ function test_ca_distrust(ee_cert, cert_
certificateUsageSSLCA);
checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
: PRErrorCodeSuccess,
certificateUsageEmailSigner);
checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
: PRErrorCodeSuccess,
certificateUsageEmailRecipient);
- checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
- : PRErrorCodeSuccess,
- certificateUsageObjectSigner);
// Now tests on the SSL trust bit
setCertTrust(cert_to_modify_trust, "p,C,C");
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
certificateUsageSSLServer);
// XXX(Bug 982340)
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageSSLClient);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
certificateUsageSSLCA);
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageEmailSigner);
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageEmailRecipient);
- checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
- certificateUsageObjectSigner);
// Inherited trust SSL
setCertTrust(cert_to_modify_trust, ",C,C");
checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
: PRErrorCodeSuccess,
certificateUsageSSLServer);
// XXX(Bug 982340)
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageSSLClient);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
certificateUsageSSLCA);
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageEmailSigner);
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageEmailRecipient);
- checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
- certificateUsageObjectSigner);
// Now tests on the EMAIL trust bit
setCertTrust(cert_to_modify_trust, "C,p,C");
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageSSLServer);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
certificateUsageSSLClient);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
certificateUsageSSLCA);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
certificateUsageEmailSigner);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
certificateUsageEmailRecipient);
- checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
- certificateUsageObjectSigner);
// inherited EMAIL Trust
setCertTrust(cert_to_modify_trust, "C,,C");
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageSSLServer);
checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
: PRErrorCodeSuccess,
@@ -141,18 +128,16 @@ function test_ca_distrust(ee_cert, cert_
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
certificateUsageSSLCA);
checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
: PRErrorCodeSuccess,
certificateUsageEmailSigner);
checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
: PRErrorCodeSuccess,
certificateUsageEmailRecipient);
- checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
- certificateUsageObjectSigner);
}
function run_test() {
let certList = [
"ca",
"int",
"ee",
@@ -186,24 +171,20 @@ function run_test() {
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNKNOWN_ISSUER,
certificateUsageSSLServer);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNKNOWN_ISSUER,
certificateUsageSSLClient);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNKNOWN_ISSUER,
certificateUsageEmailSigner);
checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNKNOWN_ISSUER,
certificateUsageEmailRecipient);
- checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNKNOWN_ISSUER,
- certificateUsageObjectSigner);
// Now make a CA trust anchor available.
setCertTrust(ca_cert, "CTu,CTu,CTu");
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageSSLServer);
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageSSLClient);
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageEmailSigner);
checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
certificateUsageEmailRecipient);
- checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
- certificateUsageObjectSigner);
}
--- a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints.js
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints.js
@@ -20,18 +20,18 @@ function test_cert_for_usages(certChainN
}
let cert = certs[0];
return asyncTestCertificateUsages(certdb, cert, expected_usages);
}
add_task(async function() {
let ee_usages = [ certificateUsageSSLClient, certificateUsageSSLServer,
- certificateUsageEmailSigner, certificateUsageEmailRecipient,
- certificateUsageObjectSigner ];
+ certificateUsageEmailSigner,
+ certificateUsageEmailRecipient ];
let ca_usages = [ certificateUsageSSLCA ];
let eku_usages = [ certificateUsageSSLClient, certificateUsageSSLServer ];
// Load the ca into mem
let ca_name = "ca";
load_cert(ca_name, "CTu,CTu,CTu");
await test_cert_for_usages([ca_name], ca_usages);
--- a/security/manager/ssl/tests/unit/test_nss_shutdown.js
+++ b/security/manager/ssl/tests/unit/test_nss_shutdown.js
@@ -1,17 +1,17 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// This test attempts to ensure that PSM doesn't deadlock or crash when shutting
// down NSS while a background thread is attempting to use NSS.
-// Uses test_signed_apps/valid_app_1.zip from test_signed_apps.js.
+// Uses test_signed_apps/signed_app.zip from test_signed_apps.js.
function startAsyncNSSOperation(certdb, appFile) {
return new Promise((resolve, reject) => {
certdb.openSignedAppFileAsync(Ci.nsIX509CertDB.AppXPCShellRoot, appFile,
function(rv, aZipReader, aSignerCert) {
// rv will either indicate success (if NSS hasn't been shut down yet) or
// it will be some error code that varies depending on when NSS got shut
// down. As such, there's nothing really to check here. Just resolve the
@@ -23,17 +23,17 @@ function startAsyncNSSOperation(certdb,
add_task(async function() {
do_get_profile();
let psm = Cc["@mozilla.org/psm;1"]
.getService(Ci.nsISupports)
.QueryInterface(Ci.nsIObserver);
let certdb = Cc["@mozilla.org/security/x509certdb;1"]
.getService(Ci.nsIX509CertDB);
- let appFile = do_get_file("test_signed_apps/valid_app_1.zip");
+ let appFile = do_get_file("test_signed_apps/signed_app.zip");
let promises = [];
for (let i = 0; i < 25; i++) {
promises.push(startAsyncNSSOperation(certdb, appFile));
}
// Trick PSM into thinking it should shut down NSS. If this test doesn't
// hang or crash, we're good.
psm.observe(null, "profile-before-change", null);
--- a/security/manager/ssl/tests/unit/test_signed_apps.js
+++ b/security/manager/ssl/tests/unit/test_signed_apps.js
@@ -131,95 +131,95 @@ function original_app_path(test_name) {
}
function tampered_app_path(test_name) {
return FileUtils.getFile("TmpD", ["test_signed_app-" + test_name + ".zip"]);
}
add_test(function () {
certdb.openSignedAppFileAsync(
- Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("valid_app_1"),
+ Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("signed_app"),
check_open_result("valid", Cr.NS_OK));
});
add_test(function () {
certdb.openSignedAppFileAsync(
- Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unsigned_app_1"),
+ Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unsigned_app"),
check_open_result("unsigned", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED));
});
add_test(function () {
certdb.openSignedAppFileAsync(
- Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unknown_issuer_app_1"),
+ Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unknown_issuer_app"),
check_open_result("unknown_issuer",
getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)));
});
// Sanity check to ensure a no-op tampering gives a valid result
add_test(function () {
let tampered = tampered_app_path("identity_tampering");
- tamper(original_app_path("valid_app_1"), tampered, { }, []);
+ tamper(original_app_path("signed_app"), tampered, { }, []);
certdb.openSignedAppFileAsync(
- Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("valid_app_1"),
+ Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("signed_app"),
check_open_result("identity_tampering", Cr.NS_OK));
});
add_test(function () {
let tampered = tampered_app_path("missing_rsa");
- tamper(original_app_path("valid_app_1"), tampered, { "META-INF/A.RSA": removeEntry }, []);
+ tamper(original_app_path("signed_app"), tampered, { "META-INF/A.RSA": removeEntry }, []);
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("missing_rsa", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED));
});
add_test(function () {
let tampered = tampered_app_path("missing_sf");
- tamper(original_app_path("valid_app_1"), tampered, { "META-INF/A.SF": removeEntry }, []);
+ tamper(original_app_path("signed_app"), tampered, { "META-INF/A.SF": removeEntry }, []);
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("missing_sf", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
});
add_test(function () {
let tampered = tampered_app_path("missing_manifest_mf");
- tamper(original_app_path("valid_app_1"), tampered, { "META-INF/MANIFEST.MF": removeEntry }, []);
+ tamper(original_app_path("signed_app"), tampered, { "META-INF/MANIFEST.MF": removeEntry }, []);
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("missing_manifest_mf",
Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
});
add_test(function () {
let tampered = tampered_app_path("missing_entry");
- tamper(original_app_path("valid_app_1"), tampered, { "manifest.webapp": removeEntry }, []);
+ tamper(original_app_path("signed_app"), tampered, { "manifest.json": removeEntry }, []);
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("missing_entry", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING));
});
add_test(function () {
let tampered = tampered_app_path("truncated_entry");
- tamper(original_app_path("valid_app_1"), tampered, { "manifest.webapp": truncateEntry }, []);
+ tamper(original_app_path("signed_app"), tampered, { "manifest.json": truncateEntry }, []);
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("truncated_entry", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY));
});
add_test(function () {
let tampered = tampered_app_path("unsigned_entry");
- tamper(original_app_path("valid_app_1"), tampered, {},
+ tamper(original_app_path("signed_app"), tampered, {},
[ { "name": "unsigned.txt", "content": "unsigned content!" } ]);
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("unsigned_entry", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
});
add_test(function () {
let tampered = tampered_app_path("unsigned_metainf_entry");
- tamper(original_app_path("valid_app_1"), tampered, {},
+ tamper(original_app_path("signed_app"), tampered, {},
[ { name: "META-INF/unsigned.txt", content: "unsigned content!" } ]);
certdb.openSignedAppFileAsync(
Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
check_open_result("unsigned_metainf_entry",
Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
});
// TODO: tampered MF, tampered SF
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app/README
@@ -0,0 +1,1 @@
+This is the readme for the test extension.
copy from dom/manifest/test/blue-150.png
copy to security/manager/ssl/tests/unit/test_signed_apps/app/data/image.png
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app/manifest.json
@@ -0,0 +1,5 @@
+{
+ "manifest_version": 2,
+ "name": "Test Extension",
+ "version": "0.0.1"
+}
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/README.md
+++ /dev/null
@@ -1,18 +0,0 @@
-This file contains the scripts and binary files needed to regenerate the signed
-files used on the signed apps test.
-
-Prerequisites:
-
-* NSS 3.4 or higher.
-* Python 2.7 (should work with 2.6 also)
-* Bash
-
-Usage:
-
-Run
-
-./create_test_files.sh
-
-The new test files will be created at the ./testApps directory. Just copy the
-contents of this directory to the test directory (dom/apps/tests/signed and
-security/manager/ssl/tests/unit) to use the new files.
deleted file mode 100755
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/create_test_files.sh
+++ /dev/null
@@ -1,213 +0,0 @@
-#!/bin/bash
-
-export NSS_DEFAULT_DB_TYPE=sql
-
-export BASE_PATH=`dirname $0`
-export SIGN_SCR_LOC=.
-export APPS_TEST_LOC=../../../../../../../dom/apps/tests/signed
-export TOOLKIT_WEBAPPS_TEST_LOC=../../../../../../../toolkit/webapps/tests/data/
-
-# Creates the entry zip files (unsigned apps) from the source directories
-packageApps() {
-APPS="unsigned_app_1 unsigned_app_origin unsigned_app_origin_toolkit_webapps"
-OLD_PWD=`pwd`
-cd ${BASE_PATH}
-for i in $APPS
-do
- echo "Creating $i.zip"
- cd $i && zip -r ../$i.zip . && cd ..
-done
-cd ${OLD_PWD}
-}
-
-
-# Function to create a signing database
-# Parameters:
-# $1: Output directory (where the DB will be created)
-createDb() {
-
- db=$1
-
- mkdir -p $db
-
- # Insecure by design, so... please don't use this for anything serious
- passwordfile=$db/passwordfile
-
- echo insecurepassword > $passwordfile
- certutil -d $db -N -f $passwordfile 2>&1 >/dev/null
-
-}
-
-# Add a CA cert and a signing cert to the database
-# Arguments:
-# $1: DB directory
-# $2: CA CN (don't include the CN=, just the value)
-# $3: Signing Cert CN (don't include the CN=, just the value)
-# $4: CA short name (don't use spaces!)
-# $5: Signing Cert short name (don't use spaces!)
-addCerts() {
- org="O=Examplla Corporation,L=Mountain View,ST=CA,C=US"
- ca_subj="CN=${2},${org}"
- ee_subj="CN=${3},${org}"
-
- noisefile=/tmp/noise.$$
- head -c 32 /dev/urandom > $noisefile
-
- ca_responses=/tmp/caresponses.$$
- ee_responses=/tmp/earesponses
-
- echo y > $ca_responses # Is this a CA?
- echo >> $ca_responses # Accept default path length constraint (no constraint)
- echo y >> $ca_responses # Is this a critical constraint?
- echo n > $ee_responses # Is this a CA?
- echo >> $ee_responses # Accept default path length constraint (no constraint)
- echo y >> $ee_responses # Is this a critical constraint?
-
- make_cert="certutil -d $db -f $passwordfile -S -g 2048 -Z SHA256 \
- -z $noisefile -y 3 -2 --extKeyUsage critical,codeSigning"
- $make_cert -v 480 -n ${4} -m 1 -s "$ca_subj" \
- --keyUsage critical,certSigning -t ",,CTu" -x < $ca_responses 2>&1 >/dev/null
- $make_cert -v 240 -n ${5} -c ${4} -m 2 -s "$ee_subj" \
- --keyUsage critical,digitalSignature -t ",,," < $ee_responses 2>&1 >/dev/null
-
- # In case we want to inspect the generated certs
-
- # Also, we'll need this one later on
- certutil -d $db -L -n ${4} -r -o $db/${4}.der
- certutil -d $db -L -n ${5} -r -o $db/${5}.der
-
- rm -f $noisefile $ee_responses $ca_responses
-}
-
-
-# Signs an app
-# Parameters:
-# $1: Database directory
-# $2: Unsigned ZIP file path
-# $3: Signed ZIP file path
-# $4: Store ID for the signed App
-# $5: Version of the signed App
-# $6: Nickname of the signing certificate
-signApp() {
-
- db=$1
-
- # Once again, this is INSECURE. It doesn't matter here but
- # DON'T use this for anything production related
- passwordfile=$db/passwordfile
-
- python ${BASE_PATH}/${SIGN_SCR_LOC}/sign_b2g_app.py -d $db -f $passwordfile \
- -k ${6} -i ${2} -o ${3} -S ${4} -V ${5}
-}
-
-DB_PATH=${BASE_PATH}/signingDB
-TEST_APP_PATH=${BASE_PATH}/testApps
-
-echo "Warning! The directories ${DB_PATH} and ${TEST_APP_PATH} will be erased!"
-echo "Do you want to proceed anyway?"
-select answer in "Yes" "No"
-do
- case $answer in
- Yes) break;;
- No) exit 1;;
- esac
-done
-
-rm -rf ${DB_PATH} ${TEST_APP_PATH}
-
-TRUSTED_EE=trusted_ee1
-UNTRUSTED_EE=untrusted_ee1
-TRUSTED_CA=trusted_ca1
-UNTRUSTED_CA=untrusted_ca1
-
-# First, we'll create a new couple of signing DBs
-createDb $DB_PATH
-addCerts $DB_PATH "Valid CA" "Store Cert" trusted_ca1 ${TRUSTED_EE}
-addCerts $DB_PATH "Invalid CA" "Invalid Cert" ${UNTRUSTED_CA} ${UNTRUSTED_EE}
-
-# Then we'll create the unsigned apps
-echo "Creating unsigned apps"
-packageApps
-
-# And then we'll create all the test apps...
-mkdir -p ${TEST_APP_PATH}
-
-# We need:
-# A valid signed file, with two different versions:
-# valid_app_1.zip
-# valid_app_2.zip
-VALID_UID=`uuidgen`
-signApp $DB_PATH ${BASE_PATH}/unsigned_app_1.zip \
- $TEST_APP_PATH/valid_app_1.zip \
- $VALID_UID 1 ${TRUSTED_EE}
-signApp $DB_PATH ${BASE_PATH}/unsigned_app_1.zip \
- $TEST_APP_PATH/valid_app_2.zip \
- $VALID_UID 2 ${TRUSTED_EE}
-
-
-# A corrupt_package:
-# corrupt_app_1.zip
-# A corrupt package is a package with a entry modified, for example...
-CURDIR=`pwd`
-export TEMP_DIR=$TEST_APP_PATH/aux_unzip_$$
-mkdir -p $TEMP_DIR
-cd $TEMP_DIR
-unzip ../valid_app_1.zip 2>&1 >/dev/null
-echo " - " >> index.html
-zip -r ../corrupt_app_1.zip * 2>&1 >/dev/null
-cd $CURDIR
-rm -rf $TEMP_DIR
-
-# A file signed by a unknown issuer
-# unknown_issuer_app_1.zip
-INVALID_UID=`uuidgen`
-signApp $DB_PATH ${BASE_PATH}/unsigned_app_1.zip \
- $TEST_APP_PATH/unknown_issuer_app_1.zip \
- $INVALID_UID 1 ${UNTRUSTED_EE}
-
-# And finally a priviledged signed file that includes the origin on the manifest
-# to avoid that reverting again
-PRIV_UID=`uuidgen`
-signApp $DB_PATH ${BASE_PATH}/unsigned_app_origin.zip \
- $TEST_APP_PATH/origin_app_1.zip \
- $PRIV_UID 1 ${TRUSTED_EE}
-
-# A privileged signed app needed for a toolkit/webapps test
-PRIV_TOOLKIT_UID=`uuidgen`
-signApp $DB_PATH ${BASE_PATH}/unsigned_app_origin_toolkit_webapps.zip \
- $TEST_APP_PATH/custom_origin.zip \
- $PRIV_TOOLKIT_UID 1 ${TRUSTED_EE}
-
-# Now let's copy the trusted cert to the app directory so we have everything
-# on the same place...
-cp ${DB_PATH}/${TRUSTED_CA}.der ${TEST_APP_PATH}
-
-cat <<EOF
-
-All done. The new test files are in ${TEST_APP_PATH}. You should copy the
-contents of that directory to the dom/apps/tests/signed directory and to
-the security/manager/ssl/tests/unit/test_signed_apps (which should be the
-parent of this directory) to install them.
-
-EOF
-
-echo "Do you wish me to do that for you now?"
-select answer in "Yes" "No"
-do
- case $answer in
- Yes) break;;
- No) echo "Ok, not installing the new files"
- echo "You should run: "
- echo cp ${TEST_APP_PATH}/* ${BASE_PATH}/${APPS_TEST_LOC}
- echo cp ${TEST_APP_PATH}/* ${TEST_APP_PATH}/../unsigned_app_1.zip ${BASE_PATH}/..
- echo cp ${TEST_APP_PATH}/* ${BASE_PATH}/${TOOLKIT_WEBAPPS_TEST_LOC}
- echo "to install them"
- exit 0;;
- esac
-done
-
-cp ${TEST_APP_PATH}/* ${BASE_PATH}/${APPS_TEST_LOC}
-cp ${TEST_APP_PATH}/* ${TEST_APP_PATH}/../unsigned_app_1.zip ${BASE_PATH}/..
-cp ${TEST_APP_PATH}/* ${BASE_PATH}/${TOOLKIT_WEBAPPS_TEST_LOC}
-
-echo "Done!"
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/nss_ctypes.py
+++ /dev/null
@@ -1,131 +0,0 @@
-# flake8: noqa
-
-from ctypes import *
-import os
-import sys
-
-if sys.platform == 'darwin':
- libprefix = "lib"
- libsuffix = ".dylib"
-elif os.name == 'posix':
- libprefix = "lib"
- libsuffix = ".so"
-else: # assume windows
- libprefix = ""
- libsuffix = ".dll"
-
-plc = cdll.LoadLibrary(libprefix + "plc4" + libsuffix)
-nspr = cdll.LoadLibrary(libprefix + "nspr4" + libsuffix)
-nss = cdll.LoadLibrary(libprefix + "nss3" + libsuffix)
-smime = cdll.LoadLibrary(libprefix + "smime3" + libsuffix)
-
-nspr.PR_GetError.argtypes = []
-nspr.PR_GetError.restype = c_int32
-nspr.PR_ErrorToName.argtypes = [c_int32]
-nspr.PR_ErrorToName.restype = c_char_p
-
-def raise_if_not_SECSuccess(rv):
- SECSuccess = 0
- if (rv != SECSuccess):
- raise ValueError(nspr.PR_ErrorToName(nspr.PR_GetError()))
-
-def raise_if_NULL(p):
- if not p:
- raise ValueError(nspr.PR_ErrorToName(nspr.PR_GetError()))
- return p
-
-PRBool = c_int
-SECStatus = c_int
-
-# from secoidt.h
-SEC_OID_SHA1 = 4
-
-# from certt.h
-certUsageObjectSigner = 6
-
-class SECItem(Structure):
- _fields_ = [("type", c_int),
- ("data", c_char_p),
- ("len", c_uint)]
-
-nss.NSS_Init.argtypes = [c_char_p]
-nss.NSS_Init.restype = SECStatus
-def NSS_Init(db_dir):
- nss.NSS_Init.argtypes = [c_char_p]
- nss.NSS_Init.restype = SECStatus
- raise_if_not_SECSuccess(nss.NSS_Init(db_dir))
-
-nss.NSS_Shutdown.argtypes = []
-nss.NSS_Shutdown.restype = SECStatus
-def NSS_Shutdown():
- raise_if_not_SECSuccess(nss.NSS_Shutdown())
-
-PK11PasswordFunc = CFUNCTYPE(c_char_p, c_void_p, PRBool, c_char_p)
-
-# pass the result of this as the wincx parameter when a wincx is required
-nss.PK11_SetPasswordFunc.argtypes = [PK11PasswordFunc]
-nss.PK11_SetPasswordFunc.restype = None
-
-# Set the return type as *void so Python doesn't touch it
-plc.PL_strdup.argtypes = [c_char_p]
-plc.PL_strdup.restype = c_void_p
-def SetPasswordContext(password):
- def callback(slot, retry, arg):
- return plc.PL_strdup(password)
- wincx = PK11PasswordFunc(callback)
- nss.PK11_SetPasswordFunc(wincx)
- return wincx
-
-nss.CERT_GetDefaultCertDB.argtypes = []
-nss.CERT_GetDefaultCertDB.restype = c_void_p
-def CERT_GetDefaultCertDB():
- return raise_if_NULL(nss.CERT_GetDefaultCertDB())
-
-nss.PK11_FindCertFromNickname.argtypes = [c_char_p, c_void_p]
-nss.PK11_FindCertFromNickname.restype = c_void_p
-def PK11_FindCertFromNickname(nickname, wincx):
- return raise_if_NULL(nss.PK11_FindCertFromNickname(nickname, wincx))
-
-nss.CERT_DestroyCertificate.argtypes = [c_void_p]
-nss.CERT_DestroyCertificate.restype = None
-def CERT_DestroyCertificate(cert):
- nss.CERT_DestroyCertificate(cert)
-
-smime.SEC_PKCS7CreateSignedData.argtypes = [c_void_p, c_int, c_void_p,
- c_int, c_void_p,
- c_void_p, c_void_p]
-smime.SEC_PKCS7CreateSignedData.restype = c_void_p
-def SEC_PKCS7CreateSignedData(cert, certusage, certdb, digestalg, digest, wincx):
- item = SECItem(0, c_char_p(digest), len(digest))
- return raise_if_NULL(smime.SEC_PKCS7CreateSignedData(cert, certusage, certdb,
- digestalg,
- pointer(item),
- None, wincx))
-
-smime.SEC_PKCS7AddSigningTime.argtypes = [c_void_p]
-smime.SEC_PKCS7AddSigningTime.restype = SECStatus
-def SEC_PKCS7AddSigningTime(p7):
- raise_if_not_SECSuccess(smime.SEC_PKCS7AddSigningTime(p7))
-
-smime.SEC_PKCS7IncludeCertChain.argtypes = [c_void_p, c_void_p]
-smime.SEC_PKCS7IncludeCertChain.restype = SECStatus
-def SEC_PKCS7IncludeCertChain(p7, wincx):
- raise_if_not_SECSuccess(smime.SEC_PKCS7IncludeCertChain(p7, wincx))
-
-SEC_PKCS7EncoderOutputCallback = CFUNCTYPE(None, c_void_p, c_void_p, c_long)
-smime.SEC_PKCS7Encode.argtypes = [c_void_p, SEC_PKCS7EncoderOutputCallback,
- c_void_p, c_void_p, c_void_p, c_void_p]
-smime.SEC_PKCS7Encode.restype = SECStatus
-def SEC_PKCS7Encode(p7, bulkkey, wincx):
- outputChunks = []
- def callback(chunks, data, len):
- outputChunks.append(string_at(data, len))
- callbackWrapper = SEC_PKCS7EncoderOutputCallback(callback)
- raise_if_not_SECSuccess(smime.SEC_PKCS7Encode(p7, callbackWrapper,
- None, None, None, wincx))
- return "".join(outputChunks)
-
-smime.SEC_PKCS7DestroyContentInfo.argtypes = [c_void_p]
-smime.SEC_PKCS7DestroyContentInfo.restype = None
-def SEC_PKCS7DestroyContentInfo(p7):
- smime.SEC_PKCS7DestroyContentInfo(p7)
deleted file mode 100644
index d6fd07a4167d2f05a9a13a2eaca6a9981287dfb6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!doctype html>
-<html lang=en>
-<head><meta charset=utf-8><title>Simple App</title></head>
-<body><p>This is a Simple App.</body>
-</html>
-
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/manifest.webapp
+++ /dev/null
@@ -1,8 +0,0 @@
-{ "name": "Simple App"
-, "description": "A Simple Open Web App"
-, "launch_path": "tests/dom/apps/tests/signed_app.sjs"
-, "icons": { "128" : "icon-128.png" }
-, "installs_allowed_from": [ "https://marketplace.mozilla.com", "http://mochi.test:8888" ]
-, "version": 1
-, "default_locale": "en-US"
-}
deleted file mode 100644
index d6fd07a4167d2f05a9a13a2eaca6a9981287dfb6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!doctype html>
-<html lang=en>
-<head><meta charset=utf-8><title>Simple App</title></head>
-<body><p>This is a Simple App.</body>
-</html>
-
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/manifest.webapp
+++ /dev/null
@@ -1,10 +0,0 @@
-{ "name": "Simple App",
- "description": "A Simple Open Web App",
- "type": "privileged",
- "origin": "app://test.origin.privileged.app",
- "launch_path": "tests/dom/apps/tests/signed_app.sjs",
- "icons": { "128" : "icon-128.png" },
- "installs_allowed_from": [ "https://marketplace.mozilla.com", "http://mochi.test:8888" ],
- "version": 1,
- "default_locale": "en-US"
-}
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/index.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<title>Test app</title>
-</head>
-<body>
-Test app:
-<iframe src="http://127.0.0.1:8888/chrome/toolkit/webapps/tests/app.sjs?appreq"></iframe>
-</body>
-</html>
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/manifest.webapp
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "Custom Origin Test",
- "version": 1,
- "size": 777,
- "package_path": "custom_origin.zip",
- "launch_path": "/index.html",
- "origin": "app://test.origin.privileged.app",
- "type": "privileged"
-}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/moz.build
@@ -0,0 +1,34 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+@template
+def SignedAppFile(name, flags):
+ if not CONFIG['COMPILE_ENVIRONMENT']:
+ return
+
+ GENERATED_FILES += [name]
+ props = GENERATED_FILES[name]
+ props.script = '/security/manager/ssl/tests/unit/sign_app.py'
+ props.inputs = ['app/']
+ if flags:
+ props.flags = flags
+ # Turn RELATIVEDIR into list entry: like
+ # 'security/manager/ssl/tests/unit/test_signed_apps' ->
+ # TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_signed_apps.
+ files = TEST_HARNESS_FILES.xpcshell
+ for part in RELATIVEDIR.split('/'):
+ files = files[part]
+ files += ['!%s' % name]
+
+# Temporarily disabled. See bug 1256495.
+#signed_app_files = (
+# ['signed_app.zip'],
+# ['unknown_issuer_app.zip', '-i', 'unknown issuer'],
+# ['unsigned_app.zip', '-n'],
+#)
+#
+#for signed_app_file_params in signed_app_files:
+# SignedAppFile(signed_app_file_params[0], signed_app_file_params[1:])
deleted file mode 100644
index 3a12106c8fc0ad3f4bccc517eba2a3012ec57103..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
rename from security/manager/ssl/tests/unit/test_signed_apps/valid_app_1.zip
rename to security/manager/ssl/tests/unit/test_signed_apps/signed_app.zip
index 74a35a4208dcee164af51abcdd5715f3d90a67ea..022181dc73b95307ea3d5597b1933fdd52606f34
GIT binary patch
literal 2813
zc$^FHW@Zs#00D#OAaAJ{jZJ<)HVE?qac*K>W?E`-iC$K5eqJ?~f`SrEB)%-Qs5mn}
zPsvKbNCza5mzbLh<S2yzB^6vNN>cN{(qK7+S|tNL13g0}u3D}D6pO!k80u&P*&xgY
z#6hl(F21fI8JWcjKva^Es!)`gn39{Skd|Kr=9Yl0NrhRXhi2UDCQEiPCI$u&<^zhQ
zB$g!VXXYlRr|K2trFRDSx$|;Kae+MP>ERLtq^E%}2gqg{#==Jo42*X@T^vIy=Da<(
zk(0qd;K+vC^)Js0F*X-2jJy4A(*F+*b4JmSMLlu}1xi)>WQ6yC(u=37pUXO@geKG!
zlp(xg&P^5u1`r0NAYfv2)b;dp(|6Ph3U)MTV!6u3snzDu_MMlJooPW6%Q2wPAtpvf
zLv903HdbvuW+o|C1_N${BE|(xOkWI|m_8UZG2U9h%*4pVBqBPea@l9@ptbJ~KVbVf
z!zZ{<_xo2QJ&fE2nuh8Is%*@mEX+J|6$Qz~8L2rr3dNb}d8sK1i3J74kbF>-pI>62
zB*AZFU}$P+Xk=jk22o%E3j-qqQ>cJ}mZ64$8pJqxEXF}h1M`c3&QvnsMYx5Lm4Ug5
zk)Hu5&c)Qk$jGp(BXEUFpV&3Wzj@m_*vtfMZG>;WdSdFe<;~ZO@En!ugU3}@Jh&mh
zcjFwk*?CfJpRymVk1Ea1N-5;1S{k+7wf(!yRh2nRb!VOb$YoxzP|%#bTH&zgliNFe
z4rS-?y%lZZ|5&#+Q)=U7CaZ2O$9KyM9`N(_{!Qc7-k>sRN$t*##L5X9!zb8YvOfA?
zhll(M^UEF1@72X_gl<oZT2;q1LEoxcpn*;0j7-k-wbK%#w?CRDbfDC5G0()iTTXrd
zvsPC1GUxl9;(W_99&WDISIW<PyrpYuqU<}hYpQcsPvr13J1wKaqO)JDTmR|D{+Z&2
zJ2wCGRQ^>Ic*O83^O{={T&8b(nV1<F7#H&!@B(9uIaHRPg@u`ky#Xcqm_gxWW^Lsz
zy;zH3CU?abzEk%z^A~pH*t_-_w8x6(t8`mDEq^jePG#j|cRAhDk^u^LS_+nkUYz&!
z|IKP~u6Coz#~#__-T(AaGU>$gEm;TC6zYQKF<z}&djIs-#Xc^3XHNTbWLBc~k6WMq
zye&VH;ks?NyxB#|k4G=2J@cGwa(v>&t!LsB?KDLa_j7S<QuVqu?O+4%<L2W3d-A>4
zxEpzD36`0?5;)9vNy6!^+OO?74%eTB@J_B!TP{)=HjgWDdieX<ucR$cpZ>OF=gau-
zKLqA?l}Rqycx%B!`RtvOmq;~Tn7Z=S%<Esz*Ir)vHTcqwlIj^JgI(K$4?53G-y*<v
zj>j&-%Yf0aiE$+`(JTdK4g*IL5;TtNfyiA8><uK~fz8Qmh@9CBlo2v4hGHxtKaY1*
zhh^6;h<(0TiK)!Y^IEG>En4zsX<}puSaroW>}iy!ot2UnciP^YUr#;=;Wb^5zg?~R
zS6{GS;J5jV#r!unNcWtYRXJ^aMuhu|uJA7DbN{0jF!mmf*5Pc}t+Q%&TC}g%P0#Yk
zpVHY!W%ph7T3o<%h^b8Yv8m02{S9w-Y}=O~`&#qg^Rv5sv}$=5gc@Abp78ye-Twlk
zoF&s9hA&CIULx7!{^!%G#d9_kYo<0DM;p{_syO!TfVJkWrCBD|UX))<nP6D*zOCli
z`@`K~cf?P9`L#hiZo%PuS7p{5QSlA(3|PBy<&P^%)~EXjh`(RQapPV4Mdm#(7Fl;m
zsp!sDl6<pDwQ>Dw@88?my<NE9Nfj|oYt-MuCY~uh$sM&2OkbD##0$vwf;I<`3c+Bv
zU|^&rmXsEy>V`q8cPj-$Jp-;_4@W~?m&|lvH0t_78w*wnUan@I!7fgw$yrHGhEXPw
zg~{oWX@OBjE}<s2XmvMR^0tF#fou>KLAKP_(a+P(H8@1i*9~Scs?}V6z&41L0!pI=
z**#VYQHdc=X$4UM?m0fjC7GF?r9oK+25#9&=K1BeFzt}02dX}|l28xFEQ^Tz!VFXA
zsFLJx3k#335bgZJOkcP@)HVjHe&bB1JhM`_O!HKCZ~dSgFTV<d(16O2>|pbJkoOsx
zM3`|m!eF3*kpaC)hOQO88HLbV32#)P>p^cTA@rQUXgQ(lMXx0hdbya<>r8aL=!pxV
km!AVIsiA8{Pa+7dEgaYrOn^5lI6T;ZFa;Qn&72?}03}TjumAu6
deleted file mode 100644
index ec137ea14ad68f022490465e2630b52af2b3d3c2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
rename from security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app_1.zip
rename to security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app.zip
index 374e45f6f83d20a0df2b0b64efa906b7f70a84b3..177f545d478d99507224c8e2e31690b68a9d652c
GIT binary patch
literal 2781
zc$^FHW@Zs#0D;djLEcg^8k_upY!K!J;@rf%%(T?v61}YA{Jd%|1qCITNPJmpQE_H|
zo|2V<kq$^AFEKY2$WaObN-DTkl%(c?rNMFtwMqth26~1{T(w*QC>DS7Fx1fovO$;)
zh=W`mU3^_bGBS%5fT$!RRiP*~F(o%uAuYcM%q;;~lM1s&56!sQO_uCpObiSl%m)-p
zNi0d!&&*9sPt_~POYaQubLZuf;sSZn)59eQNKXS{4v@_@jD?RF7#Qz*x;TbZ%z1lm
zBPWA_z>y8N>tCK1Vr(v47<c>Kr2iir=8U2ti+bb|3Y4n$$q4TOr58_EKbLh*2~DUe
zXwRMP20K|87(f`5f`Ez9QP<PYP2W*3DA>`UiDfGrr&gOs+jm|@cBTbQEX#mGi<lT0
z4Y>_C*;uvtn3<$l84S1)iWnC(F+DM8VtQcE#CU1}GZP~dlSu5hJGvi(!f#nQtetW=
zbXJT8^K(BWJ&fE2l7`|2qHN5eEX+K7rFq$T`Q>@Q3{hN~T4bOk!Ea<>XliI^WMKdX
zQD6ZJ10w@dsDOc%p@xAPM7w-NL2_|MYEF(qab|j6YKlT)K>;Kef%!#1XDJ!*BJ5^l
zWngY%<Yxeib1^kBGBWJy2wdUPCw9&8Z{D^JHZuWR8{wO;o|t-VdGj?RJV&Ma;BnOz
z4{pfs-8hGBcAiw*r|d`Tqe`>0QVKb$mPRdiZT~KFRb>uS-C5^9a+wz_6f`HVRygeW
z<n~UVL)kfeZ$+E<Kh~|yl-hWi$*Nn+@!j%*2mHLff77_NH>gZnQoFMwv2w!3@Cmk;
ztdBm};UT}m{Bno$dv&oJq1)4<R@E_0(6_1<Xkb%0Ba<_I?X<+`?T@Aj9Vj(i%ro)s
zmQ&yVtd&*0%=vz&IN$P&hnuVQmGUzmZ|RzvDEm(Bn(Exu6FL0MPRppU=<FBk)_?l3
zf2O$Mj?Mo(m46il9x=Siyylh!m+9MHCT2zk#>M;wyucV@4wdC+VPR%sZ$ODIW>7e3
z-rn=8dcM%@i##qxb=FrBD)_!1Y~1kdC*#qzK1wOfm%hJl=leNRpCwtl+Vr5)<QFG7
zW?E<c%I{-5^McJY{LAur&+XR;adh5ZUtQzepuhbjzt@@L9>?a@zLxOh<#N0ky<#ix
ztnF`C9J_E;aq_|DOB2uUU2*Ef<o%PUK0c^^AxiiAgOaS=YUNGR-f^*W`ORjYJ#;Q?
zK1;b*#_paiijr@#7S7zybnxRI<};VP{U<~#JzwGH?a=mo^Lc?7hWe$uoBGeyh^^k_
zYkl5rLD#|AlOB78zd7+@r$nd6_J14fE%$tO63@<=(Y)sM^5g&YU!`?&FJZRYxrw_y
zMD+s0PP^8{^N&q3Y+{@VOf*x0nZdviPXfk~{1LflfxUqQJa9Rg4UsdGfigmd#ZZhz
z<md5@>agtE1+mW;D>0Rsd0uNZszpoAEKQ6I0jsX~hCPiEwX;&v;!fLp^Xth6A-tvw
z^0%v1|LP0&3;Z^pv6%nn2I-ztvnr>p&xmk;(G}h$eeQqM0><9M(K?(ByLDF0PK);S
zy6IUS`BOUksO-MWUW*Hu4l$JpKQ^^_u)pE$j&1w$V_$3jdwzDek5(=3f>48t+7rHC
zv-@9Ql(S^o!|)}k*GnXO-2Z%9wRp~kV$IY><7k7rO%=z!9kABCwKU7*+KcjwDH9A!
z-nZ2pdw;k)?2h=UFTXZu$1ON~@2bq2BPzZ@o&jq&uKaOj$@+930rB_iIBvXazsS7j
z#Ukr2DHYw>N|J9@sWz@(?frW@ySEGXJE<b3X^r|@*u*oXC%K~*a_Q@GpLhY;UeM|u
zsgMhH3kDVoi6x~)sk&j1>f1`eP|tuX*u&9K*CjI@SaRt4LK_2C3SO>ep203orpZ}J
zPKHq?k%h_Wk!gWZMlPWywrF)TTk^JpXMt=G7D2Yu*U`_@%{4eg&({rRFsjvDe!w<{
zl>$nm1lc`S3Q>t6PH6>E0q!|I#wD4Vo~1!q1_o}~N#^<GwlM9GrUj}#w~|l~$1ID8
z{K5=V=ctn8a0?5MvJmb3!c1SdKGZe@s(#~4r#!P#w@mX?cW?cm9525LgV2D=knCXd
ze317UnM9azH?m-$fsp~d$%U>Jy_tm2S_yA7q3c0!3nBEJz-Sqv>qV~x5qi0p(d$HX
pz37Pxq4zyIT2e#Tik?IeT2nZ%Czt?lR&aQ*0bvR-9FsXgJOIG84%Ywx
rename from security/manager/ssl/tests/unit/test_signed_apps/unsigned_app_1.zip
rename to security/manager/ssl/tests/unit/test_signed_apps/unsigned_app.zip
index 731e050aba5e5b175cea6f4393e4d6541bcb382b..748386dc560aac505a5f7d490d556f5a5c295ebf
GIT binary patch
literal 971
zc$^FHW@Zs#0D;djLEcg^8k_upY!K!J;@rf%%(T?v61}YA{Jd%|1qCITNPJmpQE_H|
zo|2V<kq$^AFEKY2$WaObN-DTkl%(c?rNMFtwMqth26~1{T(w*QC>DS7Fx1fovO$;)
zh=W`mU3^_bGBS%5fT$!RRiP*~F(o%uAuYcM%q;;~lM1s&56!sQO_uCpObiSl%m)-p
zNi0d!&&*9sPt_~POYaQubLZuf;sSZn)59eQNKXS{4v@_@jD?RF7#Qz*x;TbZ%z1lm
zBPWA_z>y8N>tCK1Vr(v47<c>Kr2iir=8U2ti+bb|3Y4n$$q4TOr58_EKbLh*2~7ds
zj7%cTxbp%GG%zxt=L~eM=&2c@wGy7T(e<DwS%jVw7zsGQn-y#<GZ3x@(p=0S9suza
Bgw+55
rename from security/manager/ssl/tests/unit/test_signed_apps/trusted_ca1.der
rename to security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.der
index 0200a7f35d1c8344a816e4109bd9cce07327f21f..f1f5d25f63e6b0f09810ac40cf6b7bbed7c7b8bc
GIT binary patch
literal 790
zc$_n6Viq!JV*Im!nTe5!NhCLr@lmY$+&j~@K3+R#N|92fW_q~+Hyfu`n@8JsUPeZ4
z15HD9164NWP!?t$xr&11;*8Xs9EIY{^t{v*g~Wn_Vug~_;u3|T{QMFFB?*2b14B~-
zLm&i$D3E|LvH&T@H8Cm~@FJYT$jZRn#K_M86z5`UVq|34)e*SDrBCdd<KMh(9c*R-
zwl=~yUp+DP+VbXWMtF`&^}*w+D<0gC-@9=R+w454woloQ)<>0QXQdQ!R4t8K?%Muc
z=BmmZrn<Avf8;VRSSV;tUafH0^U3X<K8Lb%_}+>(@qesan<=&NGLu!emgBqS1rPXn
zd;g|!Yj04Qw4`=tM`GoKjo}k)FIgXbu){-sh56+U=lAMjH$u0kMXjo1nxJo0EzrQG
zaz-X+`r2uU(c2$Q6FN|8xR__+-7TlS|5+=mdYSY6PI12F84ovC>nr7FKHkzbHBt7R
z+BMa=t0!{!nVptVVbR$y)~)~aWB*KX!yTLdc`E-Z3Or(Xm3hrA2`<yOy-duE42+A7
zfWgWG3{Y8N7FGjhM#lfZ=mYZjSy-5vm{bgeK^#>U9s@2m4sA9@R#tXqW|RnK1_fnQ
z==NRL%vQho(AfFL^!jV<FMr=2TCzf53fCdgi{BT@POg-&>R;6^t0}1VX7BY1<xQP;
zEVw$nZoa=d!MS3i^{4p{<E>J)bu>R&F12~{@Qg)LIfqri&Agkh16qaCGJd-l1YY4d
z*gb)L@^RtBV%b%EF0<0bGdI+jaUEm1S`wA@p<uO1XZIaJ^Gl~yKE6tx75+%m<ArU^
z)EhE(ZqBJy3#=AA`!kVqL-Mgj-|YXsRp{Cq{BMR^Org-P(AO#o8OP&~zEOI$?DCJq
zoADd32B?%?5ZRpnoAr^<be}^Gow_NKPw!-X=bclKYgjh@Z?lu6`!AVL**2e6-JL%c
NJeGd_pO?8e695E(I`#kn
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.pem.certspec
@@ -0,0 +1,6 @@
+issuer:xpcshell signed apps test root
+subject:xpcshell signed apps test root
+validity:20150101-20350101
+extension:basicConstraints:cA,
+extension:keyUsage:keyEncipherment,keyCertSign
+extension:extKeyUsage:codeSigning
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -58,16 +58,20 @@ using namespace sandbox::bpf_dsl;
#ifndef MADV_FREE
#define MADV_FREE 8
#endif
#ifndef PR_SET_PTRACER
#define PR_SET_PTRACER 0x59616d61
#endif
+// The headers define O_LARGEFILE as 0 on x86_64, but we need the
+// actual value because it shows up in file flags.
+#define O_LARGEFILE_REAL 00100000
+
// To avoid visual confusion between "ifdef ANDROID" and "ifndef ANDROID":
#ifndef ANDROID
#define DESKTOP
#endif
// This file defines the seccomp-bpf system call filter policies.
// See also SandboxFilterUtil.h, for the CASES_FOR_* macros and
// SandboxFilterBase::Evaluate{Socket,Ipc}Call.
@@ -740,21 +744,45 @@ public:
.ElseIf(request == FIONREAD, Allow())
// Allow anything that isn't a tty ioctl, for now; bug 1302711
// will cover changing this to a default-deny policy.
.ElseIf(shifted_type != kTtyIoctls, Allow())
.Else(SandboxPolicyCommon::EvaluateSyscall(sysno));
}
#endif // !MOZ_ALSA
- CASES_FOR_fcntl:
- // Some fcntls have significant side effects like sending
- // arbitrary signals, and there's probably nontrivial kernel
- // attack surface; this should be locked down more if possible.
- return Allow();
+ CASES_FOR_fcntl: {
+ Arg<int> cmd(1);
+ Arg<int> flags(2);
+ // Typical use of F_SETFL is to modify the flags returned by
+ // F_GETFL and write them back, including some flags that
+ // F_SETFL ignores. This is a default-deny policy in case any
+ // new SETFL-able flags are added. (In particular we want to
+ // forbid O_ASYNC; see bug 1328896, but also see bug 1408438.)
+ static const int ignored_flags = O_ACCMODE | O_LARGEFILE_REAL | O_CLOEXEC;
+ static const int allowed_flags = ignored_flags | O_APPEND | O_NONBLOCK;
+ return Switch(cmd)
+ // Close-on-exec is meaningless when execve isn't allowed, but
+ // NSPR reads the bit and asserts that it has the expected value.
+ .Case(F_GETFD, Allow())
+ .Case(F_SETFD,
+ If((flags & ~FD_CLOEXEC) == 0, Allow())
+ .Else(InvalidSyscall()))
+ .Case(F_GETFL, Allow())
+ .Case(F_SETFL,
+ If((flags & ~allowed_flags) == 0, Allow())
+ .Else(InvalidSyscall()))
+ .Case(F_DUPFD_CLOEXEC, Allow())
+ // Pulseaudio uses F_SETLKW.
+ .Case(F_SETLKW, Allow())
+#ifdef F_SETLKW64
+ .Case(F_SETLKW64, Allow())
+#endif
+ .Default(SandboxPolicyCommon::EvaluateSyscall(sysno));
+ }
case __NR_mprotect:
case __NR_brk:
case __NR_madvise:
// libc's realloc uses mremap (Bug 1286119); wasm does too (bug 1342385).
case __NR_mremap:
return Allow();
--- a/security/sandbox/linux/reporter/SandboxReporter.cpp
+++ b/security/sandbox/linux/reporter/SandboxReporter.cpp
@@ -195,16 +195,17 @@ SubmitToTelemetry(const SandboxReport& a
} \
break
// The syscalls handled specially:
ARG_HEX(clone, 0); // flags
ARG_DECIMAL(prctl, 0); // option
ARG_HEX(ioctl, 1); // request
+ ARG_DECIMAL(fcntl, 1); // cmd
ARG_DECIMAL(madvise, 2); // advice
ARG_CLOCKID(clock_gettime, 0); // clk_id
#ifdef __NR_socketcall
ARG_DECIMAL(socketcall, 0); // call
#endif
#ifdef __NR_ipc
ARG_DECIMAL(ipc, 0); // call
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -343,16 +343,21 @@ name = "caseless"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
+name = "cc"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "cexpr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1092,29 +1097,30 @@ dependencies = [
"core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"core-text 7.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"fontsan 0.3.2 (git+https://github.com/servo/fontsan)",
"freetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx_traits 0.0.1",
- "harfbuzz-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "harfbuzz-sys 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"malloc_size_of 0.0.1",
"malloc_size_of_derive 0.0.1",
"msg 0.0.1",
"net_traits 0.0.1",
"ordered-float 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"range 0.0.1",
"serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-fontconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo_allocator 0.0.1",
"servo_arc 0.0.1",
"servo_atoms 0.0.1",
"servo_geometry 0.0.1",
"servo_url 0.0.1",
"simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"style 0.0.1",
"style_traits 0.0.1",
@@ -1188,17 +1194,17 @@ dependencies = [
"compositing 0.0.1",
"euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
"gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libservo 0.0.1",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"msg 0.0.1",
"net_traits 0.0.1",
- "osmesa-src 17.3.0-devel (git+https://github.com/servo/osmesa-src)",
+ "osmesa-src 17.3.1-devel (git+https://github.com/servo/osmesa-src)",
"osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"script_traits 0.0.1",
"servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-glutin 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_config 0.0.1",
"servo_geometry 0.0.1",
"servo_url 0.0.1",
"style_traits 0.0.1",
@@ -1225,17 +1231,17 @@ source = "registry+https://github.com/ru
[[package]]
name = "half"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "harfbuzz-sys"
-version = "0.1.14"
+version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
"freetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1435,16 +1441,34 @@ dependencies = [
]
[[package]]
name = "itoa"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "jemalloc-sys"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "jemallocator"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "jemalloc-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "jpeg-decoder"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1566,16 +1590,17 @@ dependencies = [
"profile_traits 0.0.1",
"range 0.0.1",
"rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"script 0.0.1",
"script_layout_interface 0.0.1",
"script_traits 0.0.1",
"selectors 0.19.0",
"serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo_allocator 0.0.1",
"servo_arc 0.0.1",
"servo_atoms 0.0.1",
"servo_config 0.0.1",
"servo_geometry 0.0.1",
"servo_url 0.0.1",
"style 0.0.1",
"style_traits 0.0.1",
"webrender_api 0.52.1 (git+https://github.com/servo/webrender)",
@@ -1680,20 +1705,20 @@ dependencies = [
"webrender 0.52.1 (git+https://github.com/servo/webrender)",
"webrender_api 0.52.1 (git+https://github.com/servo/webrender)",
"webvr 0.0.1",
"webvr_traits 0.0.1",
]
[[package]]
name = "libz-sys"
-version = "1.0.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "gcc 0.3.47 (registry+https://github.com/rust-lang/crates.io-index)",
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.3.8"
@@ -1728,17 +1753,16 @@ dependencies = [
name = "malloc_size_of"
version = "0.0.1"
dependencies = [
"app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
"hashglobe 0.1.0",
"js 0.1.6 (git+https://github.com/servo/rust-mozjs)",
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_arc 0.0.1",
"smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"string_cache 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"webrender_api 0.52.1 (git+https://github.com/servo/webrender)",
"xml5ever 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1876,20 +1900,20 @@ dependencies = [
[[package]]
name = "mitochondria"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "mozjs_sys"
version = "0.0.0"
-source = "git+https://github.com/servo/mozjs#834ce35c3f008010213351107b68f397989d2ffd"
+source = "git+https://github.com/servo/mozjs#424067c8d176bf1ee4c7f90c56916d35542de672"
dependencies = [
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
- "libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mp3-metadata"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -2206,18 +2230,18 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "osmesa-src"
-version = "17.3.0-devel"
-source = "git+https://github.com/servo/osmesa-src#100789c549a5716773a74a5db0a2e929470e2c9f"
+version = "17.3.1-devel"
+source = "git+https://github.com/servo/osmesa-src#6d23daede16f7edf7c66bdcce73c7583d0853733"
[[package]]
name = "osmesa-sys"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2358,16 +2382,17 @@ source = "registry+https://github.com/ru
[[package]]
name = "profile"
version = "0.0.1"
dependencies = [
"heartbeats-simple 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"influent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "jemalloc-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"profile_traits 0.0.1",
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_config 0.0.1",
"task_info 0.0.1",
@@ -2376,16 +2401,17 @@ dependencies = [
[[package]]
name = "profile_tests"
version = "0.0.1"
dependencies = [
"ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"profile 0.0.1",
"profile_traits 0.0.1",
+ "servo_allocator 0.0.1",
]
[[package]]
name = "profile_traits"
version = "0.0.1"
dependencies = [
"energy-monitor 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"energymon 0.3.0 (git+https://github.com/energymon/energymon-rust.git)",
@@ -2625,16 +2651,17 @@ dependencies = [
"ref_slice 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"script_layout_interface 0.0.1",
"script_plugins 0.0.1",
"script_traits 0.0.1",
"selectors 0.19.0",
"serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo_allocator 0.0.1",
"servo_arc 0.0.1",
"servo_atoms 0.0.1",
"servo_config 0.0.1",
"servo_geometry 0.0.1",
"servo_rand 0.0.1",
"servo_url 0.0.1",
"smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"style 0.0.1",
@@ -2832,22 +2859,22 @@ dependencies = [
]
[[package]]
name = "servo-fontconfig"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
- "servo-fontconfig-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo-fontconfig-sys 4.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "servo-fontconfig-sys"
-version = "4.0.3"
+version = "4.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"expat-sys 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2894,17 +2921,17 @@ dependencies = [
"cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
"expat-sys 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"glx 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"io-surface 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "servo-fontconfig-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo-fontconfig-sys 4.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-glutin 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"x11 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "servo-websocket"
version = "0.19.1"
@@ -2917,16 +2944,24 @@ dependencies = [
"openssl 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
+name = "servo_allocator"
+version = "0.0.1"
+dependencies = [
+ "jemallocator 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "servo_arc"
version = "0.0.1"
dependencies = [
"nodrop 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -3438,17 +3473,17 @@ name = "unicode-normalization"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-script"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "harfbuzz-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "harfbuzz-sys 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-segmentation"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -3777,16 +3812,17 @@ dependencies = [
"checksum blurz 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e73bda0f4c71c63a047351070097f3f507e6718e86b9ee525173371ef7b94b73"
"checksum bow 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c0ad826831ef9226adeaa7fa94a866b2dd316258219ec9cefb58595818dc5c52"
"checksum brotli 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "951f20a2cc403194b2746d9ac6f796292e3c0344e983c72c7d6bd6cff6c3d102"
"checksum brotli-decompressor 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e80402aa0457c3c03d3996a36af7c1e7efa2ad97ee8ec7c2761b07666bab5566"
"checksum browserhtml 0.1.17 (git+https://github.com/browserhtml/browserhtml?branch=crate)" = "<none>"
"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8"
"checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27"
"checksum caseless 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8950b075cff75cdabadee97148a8b5816c7cf62e5948a6005b5255d564b42fe7"
+"checksum cc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c674f0870e3dbd4105184ea035acb1c32c8ae69939c9e228d2b11bbfe29efad"
"checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum cgl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "86765cb42c2a2c497e142af72517c1b4d7ae5bb2f25dfa77a5c69642f2342d89"
"checksum clang-sys 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5955eab05fa8e6ff2b353753dc73a0608daa36e472a21c69f2eb51f43f593544"
"checksum clap 2.20.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7db281b0520e97fbd15cd615dcd8f8bcad0c26f5f7d5effe705f090f39e9a758"
"checksum clipboard 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd3a9a938558f33ec1baaa6ca631a69c104aafaacbc66868d9ad28cf5f30564f"
"checksum clipboard-win 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "693b1280c514045382dfdbb78d1594b1b03cdb66320aeb7ebd2bd38d49bae959"
"checksum cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "d18d68987ed4c516dcc3e7913659bfa4076f5182eea4a7e0038bb060953e76ac"
@@ -3848,17 +3884,17 @@ dependencies = [
"checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685"
"checksum gif 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8a80d6fe9e52f637df9afd4779449a7be17c39cc9c35b01589bb833f956ba596"
"checksum gl_generator 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0940975a4ca12b088d32b5d5134826c47d2e73de4b0b459b05244c01503eccbb"
"checksum gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "bf887141f0c2a83eae026cbf3fba74f0a5cb0f01d20e5cdfcd8c4ad39295be1e"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum glx 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b280007fa9c7442cfd1e0b1addb8d1a59240267110e8705f8f7e2c7bfb7e2f72"
"checksum gvr-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e84ba5e13cd925de87b669475525f956f8e936e67ddb24fbb1a077d96bbe174c"
"checksum half 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63d68db75012a85555434ee079e7e6337931f87a087ab2988becbadf64673a7f"
-"checksum harfbuzz-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "a2caaa66078fdfacea32db1351223697a1167ad2d4bbee6b8d4ca220ce5b10b3"
+"checksum harfbuzz-sys 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "52aa65c5649a0a2f1b27ab30093b3cc84681e17ddb552267e21948c5a6fa6b05"
"checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4"
"checksum heartbeats-simple 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ad003ce233955e9d95f2c69cde84e68302ba9ba4a673d351c9bff93c738aadc"
"checksum heartbeats-simple-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e1a408c0011427cc0e0049f7861c70377819aedfc006e8c901b1c70fd98fb1a4"
"checksum html5ever 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bfb46978eb757a603b7dfe2dafb1c62cb4dee3428d8ac1de734d83d6b022d06"
"checksum httparse 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e7a63e511f9edffbab707141fbb8707d1a3098615fb2adbd5769cdfcc9b17d"
"checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2"
"checksum hyper-openssl 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "85a372eb692590b3fe014c196c30f9f52d4c42f58cd49dd94caeee1593c9cc37"
"checksum hyper_serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbe43f514f80494e9329c9fc47d61b85b167d245685424637a0f4a409177e444"
@@ -3868,29 +3904,31 @@ dependencies = [
"checksum immeta 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0b9260463a221bfe3f02100c56e2d14c050d5ffe7e44a43d0a1b2b1f2b523502"
"checksum inflate 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10ec05638adf7c5c788bc0cfa608cd479a13572beda20feb4898fe1d85d2c64b"
"checksum influent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a22b311b83431be3ab9af96ca9ea41554bb4a8551ea871ae44c3ce0c57e55f2c"
"checksum io-surface 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad4578cee6ed49a17766fa608a4425008313c55ebb7a267622cbddd6b01751e2"
"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
"checksum ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c10ed089b1921b01ef342c736a37ee0788eeb9a5f373bb2df1ba88d01125064f"
"checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc"
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
+"checksum jemalloc-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94fb624d7e8345e5c42caab8d1db6ec925fdadff3fd0cb7dd781b41be8442828"
+"checksum jemallocator 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1850725977c344d63af66e8fd00857646e3ec936c490cd63667860b7b03ab5c1"
"checksum jpeg-decoder 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2805ccb10ffe4d10e06ef68a158ff94c255211ecbae848fbde2146b098f93ce7"
"checksum js 0.1.6 (git+https://github.com/servo/rust-mozjs)" = "<none>"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum khronos_api 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d5a08e2a31d665af8f1ca437eab6d00a93c9d62a549f73f9ed8fc2e55b5a91a7"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
"checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
"checksum leak 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bd100e01f1154f2908dfa7d02219aeab25d0b9c7fa955164192e3245255a0c73"
"checksum leaky-cow 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40a8225d44241fd324a8af2806ba635fc7c8a7e9a7de4d5cf3ef54e71f5926fc"
"checksum len-trait 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "723558ab8acaa07cb831b424cd164b587ddc1648b34748a30953c404e9a4a65b"
"checksum libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "e7eb6b826bfc1fdea7935d46556250d1799b7fe2d9f7951071f4291710665e3e"
"checksum libloading 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "be99f814beb3e9503a786a592c909692bb6d4fc5a695f6ed7987223acfbd5194"
-"checksum libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd64ef8ee652185674455c1d450b83cbc8ad895625d543b5324d923f82e4d8"
+"checksum libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "87f737ad6cc6fd6eefe3d9dc5412f1573865bded441300904d2f42269e140f16"
"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
"checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084"
"checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
"checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
"checksum markup5ever 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "047150a0e03b57e638fc45af33a0b63a0362305d5b9f92ecef81df472a4cceb0"
"checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1"
"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
"checksum metadeps 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829fffe7ea1d747e23f64be972991bc516b2f1ac2ae4a3b33d8bea150c410151"
@@ -3918,17 +3956,17 @@ dependencies = [
"checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba"
"checksum offscreen_gl_context 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ee0b17158af8e1fb6c8f8df048bd3f1c4b78bebbf4883f189e2a83ce853ce9bd"
"checksum ogg 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7137bf02687385302f4c0aecd77cfce052b69f5b4ee937be778e125c62f67e30"
"checksum ogg_metadata 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fc665717454399cba557c55ad226148996e9266ee291f8a37a98bb2cded0a490"
"checksum open 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3478ed1686bd1300c8a981a940abc92b06fac9cbef747f4c668d4e032ff7b842"
"checksum openssl 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bb5d1663b73d10c6a3eda53e2e9d0346f822394e7b858d7257718f65f61dfbe2"
"checksum openssl-sys 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)" = "3a5886d87d3e2a0d890bf62dc8944f5e3769a405f7e1e9ef6e517e47fd7a0897"
"checksum ordered-float 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da12c96037889ae0be29dd2bdd260e5a62a7df24e6466d5a15bb8131c1c200a8"
-"checksum osmesa-src 17.3.0-devel (git+https://github.com/servo/osmesa-src)" = "<none>"
+"checksum osmesa-src 17.3.1-devel (git+https://github.com/servo/osmesa-src)" = "<none>"
"checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b"
"checksum ovr-mobile-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7b5f9389b2015f8340f0566c488f3e96735e2e8fd7b85d571832cd274ac2998"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
"checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595"
"checksum parse-hosts 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3842db828281691db6e6f1709ed6a73025747c0cd15cc10346a5cb021e2bc28"
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
@@ -3965,17 +4003,17 @@ dependencies = [
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
"checksum serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "bcb6a7637a47663ee073391a139ed07851f27ed2532c2abc88c6bf27a16cdf34"
"checksum serde_bytes 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a73f5ad9bb83e1e407254c7a355f4efdaffe3c1442fc0657ddb8b9b6b225655"
"checksum serde_derive 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "812ff66056fd9a9a5b7c119714243b0862cf98340e7d4b5ee05a932c40d5ea6c"
"checksum serde_derive_internals 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd381f6d01a6616cdba8530492d453b7761b456ba974e98768a18cad2cd76f58"
"checksum serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "48b04779552e92037212c3615370f6bd57a40ebba7f20e554ff9f55e41a69a7b"
"checksum servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "21069a884c33fe6ee596975e1f3849ed88c4ec857fbaf11d33672d8ebe051217"
"checksum servo-fontconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93f799b649b4a2bf362398910eca35240704c7e765e780349b2bb1070d892262"
-"checksum servo-fontconfig-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6be80777ee6edecbbbf8774c76e19dddfe336256c57a4ded06d6ad3df7be358e"
+"checksum servo-fontconfig-sys 4.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "38b494f03009ee81914b0e7d387ad7c145cafcd69747c2ec89b0e17bb94f303a"
"checksum servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9232032c2e85118c0282c6562c84cab12316e655491ba0a5d1905b2320060d1b"
"checksum servo-glutin 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05bfa660ed7bceb5df3d79ca5a9c99a59fa7019c4477cbe52b70f80034bce568"
"checksum servo-skia 0.30000006.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c104b3a0b3938f760772c09584fd6b2bb7ca205e2553a767b357f8ddbfbccbc"
"checksum servo-websocket 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8a1ff13c5d852c2793805226e688044309f2c1d8f063784805a13e99cb75b611"
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
"checksum shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fb04126b6fcfd2710fb5b6d18f4207b6c535f2850a7e1a43bcd526d44f30a79a"
"checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d"
"checksum sig 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c6649e43c1a1e68d29ed56d0dc3b5b6cf3b901da77cf107c4066b9e3da036df5"
new file mode 100644
--- /dev/null
+++ b/servo/components/allocator/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "servo_allocator"
+version = "0.0.1"
+authors = ["The Servo Project Developers"]
+license = "MPL-2.0"
+publish = false
+
+[lib]
+path = "lib.rs"
+
+[features]
+unstable = ["kernel32-sys", "jemallocator"]
+
+[target.'cfg(not(windows))'.dependencies]
+jemallocator = { version = "0.1.3", optional = true }
+
+[target.'cfg(windows)'.dependencies]
+kernel32-sys = { version = "0.2.1", optional = true }
new file mode 100644
--- /dev/null
+++ b/servo/components/allocator/lib.rs
@@ -0,0 +1,62 @@
+/* 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/. */
+
+//! Selecting the default global allocator for Servo
+
+#![cfg_attr(all(feature = "unstable", windows), feature(alloc_system, allocator_api))]
+#![cfg_attr(feature = "unstable", feature(global_allocator))]
+
+#[cfg(feature = "unstable")]
+#[global_allocator]
+static ALLOC: platform::Allocator = platform::Allocator;
+
+pub use platform::usable_size;
+
+
+#[cfg(all(feature = "unstable", not(windows)))]
+mod platform {
+ extern crate jemallocator;
+
+ pub use self::jemallocator::Jemalloc as Allocator;
+ use std::os::raw::c_void;
+
+ /// Get the size of a heap block.
+ pub unsafe extern "C" fn usable_size(ptr: *const c_void) -> usize {
+ jemallocator::usable_size(ptr)
+ }
+}
+
+#[cfg(all(feature = "unstable", windows))]
+mod platform {
+ extern crate alloc_system;
+ extern crate kernel32;
+
+ pub use self::alloc_system::System as Allocator;
+ use self::kernel32::{GetProcessHeap, HeapSize, HeapValidate};
+ use std::os::raw::c_void;
+
+ /// Get the size of a heap block.
+ pub unsafe extern "C" fn usable_size(mut ptr: *const c_void) -> usize {
+ let heap = GetProcessHeap();
+
+ if HeapValidate(heap, 0, ptr) == 0 {
+ ptr = *(ptr as *const *const c_void).offset(-1);
+ }
+
+ HeapSize(heap, 0, ptr) as usize
+ }
+}
+
+#[cfg(not(feature = "unstable"))]
+mod platform {
+ use std::os::raw::c_void;
+
+ /// Without `#[global_allocator]` we cannot be certain of what allocator is used
+ /// or how it is linked. We therefore disable memory reporting. (Return zero.)
+ pub unsafe extern "C" fn usable_size(_ptr: *const c_void) -> usize {
+ 0
+ }
+}
+
+
--- a/servo/components/gfx/Cargo.toml
+++ b/servo/components/gfx/Cargo.toml
@@ -48,16 +48,17 @@ xi-unicode = "0.1.0"
[target.'cfg(target_os = "macos")'.dependencies]
byteorder = "1.0"
core-foundation = "0.4"
core-graphics = "0.9"
core-text = "7.0"
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
freetype = "0.3"
+servo_allocator = {path = "../allocator"}
[target.'cfg(target_os = "linux")'.dependencies]
servo-fontconfig = "0.2.1"
[target.'cfg(target_os = "android")'.dependencies]
xml5ever = {version = "0.10"}
[target.'cfg(any(target_feature = "sse2", target_feature = "neon"))'.dependencies]
--- a/servo/components/gfx/lib.rs
+++ b/servo/components/gfx/lib.rs
@@ -22,18 +22,18 @@ extern crate bitflags;
#[cfg(target_os = "windows")] extern crate truetype;
extern crate euclid;
extern crate fnv;
#[cfg(target_os = "linux")]
extern crate fontconfig;
extern crate fontsan;
-#[cfg(any(target_os = "linux", target_os = "android"))]
-extern crate freetype;
+#[cfg(any(target_os = "linux", target_os = "android"))] extern crate freetype;
+#[cfg(any(target_os = "linux", target_os = "android"))] extern crate servo_allocator;
extern crate gfx_traits;
// Eventually we would like the shaper to be pluggable, as many operating systems have their own
// shapers. For now, however, this is a hard dependency.
extern crate harfbuzz_sys as harfbuzz;
extern crate ipc_channel;
#[macro_use]
--- a/servo/components/gfx/platform/freetype/font_context.rs
+++ b/servo/components/gfx/platform/freetype/font_context.rs
@@ -3,17 +3,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use freetype::freetype::FT_Add_Default_Modules;
use freetype::freetype::FT_Done_Library;
use freetype::freetype::FT_Library;
use freetype::freetype::FT_Memory;
use freetype::freetype::FT_MemoryRec_;
use freetype::freetype::FT_New_Library;
-use malloc_size_of::{malloc_size_of, MallocSizeOf, MallocSizeOfOps};
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
+use servo_allocator::usable_size;
use std::mem;
use std::os::raw::{c_long, c_void};
use std::ptr;
use std::rc::Rc;
// We pass a |User| struct -- via an opaque |void*| -- to FreeType each time a new instance is
// created. FreeType passes it back to the ft_alloc/ft_realloc/ft_free callbacks. We use it to
// record the memory usage of each FreeType instance.
@@ -26,57 +27,58 @@ const FT_ALIGNMENT: usize = 1;
extern fn ft_alloc(mem: FT_Memory, req_size: c_long) -> *mut c_void {
assert!(FT_ALIGNMENT == 1);
let mut vec = Vec::<u8>::with_capacity(req_size as usize);
let ptr = vec.as_mut_ptr() as *mut c_void;
mem::forget(vec);
unsafe {
- let actual_size = malloc_size_of(ptr as *const _);
+ let actual_size = usable_size(ptr as *const _);
let user = (*mem).user as *mut User;
(*user).size += actual_size;
}
ptr
}
extern fn ft_free(mem: FT_Memory, ptr: *mut c_void) {
unsafe {
- let actual_size = malloc_size_of(ptr as *const _);
+ let actual_size = usable_size(ptr as *const _);
let user = (*mem).user as *mut User;
(*user).size -= actual_size;
assert!(FT_ALIGNMENT == 1);
mem::drop(Vec::<u8>::from_raw_parts(ptr as *mut u8, actual_size, 0))
}
}
-extern fn ft_realloc(mem: FT_Memory, _cur_size: c_long, new_req_size: c_long,
+extern fn ft_realloc(mem: FT_Memory, old_size: c_long, new_req_size: c_long,
old_ptr: *mut c_void) -> *mut c_void {
let old_actual_size;
let mut vec;
unsafe {
- old_actual_size = malloc_size_of(old_ptr as *const _);
- vec = Vec::<u8>::from_raw_parts(old_ptr as *mut u8, old_actual_size, old_actual_size);
+ old_actual_size = usable_size(old_ptr as *const _);
+ let old_size = old_size as usize;
+ vec = Vec::<u8>::from_raw_parts(old_ptr as *mut u8, old_size, old_size);
};
let new_req_size = new_req_size as usize;
if new_req_size > old_actual_size {
vec.reserve_exact(new_req_size - old_actual_size)
} else if new_req_size < old_actual_size {
vec.truncate(new_req_size);
vec.shrink_to_fit()
}
let new_ptr = vec.as_mut_ptr() as *mut c_void;
mem::forget(vec);
unsafe {
- let new_actual_size = malloc_size_of(new_ptr as *const _);
+ let new_actual_size = usable_size(new_ptr as *const _);
let user = (*mem).user as *mut User;
(*user).size += new_actual_size;
(*user).size -= old_actual_size;
}
new_ptr
}
--- a/servo/components/layout_thread/Cargo.toml
+++ b/servo/components/layout_thread/Cargo.toml
@@ -35,16 +35,17 @@ parking_lot = "0.4"
profile_traits = {path = "../profile_traits"}
range = {path = "../range"}
rayon = "0.8"
script = {path = "../script"}
script_layout_interface = {path = "../script_layout_interface"}
script_traits = {path = "../script_traits"}
selectors = { path = "../selectors" }
serde_json = "1.0"
+servo_allocator = {path = "../allocator"}
servo_arc = {path = "../servo_arc"}
servo_atoms = {path = "../atoms"}
servo_config = {path = "../config"}
servo_geometry = {path = "../geometry"}
servo_url = {path = "../url"}
style = {path = "../style"}
style_traits = {path = "../style_traits"}
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
--- a/servo/components/layout_thread/dom_wrapper.rs
+++ b/servo/components/layout_thread/dom_wrapper.rs
@@ -681,31 +681,33 @@ impl<'le> ::selectors::Element for Servo
self.element.local_name()
}
#[inline]
fn get_namespace(&self) -> &Namespace {
self.element.namespace()
}
- fn match_pseudo_element(&self,
- _pseudo: &PseudoElement,
- _context: &mut MatchingContext)
- -> bool
- {
+ fn match_pseudo_element(
+ &self,
+ _pseudo: &PseudoElement,
+ _context: &mut MatchingContext<Self::Impl>,
+ ) -> bool {
false
}
- fn match_non_ts_pseudo_class<F>(&self,
- pseudo_class: &NonTSPseudoClass,
- _: &mut MatchingContext,
- _: &RelevantLinkStatus,
- _: &mut F)
- -> bool
- where F: FnMut(&Self, ElementSelectorFlags),
+ fn match_non_ts_pseudo_class<F>(
+ &self,
+ pseudo_class: &NonTSPseudoClass,
+ _: &mut MatchingContext<Self::Impl>,
+ _: &RelevantLinkStatus,
+ _: &mut F,
+ ) -> bool
+ where
+ F: FnMut(&Self, ElementSelectorFlags),
{
match *pseudo_class {
// https://github.com/servo/servo/issues/8718
NonTSPseudoClass::Link |
NonTSPseudoClass::AnyLink => self.is_link(),
NonTSPseudoClass::Visited => false,
NonTSPseudoClass::Lang(ref lang) => self.match_element_lang(None, &*lang),
@@ -1171,21 +1173,21 @@ impl<'le> ::selectors::Element for Servo
self.element.get_local_name()
}
#[inline]
fn get_namespace(&self) -> &Namespace {
self.element.get_namespace()
}
- fn match_pseudo_element(&self,
- _pseudo: &PseudoElement,
- _context: &mut MatchingContext)
- -> bool
- {
+ fn match_pseudo_element(
+ &self,
+ _pseudo: &PseudoElement,
+ _context: &mut MatchingContext<Self::Impl>
+ ) -> bool {
false
}
fn attr_matches(&self,
ns: &NamespaceConstraint<&Namespace>,
local_name: &LocalName,
operation: &AttrSelectorOperation<&String>)
-> bool {
@@ -1198,23 +1200,25 @@ impl<'le> ::selectors::Element for Servo
let values = unsafe {
(*self.element.element.unsafe_get()).get_attr_vals_for_layout(local_name)
};
values.iter().any(|v| v.eval_selector(operation))
}
}
}
- fn match_non_ts_pseudo_class<F>(&self,
- _: &NonTSPseudoClass,
- _: &mut MatchingContext,
- _: &RelevantLinkStatus,
- _: &mut F)
- -> bool
- where F: FnMut(&Self, ElementSelectorFlags),
+ fn match_non_ts_pseudo_class<F>(
+ &self,
+ _: &NonTSPseudoClass,
+ _: &mut MatchingContext<Self::Impl>,
+ _: &RelevantLinkStatus,
+ _: &mut F,
+ ) -> bool
+ where
+ F: FnMut(&Self, ElementSelectorFlags),
{
// NB: This could maybe be implemented
warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
false
}
fn is_link(&self) -> bool {
warn!("ServoThreadSafeLayoutElement::is_link called");
--- a/servo/components/layout_thread/lib.rs
+++ b/servo/components/layout_thread/lib.rs
@@ -34,16 +34,17 @@ extern crate parking_lot;
extern crate profile_traits;
extern crate range;
extern crate rayon;
extern crate script;
extern crate script_layout_interface;
extern crate script_traits;
extern crate selectors;
extern crate serde_json;
+extern crate servo_allocator;
extern crate servo_arc;
extern crate servo_atoms;
extern crate servo_config;
extern crate servo_geometry;
extern crate servo_url;
extern crate style;
extern crate style_traits;
extern crate webrender_api;
@@ -79,17 +80,17 @@ use layout::query::{process_margin_style
use layout::query::{process_node_geometry_request, process_node_scroll_area_request};
use layout::query::{process_node_scroll_root_id_request, process_offset_parent_query};
use layout::sequential;
use layout::traversal::{ComputeStackingRelativePositions, PreorderFlowTraversal, RecalcStyleAndConstructFlows};
use layout::webrender_helpers::WebRenderDisplayListConverter;
use layout::wrapper::LayoutNodeLayoutData;
use layout_traits::LayoutThreadFactory;
use libc::c_void;
-use malloc_size_of::{malloc_size_of, MallocSizeOf, MallocSizeOfOps};
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::TopLevelBrowsingContextId;
use net_traits::image_cache::{ImageCache, UsePlaceholder};
use parking_lot::RwLock;
use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
use profile_traits::time::{self, TimerMetadata, profile};
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
@@ -770,17 +771,17 @@ impl LayoutThread {
}
fn collect_reports<'a, 'b>(&self,
reports_chan: ReportsChan,
possibly_locked_rw_data: &mut RwData<'a, 'b>) {
let mut reports = vec![];
// Servo uses vanilla jemalloc, which doesn't have a
// malloc_enclosing_size_of function.
- let mut ops = MallocSizeOfOps::new(malloc_size_of, None, None);
+ let mut ops = MallocSizeOfOps::new(::servo_allocator::usable_size, None, None);
// FIXME(njn): Just measuring the display tree for now.
let rw_data = possibly_locked_rw_data.lock();
let display_list = rw_data.display_list.as_ref();
let formatted_url = &format!("url({})", self.url);
reports.push(Report {
path: path![formatted_url, "layout-thread", "display-list"],
kind: ReportKind::ExplicitJemallocHeapSize,
--- a/servo/components/malloc_size_of/Cargo.toml
+++ b/servo/components/malloc_size_of/Cargo.toml
@@ -3,19 +3,16 @@ name = "malloc_size_of"
version = "0.0.1"
authors = ["The Servo Project Developers"]
license = "MIT/Apache-2.0"
publish = false
[lib]
path = "lib.rs"
-[target.'cfg(windows)'.dependencies]
-kernel32-sys = "0.2.1"
-
[features]
servo = ["js", "string_cache", "url", "webrender_api", "xml5ever"]
[dependencies]
app_units = "0.5.5"
cssparser = "0.22.0"
euclid = "0.15"
hashglobe = { path = "../hashglobe" }
--- a/servo/components/malloc_size_of/lib.rs
+++ b/servo/components/malloc_size_of/lib.rs
@@ -44,32 +44,28 @@
//! `<Box<_> as MallocSizeOf>::size_of(field, ops)`.
extern crate app_units;
extern crate cssparser;
extern crate euclid;
extern crate hashglobe;
#[cfg(feature = "servo")]
extern crate js;
-#[cfg(target_os = "windows")]
-extern crate kernel32;
extern crate servo_arc;
extern crate smallbitvec;
extern crate smallvec;
#[cfg(feature = "servo")]
extern crate string_cache;
#[cfg(feature = "servo")]
extern crate url;
#[cfg(feature = "servo")]
extern crate webrender_api;
#[cfg(feature = "servo")]
extern crate xml5ever;
-#[cfg(target_os = "windows")]
-use kernel32::{GetProcessHeap, HeapSize, HeapValidate};
use std::hash::{BuildHasher, Hash};
use std::mem::size_of;
use std::ops::Range;
use std::os::raw::c_void;
/// A C function that takes a pointer to a heap allocation and returns its size.
type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
@@ -141,43 +137,16 @@ impl MallocSizeOfOps {
/// Call `have_seen_ptr_op` on `ptr`.
pub fn have_seen_ptr<T>(&mut self, ptr: *const T) -> bool {
let have_seen_ptr_op = self.have_seen_ptr_op.as_mut().expect("missing have_seen_ptr_op");
have_seen_ptr_op(ptr as *const c_void)
}
}
-/// Get the size of a heap block.
-#[cfg(not(target_os = "windows"))]
-pub unsafe extern "C" fn malloc_size_of(ptr: *const c_void) -> usize {
- // The C prototype is `je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)`. On some
- // platforms `JEMALLOC_USABLE_SIZE_CONST` is `const` and on some it is empty. But in practice
- // this function doesn't modify the contents of the block that `ptr` points to, so we use
- // `*const c_void` here.
- extern "C" {
- #[cfg_attr(any(prefixed_jemalloc, target_os = "macos", target_os = "ios", target_os = "android"),
- link_name = "je_malloc_usable_size")]
- fn malloc_usable_size(ptr: *const c_void) -> usize;
- }
- malloc_usable_size(ptr)
-}
-
-/// Get the size of a heap block.
-#[cfg(target_os = "windows")]
-pub unsafe extern "C" fn malloc_size_of(mut ptr: *const c_void) -> usize {
- let heap = GetProcessHeap();
-
- if HeapValidate(heap, 0, ptr) == 0 {
- ptr = *(ptr as *const *const c_void).offset(-1);
- }
-
- HeapSize(heap, 0, ptr) as usize
-}
-
/// Trait for measuring the "deep" heap usage of a data structure. This is the
/// most commonly-used of the traits.
pub trait MallocSizeOf {
/// Measure the heap usage of all descendant heap-allocated structures, but
/// not the space taken up by the value itself.
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize;
}
--- a/servo/components/profile/Cargo.toml
+++ b/servo/components/profile/Cargo.toml
@@ -5,17 +5,17 @@ authors = ["The Servo Project Developers
license = "MPL-2.0"
publish = false
[lib]
name = "profile"
path = "lib.rs"
[features]
-unstable = []
+unstable = ["jemalloc-sys"]
[dependencies]
profile_traits = {path = "../profile_traits"}
influent = "0.4"
ipc-channel = "0.9"
heartbeats-simple = "0.4"
log = "0.3.5"
serde = "1.0"
@@ -26,8 +26,9 @@ time = "0.1.12"
[target.'cfg(target_os = "macos")'.dependencies]
task_info = {path = "../../support/rust-task_info"}
[target.'cfg(target_os = "linux")'.dependencies]
regex = "0.2"
[target.'cfg(not(target_os = "windows"))'.dependencies]
libc = "0.2"
+jemalloc-sys = {version = "0.1.3", optional = true}
--- a/servo/components/profile/lib.rs
+++ b/servo/components/profile/lib.rs
@@ -1,23 +1,20 @@
/* 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/. */
-#![cfg_attr(all(feature = "unstable", not(target_os = "windows")), feature(alloc_jemalloc))]
-
#![deny(unsafe_code)]
#[allow(unused_extern_crates)]
-#[cfg(all(feature = "unstable", not(target_os = "windows")))]
-extern crate alloc_jemalloc;
extern crate heartbeats_simple;
extern crate influent;
extern crate ipc_channel;
-#[allow(unused_extern_crates)]
+#[cfg(all(feature = "unstable", not(target_os = "windows")))]
+extern crate jemalloc_sys;
#[cfg(not(target_os = "windows"))]
extern crate libc;
#[macro_use]
extern crate log;
#[macro_use]
extern crate profile_traits;
#[cfg(target_os = "linux")]
extern crate regex;
--- a/servo/components/profile/mem.rs
+++ b/servo/components/profile/mem.rs
@@ -349,17 +349,17 @@ impl ReportsForest {
}
}
}
//---------------------------------------------------------------------------
mod system_reporter {
#[cfg(all(feature = "unstable", not(target_os = "windows")))]
- use libc::{c_char, c_void, size_t};
+ use libc::{c_void, size_t};
#[cfg(target_os = "linux")]
use libc::c_int;
use profile_traits::mem::{Report, ReportKind, ReporterRequest};
#[cfg(all(feature = "unstable", not(target_os = "windows")))]
use std::ffi::CString;
#[cfg(all(feature = "unstable", not(target_os = "windows")))]
use std::mem::size_of;
#[cfg(all(feature = "unstable", not(target_os = "windows")))]
@@ -455,21 +455,17 @@ mod system_reporter {
}
#[cfg(not(target_os = "linux"))]
fn system_heap_allocated() -> Option<usize> {
None
}
#[cfg(all(feature = "unstable", not(target_os = "windows")))]
- extern {
- #[cfg_attr(any(target_os = "macos", target_os = "android"), link_name = "je_mallctl")]
- fn mallctl(name: *const c_char, oldp: *mut c_void, oldlenp: *mut size_t,
- newp: *mut c_void, newlen: size_t) -> ::libc::c_int;
- }
+ use jemalloc_sys::mallctl;
#[cfg(all(feature = "unstable", not(target_os = "windows")))]
fn jemalloc_stat(value_name: &str) -> Option<usize> {
// Before we request the measurement of interest, we first send an "epoch"
// request. Without that jemalloc gives cached statistics(!) which can be
// highly inaccurate.
let epoch_name = "epoch";
let epoch_c_name = CString::new(epoch_name).unwrap();
--- a/servo/components/script/Cargo.toml
+++ b/servo/components/script/Cargo.toml
@@ -8,17 +8,17 @@ publish = false
build = "build.rs"
[lib]
name = "script"
path = "lib.rs"
[features]
debugmozjs = ['js/debugmozjs']
-unstable = []
+unstable = ["servo_allocator/unstable"]
[build-dependencies]
cmake = "0.1"
phf_codegen = "0.7.18"
phf_shared = "0.7.18"
serde_json = "1.0"
[target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies]
@@ -72,16 +72,17 @@ profile_traits = {path = "../profile_tra
ref_filter_map = "1.0.1"
ref_slice = "1.0"
regex = "0.2"
script_layout_interface = {path = "../script_layout_interface"}
script_plugins = {path = "../script_plugins"}
script_traits = {path = "../script_traits"}
selectors = { path = "../selectors" }
serde = "1.0"
+servo_allocator = {path = "../allocator"}
servo_arc = {path = "../servo_arc"}
servo_atoms = {path = "../atoms"}
servo_config = {path = "../config"}
servo_geometry = {path = "../geometry" }
servo_rand = {path = "../rand"}
servo_url = {path = "../url"}
smallvec = "0.4"
style = {path = "../style"}
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -2523,21 +2523,21 @@ impl<'a> SelectorsElement for DomRoot<El
fn opaque(&self) -> ::selectors::OpaqueElement {
::selectors::OpaqueElement::new(self.reflector().get_jsobject().get())
}
fn parent_element(&self) -> Option<DomRoot<Element>> {
self.upcast::<Node>().GetParentElement()
}
- fn match_pseudo_element(&self,
- _pseudo: &PseudoElement,
- _context: &mut MatchingContext)
- -> bool
- {
+ fn match_pseudo_element(
+ &self,
+ _pseudo: &PseudoElement,
+ _context: &mut MatchingContext<Self::Impl>,
+ ) -> bool {
false
}
fn first_child_element(&self) -> Option<DomRoot<Element>> {
self.node.child_elements().next()
}
@@ -2589,23 +2589,25 @@ impl<'a> SelectorsElement for DomRoot<El
fn get_local_name(&self) -> &LocalName {
self.local_name()
}
fn get_namespace(&self) -> &Namespace {
self.namespace()
}
- fn match_non_ts_pseudo_class<F>(&self,
- pseudo_class: &NonTSPseudoClass,
- _: &mut MatchingContext,
- _: &RelevantLinkStatus,
- _: &mut F)
- -> bool
- where F: FnMut(&Self, ElementSelectorFlags),
+ fn match_non_ts_pseudo_class<F>(
+ &self,
+ pseudo_class: &NonTSPseudoClass,
+ _: &mut MatchingContext<Self::Impl>,
+ _: &RelevantLinkStatus,
+ _: &mut F,
+ ) -> bool
+ where
+ F: FnMut(&Self, ElementSelectorFlags),
{
match *pseudo_class {
// https://github.com/servo/servo/issues/8718
NonTSPseudoClass::Link |
NonTSPseudoClass::AnyLink => self.is_link(),
NonTSPseudoClass::Visited => false,
NonTSPseudoClass::ServoNonZeroBorder => {
--- a/servo/components/script/lib.rs
+++ b/servo/components/script/lib.rs
@@ -73,16 +73,17 @@ extern crate phf;
extern crate profile_traits;
extern crate ref_filter_map;
extern crate ref_slice;
extern crate regex;
extern crate script_layout_interface;
extern crate script_traits;
extern crate selectors;
extern crate serde;
+extern crate servo_allocator;
extern crate servo_arc;
#[macro_use] extern crate servo_atoms;
extern crate servo_config;
extern crate servo_geometry;
extern crate servo_rand;
extern crate servo_url;
extern crate smallvec;
#[macro_use]
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -68,17 +68,17 @@ use hyper::mime::{Mime, SubLevel, TopLev
use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::glue::GetWindowProxyClass;
use js::jsapi::{JSAutoCompartment, JSContext, JS_SetWrapObjectCallbacks};
use js::jsapi::{JSTracer, SetWindowProxyClass};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
-use malloc_size_of::{malloc_size_of, MallocSizeOfOps};
+use malloc_size_of::MallocSizeOfOps;
use mem::malloc_size_of_including_self;
use metrics::PaintTimeMetrics;
use microtask::{MicrotaskQueue, Microtask};
use msg::constellation_msg::{BrowsingContextId, FrameType, PipelineId, PipelineNamespace, TopLevelBrowsingContextId};
use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg};
use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads};
use net_traits::image_cache::{ImageCache, PendingImageResponse};
use net_traits::request::{CredentialsMode, Destination, RedirectMode, RequestInit};
@@ -1501,17 +1501,17 @@ impl ScriptThread {
}
fn collect_reports(&self, reports_chan: ReportsChan) {
let mut path_seg = String::from("url(");
let mut dom_tree_size = 0;
let mut reports = vec![];
// Servo uses vanilla jemalloc, which doesn't have a
// malloc_enclosing_size_of function.
- let mut ops = MallocSizeOfOps::new(malloc_size_of, None, None);
+ let mut ops = MallocSizeOfOps::new(::servo_allocator::usable_size, None, None);
for (_, document) in self.documents.borrow().iter() {
let current_url = document.url();
for child in document.upcast::<Node>().traverse_preorder() {
dom_tree_size += malloc_size_of_including_self(&mut ops, &*child);
}
dom_tree_size += malloc_size_of_including_self(&mut ops, document.window());
--- a/servo/components/selectors/context.rs
+++ b/servo/components/selectors/context.rs
@@ -1,15 +1,16 @@
/* 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 attr::CaseSensitivity;
use bloom::BloomFilter;
use nth_index_cache::NthIndexCache;
+use parser::SelectorImpl;
use tree::OpaqueElement;
/// What kind of selector matching mode we should use.
///
/// There are two modes of selector matching. The difference is only noticeable
/// in presence of pseudo-elements.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MatchingMode {
@@ -69,17 +70,20 @@ impl QuirksMode {
QuirksMode::Quirks => CaseSensitivity::AsciiCaseInsensitive,
}
}
}
/// Data associated with the matching process for a element. This context is
/// used across many selectors for an element, so it's not appropriate for
/// transient data that applies to only a single selector.
-pub struct MatchingContext<'a> {
+pub struct MatchingContext<'a, Impl>
+where
+ Impl: SelectorImpl,
+{
/// Input with the matching mode we should use when matching selectors.
pub matching_mode: MatchingMode,
/// Input with the bloom filter used to fast-reject selectors.
pub bloom_filter: Option<&'a BloomFilter>,
/// An optional cache to speed up nth-index-like selectors.
pub nth_index_cache: Option<&'a mut NthIndexCache>,
/// Input that controls how matching for links is handled.
pub visited_handling: VisitedHandlingMode,
@@ -102,19 +106,23 @@ pub struct MatchingContext<'a> {
/// See https://drafts.csswg.org/selectors-4/#scope-pseudo
pub scope_element: Option<OpaqueElement>,
/// The current nesting level of selectors that we're matching.
pub nesting_level: usize,
quirks_mode: QuirksMode,
classes_and_ids_case_sensitivity: CaseSensitivity,
+ _impl: ::std::marker::PhantomData<Impl>,
}
-impl<'a> MatchingContext<'a> {
+impl<'a, Impl> MatchingContext<'a, Impl>
+where
+ Impl: SelectorImpl,
+{
/// Constructs a new `MatchingContext`.
pub fn new(
matching_mode: MatchingMode,
bloom_filter: Option<&'a BloomFilter>,
nth_index_cache: Option<&'a mut NthIndexCache>,
quirks_mode: QuirksMode,
) -> Self {
Self::new_for_visited(
@@ -139,16 +147,17 @@ impl<'a> MatchingContext<'a> {
bloom_filter,
visited_handling,
nth_index_cache,
quirks_mode,
relevant_link_found: false,
classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),
scope_element: None,
nesting_level: 0,
+ _impl: ::std::marker::PhantomData,
}
}
/// The quirks mode of the document.
#[inline]
pub fn quirks_mode(&self) -> QuirksMode {
self.quirks_mode
}
--- a/servo/components/selectors/matching.rs
+++ b/servo/components/selectors/matching.rs
@@ -50,25 +50,25 @@ impl ElementSelectorFlags {
/// Returns the subset of flags that apply to the parent.
pub fn for_parent(self) -> ElementSelectorFlags {
self & (HAS_SLOW_SELECTOR | HAS_SLOW_SELECTOR_LATER_SIBLINGS | HAS_EDGE_CHILD_SELECTOR)
}
}
/// Holds per-compound-selector data.
-struct LocalMatchingContext<'a, 'b: 'a> {
- shared: &'a mut MatchingContext<'b>,
+struct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> {
+ shared: &'a mut MatchingContext<'b, Impl>,
matches_hover_and_active_quirk: bool,
}
pub fn matches_selector_list<E>(
selector_list: &SelectorList<E::Impl>,
element: &E,
- context: &mut MatchingContext,
+ context: &mut MatchingContext<E::Impl>,
) -> bool
where
E: Element
{
selector_list.0.iter().any(|selector| {
matches_selector(selector,
0,
None,
@@ -136,19 +136,23 @@ impl Default for RelevantLinkStatus {
RelevantLinkStatus::NotLooking
}
}
impl RelevantLinkStatus {
/// If we found the relevant link for this element, record that in the
/// overall matching context for the element as a whole and stop looking for
/// addtional links.
- fn examine_potential_link<E>(&self, element: &E, context: &mut MatchingContext)
- -> RelevantLinkStatus
- where E: Element,
+ fn examine_potential_link<E>(
+ &self,
+ element: &E,
+ context: &mut MatchingContext<E::Impl>,
+ ) -> RelevantLinkStatus
+ where
+ E: Element,
{
// If a relevant link was previously found, we no longer want to look
// for links. Only the nearest ancestor link is considered relevant.
if *self != RelevantLinkStatus::Looking {
return RelevantLinkStatus::NotLooking
}
if !element.is_link() {
@@ -163,18 +167,23 @@ impl RelevantLinkStatus {
// specific selector's matching process.
RelevantLinkStatus::Found
}
/// Returns whether an element is considered visited for the purposes of
/// matching. This is true only if the element is a link, an relevant link
/// exists for the element, and the visited handling mode is set to accept
/// relevant links as visited.
- pub fn is_visited<E>(&self, element: &E, context: &MatchingContext) -> bool
- where E: Element,
+ pub fn is_visited<E>(
+ &self,
+ element: &E,
+ context: &MatchingContext<E::Impl>,
+ ) -> bool
+ where
+ E: Element,
{
if !element.is_link() {
return false
}
if context.visited_handling == VisitedHandlingMode::AllLinksVisitedAndUnvisited {
return true;
}
@@ -188,18 +197,23 @@ impl RelevantLinkStatus {
}
/// Returns whether an element is considered unvisited for the purposes of
/// matching. Assuming the element is a link, this is always true for
/// non-relevant links, since only relevant links can potentially be treated
/// as visited. If this is a relevant link, then is it unvisited if the
/// visited handling mode is set to treat all links as unvisted (including
/// relevant links).
- pub fn is_unvisited<E>(&self, element: &E, context: &MatchingContext) -> bool
- where E: Element,
+ pub fn is_unvisited<E>(
+ &self,
+ element: &E,
+ context: &MatchingContext<E::Impl>
+ ) -> bool
+ where
+ E: Element,
{
if !element.is_link() {
return false
}
if context.visited_handling == VisitedHandlingMode::AllLinksVisitedAndUnvisited {
return true;
}
@@ -272,17 +286,17 @@ enum SelectorMatchingResult {
/// unncessary cache miss for cases when we can fast-reject with AncestorHashes
/// (which the caller can store inline with the selector pointer).
#[inline(always)]
pub fn matches_selector<E, F>(
selector: &Selector<E::Impl>,
offset: usize,
hashes: Option<&AncestorHashes>,
element: &E,
- context: &mut MatchingContext,
+ context: &mut MatchingContext<E::Impl>,
flags_setter: &mut F,
) -> bool
where
E: Element,
F: FnMut(&E, ElementSelectorFlags),
{
// Use the bloom filter to fast-reject.
if let Some(hashes) = hashes {
@@ -313,17 +327,17 @@ pub enum CompoundSelectorMatchingResult
///
/// Requires that `from_offset` points to a `Combinator`.
///
/// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the
/// complex selector, but it happens to be the case we don't need it.
pub fn matches_compound_selector<E>(
selector: &Selector<E::Impl>,
mut from_offset: usize,
- context: &mut MatchingContext,
+ context: &mut MatchingContext<E::Impl>,
element: &E,
) -> CompoundSelectorMatchingResult
where
E: Element
{
debug_assert_ne!(from_offset, 0);
if cfg!(debug_assertions) {
selector.combinator_at_parse_order(from_offset - 1); // This asserts.
@@ -356,17 +370,17 @@ where
CompoundSelectorMatchingResult::FullyMatched
}
/// Matches a complex selector.
pub fn matches_complex_selector<E, F>(
mut iter: SelectorIter<E::Impl>,
element: &E,
- context: &mut MatchingContext,
+ context: &mut MatchingContext<E::Impl>,
flags_setter: &mut F,
) -> bool
where
E: Element,
F: FnMut(&E, ElementSelectorFlags),
{
// If this is the special pseudo-element mode, consume the ::pseudo-element
// before proceeding, since the caller has already handled that part.
@@ -405,17 +419,17 @@ where
SelectorMatchingResult::Matched => true,
_ => false
}
}
#[inline]
fn matches_hover_and_active_quirk<Impl: SelectorImpl>(
selector_iter: &SelectorIter<Impl>,
- context: &MatchingContext,
+ context: &MatchingContext<Impl>,
rightmost: Rightmost,
) -> bool {
if context.quirks_mode() != QuirksMode::Quirks {
return false;
}
if context.nesting_level != 0 {
return false;
@@ -460,17 +474,17 @@ fn matches_hover_and_active_quirk<Impl:
enum Rightmost {
Yes,
No,
}
fn matches_complex_selector_internal<E, F>(
mut selector_iter: SelectorIter<E::Impl>,
element: &E,
- context: &mut MatchingContext,
+ context: &mut MatchingContext<E::Impl>,
relevant_link: &mut RelevantLinkStatus,
flags_setter: &mut F,
rightmost: Rightmost,
) -> SelectorMatchingResult
where
E: Element,
F: FnMut(&E, ElementSelectorFlags),
{
@@ -584,17 +598,17 @@ where
}
}
/// Determines whether the given element matches the given single selector.
#[inline]
fn matches_simple_selector<E, F>(
selector: &Component<E::Impl>,
element: &E,
- context: &mut LocalMatchingContext,
+ context: &mut LocalMatchingContext<E::Impl>,
relevant_link: &RelevantLinkStatus,
flags_setter: &mut F,
) -> bool
where
E: Element,
F: FnMut(&E, ElementSelectorFlags),
{
match *selector {
@@ -756,17 +770,17 @@ fn select_name<'a, T>(is_html: bool, loc
} else {
local_name
}
}
#[inline]
fn matches_generic_nth_child<E, F>(
element: &E,
- context: &mut LocalMatchingContext,
+ context: &mut LocalMatchingContext<E::Impl>,
a: i32,
b: i32,
is_of_type: bool,
is_from_end: bool,
flags_setter: &mut F,
) -> bool
where
E: Element,
--- a/servo/components/selectors/tree.rs
+++ b/servo/components/selectors/tree.rs
@@ -62,27 +62,28 @@ pub trait Element: Sized + Clone + Debug
ns: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,
local_name: &<Self::Impl as SelectorImpl>::LocalName,
operation: &AttrSelectorOperation<&<Self::Impl as SelectorImpl>::AttrValue>)
-> bool;
fn match_non_ts_pseudo_class<F>(
&self,
pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
- context: &mut MatchingContext,
+ context: &mut MatchingContext<Self::Impl>,
relevant_link: &RelevantLinkStatus,
flags_setter: &mut F,
) -> bool
where
F: FnMut(&Self, ElementSelectorFlags);
- fn match_pseudo_element(&self,
- pe: &<Self::Impl as SelectorImpl>::PseudoElement,
- context: &mut MatchingContext)
- -> bool;
+ fn match_pseudo_element(
+ &self,
+ pe: &<Self::Impl as SelectorImpl>::PseudoElement,
+ context: &mut MatchingContext<Self::Impl>,
+ ) -> bool;
/// Whether this element is a `link`.
fn is_link(&self) -> bool;
fn has_id(&self,
id: &<Self::Impl as SelectorImpl>::Identifier,
case_sensitivity: CaseSensitivity)
-> bool;
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -1829,17 +1829,17 @@ impl<'le> ::selectors::Element for Gecko
unsafe {
WeakNamespace::new(Gecko_Namespace(self.0))
}
}
fn match_non_ts_pseudo_class<F>(
&self,
pseudo_class: &NonTSPseudoClass,
- context: &mut MatchingContext,
+ context: &mut MatchingContext<Self::Impl>,
relevant_link: &RelevantLinkStatus,
flags_setter: &mut F,
) -> bool
where
F: FnMut(&Self, ElementSelectorFlags),
{
use selectors::matching::*;
match *pseudo_class {
@@ -1970,17 +1970,17 @@ impl<'le> ::selectors::Element for Gecko
}
}