--- a/browser/base/content/browser-customization.js
+++ b/browser/base/content/browser-customization.js
@@ -55,19 +55,17 @@ var CustomizationHandler = {
PlacesToolbarHelper.customizeChange();
},
_customizationEnding: function(aDetails) {
// Update global UI elements that may have been added or removed
if (aDetails.changed) {
gURLBar = document.getElementById("urlbar");
- gProxyFavIcon = document.getElementById("page-proxy-favicon");
gHomeButton.updateTooltip();
- gIdentityHandler._cacheElements();
XULBrowserWindow.init();
#ifndef XP_MACOSX
updateEditUIVisibility();
#endif
// Hacky: update the PopupNotifications' object's reference to the iconBox,
// if it already exists, since it may have changed if the URL bar was
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -365,27 +365,27 @@ var PlacesCommandHook = {
gURLBar.handleRevert();
// If it was not requested to open directly in "edit" mode, we are done.
if (!aShowEditUI)
return;
// Try to dock the panel to:
// 1. the bookmarks menu button
- // 2. the page-proxy-favicon
+ // 2. the identity icon
// 3. the content area
if (BookmarkingUI.anchor) {
StarUI.showEditBookmarkPopup(itemId, BookmarkingUI.anchor,
"bottomcenter topright");
return;
}
- let pageProxyFavicon = document.getElementById("page-proxy-favicon");
- if (isElementVisible(pageProxyFavicon)) {
- StarUI.showEditBookmarkPopup(itemId, pageProxyFavicon,
+ let identityIcon = document.getElementById("identity-icon");
+ if (isElementVisible(identityIcon)) {
+ StarUI.showEditBookmarkPopup(itemId, identityIcon,
"bottomcenter topright");
} else {
StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap");
}
}),
// TODO: Replace bookmarkPage code with this function once legacy
// transactions are removed.
@@ -441,27 +441,27 @@ var PlacesCommandHook = {
// If it was not requested to open directly in "edit" mode, we are done.
if (!aShowEditUI)
return;
let node = yield PlacesUIUtils.promiseNodeLikeFromFetchInfo(info);
// Try to dock the panel to:
// 1. the bookmarks menu button
- // 2. the page-proxy-favicon
+ // 2. the identity icon
// 3. the content area
if (BookmarkingUI.anchor) {
StarUI.showEditBookmarkPopup(node, BookmarkingUI.anchor,
"bottomcenter topright");
return;
}
- let pageProxyFavicon = document.getElementById("page-proxy-favicon");
- if (isElementVisible(pageProxyFavicon)) {
- StarUI.showEditBookmarkPopup(node, pageProxyFavicon,
+ let identityIcon = document.getElementById("identity-icon");
+ if (isElementVisible(identityIcon)) {
+ StarUI.showEditBookmarkPopup(node, identityIcon,
"bottomcenter topright");
} else {
StarUI.showEditBookmarkPopup(node, aBrowser, "overlap");
}
}),
_getPageDetails(browser) {
return new Promise(resolve => {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -90,17 +90,16 @@ Object.defineProperty(window, "pktUIMess
XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
return Services.strings.createBundle('chrome://browser/locale/browser.properties');
});
const nsIWebNavigation = Ci.nsIWebNavigation;
var gLastBrowserCharset = null;
-var gProxyFavIcon = null;
var gLastValidURLStr = "";
var gInPrintPreviewMode = false;
var gContextMenu = null; // nsContextMenu instance
var gMultiProcessBrowser =
window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext)
.useRemoteTabs;
@@ -2557,21 +2556,17 @@ function UpdatePageProxyState()
}
function SetPageProxyState(aState)
{
BookmarkingUI.onPageProxyStateChanged(aState);
if (!gURLBar)
return;
- if (!gProxyFavIcon)
- gProxyFavIcon = document.getElementById("page-proxy-favicon");
-
gURLBar.setAttribute("pageproxystate", aState);
- gProxyFavIcon.setAttribute("pageproxystate", aState);
// the page proxy state is set to valid via OnLocationChange, which
// gets called when we switch tabs.
if (aState == "valid") {
gLastValidURLStr = gURLBar.value;
gURLBar.addEventListener("input", UpdatePageProxyState, false);
} else if (aState == "invalid") {
gURLBar.removeEventListener("input", UpdatePageProxyState, false);
@@ -6996,49 +6991,26 @@ var gIdentityHandler = {
delete this._overrideService;
return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
.getService(Ci.nsICertOverrideService);
},
get _identityIconCountryLabel () {
delete this._identityIconCountryLabel;
return this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
},
- get _identityIcons () {
- delete this._identityIcons;
- return this._identityIcons = document.getElementById("identity-icons");
- },
get _identityIcon () {
delete this._identityIcon;
- return this._identityIcon = document.getElementById("page-proxy-favicon");
+ return this._identityIcon = document.getElementById("identity-icon");
},
get _permissionList () {
delete this._permissionList;
return this._permissionList = document.getElementById("identity-popup-permission-list");
},
/**
- * Rebuild cache of the elements that may or may not exist depending
- * on whether there's a location bar.
- */
- _cacheElements : function() {
- delete this._identityBox;
- delete this._identityIcons;
- delete this._identityIconLabel;
- delete this._identityIconCountryLabel;
- delete this._identityIcon;
- delete this._permissionList;
- this._identityBox = document.getElementById("identity-box");
- this._identityIcons = document.getElementById("identity-icons");
- this._identityIconLabel = document.getElementById("identity-icon-label");
- this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
- this._identityIcon = document.getElementById("page-proxy-favicon");
- this._permissionList = document.getElementById("identity-popup-permission-list");
- },
-
- /**
* Handler for mouseclicks on the "More Information" button in the
* "identity-popup" panel.
*/
handleMoreInfoClick : function(event) {
displaySecurityInfo();
event.stopPropagation();
this._identityPopup.hidePopup();
},
@@ -7527,17 +7499,17 @@ var gIdentityHandler = {
// Update the popup strings
this.refreshIdentityPopup();
// Add the "open" attribute to the identity box for styling
this._identityBox.setAttribute("open", "true");
// Now open the popup, anchored off the primary chrome element
- this._identityPopup.openPopup(this._identityIcons, "bottomcenter topleft");
+ this._identityPopup.openPopup(this._identityIcon, "bottomcenter topleft");
},
onPopupShown(event) {
if (event.target == this._identityPopup) {
window.addEventListener("focus", this, true);
}
},
@@ -7570,17 +7542,17 @@ var gIdentityHandler = {
let urlString = value + "\n" + gBrowser.contentTitle;
let htmlString = "<a href=\"" + value + "\">" + value + "</a>";
let dt = event.dataTransfer;
dt.setData("text/x-moz-url", urlString);
dt.setData("text/uri-list", value);
dt.setData("text/plain", value);
dt.setData("text/html", htmlString);
- dt.setDragImage(gProxyFavIcon, 16, 16);
+ dt.setDragImage(this._identityIcon, 16, 16);
},
updateSitePermissions: function () {
while (this._permissionList.hasChildNodes())
this._permissionList.removeChild(this._permissionList.lastChild);
let uri = gBrowser.currentURI;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -699,17 +699,17 @@
aria-label="&urlbar.loginFillNotificationAnchor.label;"/>
<image id="password-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.passwordNotificationAnchor.label;"/>
<image id="webapps-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.webappsNotificationAnchor.label;"/>
<image id="plugins-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.pluginsNotificationAnchor.label;"/>
<image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"
- aria-label="&urlbar.webNotsNotificationAnchor2.label;"/>
+ aria-label="&urlbar.webNotsNotificationAnchor3.label;"/>
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
<image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.webRTCSharingDevicesNotificationAnchor.label;"/>
<image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.webRTCShareMicrophoneNotificationAnchor.label;"/>
<image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.webRTCSharingMicrophoneNotificationAnchor.label;"/>
@@ -733,23 +733,21 @@
We only add the identity-box button to the tab order when the location bar
has focus, otherwise pressing F6 focuses it instead of the location bar -->
<box id="identity-box" role="button"
align="center"
aria-label="&urlbar.viewSiteInfo.label;"
onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
ondragstart="gIdentityHandler.onDragStart(event);">
- <hbox id="identity-icons"
- consumeanchor="identity-box">
- <image id="tracking-protection-icon"/>
- <image id="page-proxy-favicon"
- onclick="PageProxyClickHandler(event);"
- pageproxystate="invalid"/>
- </hbox>
+ <image id="identity-icon"
+ consumeanchor="identity-box"
+ onclick="PageProxyClickHandler(event);"/>
+ <image id="tracking-protection-icon"/>
+ <image id="connection-icon"/>
<hbox id="identity-icon-labels">
<label id="identity-icon-label" class="plain" flex="1"/>
<label id="identity-icon-country-label" class="plain"/>
</hbox>
</box>
<box id="urlbar-display-box" align="center">
<label class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
</box>
--- a/browser/base/content/test/general/browser_insecureLoginForms.js
+++ b/browser/base/content/test/general/browser_insecureLoginForms.js
@@ -36,26 +36,27 @@ add_task(function* test_simple() {
waitForInsecureLoginFormsStateChange(browser, 2),
]);
let { gIdentityHandler } = gBrowser.ownerGlobal;
gIdentityHandler._identityBox.click();
document.getElementById("identity-popup-security-expander").click();
if (expectWarning) {
- let identityBoxImage = gBrowser.ownerGlobal
- .getComputedStyle(document.getElementById("page-proxy-favicon"), "")
+ is_element_visible(document.getElementById("connection-icon"));
+ let connectionIconImage = gBrowser.ownerGlobal
+ .getComputedStyle(document.getElementById("connection-icon"), "")
.getPropertyValue("list-style-image");
let securityViewBG = gBrowser.ownerGlobal
.getComputedStyle(document.getElementById("identity-popup-securityView"), "")
.getPropertyValue("background-image");
let securityContentBG = gBrowser.ownerGlobal
.getComputedStyle(document.getElementById("identity-popup-security-content"), "")
.getPropertyValue("background-image");
- is(identityBoxImage,
+ is(connectionIconImage,
"url(\"chrome://browser/skin/identity-mixed-active-loaded.svg\")",
"Using expected icon image in the identity block");
is(securityViewBG,
"url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
"Using expected icon image in the Control Center main view");
is(securityContentBG,
"url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
"Using expected icon image in the Control Center subview");
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -776,17 +776,18 @@ function assertMixedContentBlockingState
throw new Error("assertMixedContentBlockingState requires a browser and a states object");
}
let {passiveLoaded,activeLoaded,activeBlocked} = states;
let {gIdentityHandler} = tabbrowser.ownerGlobal;
let doc = tabbrowser.ownerDocument;
let identityBox = gIdentityHandler._identityBox;
let classList = identityBox.classList;
- let identityBoxImage = tabbrowser.ownerGlobal.getComputedStyle(doc.getElementById("page-proxy-favicon"), "").
+ let connectionIcon = doc.getElementById("connection-icon");
+ let connectionIconImage = tabbrowser.ownerGlobal.getComputedStyle(connectionIcon, "").
getPropertyValue("list-style-image");
let stateSecure = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_SECURE;
let stateBroken = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
let stateInsecure = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_INSECURE;
let stateActiveBlocked = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
let stateActiveLoaded = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT;
let statePassiveLoaded = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT;
@@ -794,47 +795,48 @@ function assertMixedContentBlockingState
is(activeBlocked, !!stateActiveBlocked, "Expected state for activeBlocked matches UI state");
is(activeLoaded, !!stateActiveLoaded, "Expected state for activeLoaded matches UI state");
is(passiveLoaded, !!statePassiveLoaded, "Expected state for passiveLoaded matches UI state");
if (stateInsecure) {
// HTTP request, there should be no MCB classes for the identity box and the non secure icon
// should always be visible regardless of MCB state.
ok(classList.contains("unknownIdentity"), "unknownIdentity on HTTP page");
- is(identityBoxImage, "url(\"chrome://browser/skin/identity-not-secure.svg\")", "Using 'non-secure' icon");
+ is_element_hidden(connectionIcon);
ok(!classList.contains("mixedActiveContent"), "No MCB icon on HTTP page");
ok(!classList.contains("mixedActiveBlocked"), "No MCB icon on HTTP page");
ok(!classList.contains("mixedDisplayContent"), "No MCB icon on HTTP page");
ok(!classList.contains("mixedDisplayContentLoadedActiveBlocked"), "No MCB icon on HTTP page");
} else {
// Make sure the identity box UI has the correct mixedcontent states and icons
is(classList.contains("mixedActiveContent"), activeLoaded,
"identityBox has expected class for activeLoaded");
is(classList.contains("mixedActiveBlocked"), activeBlocked && !passiveLoaded,
"identityBox has expected class for activeBlocked && !passiveLoaded");
is(classList.contains("mixedDisplayContent"), passiveLoaded && !(activeLoaded || activeBlocked),
"identityBox has expected class for passiveLoaded && !(activeLoaded || activeBlocked)");
is(classList.contains("mixedDisplayContentLoadedActiveBlocked"), passiveLoaded && activeBlocked,
"identityBox has expected class for passiveLoaded && activeBlocked");
+ is_element_visible(connectionIcon);
if (activeLoaded) {
- is(identityBoxImage, "url(\"chrome://browser/skin/identity-mixed-active-loaded.svg\")",
+ is(connectionIconImage, "url(\"chrome://browser/skin/identity-mixed-active-loaded.svg\")",
"Using active loaded icon");
}
if (activeBlocked && !passiveLoaded) {
- is(identityBoxImage, "url(\"chrome://browser/skin/identity-mixed-active-blocked.svg\")",
+ is(connectionIconImage, "url(\"chrome://browser/skin/identity-mixed-active-blocked.svg\")",
"Using active blocked icon");
}
if (passiveLoaded && !(activeLoaded || activeBlocked)) {
- is(identityBoxImage, "url(\"chrome://browser/skin/identity-mixed-passive-loaded.svg\")",
+ is(connectionIconImage, "url(\"chrome://browser/skin/identity-mixed-passive-loaded.svg\")",
"Using passive loaded icon");
}
if (passiveLoaded && activeBlocked) {
- is(identityBoxImage, "url(\"chrome://browser/skin/identity-mixed-passive-loaded.svg\")",
+ is(connectionIconImage, "url(\"chrome://browser/skin/identity-mixed-passive-loaded.svg\")",
"Using active blocked and passive loaded icon");
}
}
// Make sure the identity popup has the correct mixedcontent states
gIdentityHandler._identityBox.click();
let popupAttr = doc.getElementById("identity-popup").getAttribute("mixedcontent");
let bodyAttr = doc.getElementById("identity-popup-securityView-body").getAttribute("mixedcontent");
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -196,17 +196,17 @@ These should match what Safari and other
<!ENTITY urlbar.defaultNotificationAnchor.label "View a notification">
<!ENTITY urlbar.geolocationNotificationAnchor.label "View the location request">
<!ENTITY urlbar.addonsNotificationAnchor.label "View the add-on install message">
<!ENTITY urlbar.indexedDBNotificationAnchor.label "View the app-offline storage message">
<!ENTITY urlbar.loginFillNotificationAnchor.label "Manage your login information">
<!ENTITY urlbar.passwordNotificationAnchor.label "Check if you want to save your password">
<!ENTITY urlbar.webappsNotificationAnchor.label "View the app install message">
<!ENTITY urlbar.pluginsNotificationAnchor.label "Manage plugin usage on this page">
-<!ENTITY urlbar.webNotsNotificationAnchor2.label "Change whether the site can receive notifications">
+<!ENTITY urlbar.webNotsNotificationAnchor3.label "Change whether you can receive notifications from the site">
<!ENTITY urlbar.webRTCShareDevicesNotificationAnchor.label "Manage sharing your camera and/or microphone with the site">
<!ENTITY urlbar.webRTCSharingDevicesNotificationAnchor.label "You are sharing your camera and/or microphone with the site">
<!ENTITY urlbar.webRTCShareMicrophoneNotificationAnchor.label "Manage sharing your microphone with the site">
<!ENTITY urlbar.webRTCSharingMicrophoneNotificationAnchor.label "You are sharing your microphone with the site">
<!ENTITY urlbar.webRTCShareScreenNotificationAnchor.label "Manage sharing your windows or screen with the site">
<!ENTITY urlbar.webRTCSharingScreenNotificationAnchor.label "You are sharing a window or your screen with the site">
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -70,82 +70,100 @@
transition-delay: 0s, 100s, 100s;
}
@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
/* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 4.01px);
}
+/* MAIN IDENTITY ICON */
+
+#identity-icon {
+ width: 16px;
+ height: 16px;
+ list-style-image: url(chrome://browser/skin/identity-icon.svg#normal);
+}
+
+#identity-box:hover > #identity-icon,
+#identity-box[open=true] > #identity-icon {
+ list-style-image: url(chrome://browser/skin/identity-icon.svg#hover);
+}
+
+#urlbar[pageproxystate="valid"] > #identity-box.chromeUI > #identity-icon {
+ list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
+}
+
+#urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon {
+ opacity: 0.3;
+}
+
+#urlbar[actiontype="searchengine"] > #identity-box > #identity-icon {
+ -moz-image-region: inherit;
+ list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
+ width: 16px;
+ height: 16px;
+ opacity: 1;
+}
+
/* TRACKING PROTECTION ICON */
#tracking-protection-icon {
width: 16px;
height: 16px;
- margin-inline-start: 0;
- margin-inline-end: 2px;
+ margin-inline-start: 2px;
+ margin-inline-end: 0;
list-style-image: url(chrome://browser/skin/tracking-protection-16.svg);
opacity: 1;
}
#tracking-protection-icon[state="loaded-tracking-content"] {
list-style-image: url(chrome://browser/skin/tracking-protection-disabled-16.svg);
}
#tracking-protection-icon[animate] {
transition: margin-left 200ms ease-out, margin-right 200ms ease-out;
}
#tracking-protection-icon:not([state]) {
- margin-inline-start: -18px;
+ margin-inline-end: -18px;
pointer-events: none;
opacity: 0;
/* Only animate the shield in, when it disappears hide it immediately. */
transition: none;
}
-#urlbar[pageproxystate="invalid"] > #identity-box > #identity-icons > #tracking-protection-icon {
+#urlbar[pageproxystate="invalid"] > #identity-box > #tracking-protection-icon {
+ visibility: collapse;
+}
+
+/* CONNECTION ICON */
+
+#connection-icon {
+ width: 16px;
+ height: 16px;
+ margin-inline-start: 2px;
visibility: collapse;
}
-/* MAIN IDENTITY ICON */
-
-#page-proxy-favicon {
- width: 16px;
- height: 16px;
- list-style-image: url(chrome://browser/skin/identity-not-secure.svg);
+#urlbar[pageproxystate="valid"] > #identity-box.verifiedDomain > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #connection-icon {
+ list-style-image: url(chrome://browser/skin/identity-secure.svg);
+ visibility: visible;
}
-.chromeUI > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
- list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
-}
-
-.verifiedDomain > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
-.verifiedIdentity > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
- list-style-image: url(chrome://browser/skin/identity-secure.svg);
-}
-
-.insecureLoginForms > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
-.mixedActiveContent > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
+#urlbar[pageproxystate="valid"] > #identity-box.insecureLoginForms > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.mixedActiveContent > #connection-icon {
list-style-image: url(chrome://browser/skin/identity-mixed-active-loaded.svg);
+ visibility: visible;
}
-.weakCipher > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
-.mixedDisplayContent > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
-.mixedDisplayContentLoadedActiveBlocked > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
+#urlbar[pageproxystate="valid"] > #identity-box.weakCipher > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContent > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContentLoadedActiveBlocked > #connection-icon {
list-style-image: url(chrome://browser/skin/identity-mixed-passive-loaded.svg);
-}
-
-.mixedActiveBlocked > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
- list-style-image: url(chrome://browser/skin/identity-mixed-active-blocked.svg);
+ visibility: visible;
}
-#page-proxy-favicon[pageproxystate="invalid"] {
- opacity: 0.3;
+#urlbar[pageproxystate="valid"] > #identity-box.mixedActiveBlocked > #connection-icon {
+ list-style-image: url(chrome://browser/skin/identity-mixed-active-blocked.svg);
+ visibility: visible;
}
-
-#urlbar[actiontype="searchengine"] > #identity-box > #identity-icons > #page-proxy-favicon {
- -moz-image-region: inherit;
- list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
- width: 16px;
- height: 16px;
- opacity: 1;
-}
new file mode 100755
--- /dev/null
+++ b/browser/themes/shared/identity-block/identity-icon.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="48" height="16" viewBox="0 0 32 16">
+ <defs>
+ <circle id="shape-circle-base" cx="8" cy="8" r="7" />
+ <g id="shape-i">
+ <circle cx="8" cy="5" r="1" />
+ <rect x="7" y="7" width="2" height="5" rx="1" ry="1" />
+ </g>
+ <mask id="mask-ring-cutout">
+ <rect width="16" height="16" fill="#000" />
+ <use xlink:href="#shape-circle-base" fill="#fff" />
+ <circle cx="8" cy="8" r="6" fill="#000" />
+ </mask>
+ </defs>
+
+ <view id="normal" viewBox="0 0 16 16"/>
+ <g>
+ <use xlink:href="#shape-circle-base" mask="url(#mask-ring-cutout)" fill="#999" />
+ <use xlink:href="#shape-i" fill="#999" />
+ </g>
+
+ <view id="hover" viewBox="16 0 16 16"/>
+ <g transform="translate(16)">
+ <use xlink:href="#shape-circle-base" fill="#4c9ed9" />
+ <use xlink:href="#shape-i" fill="#fff" />
+ </g>
+</svg>
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -54,16 +54,17 @@
skin/classic/browser/customizableui/whimsy@2x.png (../shared/customizableui/whimsy@2x.png)
skin/classic/browser/downloads/contentAreaDownloadsView.css (../shared/downloads/contentAreaDownloadsView.css)
skin/classic/browser/drm-icon.svg (../shared/drm-icon.svg)
skin/classic/browser/fullscreen/insecure.svg (../shared/fullscreen/insecure.svg)
skin/classic/browser/fullscreen/secure.svg (../shared/fullscreen/secure.svg)
skin/classic/browser/heartbeat-icon.svg (../shared/heartbeat-icon.svg)
skin/classic/browser/heartbeat-star-lit.svg (../shared/heartbeat-star-lit.svg)
skin/classic/browser/heartbeat-star-off.svg (../shared/heartbeat-star-off.svg)
+ skin/classic/browser/identity-icon.svg (../shared/identity-block/identity-icon.svg)
skin/classic/browser/identity-not-secure.svg (../shared/identity-block/identity-not-secure.svg)
skin/classic/browser/identity-secure.svg (../shared/identity-block/identity-secure.svg)
skin/classic/browser/identity-mixed-active-blocked.svg (../shared/identity-block/identity-mixed-active-blocked.svg)
skin/classic/browser/identity-mixed-passive-loaded.svg (../shared/identity-block/identity-mixed-passive-loaded.svg)
skin/classic/browser/identity-mixed-active-loaded.svg (../shared/identity-block/identity-mixed-active-loaded.svg)
skin/classic/browser/info.svg (../shared/info.svg)
skin/classic/browser/tracking-protection-16.svg (../shared/identity-block/tracking-protection-16.svg)
skin/classic/browser/tracking-protection-disabled-16.svg (../shared/identity-block/tracking-protection-disabled-16.svg)
--- a/devtools/client/framework/gDevTools.jsm
+++ b/devtools/client/framework/gDevTools.jsm
@@ -77,18 +77,17 @@ DevTools.prototype = {
* - visibilityswitch: Property name to allow us to hide this tool from the
* DevTools Toolbox.
* A falsy value indicates that it cannot be hidden.
* - icon: URL pointing to a graphic which will be used as the src for an
* 16x16 img tag (string|required)
* - invertIconForLightTheme: The icon can automatically have an inversion
* filter applied (default is false). All builtin tools are true, but
* addons may omit this to prevent unwanted changes to the `icon`
- * image. See devtools/client/themes/filters.svg#invert for
- * the filter being applied to the images (boolean|optional)
+ * image. filter: invert(1) is applied to the image (boolean|optional)
* - url: URL pointing to a XUL/XHTML document containing the user interface
* (string|required)
* - label: Localized name for the tool to be displayed to the user
* (string|required)
* - hideInOptions: Boolean indicating whether or not this tool should be
shown in toolbox options or not. Defaults to false.
* (boolean)
* - build: Function that takes an iframe, which has been populated with the
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -157,20 +157,16 @@ devtools.jar:
skin/images/add.svg (themes/images/add.svg)
skin/images/filters.svg (themes/images/filters.svg)
skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
skin/images/controls.png (themes/images/controls.png)
skin/images/controls@2x.png (themes/images/controls@2x.png)
skin/images/animation-fast-track.svg (themes/images/animation-fast-track.svg)
skin/images/performance-icons.svg (themes/images/performance-icons.svg)
- skin/images/newtab.png (themes/images/newtab.png)
- skin/images/newtab@2x.png (themes/images/newtab@2x.png)
- skin/images/newtab-inverted.png (themes/images/newtab-inverted.png)
- skin/images/newtab-inverted@2x.png (themes/images/newtab-inverted@2x.png)
* skin/widgets.css (themes/widgets.css)
skin/images/power.svg (themes/images/power.svg)
skin/images/filetypes/dir-close.svg (themes/images/filetypes/dir-close.svg)
skin/images/filetypes/dir-open.svg (themes/images/filetypes/dir-open.svg)
skin/images/filetypes/globe.svg (themes/images/filetypes/globe.svg)
skin/images/filetypes/store.svg (themes/images/filetypes/store.svg)
skin/images/commandline-icon.png (themes/images/commandline-icon.png)
skin/images/commandline-icon@2x.png (themes/images/commandline-icon@2x.png)
@@ -283,17 +279,16 @@ devtools.jar:
skin/images/dock-bottom@2x.png (themes/images/dock-bottom@2x.png)
skin/images/dock-bottom-minimize@2x.png (themes/images/dock-bottom-minimize@2x.png)
skin/images/dock-bottom-maximize@2x.png (themes/images/dock-bottom-maximize@2x.png)
skin/images/dock-side@2x.png (themes/images/dock-side@2x.png)
* skin/floating-scrollbars.css (themes/floating-scrollbars.css)
skin/floating-scrollbars-light.css (themes/floating-scrollbars-light.css)
* skin/inspector.css (themes/inspector.css)
skin/images/profiler-stopwatch.svg (themes/images/profiler-stopwatch.svg)
- skin/images/profiler-stopwatch-checked.svg (themes/images/profiler-stopwatch-checked.svg)
skin/images/tool-options.svg (themes/images/tool-options.svg)
skin/images/tool-webconsole.svg (themes/images/tool-webconsole.svg)
skin/images/tool-canvas.svg (themes/images/tool-canvas.svg)
skin/images/tool-debugger.svg (themes/images/tool-debugger.svg)
skin/images/tool-debugger-paused.svg (themes/images/tool-debugger-paused.svg)
skin/images/tool-inspector.svg (themes/images/tool-inspector.svg)
skin/images/tool-shadereditor.svg (themes/images/tool-shadereditor.svg)
skin/images/tool-styleeditor.svg (themes/images/tool-styleeditor.svg)
--- a/devtools/client/shared/widgets/spectrum-frame.xhtml
+++ b/devtools/client/shared/widgets/spectrum-frame.xhtml
@@ -14,11 +14,11 @@
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body role="application">
<div id="spectrum"></div>
- <div id="eyedropper-button"></div>
+ <button id="eyedropper-button" class="devtools-button"></button>
</body>
-</html>
\ No newline at end of file
+</html>
--- a/devtools/client/shared/widgets/spectrum.css
+++ b/devtools/client/shared/widgets/spectrum.css
@@ -1,45 +1,24 @@
/* 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/. */
#eyedropper-button {
- background-image: url("chrome://devtools/skin/images/command-eyedropper.png");
- width: 16px;
- height: 16px;
- background-size: 64px 16px;
- background-position: 0 center;
- background-repeat: no-repeat;
- -moz-margin-start: 5px;
- border-radius: 2px;
- cursor: pointer;
+ margin-inline-start: 5px;
}
-.theme-light #eyedropper-button {
- filter: url(chrome://devtools/skin/images/filters.svg#invert);
- border: 1px solid #AAA;
-}
-
-.theme-dark #eyedropper-button {
- border: 1px solid #444;
-}
-
-#eyedropper-button:hover {
- background-position: -16px center;
-}
-#eyedropper-button:hover:active {
- background-position: -32px center;
-}
-#eyedropper-button[checked=true] {
- background-position: -48px center;
+#eyedropper-button::before {
+ background-image: url("chrome://devtools/skin/images/command-eyedropper.png");
+ background-size: 64px 16px;
+ background-position: 0 center;
}
@media (min-resolution: 1.1dppx) {
- #eyedropper-button {
+ #eyedropper-button::before {
background-image: url("chrome://devtools/skin/images/command-eyedropper@2x.png");
}
}
/* Mix-in classes */
.spectrum-checker {
background-color: #eee;
--- a/devtools/client/themes/animationinspector.css
+++ b/devtools/client/themes/animationinspector.css
@@ -128,21 +128,16 @@ body {
.pause-button::before {
background-image: url("images/debugger-pause.png");
}
#rewind-timeline::before {
background-image: url("images/rewind.png");
}
-#element-picker[checked]::before {
- background-position: -48px 0;
- filter: none; /* Icon is blue when checked, don't invert for light theme */
-}
-
.pause-button.paused::before {
background-image: url("images/debugger-play.png");
}
@media (min-resolution: 1.1dppx) {
#element-picker::before {
background-image: url("chrome://devtools/skin/images/command-pick@2x.png");
background-size: 64px;
@@ -399,22 +394,22 @@ body {
.animation-target .node-highlighter {
background: url("chrome://devtools/skin/images/vview-open-inspector.png") no-repeat 0 0;
padding-left: 16px;
margin-right: 5px;
cursor: pointer;
}
.animation-target .node-highlighter:hover {
- background-position: -32px 0;
+ filter: url(images/filters.svg#checked-icon-state);
}
.animation-target .node-highlighter:active,
.animation-target .node-highlighter.selected {
- background-position: -16px 0;
+ filter: url(images/filters.svg#checked-icon-state) brightness(0.9);
}
/* Animation title gutter, contains the name, duration, iteration */
.animation-title {
background-color: var(--theme-toolbar-background);
border-bottom: 1px solid var(--theme-splitter-color);
padding: 1px 4px;
--- a/devtools/client/themes/canvasdebugger.css
+++ b/devtools/client/themes/canvasdebugger.css
@@ -37,20 +37,16 @@
#snapshots-pane .devtools-toolbar {
-moz-border-end: 1px solid var(--theme-splitter-color);
}
#record-snapshot {
list-style-image: url("chrome://devtools/skin/images/profiler-stopwatch.svg");
}
-#record-snapshot[checked] {
- list-style-image: url("chrome://devtools/skin/images/profiler-stopwatch-checked.svg");
-}
-
/* Snapshots items */
.snapshot-item-thumbnail {
image-rendering: -moz-crisp-edges;
background-image: var(--checkerboard-pattern);
background-size: 12px 12px, 12px 12px;
background-position: 0px 0px, 6px 6px;
background-repeat: repeat, repeat;
@@ -98,25 +94,18 @@
}
#snapshots-list .selected label {
/* Text inside a selected item should not be custom colored. */
color: inherit !important;
}
/* Debugging pane controls */
-
-#debugging-controls .devtools-toolbarbutton > .toolbarbutton-icon {
- width: 16px;
- height: 16px;
-}
-
#resume {
list-style-image: url(images/debugger-play.png);
- -moz-image-region: rect(0px,32px,16px,16px);
}
#step-over {
list-style-image: url(images/debugger-step-over.png);
}
#step-in {
list-style-image: url(images/debugger-step-in.png);
@@ -124,40 +113,31 @@
#step-out {
list-style-image: url(images/debugger-step-out.png);
}
@media (min-resolution: 1.1dppx) {
#resume {
list-style-image: url(images/debugger-play@2x.png);
- -moz-image-region: rect(0px,64px,32px,32px);
}
#step-over {
list-style-image: url(images/debugger-step-over@2x.png);
}
#step-in {
list-style-image: url(images/debugger-step-in@2x.png);
}
#step-out {
list-style-image: url(images/debugger-step-out@2x.png);
}
}
-#debugging-controls > toolbarbutton {
- transition: opacity 0.15s ease-in-out;
-}
-
-#debugging-controls > toolbarbutton[disabled=true] {
- opacity: 0.5;
-}
-
#calls-slider {
-moz-padding-end: 24px;
}
#calls-slider .scale-slider {
margin: 0;
}
--- a/devtools/client/themes/commandline.inc.css
+++ b/devtools/client/themes/commandline.inc.css
@@ -47,17 +47,17 @@
}
.developer-toolbar-button > image {
margin: auto 10px;
}
:root[devtoolstheme="light"] #developer-toolbar > toolbarbutton:not([checked=true]) > image,
:root[devtoolstheme="light"] .gclitoolbar-input-node:not([focused=true])::before {
- filter: url("chrome://devtools/skin/images/filters.svg#invert");
+ filter: invert(1);
}
.developer-toolbar-button > .toolbarbutton-icon,
#developer-toolbar-closebutton > .toolbarbutton-icon {
width: 16px;
height: 16px;
}
--- a/devtools/client/themes/debugger.css
+++ b/devtools/client/themes/debugger.css
@@ -57,16 +57,20 @@
/* Sources toolbar */
#sources-toolbar > .devtools-toolbarbutton,
#sources-controls > .devtools-toolbarbutton {
min-width: 32px;
}
+#sources-toolbar .devtools-toolbarbutton:not([label]) {
+ -moz-image-region: rect(0,16px,16px,0);
+}
+
#black-box {
list-style-image: url(images/debugger-blackbox.png);
}
@media (min-resolution: 1.1dppx) {
#black-box {
list-style-image: url(images/debugger-blackbox@2x.png);
}
@@ -81,44 +85,43 @@
list-style-image: url(images/debugger-prettyprint@2x.png);
}
}
#toggle-breakpoints {
list-style-image: url(images/debugger-toggleBreakpoints.png);
}
+#toggle-breakpoints[checked] {
+ -moz-image-region: rect(0,32px,16px,16px) !important;
+}
+
+#toggle-breakpoints[checked] > image {
+ /* This button has a special checked image, don't make it blue */
+ filter: none;
+}
+
@media (min-resolution: 1.1dppx) {
+ #sources-toolbar .devtools-toolbarbutton:not([label]) {
+ -moz-image-region: rect(0,32px,32px,0);
+ }
+
#toggle-breakpoints {
list-style-image: url(images/debugger-toggleBreakpoints@2x.png);
}
+
+ #toggle-breakpoints[checked] {
+ -moz-image-region: rect(0,64px,32px,32px) !important;
+ }
}
#toggle-promise-debugger {
/* TODO Bug 1186119: Add a toggle promise debugger image */
}
-#sources-toolbar .devtools-toolbarbutton:not([label]) {
- -moz-image-region: rect(0px,16px,16px,0px);
-}
-
-#sources-toolbar .devtools-toolbarbutton:not([label])[checked] {
- -moz-image-region: rect(0px,32px,16px,16px);
-}
-
-@media (min-resolution: 1.1dppx) {
- #sources-toolbar .devtools-toolbarbutton:not([label]) {
- -moz-image-region: rect(0px,32px,32px,0px);
- }
-
- #sources-toolbar .devtools-toolbarbutton:not([label])[checked] {
- -moz-image-region: rect(0px,64px,32px,32px);
- }
-}
-
#sources .black-boxed {
color: rgba(128,128,128,0.4);
}
#sources .selected .black-boxed {
color: rgba(255,255,255,0.4);
}
@@ -214,31 +217,21 @@
/* Text inside a selected item should not be custom colored. */
color: inherit !important;
}
/* Tracer */
#trace {
list-style-image: url(images/tracer-icon.png);
- -moz-image-region: rect(0px,16px,16px,0px);
-}
-
-#trace[checked] {
- -moz-image-region: rect(0px,32px,16px,16px);
}
@media (min-resolution: 1.1dppx) {
#trace {
list-style-image: url(images/tracer-icon@2x.png);
- -moz-image-region: rect(0px,32px,32px,0px);
- }
-
- #trace[checked] {
- -moz-image-region: rect(0px,64px,32px,32px);
}
}
#clear-tracer {
/* Make this button as narrow as the text inside it. */
min-width: 1px;
}
@@ -544,52 +537,34 @@
}
.theme-light .dbg-results-line-contents-string[match=true] {
color: var(--theme-body-color);
}
/* Toolbar controls */
-#debugger-toolbar .devtools-toolbarbutton:not([label]) > .toolbarbutton-icon,
-#sources-toolbar .devtools-toolbarbutton:not([label]) > .toolbarbutton-icon {
- width: 16px;
- height: 16px;
-}
-
#resume {
list-style-image: url(images/debugger-pause.png);
- -moz-image-region: rect(0px,16px,16px,0px);
}
#resume[checked] {
list-style-image: url(images/debugger-play.png);
}
@media (min-resolution: 1.1dppx) {
#resume {
list-style-image: url(images/debugger-pause@2x.png);
- -moz-image-region: rect(0px,32px,32px,0px);
}
#resume[checked] {
list-style-image: url(images/debugger-play@2x.png);
- -moz-image-region: rect(0px,64px,32px,32px);
}
}
-#debugger-controls toolbarbutton {
- transition: opacity 0.15s ease-in-out;
-}
-
-#debugger-controls toolbarbutton[disabled] {
- opacity: .5;
- transition: none;
-}
-
#resume[break-on-next] {
background: var(--theme-highlight-lightorange);
}
#step-over {
list-style-image: url(images/debugger-step-over.png);
}
@@ -611,44 +586,31 @@
}
#step-out {
list-style-image: url(images/debugger-step-out@2x.png);
}
}
#instruments-pane-toggle {
- background: none;
- box-shadow: none;
- border: none;
list-style-image: url(images/debugger-collapse.png);
- -moz-image-region: rect(0px,16px,16px,0px);
}
#instruments-pane-toggle[pane-collapsed] {
list-style-image: url(images/debugger-expand.png);
}
-#instruments-pane-toggle:hover {
- -moz-image-region: rect(0px,32px,16px,16px);
-}
-
@media (min-resolution: 1.1dppx) {
#instruments-pane-toggle {
list-style-image: url(images/debugger-collapse@2x.png);
- -moz-image-region: rect(0px,32px,32px,0px);
}
#instruments-pane-toggle[pane-collapsed] {
list-style-image: url(images/debugger-expand@2x.png);
}
-
- #instruments-pane-toggle:hover {
- -moz-image-region: rect(0px,64px,32px,32px);
- }
}
/* Horizontal vs. vertical layout */
#vertical-layout-panes-container {
min-height: 35vh;
max-height: 80vh;
}
deleted file mode 100644
index 6726f282090658b241cf9dde5a566554cd4b3549..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
index 9573fc72d9430189cd685a56d121cbdadb06ed7a..75c9260436248d87df8a630a193a2e33f0542aa4
GIT binary patch
literal 192
zc$@*i06+hUP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0001oNkl<Zc-muN
z7(U?Z_g|;KeE)Tm82t6euQM11fF$0&eanZ<@Yk<j^P(Gq)c_d#`_JFC|NsBzLYF7n
z0FZaSe*eAw$B!RsWE%kW>&4ICeuh$Fz@=~BenyaMz^>1qe`=EHg`bVUK;$Ml5MRCe
u&x0)*K`}zm0HA-*5}lB~{W?F4@(=*;6FEZStfp%K0000<MNUMnLSTZCcvqeP
index 12f7223bcba6404f5fceeeae090537d9dabf11c3..1545db409483cb1c0f43d74fa8805eca7ebdfc1b
GIT binary patch
literal 216
zc%17D@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ`JOJ0Ar-gYPTR=apdjEHdB8L0
zN~O$k&KRe;2L*N3&S|_h`OZxa*3OMV%0FAbPm&jsiYqE;4-|QN>eZ6ZkwQ!ND{okG
zCYMoZ<r!y>C4YPl>`|G?uygOmYiSOQGxr=hamLa|;mz;TIN9q4jS{z(@TxX)962)S
z0LwYmMs5dhhP^Et(m8Tn_GcXZktpK!`tf1cE`F8|@{{al9yrzvwB!)u^tAcspG=>r
Q2Xrcfr>mdKI;Vst09h?r2LJ#7
index f317997264a8b8b3172ed8d96bebcf4bbe372075..a1957a8df30a62c931a82bd0ac45d80e851b9ac7
GIT binary patch
literal 201
zc$@*r05<=LP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0001xNkl<Zc-muN
z7(U?Z_g|;KeE)Tm82t6euQM11fF$0&eanZ<@Yk<j^P(Gq)c|Dv|NsBFzWw;wjVw>J
z0Y85HQ2YA*_jY74q7C@`?Pn;^j~CGu5^cb@Z$Be|23#WB0I(}Q|I`HXcad!XG!VJJ
z{rKHSk^!$?{pZ0Jji4AIXaLaPXNgWo-+rARMtKMTvMf1~q|hF100000NkvXXu0mjf
DDpy#q
index 091601f5c65085589f02f859b3d10251011b010e..b6892f6c08a32fbef7c89060cd936b39b439b4e0
GIT binary patch
literal 223
zc%17D@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJWu7jMAr-fhC0G|Hh%9K<zhAG*
z{%E$zA?=RB1HMs7i)251eXXy|CUo$6{C=L5&luQ@81Kq_`1LjW-unCX+j%;e>;C;&
z$-AZI`}Ozx{Vtzn<oG$?Fn<64T|7a~`s@l#e{Qe$G^n()Vi0^{V7beZ!S;lE1M>?3
z))K##FX!97=y$mJ1p53x_2TvV_%@a3f3K%ADzfu*vYq6bW%=Ot`KdfWNA)n7Ffa(`
W%|CzMw^b16Vg^rFKbLh*2~7ZduvrHH
index 8288bdfc37719ae15951977b499ee8c35625d93e..5863380b0d373db4f5982ef4fc9a1754d46b9ede
GIT binary patch
literal 108
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`#-1*YAr-fh6C~;$oZdb^Hh%x#
zJAd9Df3H`QWWng6$n#iO+BBg;n!!tFg3IU4((`W^Ffn)(Uo$_-xy=r!ox#)9&t;uc
GLK6UioFbI~
index 53ed141a2cb726197443d51c669bbde60067ae5e..3d38eccd2af098728d1a18ba3755d2ddd5312245
GIT binary patch
literal 151
zc%17D@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJI8PVHkcwMxr)}gsV8Fw&Ihc(v
zrFD(i%^On#zt2qxk$mT%aO88$N^S<FGtd5R{}?4bzyE!m_w!X5E2k|l4&4%<KHHgr
ykww4(Nc4$x$oJaKKO6H`^>WburNs;a8yFZ)N6fmzc(wv)6N9I#pUXO@geCxVjWyK(
index 9189a0a206b1989eb9c6998beead15c3b1ddbf1f..2bde10cb475dbab35236b4a2003a0dcec5e5118b
GIT binary patch
literal 184
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`^`0({Ar-ftUfRvs;vm5GU^m~p
zN6p%Y<XD)QXB%#0Jm_@9gN3`edG7T-gQP7Rg<tX4|2k+BXU@}EHX-OC8$+sX_nKUe
zhp*Yrxp@VrtUdGQEu&G3k-)|K>`(L*svomQq%*9GWtjA(<J)c<rXZ=ATT65emA22m
i`TUop=O<-bW%f9iT@i*d<p+RHVeoYIb6Mw<&;$TPH$$WV
index 5fe68e1d1dc71e9c53cc70391649edf409bebafa..b6b06502e830a8de7e3003d7e6497799784f9180
GIT binary patch
literal 309
zc$@(>0m}Y~P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80002|Nkl<Zc-rli
zJxT;Y5QRqs0}(bgF%~1efC?glm~3F?3B*V|fsqH$KnxWJ12I_7GM6xzz^SgkUMO+d
zrNWK2E9nnj{?Yw-)l@2#Wf0|)B-3%%bsHf9K+-0Z??n0npa%g0`tdKY2q52?*(gB3
zgQP8RJ~Okm5CIpF2SnV75b$vR0+K5)X;}de>ob7v#|ZdqOpx`q@B2}JfCorgU_Ol!
z@FW=r3-CpV0Fu59l#e66V3JsGe$2K4N~ll6AbSx*^ob~!X0~2_h)C`MV7vGf1Ip_!
z<-@Yq&`Wl0W}8hZko0|CvPWj#ZAvo`9WhVOXC<ptDl6a*3s0)E%~?-e00000NkvXX
Hu0mjfhERG`
index f2c6c52b9e69ef842671be8b56b5b22d8b25b10f..5cbc7274f071b80e6660f23f3ddd66f1aca7af86
GIT binary patch
literal 195
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`U7jwEAr-goPTI}M<S5X#mARL-
zdjtEy0|(OdHZmUM%W>#&*|6bu!t<@gALONDi)7xkd_OFH_QOFNGwtpf_g7B29K_!c
zp1RE==Reo#a?Pc+5dkNI>>~d%B}fbUC~aHbA(PzHDSxw|oBPUsmnvS)h&Km&oIi8u
tJYD<h`2`{4?CZ?CzZrL#+FZ&qV#*I?lK)zHBOT}}22WQ%mvv4FO#q?2OhNzv
index 4c3cf88fb9a5e78a2562ee91a65e7fb44c3f1d54..345d07cea08f86cd1095c1e5ceb953dc75efddd5
GIT binary patch
literal 323
zc$@)40lfZ+P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80003BNkl<Zc-rlj
zJxT;Y5QSG33`E$}#8`~<0;?b*h{*<Ko<NMm6Bu~_H&_f62Lllq&oY-Vn82y7zg}oZ
zajC>)5xbIp@WOt4bU&(kP*ha(n~35;kolx(n$^GLq?BG4Ug{u_Q~~8Jklq34t^<HR
zeE}>C$X9*H$N|8Nq!n;H>cduC01QNK5OLE3z|8RlNY1RJc>&C<GXUN30kGSsk@d1|
z+mQo+8AvK%KJ)@G2bnkn@QDiml0I}4_dTAV4y+gNDXlw{P^Vod+a81H8BtFA;9>qj
zL~;!P8`(!Opxl2@9OvDJR<iRxWG&k|M$*ThlHD@%R`w4`N3_R0KmIORQBl!9>l4A4
Vd`Ig0UFiS-002ovPDHLkV1n=2hMNEY
--- a/devtools/client/themes/images/filters.svg
+++ b/devtools/client/themes/images/filters.svg
@@ -1,20 +1,19 @@
<!-- 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/. -->
<svg height="0" xmlns="http://www.w3.org/2000/svg">
- <filter id="invert" x="0%" y="0%" width="100%" height="100%"
- primitiveUnits="objectBoundingBox">
- <feComponentTransfer>
- <feFuncR type="table" tableValues=".1 0"/>
- <feFuncG type="table" tableValues=".1 0"/>
- <feFuncB type="table" tableValues=".1 0"/>
- </feComponentTransfer>
+ <filter id="checked-icon-state">
+ <feColorMatrix in="SourceGraphic" type="matrix"
+ values="0 0 0 0 0.043
+ 0 0 0 0 0.415
+ 0 0 0 0 0.79
+ 0 0 0 1 0"/>
</filter>
<!-- Web Audio Gradients -->
<linearGradient id="bypass-light" x1="6%" y1="8%" x2="12%" y2="12%" spreadMethod="repeat">
<stop offset="0%" stop-color="#f0f1f2"/> <!-- theme-toolbar-background -->
<stop offset="50%" stop-color="#fff"/>
</linearGradient>
deleted file mode 100644
index 2d29c2cbea688fdf44ba7d1617bd9edd8a04a4c4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 6feba0e83eaad385ae512fc611187c10dec6e9d4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 32e42b04cce15cb654b44bc882b264bec5efa963..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index ffde5f05086c366c5873e0d5fa383f45982ae002..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/devtools/client/themes/images/profiler-stopwatch-checked.svg
+++ /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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
- <g fill="#3bace5" fill-rule="evenodd">
- <path d="m8,1c-3.9,0-7,3.1-7,7s3.1,7 7,7c3.9,0 7-3.1 7-7s-3.1-7-7-7zm-.1,12c-2.8,0-5-2.2-5-5 0-2.8 2.2-5 5-5s5,2.2 5,5c0,2.8-2.2,5-5,5z"/>
- <path d="m8,6.9c.6,0 1.1,.5 1.1,1.1 0,.6-.5,1.1-1.1,1.1-.6,0-1.1-.5-1.1-1.1 0-.6 .5-1.1 1.1-1.1z"/>
- <path d="m11.3,4.6l-3.9,2.5 1.5,1.4 2.4-3.9z"/>
- <path opacity=".4" d="m4.6,10c.7,1.2 2,2 3.4,2 1.5,0 2.7-.8 3.4-2h-6.8z"/>
- <g opacity=".3">
- <path d="m7.1,5.1l-.6-1.3-.9,.4 .7,1.3c.2-.1 .5-.3 .8-.4z"/>
- <path d="m9.8,5.6l.7-1.4-.9-.4-.7,1.3c.3,.2 .6,.3 .9,.5z"/>
- <path d="m10.8,7c.1,.3 .2,.7 .2,1h2v-1h-2.2z"/>
- <path d="m5,8c0-.3 .1-.7 .2-1h-2.2l-.1,1h2.1z"/>
- </g>
- </g>
-</svg>
--- a/devtools/client/themes/images/pseudo-class.svg
+++ b/devtools/client/themes/images/pseudo-class.svg
@@ -1,29 +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/. -->
-<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<svg width="16" height="16" viewBox="0 0 16 16" color="#babec3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
- <style>
- use[id^="pseudo-class"]:not(:target) {
- display: none;
- }
- </style>
<rect id="class-block-maskBG" width="8" height="8" fill="#fff"/>
<rect id="class-block" width="8" height="8" rx="1" ry="1"/>
<mask id="mask-block-solid">
<use xlink:href="#class-block-maskBG"/>
<use xlink:href="#class-block" transform="translate(3 3)" fill="#000"/>
</mask>
- <g id="pseudo-class-shape">
- <rect x=".5" y=".5" width="7" height="7" rx="1" ry="1" mask="url(#mask-block-solid)" fill="none" stroke="currentColor" stroke-width="1"/>
- <use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" fill-opacity=".4"/>
- <use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" transform="translate(4 4)"/>
- <g transform="translate(8 8)" fill="currentColor">
- <path d="M2.5,0C2.2,0,2,0.2,2,0.5C2,0.8,2.2,1,2.5,1C2.8,1,3,0.8,3,0.5 C3,0.2,2.8,0,2.5,0z M4.5,0C4.2,0,4,0.2,4,0.5C4,0.8,4.2,1,4.5,1C4.8,1,5,0.8,5,0.5C5,0.2,4.8,0,4.5,0z M0.5,6C0.8,6,1,5.8,1,5.5 C1,5.2,0.8,5,0.5,5C0.2,5,0,5.2,0,5.5C0,5.8,0.2,6,0.5,6z M0.5,4C0.8,4,1,3.8,1,3.5C1,3.2,0.8,3,0.5,3C0.2,3,0,3.2,0,3.5 C0,3.8,0.2,4,0.5,4z M7.5,2C7.2,2,7,2.2,7,2.5C7,2.8,7.2,3,7.5,3C7.8,3,8,2.8,8,2.5C8,2.2,7.8,2,7.5,2z M7.5,4C7.2,4,7,4.2,7,4.5 C7,4.8,7.2,5,7.5,5C7.8,5,8,4.8,8,4.5C8,4.2,7.8,4,7.5,4z M5.5,7C5.2,7,5,7.2,5,7.5C5,7.8,5.2,8,5.5,8C5.8,8,6,7.8,6,7.5 C6,7.2,5.8,7,5.5,7z M3.5,7C3.2,7,3,7.2,3,7.5C3,7.8,3.2,8,3.5,8C3.8,8,4,7.8,4,7.5C4,7.2,3.8,7,3.5,7z M0.5,2C0.8,2,1,1.8,1,1.5v-1 C1,0.2,0.8,0,0.5,0C0.2,0,0,0.2,0,0.5v1C0,1.8,0.2,2,0.5,2z M8,0.5C8,0.2,7.8,0,7.5,0h-1C6.2,0,6,0.2,6,0.5C6,0.8,6.2,1,6.5,1h1 C7.8,1,8,0.8,8,0.5z M7.5,6C7.2,6,7,6.2,7,6.5v1C7,7.8,7.2,8,7.5,8C7.8,8,8,7.8,8,7.5v-1C8,6.2,7.8,6,7.5,6z M1.5,7h-1 C0.2,7,0,7.2,0,7.5C0,7.8,0.2,8,0.5,8h1C1.8,8,2,7.8,2,7.5C2,7.2,1.8,7,1.5,7z"/>
- <use xlink:href="#class-block" fill-opacity=".2"/>
- </g>
- </g>
</defs>
- <use xlink:href="#pseudo-class-shape" id="pseudo-class" color="#babec3"/>
- <use xlink:href="#pseudo-class-shape" id="pseudo-class-checked" color="#3089C9"/>
+ <rect x=".5" y=".5" width="7" height="7" rx="1" ry="1" mask="url(#mask-block-solid)" fill="none" stroke="currentColor" stroke-width="1"/>
+ <use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" fill-opacity=".4"/>
+ <use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" transform="translate(4 4)"/>
+ <g transform="translate(8 8)" fill="currentColor">
+ <path d="M2.5,0C2.2,0,2,0.2,2,0.5C2,0.8,2.2,1,2.5,1C2.8,1,3,0.8,3,0.5 C3,0.2,2.8,0,2.5,0z M4.5,0C4.2,0,4,0.2,4,0.5C4,0.8,4.2,1,4.5,1C4.8,1,5,0.8,5,0.5C5,0.2,4.8,0,4.5,0z M0.5,6C0.8,6,1,5.8,1,5.5 C1,5.2,0.8,5,0.5,5C0.2,5,0,5.2,0,5.5C0,5.8,0.2,6,0.5,6z M0.5,4C0.8,4,1,3.8,1,3.5C1,3.2,0.8,3,0.5,3C0.2,3,0,3.2,0,3.5 C0,3.8,0.2,4,0.5,4z M7.5,2C7.2,2,7,2.2,7,2.5C7,2.8,7.2,3,7.5,3C7.8,3,8,2.8,8,2.5C8,2.2,7.8,2,7.5,2z M7.5,4C7.2,4,7,4.2,7,4.5 C7,4.8,7.2,5,7.5,5C7.8,5,8,4.8,8,4.5C8,4.2,7.8,4,7.5,4z M5.5,7C5.2,7,5,7.2,5,7.5C5,7.8,5.2,8,5.5,8C5.8,8,6,7.8,6,7.5 C6,7.2,5.8,7,5.5,7z M3.5,7C3.2,7,3,7.2,3,7.5C3,7.8,3.2,8,3.5,8C3.8,8,4,7.8,4,7.5C4,7.2,3.8,7,3.5,7z M0.5,2C0.8,2,1,1.8,1,1.5v-1 C1,0.2,0.8,0,0.5,0C0.2,0,0,0.2,0,0.5v1C0,1.8,0.2,2,0.5,2z M8,0.5C8,0.2,7.8,0,7.5,0h-1C6.2,0,6,0.2,6,0.5C6,0.8,6.2,1,6.5,1h1 C7.8,1,8,0.8,8,0.5z M7.5,6C7.2,6,7,6.2,7,6.5v1C7,7.8,7.2,8,7.5,8C7.8,8,8,7.8,8,7.5v-1C8,6.2,7.8,6,7.5,6z M1.5,7h-1 C0.2,7,0,7.2,0,7.5C0,7.8,0.2,8,0.5,8h1C1.8,8,2,7.8,2,7.5C2,7.2,1.8,7,1.5,7z"/>
+ <use xlink:href="#class-block" fill-opacity=".2"/>
+ </g>
</svg>
index 098e256ab8e330887a8765634e056b303ccbd02d..3e8b4db747311a8ac511e3db2cb2dab6a5541ad7
GIT binary patch
literal 203
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`lRRA<Ln?0Fon*_!<jB){T{=Uz
zg5$kHL_~$;?7a;Sc=t3!bUrvZC+U-U<cUdIclC^KziqzW@_nC@=XRx;H!s^xZ4Byj
zxD?L(FYR_u4fB-w)p7!xc{k6SaO!k-D1*yw<&(Buq6`fC7+xPgoXa4&uHjQI+m4PG
z69VOW8zPd@Y}2*gvpHxi-Qs-B;_)PRrHlKbr@QrVG09iPO+F8F9fPN<pUXO@geCw>
CZcoer
index eaac45d9435c7113dd84a12063bcce603d5c1de5..c6fee45f3da3d5d4bdda03779dac7cbcd73337c9
GIT binary patch
literal 330
zc$@)B0k!^#P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80003INkl<Zc-rlf
zJxT;Y5QSF+0}(bgF%~1efC?glm~3F?3B*V|fsqH$KnxWJ0};`)%q0vaaO%GoS{7+K
z7!wz^I_VE8eD%KIjVhJOqKPpsi%gGu=Dq(0-_y-oE+U&qc8jE+#P;kVkg}^Nc8;VU
zv+~&i+l!09H-}vyeN4q}0syd%tk<c~K_HKeNKaA1YbtXS=zHBlvb$8`Ch!&G8j4+{
zk+}-=9QKLrDLr--K$Mg8u#kXG3JGLD<Pcb!0s@(?>&7H@i4r_VI0V49QS82eK&EZm
z6<|F>32y}iGDNgNtT$x@a%Kr691&aND=^HCf#k|xU^vnpWPA7((8gIHj}&z}VGakQ
cN~Q7#Ke>N=N2o~Fn*aa+07*qoM6N<$f}fd!TL1t6
--- a/devtools/client/themes/images/timeline-filter.svg
+++ b/devtools/client/themes/images/timeline-filter.svg
@@ -1,26 +1,6 @@
-<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
- <style>
- use:not(:target) {
- display: none;
- }
- use {
- fill: #babec3;
- }
- use[id$="-disabled"] {
- fill-opacity: 0.5;
- }
- use[id$="-open"] {
- fill: #3bace5;
- }
- </style>
- <defs>
- <path id="filter-shape" d="M 2,2 v 3 l 5,4 v 6 h 2 v -6 l 5,-4 v -3 L 14,2 z"/>
- </defs>
- <use id="filter" xlink:href="#filter-shape"/>
- <use id="filter-disabled" xlink:href="#filter-shape"/>
- <use id="filter-open" xlink:href="#filter-shape"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+ <path fill="#babec3" d="M2,2v3l5,4v6h2v-6l5,-4v-3L14,2z"/>
</svg>
index 8229dd7c78f37edcf897d773a9b84bc5be707322..2b5aaf0aa40d8c0d4bd710662dd565dc3420487c
GIT binary patch
literal 290
zc$@(u0p0$IP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0002#Nkl<Zc-qyG
zy$%6E5XS+9MDPZ@h+c#!6-o&~qtIw|bP_Ls7r<j(;o@#*X7?m(wm4SU;uI2-{G0js
z%~$Gw>?+hCMA`ouAV`e`$TLth*IMUaf<9uM6GkS<DNtJjFbaXzp68Wp`oshhiEhmY
zfJZ6|K`nN6Tna(1;$k2*<C@&ZT@iJigK5VNP65cC`%r?~oOHZsU3rViTQ|U{Vf*YA
zGD4_fJfiRW)nqS1xv&oesP?R#OCiY>H!VB?K$|2VV90?1MSF!}fN~ipTo7w!4VUch
oHY5w6rq&I<GmN5!fu(+Bcd8|CN+!v5R{#J207*qoM6N<$f*XW&LI3~&
index de0ded4c8d12508c0cd4846cd794517ebcf20cbf..33c4c7d7f4b5a6836bd35afeb2da842255264ec2
GIT binary patch
literal 469
zc$@*%0V@89P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80004;Nkl<Zc-rlj
zPfJxn7{-rPR73<7R9FzLiXb3r6}4{@5cCbA0^GP11Q7uR0Tn^qhzKHzh^imRZ*bA$
zynpAyBLiN}<{r5oJ@<gz2cF%``}}5xcV@DI2D)zLIi3dbO=kPm2*9!GTVgv*X7v#O
zx0?azSmG3bhs^;bt1pFs=RFl*#&9Z9)w#rPMR?)Bww{~4D-BL(MG_~|;2OO{?w`8?
z1hbE6Q0*39E!ld0MQjIk1F+b1Rwj}7oi)w{@s+A3@wKw>pgw;BJn4HEB#XuIqa)mJ
zrVYja0Jv4Dpa|ZO`d<KdD*)`Pwtr+*MdV+IZ95f9AeRAP@hx)yR6ShVKhp|2t4W1>
znFrt=ssCff>q^1ttVsQTC%6Ql9szhj5+_n%-vRPGe@5(!7u;aWtZ|{bZ8qolwG<$*
z&((enYuqEk%Mt+F>c?AfdTQ^x0-QgiyLE;KiDLkUvND<BLlE2OHaP)a<(~lHMUNj6
z9wznW2HLO?fJdxxrWt?&;ts2iHUm&V46(I;v-n1<)q0!_bQ5(3#`4kEp*yYM00000
LNkvXXu0mjfo7Bz>
index db4b0620c4118ace6695af18f663d594170f3b20..6c0ef603c4ff108f36aeba83802311156180bc16
GIT binary patch
literal 136
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6{5@S9Lp07OCoGWPak@TQ<j^kN
zs_F9GU%m=o*KeG1M{IB1%U_&4stFJOU)PVl+H}F-vcc3pM-_RQ!nYo{RKGqeajIGZ
jlcTx77uJIX5&;Yhv&4e!l^f3kSqu!Gu6{1-oD!M<$Q3X`
index ae6fbb21c31c46e94fa7739d3be5a5b64f1ae6b7..d0718a1d657a025137a050d1785b0ef535a907cf
GIT binary patch
literal 168
zc%17D@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4U`JOJ0Ar-gYPTtLXz<|TWc=qvz
zofmx{otvZ_t{Ac)cvgqDf1Uro!qPe^=?OO+bDn(cO#Ssrg!{6a*!>&s4?kXBDl%d7
z4Q-$KY?E#_SG8t}h=~g7Ycs`#xXcm%;Ql~=Vx8MB<%{t%Wz@>%SkHZ5#B8vNvnBjX
R`W&Fu44$rjF6*2UngCykKpp@9
index f575032008eb5a312879954586423c41ecc7a412..b3009981f9b1649d93f1b10de03d6fa4e0a9d74e
GIT binary patch
literal 160
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6(mh=qLp07Gy{O35;2_{|(Q)?O
z&lMc32fHI|W}j5bh?=o~1G_;q&$N#$n-gxu$Ny1M-85_R*9;lIweJcark2iV_;{rC
za{A$t?N*0duKY_~eZxq{X}$6G(@KR4F3&uhD5$3<yH=~{8S|=?knjy(+p>XHGI+ZB
KxvX<aXaWFuF+W=X
index a34c8ede6f8bb66511270fb55caa379d7d5e7944..d0923fd0a477d6155036c1a9aa2197fa5e2e8ee2
GIT binary patch
literal 302
zc$@()0nz@6P)<h;3K|Lk000e1NJLTq001BW001Be1ONa4*>kdg0002>Nkl<Zc-rmG
zu}eZx6vy$q76cJE<>=6mYtj@#OGs1ZSm5N+)KvdQd$a@*tv0l@HbjB&?)N-ji!_Mf
z@<1N);BiF-<&MWe5Z?F9AAZN=t+DTIJ{W{YD{|S9IvI|b6QeIG(BYM9QXENmrsYMt
zIGnWQk}-}%#e)((9P|6+oDug5PMJQA<$Z<wn)M1pk7v?UaH$e|Je7um+Zu_-6I;`)
zH%L7mOI5?0WF8*|6$KCW?7PSNl#M2y?G<ke-U~`9{=3Tschcf%#=B9`=#u*MVQ#qP
zflgE~+IX~o_RyY6o8$j2qmLhXkydzM^71yS36~(=q-pU73IG5A07*qoM6N<$g3myT
ASpWb4
index b68cc5e7dc7b54060db5f48dc879bc16ff3928c4..e9b257e3a06563759153fcb287cf8b81ff909a48
GIT binary patch
literal 177
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6Dm+~rLo|Yqp4!NJC_u#d;&h(_
z93?GBFR9H|h+uf;`B_!q|GqN~5)Eo@8gsRG-RM=F-%-DK8J~*$Ccdmcy&X2n%WMo4
z_5K~Zv#}spdf_zf(|05nxc5Ka^||2%?-jv~@#QxcT1lN~s!01e)%sKtL!~)`K65_v
b>}TA9cPFNOwb&yAbOVE@tDnm{r-UW|Kk!4G
index 5d7640bbe2f04c932eec767ab8d08255af88a9d2..11d86275fee4f21f6041edfb90611f4a53f2b043
GIT binary patch
literal 272
zc$@(c0q_2aP)<h;3K|Lk000e1NJLTq001BW001Be1ONa4*>kdg0002jNkl<ZNXKJf
z7_Q(y^YzH<8?V2<{{Q-m>uatD|7Rpg`(@c1C$IlUf%`9t5v~2E^!3MR+ClL4bz$Nh
z&v@eySi|=lEmyV93*6AV-go^sSY$I%23+-rXuoQU5WD7m{SR2wo(KbOtO6;$R)Z>f
zy%%i2EFuiJ{tTq_ssviFYJv^8PK*J6um8XP5hG-nz~S(b7z6%a|9|5>7O%Yr$<W?_
zYlUFH{=7aP1O5cdq~ec49xMq675>I+z<B|J2K*m2VAKGD2K;BjC=1cyPKHr19034z
W84pFPrznL00000<MNUMnLSTZ1^LUT|
index ee1d7a59e4b2abe40bda3a5ac25071c0d860f9b1..877d195c92cab5ab76c24e36071da46e0acf5d34
GIT binary patch
literal 98
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6G(BA$Lp07OCn)g7{P$ni^!oor
vMN6)Nj~v<uOhmd(r$jo+<_F1L5n*7kxL?|sHMep;P!ofvtDnm{r-UW|y*wO|
index 2b09f01a566a624abcf0aa6b904c61fe0d5f9ce8..207e5376ffa9414425de2ebaec11703e54c25fd0
GIT binary patch
literal 116
zc%17D@N?(olHy`uVBq!ia0vp^3Lwk@BpAX3RW*PVQ%R6tFatx`<g*q)p01~hV@SoV
z<b(xAJL+m5h{POcU|!51-Y>^xFt=6BDd1qjhocFdXAe$T#V*3YAba9EW8f#obs*zB
MUHx3vIVCg!0PXZ35&!@I
--- a/devtools/client/themes/inspector.css
+++ b/devtools/client/themes/inspector.css
@@ -39,49 +39,31 @@
#inspector-searchbox[focused],
#inspector-searchbox[filled] {
max-width: 200px !important;
}
/* Expand/collapse panel toolbar button */
#inspector-pane-toggle {
- background: none;
- box-shadow: none;
- border: none;
list-style-image: url(images/debugger-collapse.png);
- -moz-image-region: rect(0px,16px,16px,0px);
-}
-
-#inspector-pane-toggle > .toolbarbutton-icon {
- width: 16px;
- height: 16px;
}
#inspector-pane-toggle[pane-collapsed] {
list-style-image: url(images/debugger-expand.png);
}
-#inspector-pane-toggle:active {
- -moz-image-region: rect(0px,32px,16px,16px);
-}
-
@media (min-resolution: 1.1dppx) {
#inspector-pane-toggle {
list-style-image: url(images/debugger-collapse@2x.png);
- -moz-image-region: rect(0px,32px,32px,0px);
}
#inspector-pane-toggle[pane-collapsed] {
list-style-image: url(images/debugger-expand@2x.png);
}
-
- #inspector-pane-toggle:active {
- -moz-image-region: rect(0px,64px,32px,32px);
- }
}
/* Tooltip: Events */
#devtools-tooltip-events-container {
margin: -4px; /* Compensate for the .panel-arrowcontent padding. */
max-width: 590px;
overflow-y: auto;
--- a/devtools/client/themes/memory.css
+++ b/devtools/client/themes/memory.css
@@ -110,28 +110,16 @@ html, body, #app, #memory-tool {
}
#filter {
align-self: stretch;
margin: 2px;
}
/**
- * TODO bug 1213100
- * Once we figure out how to store invertable buttons (pseudo element like in
- * this case?) we should add a .invertable class to handle this generally,
- * rather than the definitions in toolbars.css.
- *
- * @see bug 1173397 for another inverted related bug
- */
-.theme-light .devtools-toolbar > .devtools-toolbarbutton.take-snapshot::before {
- filter: url(images/filters.svg#invert);
-}
-
-/**
* Container (sidebar + main panel)
*/
#memory-tool-container {
/**
* Flex: contains two children: .list (sidebar) and #heap-view (main panel),
* which need to be laid out horizontally. The sidebar has a fixed width and
* the main panel needs to flex to fill out all remaining horizontal space.
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -199,17 +199,17 @@ label.requests-menu-status-code {
box.requests-menu-status:not([code]) {
background-color: var(--theme-highlight-red);
border-radius: 0; /* squares */
}
box.requests-menu-status[code="cached"] {
border: 2px solid var(--theme-content-color2);
- background-color: transparent;
+ background-color: transparent;
}
box.requests-menu-status[code^="1"] {
background-color: var(---theme-highlight-blue);
}
box.requests-menu-status[code^="2"] {
background-color: var(--theme-highlight-green);
@@ -394,49 +394,31 @@ box.requests-menu-status[code^="5"] {
.side-menu-widget-item:not(.selected):hover {
background-color: var(--theme-selection-background-semitransparent);
}
/* Network request details */
#details-pane-toggle {
- background: none;
- box-shadow: none;
- border-color: transparent;
list-style-image: url("chrome://devtools/skin/images/debugger-collapse.png");
- -moz-image-region: rect(0px,16px,16px,0px);
-}
-
-#details-pane-toggle > .toolbarbutton-icon {
- width: 16px;
- height: 16px;
}
#details-pane-toggle[pane-collapsed] {
list-style-image: url("chrome://devtools/skin/images/debugger-expand.png");
}
-#details-pane-toggle:active {
- -moz-image-region: rect(0px,32px,16px,16px);
-}
-
@media (min-resolution: 1.1dppx) {
#details-pane-toggle {
list-style-image: url("chrome://devtools/skin/images/debugger-collapse@2x.png");
- -moz-image-region: rect(0px,32px,32px,0px);
}
#details-pane-toggle[pane-collapsed] {
list-style-image: url("chrome://devtools/skin/images/debugger-expand@2x.png");
}
-
- #details-pane-toggle:active {
- -moz-image-region: rect(0px,64px,32px,32px);
- }
}
/* Network request details tabpanels */
.tabpanel-content {
background-color: var(--theme-sidebar-background);
}
--- a/devtools/client/themes/performance.css
+++ b/devtools/client/themes/performance.css
@@ -37,26 +37,17 @@
}
#performance-toolbar-controls-detail-views .toolbarbutton-text {
-moz-padding-start: 4px;
-moz-padding-end: 8px;
}
#filter-button {
- list-style-image: url(images/timeline-filter.svg#filter);
- min-width: 24px;
-}
-
-#filter-button[disabled] {
- list-style-image: url(images/timeline-filter.svg#filter-disabled);
-}
-
-#filter-button[open] {
- list-style-image: url(images/timeline-filter.svg#filter-open);
+ list-style-image: url(images/timeline-filter.svg);
}
#performance-filter-menupopup > menuitem:before {
content: "";
display: block;
width: 8px;
height: 8px;
margin: 0 8px;
@@ -84,20 +75,16 @@
}
/* Recording buttons */
#main-record-button {
list-style-image: url(images/profiler-stopwatch.svg);
}
-#main-record-button[checked] {
- list-style-image: url(images/profiler-stopwatch-checked.svg);
-}
-
#main-record-button .button-icon {
margin: 0;
}
#main-record-button .button-text {
display: none;
}
--- a/devtools/client/themes/ruleview.css
+++ b/devtools/client/themes/ruleview.css
@@ -279,53 +279,32 @@
.ruleview-selectorhighlighter {
background: url("chrome://devtools/skin/images/vview-open-inspector.png") no-repeat 0 0;
padding-left: 16px;
margin-left: 5px;
cursor: pointer;
}
.ruleview-selectorhighlighter:hover {
- background-position: -32px 0;
+ filter: url(images/filters.svg#checked-icon-state);
}
.ruleview-selectorhighlighter:active,
.ruleview-selectorhighlighter.highlighted {
- background-position: -16px 0;
+ filter: url(images/filters.svg#checked-icon-state) brightness(0.9);
}
#ruleview-add-rule-button::before {
background-image: url("chrome://devtools/skin/images/add.svg");
background-size: cover;
}
#pseudo-class-panel-toggle::before {
- background-image: url("chrome://devtools/skin/images/pseudo-class.svg#pseudo-class");
+ background-image: url("chrome://devtools/skin/images/pseudo-class.svg");
background-size: cover;
}
-#pseudo-class-panel-toggle[checked]::before {
- background-image: url("chrome://devtools/skin/images/pseudo-class.svg#pseudo-class-checked");
- filter: none !important;
-}
-/**
- * These buttons are using opacity instead of background color to indicate
- * the state
- */
-#ruleview-add-rule-button,
-#pseudo-class-panel-toggle,
.ruleview-overridden-rule-filter {
opacity: 0.8;
}
-
-#ruleview-add-rule-button:not([disabled]):hover,
-#pseudo-class-panel-toggle:hover,
-#pseudo-class-panel-toggle[checked],
.ruleview-overridden-rule-filter:hover {
opacity: 1;
}
-
-#ruleview-add-rule-button,
-#pseudo-class-panel-toggle,
-#pseudo-class-panel-toggle:hover,
-#pseudo-class-panel-toggle[checked]::before {
- background-color: transparent !important;
-}
--- a/devtools/client/themes/shadereditor.css
+++ b/devtools/client/themes/shadereditor.css
@@ -67,17 +67,17 @@
}
.side-menu-widget-item-checkbox[checked] .checkbox-check {
background-position: 0 0;
}
/* Invert all toggle icons but the one in the active row for light theme */
.theme-light .side-menu-widget-item:not(.selected) .checkbox-check {
- filter: url(images/filters.svg#invert);
+ filter: invert(1);
}
/* Shader source editors */
.editor-label {
padding: 1px 12px;
border-top: 1px solid;
}
--- a/devtools/client/themes/styleeditor.css
+++ b/devtools/client/themes/styleeditor.css
@@ -115,17 +115,17 @@
}
.disabled > .stylesheet-enabled {
background-position: -24px 8px;
}
/* Invert all toggle icons but the one in the active row for light theme */
.theme-light .splitview-nav > li:not(.splitview-active) .stylesheet-enabled {
- filter: url(images/filters.svg#invert);
+ filter: invert(1);
}
.splitview-nav > li > .stylesheet-enabled:focus,
.splitview-nav > li:hover > .stylesheet-enabled {
outline: 0;
}
.stylesheet-linked-file:not(:empty){
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -49,81 +49,128 @@
border: none !important; /* overrides .checkbox-label-box from checkbox.css */
}
.devtools-toolbar checkbox .checkbox-label-box .checkbox-label {
margin: 0 6px !important; /* overrides .checkbox-label from checkbox.css */
padding: 0;
}
/* Toolbar buttons */
+
.devtools-menulist,
-.devtools-toolbarbutton {
+.devtools-toolbarbutton,
+.devtools-button {
-moz-appearance: none;
- -moz-box-align: center;
background: transparent;
- min-width: 78px;
min-height: 18px;
- padding: 1px;
text-shadow: none;
border: none;
border-radius: 0;
- margin: 2px 3px;
- color: inherit;
+ color: var(--theme-body-color);
transition: background 0.05s ease-in-out;
- color: var(--theme-content-color1);
- background-color: var(--theme-toolbar-background);
+}
+
+.devtools-menulist,
+.devtools-toolbarbutton {
+ -moz-box-align: center;
+ min-width: 78px;
+ padding: 1px;
+ margin: 2px 3px;
}
.devtools-menulist:-moz-focusring,
.devtools-toolbarbutton:-moz-focusring {
outline: 1px dotted hsla(210,30%,85%,0.7);
outline-offset: -4px;
}
+.devtools-toolbarbutton:not([label]) > .toolbarbutton-icon,
+.devtools-button::before {
+ width: 16px;
+ height: 16px;
+ transition: opacity 0.05s ease-in-out;
+}
+
+/* HTML buttons */
+.devtools-button {
+ margin: 0;
+ padding: 0;
+ min-width: 32px;
+ /* The icon is absolutely positioned in the button using ::before */
+ position: relative;
+}
+
+.devtools-button::before {
+ content: "";
+ display: block;
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ margin: -8px 0 0 -8px;
+ background-size: cover;
+ background-repeat: no-repeat;
+ transition: opacity 0.05s ease-in-out;
+}
+
+/* Standalone buttons */
+.devtools-button[standalone],
+.devtools-button[data-standalone],
+.devtools-toolbarbutton[standalone],
+.devtools-toolbarbutton[data-standalone] {
+ border-width: 1px;
+ border-style: solid;
+ min-height: 32px;
+ background-color: var(--theme-toolbar-background);
+}
+
.devtools-toolbarbutton[standalone], .devtools-toolbarbutton[data-standalone] {
-moz-margin-end: 5px;
- border-width: 1px;
- border-style: solid;
}
+
.devtools-toolbarbutton[label][standalone] {
min-height: 2em;
}
+.theme-dark .devtools-menulist,
+.theme-dark .devtools-toolbarbutton,
+.theme-dark .devtools-button {
+ border-color: rgba(0, 0, 0, .4); /* Splitters */
+}
+
+.theme-light .devtools-menulist,
+.theme-light .devtools-toolbarbutton,
+.theme-light .devtools-button {
+ border-color: rgba(170, 170, 170, .5); /* Splitters */
+}
+
+/* Icon button styles */
.devtools-toolbarbutton:not([label]),
.devtools-toolbarbutton[text-as-image] {
min-width: 32px;
}
#toolbox-buttons .devtools-toolbarbutton[text-as-image] {
-moz-padding-start: 5px;
-moz-padding-end: 5px;
min-width: inherit;
}
-/* Command buttons with menupopups should be styled slightly differently -
- no background color and a bit more narrow */
-#toolbox-buttons .devtools-toolbarbutton:not([text-as-image]):not(:hover):not([open=true]) {
- background: transparent;
-}
#toolbox-buttons .devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker {
padding: 0 2px;
}
.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
display: none;
}
-.devtools-toolbar .devtools-toolbarbutton {
- border-width: 0;
-}
-
.devtools-toolbarbutton > .toolbarbutton-icon {
margin: 0;
}
+/* Menu button styles (eg. web console filters) */
.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
-moz-appearance: none;
color: inherit;
border-width: 0;
-moz-box-orient: horizontal;
padding: 0;
}
@@ -147,99 +194,95 @@
.devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
-moz-appearance: none !important;
list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
-moz-box-align: center;
padding: 0 3px;
}
-.theme-dark .devtools-menulist,
-.theme-dark .devtools-toolbarbutton {
- border-color: rgba(0, 0, 0, .4); /* Splitters */
+/* Icon-only buttons */
+.devtools-button:empty::before,
+.devtools-toolbarbutton:not([label]):not([disabled]) > image {
+ opacity: 0.8;
}
-.theme-light .devtools-menulist,
-.theme-light .devtools-toolbarbutton {
- border-color: rgba(170, 170, 170, .5); /* Splitters */
+
+.devtools-button:hover:empty::before,
+.devtools-button[checked]:empty::before,
+.devtools-button[open]:empty::before,
+.devtools-toolbarbutton:not([label]):hover > image,
+.devtools-toolbarbutton:not([label])[checked=true] > image,
+.devtools-toolbarbutton:not([label])[open=true] > image {
+ opacity: 1;
+}
+
+.devtools-button:disabled,
+.devtools-button[disabled],
+.devtools-toolbarbutton[disabled] {
+ opacity: 0.5 !important;
+}
+
+.devtools-button[checked]:empty::before,
+.devtools-button[open]:empty::before,
+.devtools-toolbarbutton:not([label])[checked=true] > image,
+.devtools-toolbarbutton:not([label])[open=true] > image {
+ filter: url(images/filters.svg#checked-icon-state);
}
/* Text-only buttons */
.theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
.theme-light .devtools-toolbarbutton[data-text-only],
.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
background-color: rgba(170, 170, 170, .2); /* Splitter */
}
.theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
.theme-dark .devtools-toolbarbutton[data-text-only],
.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
background-color: rgba(0, 0, 0, .2); /* Splitter */
}
-/* Button States */
-.theme-dark .devtools-toolbarbutton:not([disabled]):hover,
+/* Text-only button states */
+.theme-dark .devtools-button:not(:empty):not([disabled]):hover,
.theme-dark #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover,
-.theme-dark .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover {
+.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
background: rgba(0, 0, 0, .3); /* Splitters */
}
-.theme-light .devtools-toolbarbutton:not([disabled]):hover,
+.theme-light .devtools-button:not(:empty):not([disabled]):hover,
.theme-light #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover,
-.theme-light .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover {
+.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
background: rgba(170, 170, 170, .3); /* Splitters */
}
-.theme-dark .devtools-toolbarbutton:not([disabled]):hover:active,
+.theme-dark .devtools-button:not(:empty):not([disabled]):hover:active,
.theme-dark #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover:active,
-.theme-dark .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover:active {
+.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
background: rgba(0, 0, 0, .4); /* Splitters */
}
-.theme-light .devtools-toolbarbutton:not([disabled]):hover:active,
+.theme-light .devtools-button:not(:empty):not([disabled]):hover:active,
.theme-light #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover:active,
-.theme-light .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover:active {
+.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
background: rgba(170, 170, 170, .4); /* Splitters */
}
-/* Menu type buttons and checked states */
-.theme-dark .devtools-toolbarbutton[checked=true],
-.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked] {
+.theme-dark .devtools-toolbarbutton:not([disabled])[label][checked=true],
+.theme-dark .devtools-toolbarbutton:not([disabled])[label][open],
+.theme-dark .devtools-button:not(:empty)[checked=true],
+.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked=true] {
background: rgba(29, 79, 115, .7); /* Select highlight blue */
color: var(--theme-selection-color);
}
-
-.theme-light .devtools-toolbarbutton[checked=true],
-.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked] {
- background: rgba(76, 158, 217, .2); /* Select highlight blue */
-}
-
-.theme-dark .devtools-menulist[open=true],
-.theme-dark .devtools-toolbarbutton[open=true],
-.theme-dark .devtools-toolbarbutton[open=true]:hover,
-.theme-dark .devtools-toolbarbutton[open=true]:hover:active,
-.theme-dark .devtools-toolbarbutton[checked=true]:hover {
- background: rgba(29, 79, 115, .8); /* Select highlight blue */
- color: var(--theme-selection-color);
-}
-
-.theme-light .devtools-menulist[open=true],
-.theme-light .devtools-toolbarbutton[open=true],
-.theme-light .devtools-toolbarbutton[open=true]:hover,
-.theme-light .devtools-toolbarbutton[open=true]:hover:active,
-.theme-light .devtools-toolbarbutton[checked=true]:hover {
- background: rgba(76, 158, 217, .4); /* Select highlight blue */
+.theme-light .devtools-toolbarbutton:not([disabled])[label][checked=true],
+.theme-light .devtools-toolbarbutton:not([disabled])[label][open],
+.theme-light .devtools-button:not(:empty)[checked=true],
+.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked=true] {
+ background: rgba(76, 158, 217, .3); /* Select highlight blue */
}
.devtools-option-toolbarbutton {
- -moz-appearance: none;
list-style-image: url("chrome://devtools/skin/images/tool-options.svg");
- background: none;
- opacity: .8;
- border: none;
-}
-
-.devtools-option-toolbarbutton[open=true] {
- opacity: 1;
}
/* Toolbar button groups */
.devtools-toolbarbutton-group > .devtools-toolbarbutton {
margin-left: 1px;
margin-right: 1px;
outline-offset: -3px;
box-shadow: none;
@@ -252,83 +295,16 @@
.devtools-toolbarbutton-group + .devtools-toolbarbutton {
-moz-margin-start: 3px;
}
.devtools-separator + .devtools-toolbarbutton {
-moz-margin-start: 1px;
}
-/* HTML buttons, similar to toolbar buttons, but work in HTML documents */
-
-.devtools-button {
- border: 0 solid var(--theme-splitter-color);
- background: var(--theme-toolbar-background);
- color: var(--theme-body-color);
- margin: 0;
- padding: 0;
- min-width: 32px;
- min-height: 18px;
- /* The icon is absolutely positioned in the button using ::before */
- position: relative;
-}
-
-.devtools-button[standalone], .devtools-button[data-standalone] {
- min-height: 32px;
- border-width: 1px;
-}
-
-/* Button States */
-.theme-dark .devtools-button:not([disabled]):hover {
- background: rgba(0, 0, 0, .3); /* Splitters */
-}
-.theme-light .devtools-button:not([disabled]):hover {
- background: rgba(170, 170, 170, .3); /* Splitters */
-}
-
-.theme-dark .devtools-button:not([disabled]):hover:active {
- background: rgba(0, 0, 0, .4); /* Splitters */
-}
-.theme-light .devtools-button:not([disabled]):hover:active {
- background: rgba(170, 170, 170, .4); /* Splitters */
-}
-
-/* Menu type buttons and checked states */
-.theme-dark .devtools-button[checked] {
- background: rgba(29, 79, 115, .7) !important; /* Select highlight blue */
- color: var(--theme-selection-color);
-}
-
-.theme-light .devtools-button[checked] {
- background: rgba(76, 158, 217, .2) !important; /* Select highlight blue */
-}
-
-.devtools-button::before {
- content: "";
- display: block;
- width: 16px;
- height: 16px;
- position: absolute;
- left: 50%;
- top: 50%;
- margin: -8px 0 0 -8px;
- background-repeat: no-repeat;
-}
-
-.devtools-button[disabled]::before,
-.devtools-button:disabled::before {
- opacity: 0.5;
-}
-
-@media (min-resolution: 1.1dppx) {
- .devtools-button::before {
- background-size: 32px;
- }
-}
-
/* Text input */
.devtools-textinput,
.devtools-searchinput {
-moz-appearance: none;
margin: 1px 3px;
border: 1px solid;
border-radius: 2px;
@@ -995,37 +971,33 @@
* inside of the light theme.
*/
.theme-light .devtools-tab[icon-invertable] > image,
.theme-light #toolbox-dock-buttons > toolbarbutton > image,
.theme-light .command-button-invertable > image,
.theme-light .command-button-invertable:active > image,
.theme-light .devtools-closebutton > image,
.theme-light .devtools-toolbarbutton > image,
-.theme-light .devtools-option-toolbarbutton > image,
+.theme-light .devtools-button::before,
.theme-light #breadcrumb-separator-normal,
.theme-light .scrollbutton-up > .toolbarbutton-icon,
.theme-light .scrollbutton-down > .toolbarbutton-icon,
.theme-light #black-boxed-message-button .button-icon,
-.theme-light .notice-container button .button-icon,
.theme-light #requests-menu-perf-notice-button .button-icon,
.theme-light #requests-menu-network-summary-button .button-icon,
-.theme-light .event-tooltip-debugger-icon,
-.theme-light .devtools-button::before {
- filter: url(images/filters.svg#invert);
+.theme-light #toggle-breakpoints[checked] > image,
+.theme-light .event-tooltip-debugger-icon {
+ filter: invert(1);
}
/* Since selected backgrounds are blue, we want to use the normal
* (light) icons. */
.theme-light .command-button-invertable[checked=true]:not(:active) > image,
.theme-light .devtools-tab[icon-invertable][selected] > image,
-.theme-light .devtools-tab[icon-invertable][highlighted] > image,
-.theme-light #record-snapshot[checked] > image,
-.theme-light #profiler-start[checked] > image,
-.theme-light .notice-container button[checked] .button-icon {
+.theme-light .devtools-tab[icon-invertable][highlighted] > image {
filter: none !important;
}
.theme-light .command-button:hover {
background-color: inherit;
}
.theme-light .command-button:hover:active,
--- a/devtools/client/themes/webaudioeditor.css
+++ b/devtools/client/themes/webaudioeditor.css
@@ -203,31 +203,16 @@ text {
/**
* Inspector toolbar
*/
#audio-node-toolbar .bypass {
list-style-image: url(images/power.svg);
}
-#audio-node-toolbar toolbarbutton[disabled] {
- opacity: 0.5;
- background-color: transparent;
-}
-
-#audio-node-toolbar toolbarbutton[checked] {
- background-color: var(--theme-selection-background);
-}
-
-/* don't invert checked buttons so we can have white icons on light theme */
-#audio-node-toolbar toolbarbutton[checked] > .toolbarbutton-icon {
- filter: none;
-}
-
-
/**
* Responsive Styles
* `.devtools-responsive-container` takes care of most of
* the changing of host types.
*/
@media (max-width: 700px) {
/**
* Override the inspector toggle so it's always open
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -522,21 +522,22 @@ a {
background: url("chrome://devtools/skin/images/vview-open-inspector.png") no-repeat 0 0;
padding-left: 16px;
margin-left: 5px;
cursor: pointer;
}
.elementNode:hover .open-inspector,
.open-inspector:hover {
- background-position: -32px 0;
+ filter: url(images/filters.svg#checked-icon-state);
}
+.elementNode:hover .open-inspector:active,
.open-inspector:active {
- background-position: -16px 0;
+ filter: url(images/filters.svg#checked-icon-state) brightness(0.9);
}
@media (max-width: 500px) {
.message > .timestamp {
display: none;
}
.hud-console-filter-toolbar .webconsole-filter-button .toolbarbutton-text {
display: none;
--- a/devtools/client/themes/widgets.css
+++ b/devtools/client/themes/widgets.css
@@ -734,93 +734,73 @@
.variable-or-property[safe-getter] > tooltip > label.WebIDL {
-moz-padding-start: 4px;
-moz-border-start: 1px dotted #000;
color: #080;
}
/* Variables and properties editing */
+.variables-view-delete,
+.variables-view-edit,
+.variables-view-open-inspector {
+ width: 16px;
+ height: 16px;
+ background-size: cover;
+ cursor: pointer;
+}
+
+.variables-view-delete:hover,
+.variables-view-edit:hover,
+.variables-view-open-inspector:hover {
+ filter: url(images/filters.svg#checked-icon-state);
+}
+
+.variables-view-delete:active,
+.variables-view-edit:active,
+.variables-view-open-inspector:active {
+ filter: url(images/filters.svg#checked-icon-state) brightness(0.9);
+}
+
+.variable-or-property:focus > .title > .variables-view-delete,
+.variable-or-property:focus > .title > .variables-view-edit,
+.variable-or-property:focus > .title > .variables-view-open-inspector {
+ filter: none;
+}
.variables-view-delete {
- background: url("chrome://devtools/skin/images/vview-delete.png");
- background-size: cover;
- width: 16px;
- height: 16px;
+ background-image: url("chrome://devtools/skin/images/vview-delete.png");
}
@media (min-resolution: 1.1dppx) {
.variables-view-delete {
background-image: url("chrome://devtools/skin/images/vview-delete@2x.png");
}
}
-.variables-view-delete:hover {
- background-position: 16px;
-}
-
-.variables-view-delete:active {
- background-position: 32px;
-}
-
-.variable-or-property:focus > .title > .variables-view-delete {
- background-position: 0px;
-}
-
.variables-view-edit {
- background: url("chrome://devtools/skin/images/vview-edit.png");
- background-size: cover;
- width: 16px;
- height: 16px;
- cursor: pointer;
+ background-image: url("chrome://devtools/skin/images/vview-edit.png");
}
@media (min-resolution: 1.1dppx) {
.variables-view-edit {
background-image: url("chrome://devtools/skin/images/vview-edit@2x.png");
}
}
-.variables-view-edit:hover {
- background-position: 16px;
-}
-
-.variables-view-edit:active {
- background-position: 32px;
-}
-
-.variable-or-property:focus > .title > .variables-view-edit {
- background-position: 0px;
-}
-
.variables-view-open-inspector {
- background: url("chrome://devtools/skin/images/vview-open-inspector.png");
- background-size: cover;
- width: 16px;
- height: 16px;
- cursor: pointer;
+ background-image: url("chrome://devtools/skin/images/vview-open-inspector.png");
}
@media (min-resolution: 1.1dppx) {
.variables-view-open-inspector {
background-image: url("chrome://devtools/skin/images/vview-open-inspector@2x.png");
}
}
-.variables-view-open-inspector:hover {
- background-position: 16px;
-}
-
-.variables-view-open-inspector:active {
- background-position: 32px;
-}
-
-.variable-or-property:focus > .title > .variables-view-open-inspector {
- background-position: 0px;
-}
/* Variables and properties input boxes */
.variable-or-property > .title > .separator + .element-value-input {
-moz-margin-start: -2px !important;
-moz-margin-end: 2px !important;
}
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -75,42 +75,45 @@ namespace mozilla {
namespace dom {
// Calls LoadSelectedImage on host element unless it has been superseded or
// canceled -- this is the synchronous section of "update the image data".
// https://html.spec.whatwg.org/multipage/embedded-content.html#update-the-image-data
class ImageLoadTask : public nsRunnable
{
public:
- explicit ImageLoadTask(HTMLImageElement *aElement) :
- mElement(aElement)
+ ImageLoadTask(HTMLImageElement *aElement, bool aAlwaysLoad)
+ : mElement(aElement)
+ , mAlwaysLoad(aAlwaysLoad)
{
mDocument = aElement->OwnerDoc();
mDocument->BlockOnload();
}
NS_IMETHOD Run()
{
if (mElement->mPendingImageLoadTask == this) {
mElement->mPendingImageLoadTask = nullptr;
- mElement->LoadSelectedImage(true, true);
+ mElement->LoadSelectedImage(true, true, mAlwaysLoad);
}
mDocument->UnblockOnload(false);
return NS_OK;
}
private:
~ImageLoadTask() {}
RefPtr<HTMLImageElement> mElement;
nsCOMPtr<nsIDocument> mDocument;
+ bool mAlwaysLoad;
};
HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
: nsGenericHTMLElement(aNodeInfo)
, mForm(nullptr)
+ , mInDocResponsiveContent(false)
{
// We start out broken
AddStatesSilently(NS_EVENT_STATE_BROKEN);
}
HTMLImageElement::~HTMLImageElement()
{
DestroyImageLoadingContent();
@@ -422,17 +425,17 @@ HTMLImageElement::AfterSetAttr(int32_t a
!aValue) {
// SetAttr handles setting src since it needs to catch img.src =
// img.src, so we only need to handle the unset case
if (InResponsiveMode()) {
if (mResponsiveSelector &&
mResponsiveSelector->Content() == this) {
mResponsiveSelector->SetDefaultSource(NullString());
}
- QueueImageLoadTask();
+ QueueImageLoadTask(true);
} else {
// Bug 1076583 - We still behave synchronously in the non-responsive case
CancelImageRequests(aNotify);
}
} else if (aName == nsGkAtoms::srcset &&
aNameSpaceID == kNameSpaceID_None &&
IsSrcsetEnabled()) {
PictureSourceSrcsetChanged(this, attrVal.String(), aNotify);
@@ -442,17 +445,17 @@ HTMLImageElement::AfterSetAttr(int32_t a
PictureSourceSizesChanged(this, attrVal.String(), aNotify);
} else if (aName == nsGkAtoms::crossorigin &&
aNameSpaceID == kNameSpaceID_None &&
aNotify) {
// Force a new load of the image with the new cross origin policy.
if (InResponsiveMode()) {
// per spec, full selection runs when this changes, even though
// it doesn't directly affect the source selection
- QueueImageLoadTask();
+ QueueImageLoadTask(true);
} else {
// Bug 1076583 - We still use the older synchronous algorithm in
// non-responsive mode. Force a new load of the image with the
// new cross origin policy.
ForceReload(aNotify);
}
}
@@ -538,17 +541,17 @@ HTMLImageElement::SetAttr(int32_t aNameS
return NS_OK;
}
if (InResponsiveMode()) {
if (mResponsiveSelector &&
mResponsiveSelector->Content() == this) {
mResponsiveSelector->SetDefaultSource(aValue);
}
- QueueImageLoadTask();
+ QueueImageLoadTask(true);
} else if (aNotify) {
// If aNotify is false, we are coming from the parser or some such place;
// we'll get bound after all the attributes have been set, so we'll do the
// sync image load from BindToTree. Skip the LoadImage call in that case.
// Note that this sync behavior is partially removed from the spec, bug 1076583
// A hack to get animations to reset. See bug 594771.
@@ -581,23 +584,25 @@ HTMLImageElement::BindToTree(nsIDocument
nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
aCompileEventHandlers);
if (aParent) {
UpdateFormOwner();
}
- bool addedToPicture = aParent && aParent->IsHTMLElement(nsGkAtoms::picture) &&
- HTMLPictureElement::IsPictureEnabled();
- if (addedToPicture) {
- if (aDocument) {
+ if (HaveSrcsetOrInPicture()) {
+ if (aDocument && !mInDocResponsiveContent) {
aDocument->AddResponsiveContent(this);
+ mInDocResponsiveContent = true;
}
- QueueImageLoadTask();
+
+ bool forceLoadEvent = HTMLPictureElement::IsPictureEnabled() &&
+ aParent && aParent->IsHTMLElement(nsGkAtoms::picture);
+ QueueImageLoadTask(forceLoadEvent);
} else if (!InResponsiveMode() &&
HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
// We skip loading when our attributes were set from parser land,
// so trigger a aForce=false load now to check if things changed.
// This isn't necessary for responsive mode, since creating the
// image load task is asynchronous we don't need to take special
// care to avoid doing so when being filled by the parser.
@@ -628,27 +633,32 @@ HTMLImageElement::UnbindFromTree(bool aD
if (mForm) {
if (aNullParent || !FindAncestorForm(mForm)) {
ClearForm(true);
} else {
UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
}
}
+ if (mInDocResponsiveContent) {
+ nsIDocument* doc = GetOurOwnerDoc();
+ MOZ_ASSERT(doc);
+ if (doc) {
+ doc->RemoveResponsiveContent(this);
+ mInDocResponsiveContent = false;
+ }
+ }
+
if (GetParent() &&
GetParent()->IsHTMLElement(nsGkAtoms::picture) &&
HTMLPictureElement::IsPictureEnabled()) {
- nsIDocument* doc = GetOurOwnerDoc();
- if (doc) {
- doc->RemoveResponsiveContent(this);
- }
// Being removed from picture re-triggers selection, even if we
// weren't using a <source> peer
if (aNullParent) {
- QueueImageLoadTask();
+ QueueImageLoadTask(true);
}
}
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
}
void
@@ -682,17 +692,17 @@ void
HTMLImageElement::MaybeLoadImage()
{
// Our base URI may have changed, or we may have had responsive parameters
// change while not bound to the tree. Re-parse src/srcset and call LoadImage,
// which is a no-op if it resolves to the same effective URI without aForce.
// Note, check LoadingEnabled() after LoadImage call.
- LoadSelectedImage(false, true);
+ LoadSelectedImage(false, true, false);
if (!LoadingEnabled()) {
CancelImageRequests(true);
}
}
EventStates
HTMLImageElement::IntrinsicState() const
@@ -882,25 +892,25 @@ HTMLImageElement::ClearForm(bool aRemove
}
}
UnsetFlags(ADDED_TO_FORM);
mForm = nullptr;
}
void
-HTMLImageElement::QueueImageLoadTask()
+HTMLImageElement::QueueImageLoadTask(bool aAlwaysLoad)
{
// If loading is temporarily disabled, we don't want to queue tasks
// that may then run when loading is re-enabled.
if (!LoadingEnabled() || !this->OwnerDoc()->IsCurrentActiveDocument()) {
return;
}
- nsCOMPtr<nsIRunnable> task = new ImageLoadTask(this);
+ nsCOMPtr<nsIRunnable> task = new ImageLoadTask(this, aAlwaysLoad);
// The task checks this to determine if it was the last
// queued event, and so earlier tasks are implicitly canceled.
mPendingImageLoadTask = task;
nsContentUtils::RunInStableState(task.forget());
}
bool
HTMLImageElement::HaveSrcsetOrInPicture()
@@ -924,25 +934,27 @@ HTMLImageElement::InResponsiveMode()
// will happen from the microtask, and we should behave responsively in the
// interim
return mResponsiveSelector ||
mPendingImageLoadTask ||
HaveSrcsetOrInPicture();
}
nsresult
-HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify)
+HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad)
{
nsresult rv = NS_ERROR_FAILURE;
if (aForce) {
// In responsive mode we generally want to re-run the full
// selection algorithm whenever starting a new load, per
// spec. This also causes us to re-resolve the URI as appropriate.
- UpdateResponsiveSource();
+ if (!UpdateResponsiveSource() && !aAlwaysLoad) {
+ return NS_OK;
+ }
}
if (mResponsiveSelector) {
nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL();
if (url) {
rv = LoadImage(url, aForce, aNotify, eImageLoadType_Imageset);
}
} else {
@@ -985,19 +997,27 @@ HTMLImageElement::PictureSourceSrcsetCha
mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
if (aSourceNode == currentSrc) {
// We're currently using this node as our responsive selector
// source.
mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue);
}
+ if (!mInDocResponsiveContent) {
+ nsIDocument* doc = GetOurOwnerDoc();
+ if (doc) {
+ doc->AddResponsiveContent(this);
+ mInDocResponsiveContent = true;
+ }
+ }
+
// This always triggers the image update steps per the spec, even if
// we are not using this source.
- QueueImageLoadTask();
+ QueueImageLoadTask(true);
}
void
HTMLImageElement::PictureSourceSizesChanged(nsIContent *aSourceNode,
const nsAString& aNewValue,
bool aNotify)
{
if (!HTMLPictureElement::IsPictureEnabled()) {
@@ -1014,61 +1034,63 @@ HTMLImageElement::PictureSourceSizesChan
if (aSourceNode == currentSrc) {
// We're currently using this node as our responsive selector
// source.
mResponsiveSelector->SetSizesFromDescriptor(aNewValue);
}
// This always triggers the image update steps per the spec, even if
// we are not using this source.
- QueueImageLoadTask();
+ QueueImageLoadTask(true);
}
void
HTMLImageElement::PictureSourceMediaOrTypeChanged(nsIContent *aSourceNode,
bool aNotify)
{
if (!HTMLPictureElement::IsPictureEnabled()) {
return;
}
MOZ_ASSERT(IsPreviousSibling(aSourceNode, this),
"Should not be getting notifications for non-previous-siblings");
// This always triggers the image update steps per the spec, even if
// we are not switching to/from this source
- QueueImageLoadTask();
+ QueueImageLoadTask(true);
}
void
HTMLImageElement::PictureSourceAdded(nsIContent *aSourceNode)
{
if (!HTMLPictureElement::IsPictureEnabled()) {
return;
}
- QueueImageLoadTask();
+ QueueImageLoadTask(true);
}
void
HTMLImageElement::PictureSourceRemoved(nsIContent *aSourceNode)
{
if (!HTMLPictureElement::IsPictureEnabled()) {
return;
}
- QueueImageLoadTask();
+ QueueImageLoadTask(true);
}
-void
+bool
HTMLImageElement::UpdateResponsiveSource()
{
+ bool hadSelector = !!mResponsiveSelector;
+
if (!IsSrcsetEnabled()) {
mResponsiveSelector = nullptr;
- return;
+ return hadSelector;
}
nsIContent *currentSource =
mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
bool pictureEnabled = HTMLPictureElement::IsPictureEnabled();
Element *parent = pictureEnabled ? nsINode::GetParentElement() : nullptr;
nsINode *candidateSource = nullptr;
@@ -1078,29 +1100,29 @@ HTMLImageElement::UpdateResponsiveSource
} else {
candidateSource = this;
}
while (candidateSource) {
if (candidateSource == currentSource) {
// found no better source before current, re-run selection on
// that and keep it if it's still usable.
- mResponsiveSelector->SelectImage(true);
+ bool changed = mResponsiveSelector->SelectImage(true);
if (mResponsiveSelector->NumCandidates()) {
bool isUsableCandidate = true;
// an otherwise-usable source element may still have a media query that may not
// match any more.
if (candidateSource->IsHTMLElement(nsGkAtoms::source) &&
!SourceElementMatches(candidateSource->AsContent())) {
isUsableCandidate = false;
}
if (isUsableCandidate) {
- break;
+ return changed;
}
}
// no longer valid
mResponsiveSelector = nullptr;
if (candidateSource == this) {
// No further possibilities
break;
@@ -1119,16 +1141,18 @@ HTMLImageElement::UpdateResponsiveSource
}
candidateSource = candidateSource->GetNextSibling();
}
if (!candidateSource) {
// Ran out of siblings without finding ourself, e.g. XBL magic.
mResponsiveSelector = nullptr;
}
+
+ return !hadSelector || mResponsiveSelector;
}
/*static */ bool
HTMLImageElement::SupportedPictureSourceType(const nsAString& aType)
{
return
imgLoader::SupportImageWithMimeType(NS_ConvertUTF16toUTF8(aType).get(),
AcceptedMimeTypes::IMAGES_AND_DOCUMENTS);
@@ -1291,17 +1315,17 @@ void
HTMLImageElement::DestroyContent()
{
mResponsiveSelector = nullptr;
}
void
HTMLImageElement::MediaFeatureValuesChanged()
{
- QueueImageLoadTask();
+ QueueImageLoadTask(false);
}
void
HTMLImageElement::FlushUseCounters()
{
nsCOMPtr<imgIRequest> request;
GetRequest(CURRENT_REQUEST, getter_AddRefs(request));
--- a/dom/html/HTMLImageElement.h
+++ b/dom/html/HTMLImageElement.h
@@ -275,29 +275,29 @@ protected:
virtual ~HTMLImageElement();
// Queues a task to run LoadSelectedImage pending stable state.
//
// Pending Bug 1076583 this is only used by the responsive image
// algorithm (InResponsiveMode()) -- synchronous actions when just
// using img.src will bypass this, and update source and kick off
// image load synchronously.
- void QueueImageLoadTask();
+ void QueueImageLoadTask(bool aAlwaysLoad);
// True if we have a srcset attribute or a <picture> parent, regardless of if
// any valid responsive sources were parsed from either.
bool HaveSrcsetOrInPicture();
// True if we are using the newer image loading algorithm. This will be the
// only mode after Bug 1076583
bool InResponsiveMode();
// Resolve and load the current mResponsiveSelector (responsive mode) or src
// attr image.
- nsresult LoadSelectedImage(bool aForce, bool aNotify);
+ nsresult LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad);
// True if this string represents a type we would support on <source type>
static bool SupportedPictureSourceType(const nsAString& aType);
// Update/create/destroy mResponsiveSelector
void PictureSourceSrcsetChanged(nsIContent *aSourceNode,
const nsAString& aNewValue, bool aNotify);
void PictureSourceSizesChanged(nsIContent *aSourceNode,
@@ -316,17 +316,19 @@ protected:
// the current ResponsiveSelector is not changed, runs
// SelectImage(true) to re-evaluate its candidates.
//
// Because keeping the existing selector is the common case (and we
// often do no-op reselections), this does not re-parse values for
// the existing mResponsiveSelector, meaning you need to update its
// parameters as appropriate before calling (or null it out to force
// recreation)
- void UpdateResponsiveSource();
+ //
+ // Returns true if the source has changed, and false otherwise.
+ bool UpdateResponsiveSource();
// Given a <source> node that is a previous sibling *or* ourselves, try to
// create a ResponsiveSelector.
// If the node's srcset/sizes make for an invalid selector, returns
// false. This does not guarantee the resulting selector matches an image,
// only that it is valid.
bool TryCreateResponsiveSelector(nsIContent *aSourceNode,
@@ -354,15 +356,16 @@ protected:
RefPtr<ResponsiveImageSelector> mResponsiveSelector;
private:
bool SourceElementMatches(nsIContent* aSourceNode);
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsRuleData* aData);
+ bool mInDocResponsiveContent;
nsCOMPtr<nsIRunnable> mPendingImageLoadTask;
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_HTMLImageElement_h */
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -3238,16 +3238,22 @@ nsHTMLDocument::ExecCommand(const nsAStr
if (doShowUI) {
return false;
}
// special case for cut & copy
// cut & copy are allowed in non editable documents
if (isCutCopy) {
if (!nsContentUtils::IsCutCopyAllowed()) {
+ // We have rejected the event due to it not being performed in an
+ // input-driven context therefore, we report the error to the console.
+ nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("DOM"), this,
+ nsContentUtils::eDOM_PROPERTIES,
+ "ExecCommandCutCopyDeniedNotInputDriven");
return false;
}
// For cut & copy commands, we need the behaviour from nsWindowRoot::GetControllers
// which is to look at the focused element, and defer to a focused textbox's controller
// The code past taken by other commands in ExecCommand always uses the window directly,
// rather than deferring to the textbox, which is desireable for most editor commands,
// but not 'cut' and 'copy' (as those should allow copying out of embedded editors).
new file mode 100644
index 0000000000000000000000000000000000000000..df421453c25631353cfe86bde5973fdca15b1ea9
GIT binary patch
literal 91
zc%17D@N?(olHy`uVBq!ia0vp^DIm<q3?#jD*u{YqbAV5XtC5k>QX|f*K(4T-i(`m{
lWU>V7;slYNra*^q1_ss&2F5$COwvGU22WQ%mvv4FO#rqO5n=!U
new file mode 100644
index 0000000000000000000000000000000000000000..6f76d4438724111983a11860f13568361b52d9bc
GIT binary patch
literal 100
zc%17D@N?(olHy`uVBq!ia0vp^CqS5y8Awi_W^)%vF$egBxTd6}EPfSw1jv=~ba4!k
rkbHZv5y)XUyx@1R|3ikWAU1;^qeuV)<1IPC3=q%L)z4*}Q$iB}r??p{
new file mode 100644
index 0000000000000000000000000000000000000000..144a2f0b93c5913872f30056999da000ae6049cd
GIT binary patch
literal 85
zc%17D@N?(olHy`uVBq!ia0vp^Mj*_{3?x-PN__%S%mF?ju9A|H-p`i&1*>y$43Usb
dmSAlLYGa(hz<BW6Dm$PAgQu&X%Q~loCIF<=5=;O9
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -192,16 +192,19 @@ support-files =
image-allow-credentials.png^headers^
nnc_lockup.gif
reflect.js
wakelock.ogg
wakelock.ogv
file_ignoreuserfocus.html
simpleFileOpener.js
file_mozaudiochannel.html
+ file_bug1166138_1x.png
+ file_bug1166138_2x.png
+ file_bug1166138_def.png
[test_a_text.html]
[test_anchor_href_cache_invalidation.html]
[test_applet_attributes_reflection.html]
[test_audio_wakelock.html]
skip-if = buildapp == 'mulet' # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
[test_base_attributes_reflection.html]
[test_bug100533.html]
@@ -590,8 +593,9 @@ support-files = file_bug871161-1.html fi
[test_hash_encoded.html]
[test_bug1081037.html]
[test_window_open_close.html]
skip-if = buildapp == 'b2g' # bug 1129014
[test_img_complete.html]
[test_viewport_resize.html]
[test_extapp.html]
[test_image_clone_load.html]
+[test_bug1166138.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_bug1166138.html
@@ -0,0 +1,130 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1166138
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1166138</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1166138">Mozilla Bug 1166138</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+
+ <img srcset="file_bug1166138_1x.png 1x, file_bug1166138_2x.png 2x"
+ src="file_bug1166138_def.png"
+ onload="onLoad()">
+
+ <script type="application/javascript">
+ var img1x = "http://mochi.test:8888/tests/dom/html/test/file_bug1166138_1x.png";
+ var img2x = "http://mochi.test:8888/tests/dom/html/test/file_bug1166138_2x.png";
+ var imgdef = "http://mochi.test:8888/tests/dom/html/test/file_bug1166138_def.png";
+ var onLoadCallback = null;
+ var done = false;
+
+ var startPromise = new Promise((a) => {
+ onLoadCallback = () => {
+ var image = document.querySelector('img');
+ // If we aren't starting at 2x scale, resize to 2x scale, and wait for a load
+ if (image.currentSrc != img2x) {
+ onLoadCallback = a;
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 2]]});
+ } else {
+ a();
+ }
+ };
+ });
+
+ // if aLoad is true, waits for a load event. Otherwise, spins the event loop twice to
+ // ensure that no events were queued to be fired.
+ function spin(aLoad) {
+ if (aLoad) {
+ return new Promise((a) => {
+ ok(!onLoadCallback, "Shouldn't be an existing callback");
+ onLoadCallback = a;
+ });
+ } else {
+ return new Promise((a) => SimpleTest.executeSoon(() => SimpleTest.executeSoon(a)));
+ }
+ }
+
+ function onLoad() {
+ if (done) return;
+ ok(onLoadCallback, "Expected a load event");
+ if (onLoadCallback) {
+ var cb = onLoadCallback;
+ onLoadCallback = null;
+ cb();
+ }
+ }
+
+ add_task(function* () {
+ yield startPromise;
+ var image = document.querySelector('img');
+ is(image.currentSrc, img2x, "initial scale must be 2x");
+
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 1]]});
+ yield spin(true);
+ is(image.currentSrc, img1x, "pre-existing img tag to 1x");
+
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 2]]});
+ yield spin(true);
+ is(image.currentSrc, img2x, "pre-existing img tag to 2x");
+
+ // Try removing & re-adding the image
+ document.body.removeChild(image);
+
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 1]]});
+ yield spin(false); // No load should occur because the element is unbound
+
+ document.body.appendChild(image);
+ yield spin(true);
+ is(image.currentSrc, img1x, "remove and re-add tag after changing to 1x");
+
+ document.body.removeChild(image);
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 2]]});
+ yield spin(false); // No load should occur because the element is unbound
+
+ document.body.appendChild(image);
+ yield spin(true);
+ is(image.currentSrc, img2x, "remove and re-add tag after changing to 2x");
+
+ // get rid of the srcset attribute! It should become the default
+ image.removeAttribute('srcset');
+ yield spin(true);
+ is(image.currentSrc, imgdef, "remove srcset attribute");
+
+ // Setting srcset again should return it to the correct value
+ image.setAttribute('srcset', "file_bug1166138_1x.png 1x, file_bug1166138_2x.png 2x");
+ yield spin(true);
+ is(image.currentSrc, img2x, "restore srcset attribute");
+
+ // Create a new image
+ var newImage = document.createElement('img');
+ // Switch load listening over to newImage
+ newImage.addEventListener('load', onLoad);
+ image.removeEventListener('load', onLoad);
+
+ document.body.appendChild(newImage);
+ yield spin(false); // no load event should fire - as the image has no attributes
+ is(newImage.currentSrc, "", "New element with no attributes");
+ newImage.setAttribute('srcset', "file_bug1166138_1x.png 1x, file_bug1166138_2x.png 2x");
+ yield spin(true);
+ is(newImage.currentSrc, img2x, "Adding srcset attribute");
+
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 1]]});
+ yield spin(true);
+ is(newImage.currentSrc, img1x, "new image after switching to 1x");
+ is(image.currentSrc, img1x, "old image after switching to 1x");
+
+ // Clear the listener
+ done = true;
+ });
+ </script>
+</body>
+</html>
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -183,8 +183,9 @@ InterceptedUsedResponseWithURL=Failed to
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "opaqueredirect", "Response", "FetchEvent.respondWith()", or "FetchEvent". %s is a URL.
BadOpaqueRedirectInterceptionWithURL=Failed to load '%S'. A ServiceWorker passed an opaqueredirect Response to FetchEvent.respondWith() while handling a non-navigation FetchEvent.
# LOCALIZATION NOTE: Do not translate "ServiceWorker" or "FetchEvent.preventDefault()". %S is a URL.
InterceptionCanceledWithURL=Failed to load '%S'. A ServiceWorker canceled the load by calling FetchEvent.preventDefault().
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "promise", or "FetchEvent.respondWith()". %1$S is a URL. %2$S is an error string.
InterceptionRejectedResponseWithURL=Failed to load '%1$S'. A ServiceWorker passed a promise to FetchEvent.respondWith() that rejected with '%2$S'.
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "promise", "FetchEvent.respondWith()", or "Response". %1$S is a URL. %2$S is an error string.
InterceptedNonResponseWithURL=Failed to load '%1$S'. A ServiceWorker passed a promise to FetchEvent.respondWith() that resolved with non-Response value '%2$S'.
+ExecCommandCutCopyDeniedNotInputDriven=document.execCommand('cut'/'copy') was denied because it was not called from inside a short running user-generated event handler.
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -106,17 +106,17 @@ public:
virtual void OnReadMetadataCompleted() = 0;
// Returns the owner of this media decoder. The owner should only be used
// on the main thread.
virtual MediaDecoderOwner* GetOwner() = 0;
// Called by the reader's MediaResource as data arrives over the network.
// Must be called on the main thread.
- virtual void NotifyDataArrived(bool aThrottleUpdates) = 0;
+ virtual void NotifyDataArrived() = 0;
// Set by Reader if the current audio track can be offloaded
virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) {}
// Called from HTMLMediaElement when owner document activity changes
virtual void SetElementVisibility(bool aIsVisible) {}
// Stack based class to assist in notifying the frame statistics of
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1336,21 +1336,21 @@ MediaDecoder::SizeOfAudioQueue() {
void MediaDecoder::AddSizeOfResources(ResourceSizes* aSizes) {
MOZ_ASSERT(NS_IsMainThread());
if (GetResource()) {
aSizes->mByteSize += GetResource()->SizeOfIncludingThis(aSizes->mMallocSizeOf);
}
}
void
-MediaDecoder::NotifyDataArrived(bool aThrottleUpdates) {
+MediaDecoder::NotifyDataArrived() {
MOZ_ASSERT(NS_IsMainThread());
if (mDecoderStateMachine) {
- mDecoderStateMachine->DispatchNotifyDataArrived(aThrottleUpdates);
+ mDecoderStateMachine->DispatchNotifyDataArrived();
}
// ReadyState computation depends on MediaDecoder::CanPlayThrough, which
// depends on the download rate.
UpdateReadyState();
}
// Provide access to the state machine object
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -428,17 +428,17 @@ public:
// Called by nsChannelToPipeListener or MediaResource when the
// download has ended. Called on the main thread only. aStatus is
// the result from OnStopRequest.
virtual void NotifyDownloadEnded(nsresult aStatus);
// Called as data arrives on the stream and is read into the cache. Called
// on the main thread only.
- virtual void NotifyDataArrived(bool aThrottleUpdates) override;
+ virtual void NotifyDataArrived() override;
// Called by MediaResource when the principal of the resource has
// changed. Called on main thread only.
virtual void NotifyPrincipalChanged() override;
// Called by the MediaResource to keep track of the number of bytes read
// from the resource. Called on the main by an event runner dispatched
// by the MediaResource read functions.
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -63,21 +63,18 @@ public:
};
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
: mAudioCompactor(mAudioQueue)
, mDecoder(aDecoder)
, mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
/* aSupportsTailDispatch = */ true))
, mWatchManager(this, mTaskQueue)
- , mTimer(new MediaTimer())
, mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
, mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)")
- , mThrottleDuration(TimeDuration::FromMilliseconds(500))
- , mLastThrottledNotify(TimeStamp::Now() - mThrottleDuration)
, mIgnoreAudioOutputFormat(false)
, mHitAudioDecodeError(false)
, mShutdown(false)
, mAudioDiscontinuity(false)
, mVideoDiscontinuity(false)
{
MOZ_COUNT_CTOR(MediaDecoderReader);
MOZ_ASSERT(NS_IsMainThread());
@@ -177,53 +174,16 @@ MediaDecoderReader::DecodeToFirstVideoDa
void
MediaDecoderReader::UpdateBuffered()
{
MOZ_ASSERT(OnTaskQueue());
NS_ENSURE_TRUE_VOID(!mShutdown);
mBuffered = GetBuffered();
}
-void
-MediaDecoderReader::ThrottledNotifyDataArrived()
-{
- MOZ_ASSERT(OnTaskQueue());
- NS_ENSURE_TRUE_VOID(!mShutdown);
-
- // If it's been long enough since our last update, do it.
- if (TimeStamp::Now() - mLastThrottledNotify > mThrottleDuration) {
- DoThrottledNotify();
- } else if (!mThrottledNotify.Exists()) {
- // Otherwise, schedule an update if one isn't scheduled already.
- RefPtr<MediaDecoderReader> self = this;
- mThrottledNotify.Begin(
- mTimer->WaitUntil(mLastThrottledNotify + mThrottleDuration, __func__)
- ->Then(OwnerThread(), __func__,
- [self] () -> void {
- self->mThrottledNotify.Complete();
- NS_ENSURE_TRUE_VOID(!self->mShutdown);
- self->DoThrottledNotify();
- },
- [self] () -> void {
- self->mThrottledNotify.Complete();
- NS_WARNING("throttle callback rejected");
- })
- );
- }
-}
-
-void
-MediaDecoderReader::DoThrottledNotify()
-{
- MOZ_ASSERT(OnTaskQueue());
- mLastThrottledNotify = TimeStamp::Now();
- mThrottledNotify.DisconnectIfExists();
- NotifyDataArrived();
-}
-
media::TimeIntervals
MediaDecoderReader::GetBuffered()
{
MOZ_ASSERT(OnTaskQueue());
if (!HaveStartTime()) {
return media::TimeIntervals();
}
AutoPinned<MediaResource> stream(mDecoder->GetResource());
@@ -395,28 +355,25 @@ RefPtr<ShutdownPromise>
MediaDecoderReader::Shutdown()
{
MOZ_ASSERT(OnTaskQueue());
mShutdown = true;
mBaseAudioPromise.RejectIfExists(END_OF_STREAM, __func__);
mBaseVideoPromise.RejectIfExists(END_OF_STREAM, __func__);
- mThrottledNotify.DisconnectIfExists();
-
ReleaseMediaResources();
mDuration.DisconnectIfConnected();
mBuffered.DisconnectAll();
// Shut down the watch manager before shutting down our task queue.
mWatchManager.Shutdown();
RefPtr<ShutdownPromise> p;
- mTimer = nullptr;
mDecoder = nullptr;
return mTaskQueue->BeginShutdown();
}
} // namespace mozilla
#undef DECODER_LOG
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -218,28 +218,20 @@ public:
// Returns the number of bytes of memory allocated by structures/frames in
// the audio queue.
size_t SizeOfAudioQueueInBytes() const;
virtual size_t SizeOfVideoQueueInFrames();
virtual size_t SizeOfAudioQueueInFrames();
- // In situations where these notifications come from stochastic network
- // activity, we can save significant recomputation by throttling the delivery
- // of these updates to the reader implementation. We don't want to do this
- // throttling when the update comes from MSE code, since that code needs the
- // updates to be observable immediately, and is generally less
- // trigger-happy with notifications anyway.
- void DispatchNotifyDataArrived(bool aThrottleUpdates)
+ void DispatchNotifyDataArrived()
{
RefPtr<nsRunnable> r = NS_NewRunnableMethod(
- this,
- aThrottleUpdates ? &MediaDecoderReader::ThrottledNotifyDataArrived :
- &MediaDecoderReader::NotifyDataArrived);
+ this, &MediaDecoderReader::NotifyDataArrived);
OwnerThread()->Dispatch(
r.forget(), AbstractThread::DontAssertDispatchSuccess);
}
void NotifyDataArrived()
{
MOZ_ASSERT(OnTaskQueue());
@@ -345,33 +337,25 @@ protected:
AbstractMediaDecoder* mDecoder;
// Decode task queue.
RefPtr<TaskQueue> mTaskQueue;
// State-watching manager.
WatchManager<MediaDecoderReader> mWatchManager;
- // MediaTimer.
- RefPtr<MediaTimer> mTimer;
-
// Buffered range.
Canonical<media::TimeIntervals> mBuffered;
// Stores presentation info required for playback.
MediaInfo mInfo;
// Duration, mirrored from the state machine task queue.
Mirror<media::NullableTimeUnit> mDuration;
- // State for ThrottledNotifyDataArrived.
- MozPromiseRequestHolder<MediaTimerPromise> mThrottledNotify;
- const TimeDuration mThrottleDuration;
- TimeStamp mLastThrottledNotify;
-
// Whether we should accept media that we know we can't play
// directly, because they have a number of channel higher than
// what we support.
bool mIgnoreAudioOutputFormat;
// The start time of the media, in microseconds. This is the presentation
// time of the first frame decoded from the media. This is initialized to -1,
// and then set to a value >= by MediaDecoderStateMachine::SetStartTime(),
@@ -410,21 +394,16 @@ private:
MOZ_CRASH();
}
// Recomputes mBuffered.
virtual void UpdateBuffered();
virtual void NotifyDataArrivedInternal() {}
- // Invokes NotifyDataArrived while throttling the calls to occur
- // at most every mThrottleDuration ms.
- void ThrottledNotifyDataArrived();
- void DoThrottledNotify();
-
// Overrides of this function should decodes an unspecified amount of
// audio data, enqueuing the audio data in mAudioQueue. Returns true
// when there's more audio to decode, false if the audio is finished,
// end of file has been reached, or an un-recoverable read error has
// occured. This function blocks until the decode is complete.
virtual bool DecodeAudioData()
{
return false;
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -163,19 +163,19 @@ public:
void DispatchStartBuffering()
{
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::StartBuffering);
OwnerThread()->Dispatch(runnable.forget());
}
- void DispatchNotifyDataArrived(bool aThrottleUpdates)
+ void DispatchNotifyDataArrived()
{
- mReader->DispatchNotifyDataArrived(aThrottleUpdates);
+ mReader->DispatchNotifyDataArrived();
}
// Notifies the state machine that should minimize the number of samples
// decoded we preroll, until playback starts. The first time playback starts
// the state machine is free to return to prerolling normally. Note
// "prerolling" in this context refers to when we decode and buffer decoded
// samples in advance of when they're needed for playback.
void DispatchMinimizePrerollUntilPlaybackStarts()
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -450,17 +450,17 @@ ChannelMediaResource::CopySegmentToCache
void *aClosure,
const char *aFromSegment,
uint32_t aToOffset,
uint32_t aCount,
uint32_t *aWriteCount)
{
CopySegmentClosure* closure = static_cast<CopySegmentClosure*>(aClosure);
- closure->mResource->mCallback->NotifyDataArrived(/* aThrottleUpdates = */ true);
+ closure->mResource->mCallback->NotifyDataArrived();
// Keep track of where we're up to.
RESOURCE_LOG("%p [ChannelMediaResource]: CopySegmentToCache at mOffset [%lld] add "
"[%d] bytes for decoder[%p]",
closure->mResource, closure->mResource->mOffset, aCount,
closure->mResource->mCallback);
closure->mResource->mOffset += aCount;
--- a/dom/media/MediaResourceCallback.h
+++ b/dom/media/MediaResourceCallback.h
@@ -44,17 +44,17 @@ public:
// Notify that a network error is encountered.
virtual void NotifyNetworkError() {}
// Notify that decoding has failed.
virtual void NotifyDecodeError() {}
// Notify that data arrives on the stream and is read into the cache.
- virtual void NotifyDataArrived(bool aThrottleUpdates) {}
+ virtual void NotifyDataArrived() {}
// Notify that MediaResource has received some data.
virtual void NotifyBytesDownloaded() {}
// Notify download is ended.
// NOTE: this can be called with the media cache lock held, so don't
// block or do anything which might try to acquire a lock!
virtual void NotifyDataEnded(nsresult aStatus) {}
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -296,17 +296,17 @@ void
SourceBuffer::Ended()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(IsAttached());
MSE_DEBUG("Ended");
mContentManager->Ended();
// We want the MediaSourceReader to refresh its buffered range as it may
// have been modified (end lined up).
- mMediaSource->GetDecoder()->NotifyDataArrived(/* aThrottleUpdates = */ false);
+ mMediaSource->GetDecoder()->NotifyDataArrived();
}
SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
: DOMEventTargetHelper(aMediaSource->GetParentObject())
, mMediaSource(aMediaSource)
, mUpdating(false)
, mActive(false)
, mUpdateID(0)
@@ -476,19 +476,17 @@ SourceBuffer::AppendDataCompletedWithSuc
if (aHasActiveTracks) {
if (!mActive) {
mActive = true;
mMediaSource->SourceBufferIsActive(this);
}
}
if (mActive) {
// Tell our parent decoder that we have received new data.
- // The information provided do not matter much so long as it is monotonically
- // increasing.
- mMediaSource->GetDecoder()->NotifyDataArrived(/* aThrottleUpdates = */ false);
+ mMediaSource->GetDecoder()->NotifyDataArrived();
// Send progress event.
mMediaSource->GetDecoder()->NotifyBytesDownloaded();
}
CheckEndTime();
StopUpdating();
}
--- a/dom/media/webaudio/BufferDecoder.h
+++ b/dom/media/webaudio/BufferDecoder.h
@@ -52,17 +52,17 @@ public:
MediaDecoderEventVisibility aEventVisibility) final override;
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
MediaDecoderEventVisibility aEventVisibility) final override;
virtual void OnReadMetadataCompleted() final override;
virtual MediaDecoderOwner* GetOwner() final override;
- virtual void NotifyDataArrived(bool) final override {};
+ virtual void NotifyDataArrived() final override {};
private:
virtual ~BufferDecoder();
RefPtr<TaskQueue> mTaskQueueIdentity;
RefPtr<MediaResource> mResource;
};
} // namespace mozilla
--- a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.h
+++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.h
@@ -14,17 +14,17 @@
namespace mozilla {
namespace dom {
class OSXSpeechSynthesizerService final : public nsISpeechService
, public nsIObserver
{
public:
- NS_DECL_ISUPPORTS
+ NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISPEECHSERVICE
NS_DECL_NSIOBSERVER
bool Init();
static OSXSpeechSynthesizerService* GetInstance();
static already_AddRefed<OSXSpeechSynthesizerService> GetInstanceForService();
static void Shutdown();
--- a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
+++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
@@ -174,77 +174,129 @@ SpeechTaskCallback::OnDidFinishSpeaking(
{
mCallback->OnError(aCharacterIndex);
}
@end
namespace mozilla {
namespace dom {
+struct OSXVoice
+{
+ OSXVoice() : mIsDefault(false)
+ {
+ }
+
+ nsString mUri;
+ nsString mName;
+ nsString mLocale;
+ bool mIsDefault;
+};
+
class RegisterVoicesRunnable final : public nsRunnable
{
public:
- explicit RegisterVoicesRunnable(OSXSpeechSynthesizerService* aSpeechService)
+ RegisterVoicesRunnable(OSXSpeechSynthesizerService* aSpeechService,
+ nsTArray<OSXVoice>& aList)
: mSpeechService(aSpeechService)
+ , mVoices(aList)
{
}
- NS_IMETHOD Run();
+ NS_IMETHOD Run() override;
private:
~RegisterVoicesRunnable()
{
}
- RefPtr<OSXSpeechSynthesizerService> mSpeechService;
+ // This runnable always use sync mode. It is unnecesarry to reference object
+ OSXSpeechSynthesizerService* mSpeechService;
+ nsTArray<OSXVoice>& mVoices;
};
NS_IMETHODIMP
RegisterVoicesRunnable::Run()
{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-
nsresult rv;
nsCOMPtr<nsISynthVoiceRegistry> registry =
do_GetService(NS_SYNTHVOICEREGISTRY_CONTRACTID, &rv);
if (!registry) {
return rv;
}
+ for (OSXVoice voice : mVoices) {
+ rv = registry->AddVoice(mSpeechService, voice.mUri, voice.mName, voice.mLocale, true, false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+
+ if (voice.mIsDefault) {
+ registry->SetDefaultVoice(voice.mUri, true);
+ }
+ }
+ return NS_OK;
+}
+
+class EnumVoicesRunnable final : public nsRunnable
+{
+public:
+ explicit EnumVoicesRunnable(OSXSpeechSynthesizerService* aSpeechService)
+ : mSpeechService(aSpeechService)
+ {
+ }
+
+ NS_IMETHOD Run() override;
+
+private:
+ ~EnumVoicesRunnable()
+ {
+ }
+
+ RefPtr<OSXSpeechSynthesizerService> mSpeechService;
+};
+
+NS_IMETHODIMP
+EnumVoicesRunnable::Run()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsAutoTArray<OSXVoice, 64> list;
+
NSArray* voices = [NSSpeechSynthesizer availableVoices];
NSString* defaultVoice = [NSSpeechSynthesizer defaultVoice];
for (NSString* voice in voices) {
+ OSXVoice item;
+
NSDictionary* attr = [NSSpeechSynthesizer attributesForVoice:voice];
nsAutoString identifier;
nsCocoaUtils::GetStringForNSString([attr objectForKey:NSVoiceIdentifier],
identifier);
- nsAutoString name;
- nsCocoaUtils::GetStringForNSString([attr objectForKey:NSVoiceName], name);
+ nsCocoaUtils::GetStringForNSString([attr objectForKey:NSVoiceName], item.mName);
- nsAutoString locale;
nsCocoaUtils::GetStringForNSString(
- [attr objectForKey:NSVoiceLocaleIdentifier], locale);
- locale.ReplaceChar('_', '-');
+ [attr objectForKey:NSVoiceLocaleIdentifier], item.mLocale);
+ item.mLocale.ReplaceChar('_', '-');
- nsAutoString uri;
- uri.AssignLiteral("urn:moz-tts:osx:");
- uri.Append(identifier);
- rv = registry->AddVoice(mSpeechService, uri, name, locale, true, false);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- continue;
+ item.mUri.AssignLiteral("urn:moz-tts:osx:");
+ item.mUri.Append(identifier);
+
+ if ([voice isEqualToString:defaultVoice]) {
+ item.mIsDefault = true;
}
- if ([voice isEqualToString:defaultVoice]) {
- registry->SetDefaultVoice(uri, true);
- }
+ list.AppendElement(item);
}
+ RefPtr<RegisterVoicesRunnable> runnable = new RegisterVoicesRunnable(mSpeechService, list);
+ NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC);
+
return NS_OK;
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
StaticRefPtr<OSXSpeechSynthesizerService> OSXSpeechSynthesizerService::sSingleton;
NS_INTERFACE_MAP_BEGIN(OSXSpeechSynthesizerService)
@@ -269,19 +321,24 @@ bool
OSXSpeechSynthesizerService::Init()
{
if (Preferences::GetBool("media.webspeech.synth.test") ||
!Preferences::GetBool("media.webspeech.synth.enabled")) {
// When test is enabled, we shouldn't add OS backend (Bug 1160844)
return false;
}
+ nsCOMPtr<nsIThread> thread;
+ if (NS_FAILED(NS_NewNamedThread("SpeechWorker", getter_AddRefs(thread)))) {
+ return false;
+ }
+
// Get all the voices and register in the SynthVoiceRegistry
- nsCOMPtr<nsIRunnable> runnable = new RegisterVoicesRunnable(this);
- NS_DispatchToMainThread(runnable);
+ nsCOMPtr<nsIRunnable> runnable = new EnumVoicesRunnable(this);
+ thread->Dispatch(runnable, NS_DISPATCH_NORMAL);
mInitialized = true;
return true;
}
NS_IMETHODIMP
OSXSpeechSynthesizerService::Speak(const nsAString& aText,
const nsAString& aUri,
--- a/dom/tests/mochitest/general/test_img_mutations.html
+++ b/dom/tests/mochitest/general/test_img_mutations.html
@@ -28,27 +28,29 @@
var expectingLoads = 0;
var afterExpectCallback;
function onImgLoad() {
ok(expectingLoads > 0, "expected load");
if (expectingLoads > 0) {
expectingLoads--;
}
- if (!expectingLoads && !expectingErrors) {
+ if (!expectingLoads && !expectingErrors && afterExpectCallback) {
setTimeout(afterExpectCallback, 0);
+ afterExpectCallback = null;
}
}
function onImgError() {
ok(expectingErrors > 0, "expected error");
if (expectingErrors > 0) {
expectingErrors--;
}
- if (!expectingLoads && !expectingErrors) {
+ if (!expectingLoads && !expectingErrors && afterExpectCallback) {
setTimeout(afterExpectCallback, 0);
+ afterExpectCallback = null;
}
}
function expectEvents(loads, errors, callback) {
if (!loads && !errors) {
setTimeout(callback, 0);
} else {
expectingLoads += loads;
expectingErrors += errors;
@@ -145,46 +147,47 @@
tests.push(function () {
info("test 7");
img.srcset = img.srcset;
is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
expectEvents(0, 0, nextTest);
});
- // Re-binding image to document should be a no-op
+ // re-binding the image to the document should be a no-op
tests.push(function () {
info("test 8");
document.body.appendChild(img);
is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
expectEvents(0, 0, nextTest);
});
// We should re-run our selection algorithm when any load event occurs
tests.push(function () {
info("test 9");
img.srcset = testPNG50 + " 1x, " + testPNG200 + " 2x";
is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
expectEvents(1, 0, function() {
is(img.currentSrc, testPNG50, "Should now have testPNG50 as current request");
- SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] },
- function() {
- // We don't currently dynamically switch, but doing so is
- // up to the UA so we may in the future. In which case
- // this test needs to be changed.
- is(img.currentSrc, testPNG50, "Should now have testPNG50 as current request");
+
+ // The preference change will trigger a load, as the image will change
+ SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] });
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request");
img.src = img.src;
- is(img.currentSrc, testPNG50, "Should still have testPNG50 as current request");
+ is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request");
+ // img.src = img.src is special-cased by the spec. It should always
+ // trigger an load event
expectEvents(1, 0, function() {
- is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request");
- nextTest();
+ is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request");
+ expectEvents(0, 0, nextTest);
});
- });
+ })
});
});
// Removing srcset attr should async switch back to src
tests.push(function () {
info("test 10");
is(img.currentSrc, testPNG200, "Should have testPNG200 as current request");
@@ -201,16 +204,20 @@
function nextTest() {
if (tests.length) {
// Spin event loop to make sure no unexpected image events are
// pending (unexpected events will assert in the handlers)
setTimeout(function() {
(tests.shift())();
}, 0);
} else {
+ // Remove the event listeners to prevent the prefenv being popped from
+ // causing test failures.
+ img.removeEventListener("load", onImgLoad);
+ img.removeEventListener("error", onImgError);
SimpleTest.finish();
}
}
addEventListener("load", function() {
SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "1.0"],
[ "dom.image.srcset.enabled", true ]] },
function() {
--- a/gfx/2d/convolverLS3.cpp
+++ b/gfx/2d/convolverLS3.cpp
@@ -32,51 +32,52 @@
#if defined(_MIPS_ARCH_LOONGSON3A)
#include "MMIHelpers.h"
namespace skia {
// Convolves horizontally along a single row. The row data is given in
-// |src_data| and continues for the [begin, end) of the filter.
+// |src_data| and continues for the num_values() of the filter.
void ConvolveHorizontally_LS3(const unsigned char* src_data,
- int begin, int end,
const ConvolutionFilter1D& filter,
unsigned char* out_row) {
+ int num_values = filter.num_values();
int tmp, filter_offset, filter_length;
- double zero, mask[3], shuf_50, shuf_fa;
+ double zero, mask[4], shuf_50, shuf_fa;
asm volatile (
".set push \n\t"
".set arch=loongson3a \n\t"
"xor %[zero], %[zero], %[zero] \n\t"
// |mask| will be used to decimate all extra filter coefficients that are
// loaded by SIMD when |filter_length| is not divisible by 4.
+ // mask[0] is not used in following algorithm.
"li %[tmp], 1 \n\t"
"dsll32 %[tmp], 0x10 \n\t"
"daddiu %[tmp], -1 \n\t"
- "dmtc1 %[tmp], %[mask2] \n\t"
+ "dmtc1 %[tmp], %[mask3] \n\t"
+ "dsrl %[tmp], 0x10 \n\t"
+ "mtc1 %[tmp], %[mask2] \n\t"
"dsrl %[tmp], 0x10 \n\t"
"mtc1 %[tmp], %[mask1] \n\t"
- "dsrl %[tmp], 0x10 \n\t"
- "mtc1 %[tmp], %[mask0] \n\t"
"ori %[tmp], $0, 0x50 \n\t"
"mtc1 %[tmp], %[shuf_50] \n\t"
"ori %[tmp], $0, 0xfa \n\t"
"mtc1 %[tmp], %[shuf_fa] \n\t"
".set pop \n\t"
- :[zero]"=f"(zero), [mask0]"=f"(mask[0]),
- [mask1]"=f"(mask[1]), [mask2]"=f"(mask[2]),
+ :[zero]"=f"(zero), [mask1]"=f"(mask[1]),
+ [mask2]"=f"(mask[2]), [mask3]"=f"(mask[3]),
[shuf_50]"=f"(shuf_50), [shuf_fa]"=f"(shuf_fa),
[tmp]"=&r"(tmp)
);
// Output one pixel each iteration, calculating all channels (RGBA) together.
- for (int out_x = begin; out_x < end; out_x++) {
+ for (int out_x = 0; out_x < num_values; out_x++) {
const ConvolutionFilter1D::Fixed* filter_values =
filter.FilterForValue(out_x, &filter_offset, &filter_length);
double accumh, accuml;
// Compute the first pixel in this row that the filter affects. It will
// touch |filter_length| pixels (4 bytes each) after this.
const void *row_to_filter =
reinterpret_cast<const void*>(&src_data[filter_offset << 2]);
@@ -198,17 +199,17 @@ void ConvolveHorizontally_LS3(const unsi
[src8h]"=&f"(src8h), [src8l]"=&f"(src8l),
[accumh]"+f"(accumh), [accuml]"+f"(accuml),
[src16h]"=&f"(src16h), [src16l]"=&f"(src16l),
[coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
[coeff16h]"=&f"(coeff16h), [coeff16l]"=&f"(coeff16l),
[mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil),
[mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol)
:[fval]"r"(filter_values), [rtf]"r"(row_to_filter),
- [zeroh]"f"(zero), [zerol]"f"(zero), [mask]"f"(mask[r-1]),
+ [zeroh]"f"(zero), [zerol]"f"(zero), [mask]"f"(mask[r]),
[shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa)
);
}
double t, sra;
asm volatile (
".set push \n\t"
".set arch=loongson3a \n\t"
@@ -230,53 +231,54 @@ void ConvolveHorizontally_LS3(const unsi
:"memory"
);
out_row += 4;
}
}
// Convolves horizontally along four rows. The row data is given in
-// |src_data| and continues for the [begin, end) of the filter.
+// |src_data| and continues for the num_values() of the filter.
// The algorithm is almost same as |ConvolveHorizontally_LS3|. Please
// refer to that function for detailed comments.
void ConvolveHorizontally4_LS3(const unsigned char* src_data[4],
- int begin, int end,
const ConvolutionFilter1D& filter,
unsigned char* out_row[4]) {
+ int num_values = filter.num_values();
int tmp, filter_offset, filter_length;
- double zero, mask[3], shuf_50, shuf_fa;
+ double zero, mask[4], shuf_50, shuf_fa;
asm volatile (
".set push \n\t"
".set arch=loongson3a \n\t"
"xor %[zero], %[zero], %[zero] \n\t"
// |mask| will be used to decimate all extra filter coefficients that are
// loaded by SIMD when |filter_length| is not divisible by 4.
+ // mask[0] is not used in following algorithm.
"li %[tmp], 1 \n\t"
"dsll32 %[tmp], 0x10 \n\t"
"daddiu %[tmp], -1 \n\t"
- "dmtc1 %[tmp], %[mask2] \n\t"
+ "dmtc1 %[tmp], %[mask3] \n\t"
+ "dsrl %[tmp], 0x10 \n\t"
+ "mtc1 %[tmp], %[mask2] \n\t"
"dsrl %[tmp], 0x10 \n\t"
"mtc1 %[tmp], %[mask1] \n\t"
- "dsrl %[tmp], 0x10 \n\t"
- "mtc1 %[tmp], %[mask0] \n\t"
"ori %[tmp], $0, 0x50 \n\t"
"mtc1 %[tmp], %[shuf_50] \n\t"
"ori %[tmp], $0, 0xfa \n\t"
"mtc1 %[tmp], %[shuf_fa] \n\t"
".set pop \n\t"
- :[zero]"=f"(zero), [mask0]"=f"(mask[0]),
- [mask1]"=f"(mask[1]), [mask2]"=f"(mask[2]),
+ :[zero]"=f"(zero), [mask1]"=f"(mask[1]),
+ [mask2]"=f"(mask[2]), [mask3]"=f"(mask[3]),
[shuf_50]"=f"(shuf_50), [shuf_fa]"=f"(shuf_fa),
[tmp]"=&r"(tmp)
);
// Output one pixel each iteration, calculating all channels (RGBA) together.
- for (int out_x = begin; out_x < end; out_x++) {
+ for (int out_x = 0; out_x < num_values; out_x++) {
const ConvolutionFilter1D::Fixed* filter_values =
filter.FilterForValue(out_x, &filter_offset, &filter_length);
double accum0h, accum0l, accum1h, accum1l;
double accum2h, accum2l, accum3h, accum3l;
// four pixels in a column per iteration.
asm volatile (
".set push \n\t"
@@ -380,17 +382,17 @@ void ConvolveHorizontally4_LS3(const uns
/* c1 c1 c1 c1 c0 c0 c0 c0 */
_mm_punpcklhw(coeff16lo, coeff16lo, coeff16lo)
_mm_pshuflh(coeff16hi, coeff, shuf_fa)
_mm_punpcklhw(coeff16hi, coeff16hi, coeff16hi)
".set pop \n\t"
:[coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
[coeff16loh]"=&f"(coeff16loh), [coeff16lol]"=&f"(coeff16lol),
[coeff16hih]"=&f"(coeff16hih), [coeff16hil]"=&f"(coeff16hil)
- :[fval]"r"(filter_values), [mask]"f"(mask[r-1]),
+ :[fval]"r"(filter_values), [mask]"f"(mask[r]),
[shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa)
);
ITERATION(src_data[0] + start, accum0h, accum0l);
ITERATION(src_data[1] + start, accum1h, accum1l);
ITERATION(src_data[2] + start, accum2h, accum2l);
ITERATION(src_data[3] + start, accum3h, accum3l);
}
@@ -435,26 +437,27 @@ void ConvolveHorizontally4_LS3(const uns
out_row[2] += 4;
out_row[3] += 4;
}
}
// Does vertical convolution to produce one output row. The filter values and
// length are given in the first two parameters. These are applied to each
// of the rows pointed to in the |source_data_rows| array, with each row
-// being |end - begin| wide.
+// being |pixel_width| wide.
//
-// The output must have room for |(end - begin) * 4| bytes.
+// The output must have room for |pixel_width * 4| bytes.
template<bool has_alpha>
void ConvolveVertically_LS3_impl(const ConvolutionFilter1D::Fixed* filter_values,
int filter_length,
unsigned char* const* source_data_rows,
- int begin, int end,
+ int pixel_width,
unsigned char* out_row) {
uint64_t tmp;
+ int width = pixel_width & ~3;
double zero, sra, coeff16h, coeff16l;
double accum0h, accum0l, accum1h, accum1l;
double accum2h, accum2l, accum3h, accum3l;
const void *src;
int out_x;
asm volatile (
".set push \n\t"
@@ -463,17 +466,17 @@ void ConvolveVertically_LS3_impl(const C
"ori %[tmp], $0, %[sk_sra] \n\t"
"mtc1 %[tmp], %[sra] \n\t"
".set pop \n\t"
:[zero]"=f"(zero), [sra]"=f"(sra), [tmp]"=&r"(tmp)
:[sk_sra]"i"(ConvolutionFilter1D::kShiftBits)
);
// Output four pixels per iteration (16 bytes).
- for (out_x = begin; out_x + 3 < end; out_x += 4) {
+ for (out_x = 0; out_x < width; out_x += 4) {
// Accumulated result for each pixel. 32 bits per RGBA channel.
asm volatile (
".set push \n\t"
".set arch=loongson3a \n\t"
_mm_xor(accum0, accum0, accum0)
_mm_xor(accum1, accum1, accum1)
_mm_xor(accum2, accum2, accum2)
_mm_xor(accum3, accum3, accum3)
@@ -646,18 +649,17 @@ void ConvolveVertically_LS3_impl(const C
[out_row]"r"(out_row)
:"memory"
);
out_row += 16;
}
// When the width of the output is not divisible by 4, We need to save one
// pixel (4 bytes) each time. And also the fourth pixel is always absent.
- int r = end - out_x;
- if (r > 0) {
+ if (pixel_width & 3) {
asm volatile (
".set push \n\t"
".set arch=loongson3a \n\t"
_mm_xor(accum0, accum0, accum0)
_mm_xor(accum1, accum1, accum1)
_mm_xor(accum2, accum2, accum2)
".set pop \n\t"
:[accum0h]"=&f"(accum0h), [accum0l]"=&f"(accum0l),
@@ -788,17 +790,17 @@ void ConvolveVertically_LS3_impl(const C
"li %[tmp], 4 \n\t"
"mtc1 %[tmp], %[s4] \n\t"
"li %[tmp], 64 \n\t"
"mtc1 %[tmp], %[s64] \n\t"
".set pop \n\t"
:[s4]"=f"(s4), [s64]"=f"(s64),
[tmp]"=&r"(tmp)
);
- for (; out_x < end; out_x++) {
+ for (int out_x = width; out_x < pixel_width; out_x++) {
double t;
asm volatile (
".set push \n\t"
".set arch=loongson3a \n\t"
"swc1 %[accum0l], (%[out_row]) \n\t"
_mm_psrlq(accum0, accum0, s4, s64, t)
".set pop \n\t"
@@ -810,22 +812,22 @@ void ConvolveVertically_LS3_impl(const C
out_row += 4;
}
}
}
void ConvolveVertically_LS3(const ConvolutionFilter1D::Fixed* filter_values,
int filter_length,
unsigned char* const* source_data_rows,
- int begin, int end,
+ int pixel_width,
unsigned char* out_row, bool has_alpha) {
if (has_alpha) {
ConvolveVertically_LS3_impl<true>(filter_values, filter_length,
- source_data_rows, begin, end, out_row);
+ source_data_rows, pixel_width, out_row);
} else {
ConvolveVertically_LS3_impl<false>(filter_values, filter_length,
- source_data_rows, begin, end, out_row);
+ source_data_rows, pixel_width, out_row);
}
}
} // namespace skia
#endif /* _MIPS_ARCH_LOONGSON3A */
--- a/gfx/2d/convolverLS3.h
+++ b/gfx/2d/convolverLS3.h
@@ -35,36 +35,34 @@
#include "skia/include/core/SkTypes.h"
namespace skia {
// Convolves horizontally along a single row. The row data is given in
// |src_data| and continues for the [begin, end) of the filter.
void ConvolveHorizontally_LS3(const unsigned char* src_data,
- int begin, int end,
const ConvolutionFilter1D& filter,
unsigned char* out_row);
// Convolves horizontally along four rows. The row data is given in
// |src_data| and continues for the [begin, end) of the filter.
// The algorithm is almost same as |ConvolveHorizontally_LS3|. Please
// refer to that function for detailed comments.
void ConvolveHorizontally4_LS3(const unsigned char* src_data[4],
- int begin, int end,
const ConvolutionFilter1D& filter,
unsigned char* out_row[4]);
// Does vertical convolution to produce one output row. The filter values and
// length are given in the first two parameters. These are applied to each
// of the rows pointed to in the |source_data_rows| array, with each row
// being |pixel_width| wide.
//
// The output must have room for |pixel_width * 4| bytes.
void ConvolveVertically_LS3(const ConvolutionFilter1D::Fixed* filter_values,
int filter_length,
unsigned char* const* source_data_rows,
- int begin, int end,
+ int pixel_width,
unsigned char* out_row, bool has_alpha);
} // namespace skia
#endif // SKIA_EXT_CONVOLVER_LS3_H_
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1161,17 +1161,16 @@ OOMTest(JSContext* cx, unsigned argc, Va
RootedValue result(cx);
bool ok = JS_CallFunction(cx, cx->global(), function,
HandleValueArray::empty(), &result);
handledOOM = OOM_counter >= OOM_maxAllocations;
OOM_maxAllocations = UINT32_MAX;
MOZ_ASSERT_IF(ok, !cx->isExceptionPending());
- MOZ_ASSERT_IF(!ok, cx->isExceptionPending());
// Note that it is possible that the function throws an exception
// unconnected to OOM, in which case we ignore it. More correct
// would be to have the caller pass some kind of exception
// specification and to check the exception against it.
cx->clearPendingException();
cx->runtime()->hadOutOfMemory = false;
--- a/js/src/jit-test/tests/debug/Script-getOffsetsCoverage-02.js
+++ b/js/src/jit-test/tests/debug/Script-getOffsetsCoverage-02.js
@@ -1,8 +1,9 @@
+// |jit-test| --ion-pgo=off;
// This script check that when we enable / disable the code coverage collection,
// then we have different results for the getOffsetsCoverage methods.
var g = newGlobal();
var dbg = Debugger(g);
var coverageInfo = [];
var num = 20;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1219905.js
@@ -0,0 +1,14 @@
+// |jit-test| allow-oom
+
+// We need allow-oom here because the debugger reports an uncaught exception if
+// it hits OOM calling the exception unwind hook. This causes the shell to exit
+// with the OOM reason.
+
+if (!('oomTest' in this))
+ quit();
+
+var g = newGlobal();
+g.parent = this;
+g.eval("new Debugger(parent).onExceptionUnwind = function() {}");
+let finished = false;
+oomTest(() => l);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1223021.js
@@ -0,0 +1,12 @@
+if (!('oomTest' in this))
+ quit();
+
+function f() {
+ return this === null;
+};
+
+function g() {
+ if (!f.apply(9)) {}
+}
+
+oomTest(g);
--- a/js/src/jit-test/tests/ion/recover-object-bug1175233.js
+++ b/js/src/jit-test/tests/ion/recover-object-bug1175233.js
@@ -1,19 +1,19 @@
-// |jit-test| test-join=--no-unboxed-objects
+// |jit-test| test-join=--no-unboxed-objects; --ion-pgo=on
//
// Unboxed object optimization might not trigger in all cases, thus we ensure
// that Scalar Replacement optimization is working well independently of the
// object representation.
// Ion eager fails the test below because we have not yet created any
// template object in baseline before running the content of the top-level
// function.
-if (getJitCompilerOptions()["ion.warmup.trigger"] <= 30)
- setJitCompilerOption("ion.warmup.trigger", 30);
+if (getJitCompilerOptions()["ion.warmup.trigger"] <= 90)
+ setJitCompilerOption("ion.warmup.trigger", 90);
// This test checks that we are able to remove the getelem & setelem with scalar
// replacement, so we should not force inline caches, as this would skip the
// generation of getelem & setelem instructions.
if (getJitCompilerOptions()["ion.forceinlineCaches"])
setJitCompilerOption("ion.forceinlineCaches", 0);
var uceFault = function (j) {
@@ -23,18 +23,21 @@ var uceFault = function (j) {
}
function f(j) {
var i = Math.pow(2, j) | 0;
var obj = {
i: i,
v: i + i
};
- assertRecoveredOnBailout(obj, false); // :TODO: Fixed by Bug 1165348
- assertRecoveredOnBailout(obj.v, false); // :TODO: Fixed by Bug 1165348
+ // These can only be recovered on bailout iff either we have type
+ // information for the property access in the branch, or the branch is
+ // removed before scalar replacement.
+ assertRecoveredOnBailout(obj, true);
+ assertRecoveredOnBailout(obj.v, true);
if (uceFault(j) || uceFault(j)) {
// MObjectState::recover should neither fail,
// nor coerce its result to an int32.
assertEq(obj.v, 2 * i);
}
return 2 * obj.i;
}
--- a/js/src/jit-test/tests/ion/recover-objects.js
+++ b/js/src/jit-test/tests/ion/recover-objects.js
@@ -1,9 +1,9 @@
-// |jit-test| test-join=--no-unboxed-objects
+// |jit-test| test-join=--no-unboxed-objects; --ion-pgo=on
//
// Unboxed object optimization might not trigger in all cases, thus we ensure
// that Scalar Replacement optimization is working well independently of the
// object representation.
// Ion eager fails the test below because we have not yet created any
// template object in baseline before running the content of the top-level
// function.
@@ -20,17 +20,17 @@ function resumeHere() {}
var uceFault = function (i) {
if (i > 98)
uceFault = function (i) { return true; };
return false;
};
// Without "use script" in the inner function, the arguments might be
-// obersvable.
+// observable.
function inline_notSoEmpty1(a, b, c, d) {
// This function is not strict, so we might be able to observe its
// arguments, if somebody ever called fun.arguments inside it.
return { v: (a.v + b.v + c.v + d.v - 10) / 4 };
}
var uceFault_notSoEmpty1 = eval(uneval(uceFault).replace('uceFault', 'uceFault_notSoEmpty1'));
function notSoEmpty1() {
var a = { v: i };
@@ -45,19 +45,20 @@ function notSoEmpty1() {
// optimizing these cases by executing recover instruction before the
// execution of the bailout. This ensures that the observed objects are
// allocated once and used by the unexpected observation and the bailout.
assertRecoveredOnBailout(a, true);
assertRecoveredOnBailout(b, true);
assertRecoveredOnBailout(c, true);
assertRecoveredOnBailout(d, true);
assertRecoveredOnBailout(unused, true);
- // Scalar Replacement is coming after the branch removal made by GVN, and
- // the ucefault branch is not taken yet.
- assertRecoveredOnBailout(res, false);
+ // This can only be recovered on bailout iff either we have type
+ // information for the property access in the branch, or the branch is
+ // removed before scalar replacement.
+ assertRecoveredOnBailout(res, true);
}
// Check that we can recover objects with their content.
function inline_notSoEmpty2(a, b, c, d) {
"use strict";
return { v: (a.v + b.v + c.v + d.v - 10) / 4 };
}
var uceFault_notSoEmpty2 = eval(uneval(uceFault).replace('uceFault', 'uceFault_notSoEmpty2'));
@@ -70,19 +71,20 @@ function notSoEmpty2(i) {
var res = inline_notSoEmpty2(a, b, c, d);
if (uceFault_notSoEmpty2(i) || uceFault_notSoEmpty2(i))
assertEq(i, res.v);
assertRecoveredOnBailout(a, true);
assertRecoveredOnBailout(b, true);
assertRecoveredOnBailout(c, true);
assertRecoveredOnBailout(d, true);
assertRecoveredOnBailout(unused, true);
- // Scalar Replacement is coming after the branch removal made by GVN, and
- // the ucefault branch is not taken yet.
- assertRecoveredOnBailout(res, false);
+ // This can only be recovered on bailout iff either we have type
+ // information for the property access in the branch, or the branch is
+ // removed before scalar replacement.
+ assertRecoveredOnBailout(res, true);
}
// Check that we can recover objects with their content.
var argFault_observeArg = function (i) {
if (i > 98)
return inline_observeArg.arguments[0];
return { test : i };
};
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -265,27 +265,31 @@ jit::EnsureHasScopeObjects(JSContext* cx
!fp.hasCallObj())
{
return fp.initFunctionScopeObjects(cx);
}
return true;
}
bool
-jit::CheckFrequentBailouts(JSContext* cx, JSScript* script)
+jit::CheckFrequentBailouts(JSContext* cx, JSScript* script, BailoutKind bailoutKind)
{
if (script->hasIonScript()) {
// Invalidate if this script keeps bailing out without invalidation. Next time
// we compile this script LICM will be disabled.
IonScript* ionScript = script->ionScript();
- if (ionScript->numBailouts() >= js_JitOptions.frequentBailoutThreshold &&
- !script->hadFrequentBailouts())
- {
- script->setHadFrequentBailouts();
+ if (ionScript->numBailouts() >= js_JitOptions.frequentBailoutThreshold) {
+ // If we bailout because of the first execution of a basic block,
+ // then we should record which basic block we are returning in,
+ // which should prevent this from happening again. Also note that
+ // the first execution bailout can be related to an inlined script,
+ // so there is no need to penalize the caller.
+ if (bailoutKind != Bailout_FirstExecution && !script->hadFrequentBailouts())
+ script->setHadFrequentBailouts();
JitSpew(JitSpew_IonInvalidate, "Invalidating due to too many bailouts");
if (!Invalidate(cx, script))
return false;
}
}
--- a/js/src/jit/Bailouts.h
+++ b/js/src/jit/Bailouts.h
@@ -206,14 +206,14 @@ class ExceptionBailoutInfo
// Returns a BAILOUT_* error code.
uint32_t ExceptionHandlerBailout(JSContext* cx, const InlineFrameIterator& frame,
ResumeFromException* rfe,
const ExceptionBailoutInfo& excInfo,
bool* overrecursed);
uint32_t FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfo);
-bool CheckFrequentBailouts(JSContext* cx, JSScript* script);
+bool CheckFrequentBailouts(JSContext* cx, JSScript* script, BailoutKind bailoutKind);
} // namespace jit
} // namespace js
#endif /* jit_Bailouts_h */
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -790,16 +790,26 @@ InitFromBailout(JSContext* cx, HandleScr
return false;
}
// Get the pc. If we are handling an exception, resume at the pc of the
// catch or finally block.
jsbytecode* pc = catchingException ? excInfo->resumePC() : script->offsetToPC(iter.pcOffset());
bool resumeAfter = catchingException ? false : iter.resumeAfter();
+ // When pgo is enabled, increment the counter of the block in which we
+ // resume, as Ion does not keep track of the code coverage.
+ //
+ // We need to do that when pgo is enabled, as after a specific number of
+ // FirstExecution bailouts, we invalidate and recompile the script with
+ // IonMonkey. Failing to increment the counter of the current basic block
+ // might lead to repeated bailouts and invalidations.
+ if (!js_JitOptions.disablePgo && script->hasScriptCounts())
+ script->incHitCount(pc);
+
JSOp op = JSOp(*pc);
// Fixup inlined JSOP_FUNCALL, JSOP_FUNAPPLY, and accessors on the caller side.
// On the caller side this must represent like the function wasn't inlined.
uint32_t pushedSlots = 0;
AutoValueVector savedCallerArgs(cx);
bool needToSaveArgs = op == JSOP_FUNAPPLY || IsGetPropPC(pc) || IsSetPropPC(pc);
if (iter.moreFrames() && (op == JSOP_FUNCALL || needToSaveArgs))
@@ -1885,16 +1895,22 @@ jit::FinishBailoutToBaseline(BaselineBai
case Bailout_NonSimdFloat32x4Input:
case Bailout_InitialState:
case Bailout_Debugger:
case Bailout_UninitializedThis:
case Bailout_BadDerivedConstructorReturn:
// Do nothing.
break;
+ case Bailout_FirstExecution:
+ // Do not return directly, as this was not frequent in the first place,
+ // thus rely on the check for frequent bailouts to recompile the current
+ // script.
+ break;
+
// Invalid assumption based on baseline code.
case Bailout_OverflowInvalidate:
case Bailout_NonStringInputInvalidate:
case Bailout_DoubleOutput:
case Bailout_ObjectIdentityOrTypeGuard:
if (!HandleBaselineInfoBailout(cx, outerScript, innerScript))
return false;
break;
@@ -1918,15 +1934,15 @@ jit::FinishBailoutToBaseline(BaselineBai
case Bailout_IonExceptionDebugMode:
// Return false to resume in HandleException with reconstructed
// baseline frame.
return false;
default:
MOZ_CRASH("Unknown bailout kind!");
}
- if (!CheckFrequentBailouts(cx, outerScript))
+ if (!CheckFrequentBailouts(cx, outerScript, bailoutKind))
return false;
// We're returning to JIT code, so we should clear the override pc.
topFrame->clearOverridePc();
return true;
}
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1498,16 +1498,27 @@ OptimizeMIR(MIRGenerator* mir)
}
gs.spewPass("BuildSSA");
AssertBasicGraphCoherency(graph);
if (mir->shouldCancel("Start"))
return false;
+ if (!js_JitOptions.disablePgo && !mir->compilingAsmJS()) {
+ AutoTraceLog log(logger, TraceLogger_PruneUnusedBranches);
+ if (!PruneUnusedBranches(mir, graph))
+ return false;
+ gs.spewPass("Prune Unused Branches");
+ AssertBasicGraphCoherency(graph);
+
+ if (mir->shouldCancel("Prune Unused Branches"))
+ return false;
+ }
+
{
AutoTraceLog log(logger, TraceLogger_FoldTests);
if (!FoldTests(graph))
return false;
gs.spewPass("Fold Tests");
AssertBasicGraphCoherency(graph);
if (mir->shouldCancel("Fold Tests"))
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -22,16 +22,400 @@
#include "jit/shared/Lowering-shared-inl.h"
using namespace js;
using namespace js::jit;
using mozilla::DebugOnly;
+typedef Vector<MPhi*, 16, SystemAllocPolicy> MPhiVector;
+
+static bool
+FlagPhiInputsAsHavingRemovedUses(MBasicBlock* block, MBasicBlock* succ, MPhiVector& worklist)
+{
+ // When removing an edge between 2 blocks, we might remove the ability of
+ // later phases to figure out that the uses of a Phi should be considered as
+ // a use of all its inputs. Thus we need to mark the Phi inputs as having
+ // removed uses iff the phi has any uses.
+ //
+ //
+ // +--------------------+ +---------------------+
+ // |12 MFoo 6 | |32 MBar 5 |
+ // | | | |
+ // | ... | | ... |
+ // | | | |
+ // |25 MGoto Block 4 | |43 MGoto Block 4 |
+ // +--------------------+ +---------------------+
+ // | |
+ // | | |
+ // | | |
+ // | +-----X------------------------+
+ // | Edge |
+ // | Removed |
+ // | |
+ // | +------------v-----------+
+ // | |50 MPhi 12 32 |
+ // | | |
+ // | | ... |
+ // | | |
+ // | |70 MReturn 50 |
+ // | +------------------------+
+ // |
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // |
+ // v
+ //
+ // ^ +--------------------+ +---------------------+
+ // /!\ |12 MConst opt-out | |32 MBar 5 |
+ // '---' | | | |
+ // | ... | | ... |
+ // |78 MBail | | |
+ // |80 MUnreachable | |43 MGoto Block 4 |
+ // +--------------------+ +---------------------+
+ // |
+ // |
+ // |
+ // +---------------+
+ // |
+ // |
+ // |
+ // +------------v-----------+
+ // |50 MPhi 32 |
+ // | |
+ // | ... |
+ // | |
+ // |70 MReturn 50 |
+ // +------------------------+
+ //
+ //
+ // If the inputs of the Phi are not flagged as having removed uses, then
+ // later compilation phase might optimize them out. The problem is that a
+ // bailout will use this value and give it back to baseline, which will then
+ // use the OptimizedOut magic value in a computation.
+
+ // Conservative upper limit for the number of Phi instructions which are
+ // visited while looking for uses.
+ const size_t conservativeUsesLimit = 128;
+
+ MOZ_ASSERT(worklist.empty());
+ size_t predIndex = succ->getPredecessorIndex(block);
+ MPhiIterator end = succ->phisEnd();
+ MPhiIterator it = succ->phisBegin();
+ for (; it != end; it++) {
+ MPhi* phi = *it;
+
+ // We are looking to mark the Phi inputs which are used across the edge
+ // between the |block| and its successor |succ|.
+ MDefinition* def = phi->getOperand(predIndex);
+ if (def->isUseRemoved())
+ continue;
+
+ phi->setInWorklist();
+ if (!worklist.append(phi))
+ return false;
+
+ // Fill the work list with all the Phi nodes uses until we reach either:
+ // - A resume point which uses the Phi as an observable operand.
+ // - An explicit use of the Phi instruction.
+ // - An implicit use of the Phi instruction.
+ bool isUsed = false;
+ for (size_t idx = 0; !isUsed && idx < worklist.length(); idx++) {
+ phi = worklist[idx];
+ MUseIterator usesEnd(phi->usesEnd());
+ for (MUseIterator use(phi->usesBegin()); use != usesEnd; use++) {
+ MNode* consumer = (*use)->consumer();
+ if (consumer->isResumePoint()) {
+ MResumePoint* rp = consumer->toResumePoint();
+ if (rp->isObservableOperand(*use)) {
+ // The phi is observable via a resume point operand.
+ isUsed = true;
+ break;
+ }
+ continue;
+ }
+
+ MDefinition* cdef = consumer->toDefinition();
+ if (!cdef->isPhi()) {
+ // The phi is explicitly used.
+ isUsed = true;
+ break;
+ }
+
+ phi = cdef->toPhi();
+ if (phi->isInWorklist())
+ continue;
+
+ if (phi->isUseRemoved() || phi->isImplicitlyUsed()) {
+ // The phi is implicitly used.
+ isUsed = true;
+ break;
+ }
+
+ phi->setInWorklist();
+ if (!worklist.append(phi))
+ return false;
+ }
+
+ // Use a conservative upper bound to avoid iterating too many times
+ // on very large graphs.
+ if (idx >= conservativeUsesLimit) {
+ isUsed = true;
+ break;
+ }
+ }
+
+ if (isUsed)
+ def->setUseRemoved();
+
+ // Remove all the InWorklist flags.
+ while (!worklist.empty()) {
+ phi = worklist.popCopy();
+ phi->setNotInWorklist();
+ }
+ }
+
+ return true;
+}
+
+static bool
+FlagAllOperandsAsHavingRemovedUses(MBasicBlock* block)
+{
+ // Flag all instructions operands as having removed uses.
+ MInstructionIterator end = block->end();
+ for (MInstructionIterator it = block->begin(); it != end; it++) {
+ MInstruction* ins = *it;
+ for (size_t i = 0, e = ins->numOperands(); i < e; i++)
+ ins->getOperand(i)->setUseRemovedUnchecked();
+
+ // Flag observable resume point operands as having removed uses.
+ if (MResumePoint* rp = ins->resumePoint()) {
+ // Note: no need to iterate over the caller's of the resume point as
+ // this is the same as the entry resume point.
+ for (size_t i = 0, e = rp->numOperands(); i < e; i++) {
+ if (!rp->isObservableOperand(i))
+ continue;
+ rp->getOperand(i)->setUseRemovedUnchecked();
+ }
+ }
+ }
+
+ // Flag observable operands of the entry resume point as having removed uses.
+ MResumePoint* rp = block->entryResumePoint();
+ do {
+ for (size_t i = 0, e = rp->numOperands(); i < e; i++) {
+ if (!rp->isObservableOperand(i))
+ continue;
+ rp->getOperand(i)->setUseRemovedUnchecked();
+ }
+ rp = rp->caller();
+ } while (rp);
+
+ // Flag Phi inputs of the successors has having removed uses.
+ MPhiVector worklist;
+ for (size_t i = 0, e = block->numSuccessors(); i < e; i++) {
+ if (!FlagPhiInputsAsHavingRemovedUses(block, block->getSuccessor(i), worklist))
+ return false;
+ }
+
+ return true;
+}
+
+static void
+RemoveFromSuccessors(MBasicBlock* block)
+{
+ // Remove this block from its successors.
+ size_t numSucc = block->numSuccessors();
+ while (numSucc--) {
+ MBasicBlock* succ = block->getSuccessor(numSucc);
+ if (succ->isDead())
+ continue;
+ JitSpew(JitSpew_Prune, "Remove block edge %d -> %d.", block->id(), succ->id());
+ succ->removePredecessor(block);
+ }
+}
+
+static void
+ConvertToBailingBlock(TempAllocator& alloc, MBasicBlock* block)
+{
+ // Add a bailout instruction.
+ MBail* bail = MBail::New(alloc, Bailout_FirstExecution);
+ MInstruction* bailPoint = block->safeInsertTop();
+ block->insertBefore(block->safeInsertTop(), bail);
+
+ // Discard all remaining instructions.
+ MInstructionIterator clearStart = block->begin(bailPoint);
+ block->discardAllInstructionsStartingAt(clearStart);
+ if (block->outerResumePoint())
+ block->clearOuterResumePoint();
+
+ // And replace the last instruction by the unreachable control instruction.
+ block->end(MUnreachable::New(alloc));
+}
+
+bool
+jit::PruneUnusedBranches(MIRGenerator* mir, MIRGraph& graph)
+{
+ MOZ_ASSERT(!mir->compilingAsmJS(), "AsmJS compilation have no code coverage support.");
+
+ // We do a reverse-post-order traversal, marking basic blocks when the block
+ // have to be converted into bailing blocks, and flagging block as
+ // unreachable if all predecessors are flagged as bailing or unreachable.
+ bool someUnreachable = false;
+ for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
+ JitSpew(JitSpew_Prune, "Investigate Block %d:", block->id());
+
+ // Do not touch entry basic blocks.
+ if (*block == graph.osrBlock() || *block == graph.entryBlock())
+ continue;
+
+ // Compute if all the predecessors of this block are either bailling out
+ // or are already flagged as unreachable.
+ bool isUnreachable = true;
+ bool isLoopHeader = block->isLoopHeader();
+ size_t numPred = block->numPredecessors();
+ size_t i = 0;
+ for (; i < numPred; i++) {
+ MBasicBlock* pred = block->getPredecessor(i);
+
+ // The backedge is visited after the loop header, but if the loop
+ // header is unreachable, then we can assume that the backedge would
+ // be unreachable too.
+ if (isLoopHeader && pred == block->backedge())
+ continue;
+
+ // Break if any of the predecessor can continue in this block.
+ if (!pred->isMarked() && !pred->unreachable()) {
+ isUnreachable = false;
+ break;
+ }
+ }
+
+ // Compute if the block should bailout, based on the trivial heuristic
+ // which is that if the block never got visited before, then it is
+ // likely to not be visited after.
+ bool shouldBailout =
+ block->getHitState() == MBasicBlock::HitState::Count &&
+ block->getHitCount() == 0;
+
+ // Check if the predecessors got accessed a large number of times in
+ // comparisons of the current block, in order to know if our attempt at
+ // removing this block is not premature.
+ if (shouldBailout) {
+ size_t p = numPred;
+ size_t predCount = 0;
+ bool isLoopExit = false;
+ while (p--) {
+ MBasicBlock* pred = block->getPredecessor(p);
+ if (pred->getHitState() == MBasicBlock::HitState::Count)
+ predCount += pred->getHitCount();
+ isLoopExit |= pred->isLoopHeader() && pred->backedge() != *block;
+ }
+
+ // This assumes that instructions are numbered in sequence, which is
+ // the case after IonBuilder creation of basic blocks.
+ size_t numInst = block->rbegin()->id() - block->begin()->id();
+
+ // This sum is not homogeneous but gives good results on benchmarks
+ // like Kraken and Octane. The current block has not been executed
+ // yet, and ...
+ //
+ // 1. If the number of times the predecessor got executed is
+ // larger, then we are less likely to hit this block.
+ //
+ // 2. If the block is large, then this is likely a corner case,
+ // and thus we are less likely to hit this block.
+ if (predCount + numInst < 75)
+ shouldBailout = false;
+
+ // If this is the exit block of a loop, then keep this basic
+ // block. This heuristic is useful as a bailout is often much more
+ // costly than a simple exit sequence.
+ if (isLoopExit)
+ shouldBailout = false;
+ }
+
+ // Continue to the next basic block if the current basic block can
+ // remain unchanged.
+ if (!isUnreachable && !shouldBailout)
+ continue;
+
+ JitSpewIndent indent(JitSpew_Prune);
+ someUnreachable = true;
+ if (isUnreachable) {
+ JitSpew(JitSpew_Prune, "Mark block %d as unreachable.", block->id());
+ block->setUnreachable();
+ // If the block is unreachable, then there is no need to convert it
+ // to a bailing block.
+ } else if (shouldBailout) {
+ JitSpew(JitSpew_Prune, "Mark block %d as bailing block.", block->id());
+ block->markUnchecked();
+ }
+
+ // When removing a loop header, we should ensure that its backedge is
+ // removed first, otherwise this triggers an assertion in
+ // removePredecessorsWithoutPhiOperands.
+ if (block->isLoopHeader()) {
+ JitSpew(JitSpew_Prune, "Mark block %d as bailing block. (loop backedge)", block->backedge()->id());
+ block->backedge()->markUnchecked();
+ }
+ }
+
+ // Returns early if nothing changed.
+ if (!someUnreachable)
+ return true;
+
+ JitSpew(JitSpew_Prune, "Convert basic block to bailing blocks, and remove unreachable blocks:");
+ JitSpewIndent indent(JitSpew_Prune);
+
+ // As we are going to remove edges and basic block, we have to mark
+ // instructions which would be needed by baseline if we were to bailout.
+ for (PostorderIterator it(graph.poBegin()); it != graph.poEnd();) {
+ MBasicBlock* block = *it++;
+ if (!block->isMarked() && !block->unreachable())
+ continue;
+
+ FlagAllOperandsAsHavingRemovedUses(block);
+ }
+
+ // Remove the blocks in post-order such that consumers are visited before
+ // the predecessors, the only exception being the Phi nodes of loop headers.
+ for (PostorderIterator it(graph.poBegin()); it != graph.poEnd();) {
+ MBasicBlock* block = *it++;
+ if (!block->isMarked() && !block->unreachable())
+ continue;
+
+ JitSpew(JitSpew_Prune, "Remove / Replace block %d.", block->id());
+ JitSpewIndent indent(JitSpew_Prune);
+
+ // As we are going to replace/remove the last instruction, we first have
+ // to remove this block from the predecessor list of its successors.
+ RemoveFromSuccessors(block);
+
+ // Convert the current basic block to a bailing block which ends with an
+ // Unreachable control instruction.
+ if (block->isMarked()) {
+ JitSpew(JitSpew_Prune, "Convert Block %d to a bailing block.", block->id());
+ if (!graph.alloc().ensureBallast())
+ return false;
+ ConvertToBailingBlock(graph.alloc(), block);
+ block->unmark();
+ }
+
+ // Remove all instructions.
+ if (block->unreachable()) {
+ JitSpew(JitSpew_Prune, "Remove Block %d.", block->id());
+ JitSpewIndent indent(JitSpew_Prune);
+ graph.removeBlock(block);
+ }
+ }
+
+ return true;
+}
+
static bool
SplitCriticalEdgesForBlock(MIRGraph& graph, MBasicBlock* block)
{
if (block->numSuccessors() < 2)
return true;
for (size_t i = 0; i < block->numSuccessors(); i++) {
MBasicBlock* target = block->getSuccessor(i);
if (target->numPredecessors() < 2)
@@ -491,17 +875,17 @@ jit::EliminateDeadResumePointOperands(MI
if (ins->isNewDerivedTypedObject() || ins->isRecoveredOnBailout()) {
MOZ_ASSERT(ins->canRecoverOnBailout());
continue;
}
// If the instruction's behavior has been constant folded into a
// separate instruction, we can't determine precisely where the
// instruction becomes dead and can't eliminate its uses.
- if (ins->isImplicitlyUsed())
+ if (ins->isImplicitlyUsed() || ins->isUseRemoved())
continue;
// Check if this instruction's result is only used within the
// current block, and keep track of its last use in a definition
// (not resume point). This requires the instructions in the block
// to be numbered, ensured by running this immediately after alias
// analysis.
uint32_t maxDefinition = 0;
@@ -604,17 +988,17 @@ jit::EliminateDeadCode(MIRGenerator* mir
return true;
}
static inline bool
IsPhiObservable(MPhi* phi, Observability observe)
{
// If the phi has uses which are not reflected in SSA, then behavior in the
// interpreter may be affected by removing the phi.
- if (phi->isImplicitlyUsed())
+ if (phi->isImplicitlyUsed() || phi->isUseRemoved())
return true;
// Check for uses of this phi node outside of other phi nodes.
// Note that, initially, we skip reading resume points, which we
// don't count as actual uses. If the only uses are resume points,
// then the SSA name is never consumed by the program. However,
// after optimizations have been performed, it's possible that the
// actual uses in the program have been (incorrectly) optimized
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -14,16 +14,19 @@
namespace js {
namespace jit {
class MIRGenerator;
class MIRGraph;
bool
+PruneUnusedBranches(MIRGenerator* mir, MIRGraph& graph);
+
+bool
FoldTests(MIRGraph& graph);
bool
SplitCriticalEdges(MIRGraph& graph);
enum Observability {
ConservativeObservability,
AggressiveObservability
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -728,22 +728,19 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl
}
}
return true;
}
bool
IonBuilder::pushLoop(CFGState::State initial, jsbytecode* stopAt, MBasicBlock* entry, bool osr,
jsbytecode* loopHead, jsbytecode* initialPc,
- jsbytecode* bodyStart, jsbytecode* bodyEnd, jsbytecode* exitpc,
- jsbytecode* continuepc)
-{
- if (!continuepc)
- continuepc = entry->pc();
-
+ jsbytecode* bodyStart, jsbytecode* bodyEnd,
+ jsbytecode* exitpc, jsbytecode* continuepc)
+{
ControlFlowInfo loop(cfgStack_.length(), continuepc);
if (!loops_.append(loop))
return false;
CFGState state;
state.state = initial;
state.stopAt = stopAt;
state.loop.bodyStart = bodyStart;
@@ -3128,26 +3125,26 @@ IonBuilder::doWhileLoop(JSOp op, jssrcno
MOZ_ASSERT(JSOp(*loopHead) == JSOP_LOOPHEAD);
MOZ_ASSERT(loopHead == ifne + GetJumpOffset(ifne));
jsbytecode* loopEntry = GetNextPc(loopHead);
bool canOsr = LoopEntryCanIonOsr(loopEntry);
bool osr = info().hasOsrAt(loopEntry);
if (osr) {
- MBasicBlock* preheader = newOsrPreheader(current, loopEntry);
+ MBasicBlock* preheader = newOsrPreheader(current, loopEntry, pc);
if (!preheader)
return ControlStatus_Error;
current->end(MGoto::New(alloc(), preheader));
if (!setCurrentAndSpecializePhis(preheader))
return ControlStatus_Error;
}
unsigned stackPhiCount = 0;
- MBasicBlock* header = newPendingLoopHeader(current, pc, osr, canOsr, stackPhiCount);
+ MBasicBlock* header = newPendingLoopHeader(current, loopEntry, osr, canOsr, stackPhiCount);
if (!header)
return ControlStatus_Error;
current->end(MGoto::New(alloc(), header));
jsbytecode* loophead = GetNextPc(pc);
jsbytecode* bodyStart = GetNextPc(loophead);
jsbytecode* bodyEnd = conditionpc;
jsbytecode* exitpc = GetNextPc(ifne);
@@ -3193,46 +3190,47 @@ IonBuilder::whileOrForInLoop(jssrcnote*
MOZ_ASSERT(JSOp(*GetNextPc(pc)) == JSOP_LOOPHEAD);
MOZ_ASSERT(GetNextPc(pc) == ifne + GetJumpOffset(ifne));
jsbytecode* loopEntry = pc + GetJumpOffset(pc);
bool canOsr = LoopEntryCanIonOsr(loopEntry);
bool osr = info().hasOsrAt(loopEntry);
if (osr) {
- MBasicBlock* preheader = newOsrPreheader(current, loopEntry);
+ MBasicBlock* preheader = newOsrPreheader(current, loopEntry, pc);
if (!preheader)
return ControlStatus_Error;
current->end(MGoto::New(alloc(), preheader));
if (!setCurrentAndSpecializePhis(preheader))
return ControlStatus_Error;
}
unsigned stackPhiCount;
if (SN_TYPE(sn) == SRC_FOR_OF)
stackPhiCount = 2;
else if (SN_TYPE(sn) == SRC_FOR_IN)
stackPhiCount = 1;
else
stackPhiCount = 0;
- MBasicBlock* header = newPendingLoopHeader(current, pc, osr, canOsr, stackPhiCount);
+ MBasicBlock* header = newPendingLoopHeader(current, loopEntry, osr, canOsr, stackPhiCount);
if (!header)
return ControlStatus_Error;
current->end(MGoto::New(alloc(), header));
// Skip past the JSOP_LOOPHEAD for the body start.
jsbytecode* loopHead = GetNextPc(pc);
jsbytecode* bodyStart = GetNextPc(loopHead);
jsbytecode* bodyEnd = pc + GetJumpOffset(pc);
jsbytecode* exitpc = GetNextPc(ifne);
+ jsbytecode* continuepc = pc;
if (!analyzeNewLoopTypes(header, bodyStart, exitpc))
return ControlStatus_Error;
if (!pushLoop(CFGState::WHILE_LOOP_COND, ifne, header, osr,
- loopHead, bodyEnd, bodyStart, bodyEnd, exitpc))
+ loopHead, bodyEnd, bodyStart, bodyEnd, exitpc, continuepc))
{
return ControlStatus_Error;
}
// Parse the condition first.
if (!setCurrentAndSpecializePhis(header))
return ControlStatus_Error;
if (!jsop_loophead(loopHead))
@@ -3294,26 +3292,26 @@ IonBuilder::forLoop(JSOp op, jssrcnote*
MOZ_ASSERT(JSOp(*bodyStart) == JSOP_LOOPHEAD);
MOZ_ASSERT(ifne + GetJumpOffset(ifne) == bodyStart);
bodyStart = GetNextPc(bodyStart);
bool osr = info().hasOsrAt(loopEntry);
bool canOsr = LoopEntryCanIonOsr(loopEntry);
if (osr) {
- MBasicBlock* preheader = newOsrPreheader(current, loopEntry);
+ MBasicBlock* preheader = newOsrPreheader(current, loopEntry, pc);
if (!preheader)
return ControlStatus_Error;
current->end(MGoto::New(alloc(), preheader));
if (!setCurrentAndSpecializePhis(preheader))
return ControlStatus_Error;
}
unsigned stackPhiCount = 0;
- MBasicBlock* header = newPendingLoopHeader(current, pc, osr, canOsr, stackPhiCount);
+ MBasicBlock* header = newPendingLoopHeader(current, loopEntry, osr, canOsr, stackPhiCount);
if (!header)
return ControlStatus_Error;
current->end(MGoto::New(alloc(), header));
// If there is no condition, we immediately parse the body. Otherwise, we
// parse the condition.
jsbytecode* stopAt;
CFGState::State initial;
@@ -7275,16 +7273,18 @@ IonBuilder::jsop_initelem_getter_setter(
return resumeAfter(init);
}
MBasicBlock*
IonBuilder::addBlock(MBasicBlock* block, uint32_t loopDepth)
{
if (!block)
return nullptr;
+ if (block->pc() && script()->hasScriptCounts())
+ block->setHitCount(script()->getHitCount(block->pc()));
graph().addBlock(block);
block->setLoopDepth(loopDepth);
return block;
}
MBasicBlock*
IonBuilder::newBlock(MBasicBlock* predecessor, jsbytecode* pc)
{
@@ -7311,42 +7311,47 @@ IonBuilder::newBlockPopN(MBasicBlock* pr
MBasicBlock*
IonBuilder::newBlockAfter(MBasicBlock* at, MBasicBlock* predecessor, jsbytecode* pc)
{
MBasicBlock* block = MBasicBlock::New(graph(), &analysis(), info(), predecessor,
bytecodeSite(pc), MBasicBlock::NORMAL);
if (!block)
return nullptr;
+ block->setHitCount(0); // osr block
graph().insertBlockAfter(at, block);
return block;
}
MBasicBlock*
IonBuilder::newBlock(MBasicBlock* predecessor, jsbytecode* pc, uint32_t loopDepth)
{
MBasicBlock* block = MBasicBlock::New(graph(), &analysis(), info(), predecessor,
bytecodeSite(pc), MBasicBlock::NORMAL);
return addBlock(block, loopDepth);
}
MBasicBlock*
-IonBuilder::newOsrPreheader(MBasicBlock* predecessor, jsbytecode* loopEntry)
+IonBuilder::newOsrPreheader(MBasicBlock* predecessor, jsbytecode* loopEntry, jsbytecode* beforeLoopEntry)
{
MOZ_ASSERT(LoopEntryCanIonOsr(loopEntry));
MOZ_ASSERT(loopEntry == info().osrPc());
// Create two blocks: one for the OSR entry with no predecessors, one for
// the preheader, which has the OSR entry block as a predecessor. The
// OSR block is always the second block (with id 1).
MBasicBlock* osrBlock = newBlockAfter(*graph().begin(), loopEntry);
MBasicBlock* preheader = newBlock(predecessor, loopEntry);
if (!osrBlock || !preheader)
return nullptr;
+ // Give the pre-header the same hit count as the code before the loop.
+ if (script()->hasScriptCounts())
+ preheader->setHitCount(script()->getHitCount(beforeLoopEntry));
+
MOsrEntry* entry = MOsrEntry::New(alloc());
osrBlock->add(entry);
// Initialize |scopeChain|.
{
uint32_t slot = info().scopeChainSlot();
MInstruction* scopev;
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -263,27 +263,28 @@ class IonBuilder
ControlStatus processTryEnd(CFGState& state);
ControlStatus processReturn(JSOp op);
ControlStatus processThrow();
ControlStatus processContinue(JSOp op);
ControlStatus processBreak(JSOp op, jssrcnote* sn);
ControlStatus maybeLoop(JSOp op, jssrcnote* sn);
bool pushLoop(CFGState::State state, jsbytecode* stopAt, MBasicBlock* entry, bool osr,
jsbytecode* loopHead, jsbytecode* initialPc,
- jsbytecode* bodyStart, jsbytecode* bodyEnd, jsbytecode* exitpc,
- jsbytecode* continuepc = nullptr);
+ jsbytecode* bodyStart, jsbytecode* bodyEnd,
+ jsbytecode* exitpc, jsbytecode* continuepc);
bool analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecode* end);
MBasicBlock* addBlock(MBasicBlock* block, uint32_t loopDepth);
MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc);
MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc, uint32_t loopDepth);
MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc, MResumePoint* priorResumePoint);
MBasicBlock* newBlockPopN(MBasicBlock* predecessor, jsbytecode* pc, uint32_t popped);
MBasicBlock* newBlockAfter(MBasicBlock* at, MBasicBlock* predecessor, jsbytecode* pc);
- MBasicBlock* newOsrPreheader(MBasicBlock* header, jsbytecode* loopEntry);
+ MBasicBlock* newOsrPreheader(MBasicBlock* header, jsbytecode* loopEntry,
+ jsbytecode* beforeLoopEntry);
MBasicBlock* newPendingLoopHeader(MBasicBlock* predecessor, jsbytecode* pc, bool osr, bool canOsr,
unsigned stackPhiCount);
MBasicBlock* newBlock(jsbytecode* pc) {
return newBlock(nullptr, pc);
}
MBasicBlock* newBlockAfter(MBasicBlock* at, jsbytecode* pc) {
return newBlockAfter(at, nullptr, pc);
}
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -112,16 +112,19 @@ enum BailoutKind
Bailout_Debugger,
// |this| used uninitialized in a derived constructor
Bailout_UninitializedThis,
// Derived constructors must return object or undefined
Bailout_BadDerivedConstructorReturn,
+ // We hit this code for the first time.
+ Bailout_FirstExecution,
+
// END Normal bailouts
// Bailouts caused by invalid assumptions based on Baseline code.
// Causes immediate invalidation.
// Like Bailout_Overflow, but causes immediate invalidation.
Bailout_OverflowInvalidate,
@@ -213,16 +216,18 @@ BailoutKindString(BailoutKind kind)
case Bailout_InitialState:
return "Bailout_InitialState";
case Bailout_Debugger:
return "Bailout_Debugger";
case Bailout_UninitializedThis:
return "Bailout_UninitializedThis";
case Bailout_BadDerivedConstructorReturn:
return "Bailout_BadDerivedConstructorReturn";
+ case Bailout_FirstExecution:
+ return "Bailout_FirstExecution";
// Bailouts caused by invalid assumptions.
case Bailout_OverflowInvalidate:
return "Bailout_OverflowInvalidate";
case Bailout_NonStringInputInvalidate:
return "Bailout_NonStringInputInvalidate";
case Bailout_DoubleOutput:
return "Bailout_DoubleOutput";
--- a/js/src/jit/JSONSpewer.cpp
+++ b/js/src/jit/JSONSpewer.cpp
@@ -254,16 +254,18 @@ JSONSpewer::spewMIR(MIRGraph* mir)
{
beginObjectProperty("mir");
beginListProperty("blocks");
for (MBasicBlockIterator block(mir->begin()); block != mir->end(); block++) {
beginObject();
integerProperty("number", block->id());
+ if (block->getHitState() == MBasicBlock::HitState::Count)
+ integerProperty("count", block->getHitCount());
beginListProperty("attributes");
if (block->isLoopBackedge())
stringValue("backedge");
if (block->isLoopHeader())
stringValue("loopheader");
if (block->isSplitEdge())
stringValue("splitedge");
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -93,16 +93,19 @@ JitOptions::JitOptions()
SET_DEFAULT(disableInlining, false);
// Toggles whether loop invariant code motion is globally disabled.
SET_DEFAULT(disableLicm, false);
// Toggles whether Loop Unrolling is globally disabled.
SET_DEFAULT(disableLoopUnrolling, true);
+ // Toggle whether Profile Guided Optimization is globally disabled.
+ SET_DEFAULT(disablePgo, true);
+
// Toggles whether instruction reordering is globally disabled.
SET_DEFAULT(disableInstructionReordering, false);
// Toggles whether Range Analysis is globally disabled.
SET_DEFAULT(disableRangeAnalysis, false);
// Toggle whether eager scalar replacement is globally disabled.
SET_DEFAULT(disableScalarReplacement, false);
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -50,16 +50,17 @@ struct JitOptions
bool disableAma;
bool disableEaa;
bool disableEagerSimdUnbox;
bool disableEdgeCaseAnalysis;
bool disableGvn;
bool disableInlining;
bool disableLicm;
bool disableLoopUnrolling;
+ bool disablePgo;
bool disableInstructionReordering;
bool disableRangeAnalysis;
bool disableScalarReplacement;
bool disableSharedStubs;
bool disableSincos;
bool disableSink;
bool eagerCompilation;
bool forceInlineCaches;
--- a/js/src/jit/JitSpewer.cpp
+++ b/js/src/jit/JitSpewer.cpp
@@ -83,17 +83,18 @@ class MOZ_RAII AutoLockIonSpewerOutput
explicit AutoLockIonSpewerOutput(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
~AutoLockIonSpewerOutput();
};
// IonSpewer singleton.
static IonSpewer ionspewer;
static bool LoggingChecked = false;
-static uint32_t LoggingBits = 0;
+static_assert(JitSpew_Terminator <= 64, "Increase the size of the LoggingBits global.");
+static uint64_t LoggingBits = 0;
static mozilla::Atomic<uint32_t, mozilla::Relaxed> filteredOutCompilations(0);
static const char * const ChannelNames[] =
{
#define JITSPEW_CHANNEL(name) #name,
JITSPEW_CHANNEL_LIST(JITSPEW_CHANNEL)
#undef JITSPEW_CHANNEL
};
@@ -395,16 +396,17 @@ jit::CheckLogging()
fflush(nullptr);
printf(
"\n"
"usage: IONFLAGS=option,option,option,... where options can be:\n"
"\n"
" aborts Compilation abort messages\n"
" scripts Compiled scripts\n"
" mir MIR information\n"
+ " prune Prune unused branches\n"
" escape Escape analysis\n"
" alias Alias analysis\n"
" gvn Global Value Numbering\n"
" licm Loop invariant code motion\n"
" sincos Replace sin/cos by sincos\n"
" sink Sink transformation\n"
" regalloc Register allocation\n"
" inline Inlining\n"
@@ -435,16 +437,18 @@ jit::CheckLogging()
" bl-all All baseline spew\n"
"\n"
);
exit(0);
/*NOTREACHED*/
}
if (ContainsFlag(env, "aborts"))
EnableChannel(JitSpew_IonAbort);
+ if (ContainsFlag(env, "prune"))
+ EnableChannel(JitSpew_Prune);
if (ContainsFlag(env, "escape"))
EnableChannel(JitSpew_Escape);
if (ContainsFlag(env, "alias"))
EnableChannel(JitSpew_Alias);
if (ContainsFlag(env, "scripts"))
EnableChannel(JitSpew_IonScripts);
if (ContainsFlag(env, "mir"))
EnableChannel(JitSpew_IonMIR);
@@ -484,17 +488,17 @@ jit::CheckLogging()
EnableIonDebugAsyncLogging();
if (ContainsFlag(env, "logs-sync"))
EnableIonDebugSyncLogging();
if (ContainsFlag(env, "profiling"))
EnableChannel(JitSpew_Profiling);
if (ContainsFlag(env, "trackopts"))
EnableChannel(JitSpew_OptimizationTracking);
if (ContainsFlag(env, "all"))
- LoggingBits = uint32_t(-1);
+ LoggingBits = uint64_t(-1);
if (ContainsFlag(env, "bl-aborts"))
EnableChannel(JitSpew_BaselineAbort);
if (ContainsFlag(env, "bl-scripts"))
EnableChannel(JitSpew_BaselineScripts);
if (ContainsFlag(env, "bl-op"))
EnableChannel(JitSpew_BaselineOp);
if (ContainsFlag(env, "bl-ic"))
@@ -620,27 +624,27 @@ jit::JitSpewHeader(JitSpewChannel channe
for (size_t i = ChannelIndentLevel[channel]; i != 0; i--)
out.put(" ");
}
bool
jit::JitSpewEnabled(JitSpewChannel channel)
{
MOZ_ASSERT(LoggingChecked);
- return (LoggingBits & (1 << uint32_t(channel))) && !filteredOutCompilations;
+ return (LoggingBits & (uint64_t(1) << uint32_t(channel))) && !filteredOutCompilations;
}
void
jit::EnableChannel(JitSpewChannel channel)
{
MOZ_ASSERT(LoggingChecked);
- LoggingBits |= (1 << uint32_t(channel));
+ LoggingBits |= uint64_t(1) << uint32_t(channel);
}
void
jit::DisableChannel(JitSpewChannel channel)
{
MOZ_ASSERT(LoggingChecked);
- LoggingBits &= ~(1 << uint32_t(channel));
+ LoggingBits &= ~(uint64_t(1) << uint32_t(channel));
}
#endif /* JS_JITSPEW */
--- a/js/src/jit/JitSpewer.h
+++ b/js/src/jit/JitSpewer.h
@@ -16,16 +16,18 @@
#include "jit/JSONSpewer.h"
#include "js/RootingAPI.h"
namespace js {
namespace jit {
// New channels may be added below.
#define JITSPEW_CHANNEL_LIST(_) \
+ /* Information during sinking */ \
+ _(Prune) \
/* Information during escape analysis */\
_(Escape) \
/* Information during alias analysis */ \
_(Alias) \
/* Information during GVN */ \
_(GVN) \
/* Information during sincos */ \
_(Sincos) \
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -597,27 +597,37 @@ MDefinition::replaceAllUsesWith(MDefinit
}
void
MDefinition::justReplaceAllUsesWith(MDefinition* dom)
{
MOZ_ASSERT(dom != nullptr);
MOZ_ASSERT(dom != this);
+ // Carry over the fact the value has uses which are no longer inspectable
+ // with the graph.
+ if (isUseRemoved())
+ dom->setUseRemovedUnchecked();
+
for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i)
i->setProducerUnchecked(dom);
dom->uses_.takeElements(uses_);
}
void
MDefinition::justReplaceAllUsesWithExcept(MDefinition* dom)
{
MOZ_ASSERT(dom != nullptr);
MOZ_ASSERT(dom != this);
+ // Carry over the fact the value has uses which are no longer inspectable
+ // with the graph.
+ if (isUseRemoved())
+ dom->setUseRemovedUnchecked();
+
// Move all uses to new dom. Save the use of the dominating instruction.
MUse *exceptUse = nullptr;
for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i) {
if (i->consumer() != dom) {
i->setProducerUnchecked(dom);
} else {
MOZ_ASSERT(!exceptUse);
exceptUse = *i;
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -389,26 +389,29 @@ MBasicBlock::MBasicBlock(MIRGraph& graph
: unreachable_(false),
graph_(graph),
info_(info),
predecessors_(graph.alloc()),
stackPosition_(info_.firstStackSlot()),
numDominated_(0),
pc_(site->pc()),
lir_(nullptr),
+ callerResumePoint_(nullptr),
entryResumePoint_(nullptr),
outerResumePoint_(nullptr),
successorWithPhis_(nullptr),
positionInPhiSuccessor_(0),
loopDepth_(0),
kind_(kind),
mark_(false),
immediatelyDominated_(graph.alloc()),
immediateDominator_(nullptr),
- trackedSite_(site)
+ trackedSite_(site),
+ hitCount_(0),
+ hitState_(HitState::NotDefined)
#if defined (JS_ION_PERF)
, lineno_(0u),
columnIndex_(0u)
#endif
{
}
bool
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -52,18 +52,16 @@ class MBasicBlock : public TempObject, p
bool inherit(TempAllocator& alloc, BytecodeAnalysis* analysis, MBasicBlock* pred,
uint32_t popped, unsigned stackPhiCount = 0);
bool inheritResumePoint(MBasicBlock* pred);
void assertUsesAreNotWithin(MUseIterator use, MUseIterator end);
// This block cannot be reached by any means.
bool unreachable_;
- MResumePoint* callerResumePoint_;
-
// Pushes a copy of a local variable or argument.
void pushVariable(uint32_t slot);
// Sets a variable slot to the top of the stack, correctly creating copies
// as needed.
void setVariable(uint32_t slot);
enum ReferencesType {
@@ -600,16 +598,42 @@ class MBasicBlock : public TempObject, p
}
void dumpStack(GenericPrinter& out);
void dumpStack();
void dump(GenericPrinter& out);
void dump();
+ // Hit count
+ enum class HitState {
+ // Not hit information is attached to this basic block.
+ NotDefined,
+
+ // The hit information is a raw counter. Note that due to inlining this
+ // counter is not guaranteed to be consistent over the graph.
+ Count,
+
+ // The hit information is a frequency, which is a form of normalized
+ // counter, where a hit-count can be compared against any previous block
+ // in the graph.
+ Frequency
+ };
+ HitState getHitState() const {
+ return hitState_;
+ }
+ void setHitCount(uint64_t count) {
+ hitCount_ = count;
+ hitState_ = HitState::Count;
+ }
+ uint64_t getHitCount() const {
+ MOZ_ASSERT(hitState_ == HitState::Count);
+ return hitCount_;
+ }
+
// Track bailouts by storing the current pc in MIR instruction added at
// this cycle. This is also used for tracking calls and optimizations when
// profiling.
void updateTrackedSite(BytecodeSite* site) {
MOZ_ASSERT(site->tree() == trackedSite_->tree());
trackedSite_ = site;
}
BytecodeSite* trackedSite() const {
@@ -631,16 +655,21 @@ class MBasicBlock : public TempObject, p
FixedList<MDefinition*> slots_;
uint32_t stackPosition_;
uint32_t id_;
uint32_t domIndex_; // Index in the dominator tree.
uint32_t numDominated_;
jsbytecode* pc_;
LBlock* lir_;
+ // Copy of a dominator block's outerResumePoint_ which holds the state of
+ // caller frame at the time of the call. If not null, this implies that this
+ // basic block corresponds to an inlined script.
+ MResumePoint* callerResumePoint_;
+
// Resume point holding baseline-like frame for the PC corresponding to the
// entry of this basic block.
MResumePoint* entryResumePoint_;
// Resume point holding baseline-like frame for the PC corresponding to the
// beginning of the call-site which is being inlined after this block.
MResumePoint* outerResumePoint_;
@@ -658,16 +687,21 @@ class MBasicBlock : public TempObject, p
// Utility mark for traversal algorithms.
bool mark_;
Vector<MBasicBlock*, 1, JitAllocPolicy> immediatelyDominated_;
MBasicBlock* immediateDominator_;
BytecodeSite* trackedSite_;
+ // Record the number of times a block got visited. Note, due to inlined
+ // scripts these numbers might not be continuous.
+ uint64_t hitCount_;
+ HitState hitState_;
+
#if defined(JS_ION_PERF) || defined(DEBUG)
unsigned lineno_;
unsigned columnIndex_;
public:
void setLineno(unsigned l) { lineno_ = l; }
unsigned lineno() const { return lineno_; }
void setColumnIndex(unsigned c) { columnIndex_ = c; }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -13,16 +13,17 @@
#include "jsfriendapi.h"
#include "jsgc.h"
#include "jsiter.h"
#include "jswatchpoint.h"
#include "jswrapper.h"
#include "gc/Marking.h"
#include "jit/JitCompartment.h"
+#include "jit/JitOptions.h"
#include "js/Date.h"
#include "js/Proxy.h"
#include "js/RootingAPI.h"
#include "proxy/DeadObjectProxy.h"
#include "vm/Debugger.h"
#include "vm/StopIterationObject.h"
#include "vm/WrapperObject.h"
@@ -1070,16 +1071,25 @@ JSCompartment::updateDebuggerObservesCov
// If code coverage is enabled by any other means, keep it.
if (collectCoverage())
return;
clearScriptCounts();
}
+bool
+JSCompartment::collectCoverage() const
+{
+ return !js_JitOptions.disablePgo ||
+ debuggerObservesCoverage() ||
+ runtimeFromAnyThread()->profilingScripts ||
+ runtimeFromAnyThread()->lcovOutput.isEnabled();
+}
+
void
JSCompartment::clearScriptCounts()
{
if (!scriptCountsMap)
return;
// Clear all hasScriptCounts_ flags of JSScript, in order to release all
// ScriptCounts entry of the current compartment.
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -675,21 +675,17 @@ struct JSCompartment
bool debuggerObservesCoverage() const {
static const unsigned Mask = DebuggerObservesCoverage;
return (debugModeBits & Mask) == Mask;
}
void updateDebuggerObservesCoverage();
// The code coverage can be enabled either for each compartment, with the
// Debugger API, or for the entire runtime.
- bool collectCoverage() const {
- return debuggerObservesCoverage() ||
- runtimeFromAnyThread()->profilingScripts ||
- runtimeFromAnyThread()->lcovOutput.isEnabled();
- }
+ bool collectCoverage() const;
void clearScriptCounts();
bool needsDelazificationForDebugger() const {
return debugModeBits & DebuggerNeedsDelazification;
}
/*
* Schedule the compartment to be delazified. Called from
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1410,25 +1410,56 @@ const js::PCCounts*
ScriptCounts::maybeGetPCCounts(size_t offset) const {
PCCounts searched = PCCounts(offset);
const PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
if (elem == pcCounts_.end() || elem->pcOffset() != offset)
return nullptr;
return elem;
}
+js::PCCounts*
+ScriptCounts::getImmediatePrecedingPCCounts(size_t offset)
+{
+ PCCounts searched = PCCounts(offset);
+ PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
+ if (elem == pcCounts_.end())
+ return &pcCounts_.back();
+ if (elem->pcOffset() == offset)
+ return elem;
+ if (elem != pcCounts_.begin())
+ return elem - 1;
+ return nullptr;
+}
+
const js::PCCounts*
ScriptCounts::maybeGetThrowCounts(size_t offset) const {
PCCounts searched = PCCounts(offset);
const PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
if (elem == throwCounts_.end() || elem->pcOffset() != offset)
return nullptr;
return elem;
}
+const js::PCCounts*
+ScriptCounts::getImmediatePrecedingThrowCounts(size_t offset) const
+{
+ PCCounts searched = PCCounts(offset);
+ const PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
+ if (elem == throwCounts_.end()) {
+ if (throwCounts_.begin() == throwCounts_.end())
+ return nullptr;
+ return &throwCounts_.back();
+ }
+ if (elem->pcOffset() == offset)
+ return elem;
+ if (elem != throwCounts_.begin())
+ return elem - 1;
+ return nullptr;
+}
+
js::PCCounts*
ScriptCounts::getThrowCounts(size_t offset) {
PCCounts searched = PCCounts(offset);
PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
if (elem == throwCounts_.end() || elem->pcOffset() != offset)
elem = throwCounts_.insert(elem, searched);
return elem;
}
@@ -1457,16 +1488,57 @@ JSScript::maybeGetThrowCounts(jsbytecode
}
js::PCCounts*
JSScript::getThrowCounts(jsbytecode* pc) {
MOZ_ASSERT(containsPC(pc));
return getScriptCounts().getThrowCounts(pcToOffset(pc));
}
+uint64_t
+JSScript::getHitCount(jsbytecode* pc)
+{
+ MOZ_ASSERT(containsPC(pc));
+ if (pc < main())
+ pc = main();
+
+ ScriptCounts& sc = getScriptCounts();
+ size_t targetOffset = pcToOffset(pc);
+ const js::PCCounts* baseCount = sc.getImmediatePrecedingPCCounts(targetOffset);
+ if (!baseCount)
+ return 0;
+ if (baseCount->pcOffset() == targetOffset)
+ return baseCount->numExec();
+ MOZ_ASSERT(baseCount->pcOffset() < targetOffset);
+ uint64_t count = baseCount->numExec();
+ do {
+ const js::PCCounts* throwCount = sc.getImmediatePrecedingThrowCounts(targetOffset);
+ if (!throwCount)
+ return count;
+ if (throwCount->pcOffset() <= baseCount->pcOffset())
+ return count;
+ count -= throwCount->numExec();
+ targetOffset = throwCount->pcOffset() - 1;
+ } while (true);
+}
+
+void
+JSScript::incHitCount(jsbytecode* pc)
+{
+ MOZ_ASSERT(containsPC(pc));
+ if (pc < main())
+ pc = main();
+
+ ScriptCounts& sc = getScriptCounts();
+ js::PCCounts* baseCount = sc.getImmediatePrecedingPCCounts(pcToOffset(pc));
+ if (!baseCount)
+ return;
+ baseCount->numExec()++;
+}
+
void
JSScript::addIonCounts(jit::IonScriptCounts* ionCounts)
{
ScriptCounts& sc = getScriptCounts();
if (sc.ionCounts_)
ionCounts->setPrevious(sc.ionCounts_);
sc.ionCounts_ = ionCounts;
}
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -463,20 +463,32 @@ class ScriptCounts
inline ScriptCounts& operator=(ScriptCounts&& src);
// Return the counter used to count the number of visits. Returns null if
// the element is not found.
PCCounts* maybeGetPCCounts(size_t offset);
const PCCounts* maybeGetPCCounts(size_t offset) const;
+ // PCCounts are stored at jump-target offsets. This function looks for the
+ // previous PCCount which is in the same basic block as the current offset.
+ PCCounts* getImmediatePrecedingPCCounts(size_t offset);
+
// Return the counter used to count the number of throws. Returns null if
// the element is not found.
const PCCounts* maybeGetThrowCounts(size_t offset) const;
+ // Throw counts are stored at the location of each throwing
+ // instruction. This function looks for the previous throw count.
+ //
+ // Note: if the offset of the returned count is higher than the offset of
+ // the immediate preceding PCCount, then this throw happened in the same
+ // basic block.
+ const PCCounts* getImmediatePrecedingThrowCounts(size_t offset) const;
+
// Return the counter used to count the number of throws. Allocate it if
// none exists yet. Returns null if the allocation failed.
PCCounts* getThrowCounts(size_t offset);
private:
friend class ::JSScript;
friend struct ScriptAndCounts;
@@ -1666,16 +1678,18 @@ class JSScript : public js::gc::TenuredC
void resetWarmUpResetCounter() { warmUpResetCount = 0; }
public:
bool initScriptCounts(JSContext* cx);
js::ScriptCounts& getScriptCounts();
js::PCCounts* maybeGetPCCounts(jsbytecode* pc);
const js::PCCounts* maybeGetThrowCounts(jsbytecode* pc);
js::PCCounts* getThrowCounts(jsbytecode* pc);
+ uint64_t getHitCount(jsbytecode* pc);
+ void incHitCount(jsbytecode* pc); // Used when we bailout out of Ion.
void addIonCounts(js::jit::IonScriptCounts* ionCounts);
js::jit::IonScriptCounts* getIonCounts();
void releaseScriptCounts(js::ScriptCounts* counts);
void destroyScriptCounts(js::FreeOp* fop);
// The entry should be removed after using this function.
void takeOverScriptCountsMapEntry(js::ScriptCounts* entryValue);
jsbytecode* main() {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -6240,16 +6240,25 @@ SetRuntimeOptions(JSRuntime* rt, const O
if (strcmp(str, "on") == 0)
jit::js_JitOptions.disableEdgeCaseAnalysis = false;
else if (strcmp(str, "off") == 0)
jit::js_JitOptions.disableEdgeCaseAnalysis = true;
else
return OptionFailure("ion-edgecase-analysis", str);
}
+ if (const char* str = op.getStringOption("ion-pgo")) {
+ if (strcmp(str, "on") == 0)
+ jit::js_JitOptions.disablePgo = false;
+ else if (strcmp(str, "off") == 0)
+ jit::js_JitOptions.disablePgo = true;
+ else
+ return OptionFailure("ion-pgo", str);
+ }
+
if (const char* str = op.getStringOption("ion-range-analysis")) {
if (strcmp(str, "on") == 0)
jit::js_JitOptions.disableRangeAnalysis = false;
else if (strcmp(str, "off") == 0)
jit::js_JitOptions.disableRangeAnalysis = true;
else
return OptionFailure("ion-range-analysis", str);
}
@@ -6594,16 +6603,18 @@ main(int argc, char** argv, char** envp)
|| !op.addStringOption('\0', "ion-gvn", "[mode]",
"Specify Ion global value numbering:\n"
" off: disable GVN\n"
" on: enable GVN (default)\n")
|| !op.addStringOption('\0', "ion-licm", "on/off",
"Loop invariant code motion (default: on, off to disable)")
|| !op.addStringOption('\0', "ion-edgecase-analysis", "on/off",
"Find edge cases where Ion can avoid bailouts (default: on, off to disable)")
+ || !op.addStringOption('\0', "ion-pgo", "on/off",
+ "Profile guided optimization (default: off, on to enable)")
|| !op.addStringOption('\0', "ion-range-analysis", "on/off",
"Range analysis (default: on, off to disable)")
#if defined(__APPLE__)
|| !op.addStringOption('\0', "ion-sincos", "on/off",
"Replace sin(x)/cos(x) to sincos(x) (default: on, off to disable)")
#else
|| !op.addStringOption('\0', "ion-sincos", "on/off",
"Replace sin(x)/cos(x) to sincos(x) (default: off, on to enable)")
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -677,16 +677,17 @@ TraceLoggerThreadState::init()
enabledTextIds[TraceLogger_IrregexpExecute] = true;
enabledTextIds[TraceLogger_Scripts] = true;
enabledTextIds[TraceLogger_Engine] = true;
}
if (ContainsFlag(env, "IonCompiler")) {
enabledTextIds[TraceLogger_IonCompilation] = true;
enabledTextIds[TraceLogger_IonLinking] = true;
+ enabledTextIds[TraceLogger_PruneUnusedBranches] = true;
enabledTextIds[TraceLogger_FoldTests] = true;
enabledTextIds[TraceLogger_SplitCriticalEdges] = true;
enabledTextIds[TraceLogger_RenumberBlocks] = true;
enabledTextIds[TraceLogger_ScalarReplacement] = true;
enabledTextIds[TraceLogger_DominatorTree] = true;
enabledTextIds[TraceLogger_PhiAnalysis] = true;
enabledTextIds[TraceLogger_MakeLoopsContiguous] = true;
enabledTextIds[TraceLogger_ApplyTypes] = true;
--- a/js/src/vm/TraceLoggingTypes.h
+++ b/js/src/vm/TraceLoggingTypes.h
@@ -32,16 +32,17 @@
_(ParserCompileFunction) \
_(ParserCompileLazy) \
_(ParserCompileScript) \
_(ParserCompileModule) \
_(Scripts) \
_(VM) \
\
/* Specific passes during ion compilation */ \
+ _(PruneUnusedBranches) \
_(FoldTests) \
_(SplitCriticalEdges) \
_(RenumberBlocks) \
_(ScalarReplacement) \
_(DominatorTree) \
_(PhiAnalysis) \
_(MakeLoopsContiguous) \
_(ApplyTypes) \
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1223694-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+ var sheet = document.createElement("style");
+ sheet.scoped = true;
+ document.documentElement.appendChild(sheet);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -123,12 +123,13 @@ load 1161320-1.html
pref(dom.animations-api.core.enabled,true) load 1161320-2.html
load 1161366-1.html
load 1163446-1.html
load 1164813-1.html
load 1167782-1.html
load 1186768-1.xhtml
load 1200568-1.html
load 1206105-1.html
+load 1223694-1.html
load border-image-visited-link.html
load font-face-truncated-src.html
load large_border_image_width.html
load long-url-list-stack-overflow.html
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -4121,16 +4121,21 @@ AncestorFilter::AssertHasAllAncestors(El
MOZ_ASSERT(mElements.Contains(cur));
cur = cur->GetParentElementCrossingShadowRoot();
}
}
void
TreeMatchContext::AssertHasAllStyleScopes(Element* aElement) const
{
+ if (aElement->IsInNativeAnonymousSubtree()) {
+ // Document style sheets are never applied to native anonymous content,
+ // so it's not possible for them to be in a <style scoped> scope.
+ return;
+ }
Element* cur = aElement->GetParentElementCrossingShadowRoot();
while (cur) {
if (cur->IsScopedStyleRoot()) {
MOZ_ASSERT(mStyleScopes.Contains(cur));
}
cur = cur->GetParentElementCrossingShadowRoot();
}
}
--- a/layout/style/test/test_property_syntax_errors.html
+++ b/layout/style/test/test_property_syntax_errors.html
@@ -14,16 +14,17 @@
<div id="content" style="display: none">
<div id="testnode"></div>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
+SimpleTest.requestLongerTimeout(2);
SimpleTest.waitForExplicitFinish();
function check_not_accepted(decl, property, info, badval)
{
decl.setProperty(property, badval, "");
is(decl.getPropertyValue(property), "",
"invalid value '" + badval + "' not accepted for '" + property +
--- a/mobile/android/base/toolbar/BrowserToolbar.java
+++ b/mobile/android/base/toolbar/BrowserToolbar.java
@@ -23,33 +23,37 @@ import org.mozilla.gecko.animation.ViewH
import org.mozilla.gecko.lwt.LightweightTheme;
import org.mozilla.gecko.lwt.LightweightThemeDrawable;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.MenuPopup;
import org.mozilla.gecko.tabs.TabHistoryController;
import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnStopListener;
import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnTitleChangeListener;
import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.UpdateFlags;
+import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.ColorUtils;
import org.mozilla.gecko.util.HardwareUtils;
+import org.mozilla.gecko.util.MenuUtils;
import org.mozilla.gecko.widget.themed.ThemedFrameLayout;
import org.mozilla.gecko.widget.themed.ThemedImageButton;
import org.mozilla.gecko.widget.themed.ThemedImageView;
import org.mozilla.gecko.widget.themed.ThemedRelativeLayout;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.ContextMenu;
import android.view.LayoutInflater;
+import android.view.MenuInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
@@ -114,16 +118,17 @@ public abstract class BrowserToolbar ext
private ToolbarProgressView progressBar;
protected final TabCounter tabsCounter;
protected final ThemedFrameLayout menuButton;
protected final ThemedImageView menuIcon;
private MenuPopup menuPopup;
protected final List<View> focusOrder;
+ private OnActivateListener activateListener;
private OnFocusChangeListener focusChangeListener;
private OnStartEditingListener startEditingListener;
private OnStopEditingListener stopEditingListener;
protected final BrowserApp activity;
protected boolean hasSoftMenuButton;
protected UIMode uiMode;
@@ -203,16 +208,59 @@ public abstract class BrowserToolbar ext
shadowPaint.setColor(shadowColor);
shadowPaint.setStrokeWidth(0.0f);
setUIMode(UIMode.DISPLAY);
prefs = new ToolbarPrefs();
urlDisplayLayout.setToolbarPrefs(prefs);
urlEditLayout.setToolbarPrefs(prefs);
+
+ setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ // NOTE: Use MenuUtils.safeSetVisible because some actions might
+ // be on the Page menu
+ MenuInflater inflater = activity.getMenuInflater();
+ inflater.inflate(R.menu.titlebar_contextmenu, menu);
+
+ String clipboard = Clipboard.getText();
+ if (TextUtils.isEmpty(clipboard)) {
+ menu.findItem(R.id.pasteandgo).setVisible(false);
+ menu.findItem(R.id.paste).setVisible(false);
+ }
+
+ Tab tab = Tabs.getInstance().getSelectedTab();
+ if (tab != null) {
+ String url = tab.getURL();
+ if (url == null) {
+ menu.findItem(R.id.copyurl).setVisible(false);
+ menu.findItem(R.id.add_to_launcher).setVisible(false);
+ }
+
+ MenuUtils.safeSetVisible(menu, R.id.subscribe, tab.hasFeeds());
+ MenuUtils.safeSetVisible(menu, R.id.add_search_engine, tab.hasOpenSearch());
+ } else {
+ // if there is no tab, remove anything tab dependent
+ menu.findItem(R.id.copyurl).setVisible(false);
+ menu.findItem(R.id.add_to_launcher).setVisible(false);
+ MenuUtils.safeSetVisible(menu, R.id.subscribe, false);
+ MenuUtils.safeSetVisible(menu, R.id.add_search_engine, false);
+ }
+ }
+ });
+
+ setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (activateListener != null) {
+ activateListener.onActivate();
+ }
+ }
+ });
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
prefs.open();
@@ -321,27 +369,16 @@ public abstract class BrowserToolbar ext
cancelEdit();
return true;
}
return urlDisplayLayout.dismissSiteIdentityPopup();
}
@Override
- public boolean onTouchEvent(MotionEvent event) {
- // If the motion event has occurred below the toolbar (due to the scroll
- // offset), let it pass through to the page.
- if (event != null && event.getY() > getHeight() + ViewHelper.getTranslationY(this)) {
- return false;
- }
-
- return super.onTouchEvent(event);
- }
-
- @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (h != oldh) {
// Post this to happen outside of onSizeChanged, as this may cause
// a layout change and relayouts within a layout change don't work.
post(new Runnable() {
@Override
@@ -630,18 +667,18 @@ public abstract class BrowserToolbar ext
urlEditLayout.onEditSuggestion(suggestion);
}
public void setTitle(CharSequence title) {
urlDisplayLayout.setTitle(title);
}
- public void setOnActivateListener(OnActivateListener listener) {
- urlDisplayLayout.setOnActivateListener(listener);
+ public void setOnActivateListener(final OnActivateListener listener) {
+ activateListener = listener;
}
public void setOnCommitListener(OnCommitListener listener) {
urlEditLayout.setOnCommitListener(listener);
}
public void setOnDismissListener(OnDismissListener listener) {
urlEditLayout.setOnDismissListener(listener);
--- a/mobile/android/base/toolbar/ToolbarDisplayLayout.java
+++ b/mobile/android/base/toolbar/ToolbarDisplayLayout.java
@@ -10,46 +10,41 @@ import java.util.EnumSet;
import java.util.List;
import org.mozilla.gecko.AboutPages;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.R;
import org.mozilla.gecko.ReaderModeUtils;
import org.mozilla.gecko.SiteIdentity;
+import org.mozilla.gecko.SiteIdentity.MixedMode;
import org.mozilla.gecko.SiteIdentity.SecurityMode;
-import org.mozilla.gecko.SiteIdentity.MixedMode;
import org.mozilla.gecko.SiteIdentity.TrackingMode;
import org.mozilla.gecko.Tab;
-import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.animation.PropertyAnimator;
import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.toolbar.BrowserToolbarTabletBase.ForwardButtonAnimation;
-import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.ColorUtils;
import org.mozilla.gecko.util.HardwareUtils;
-import org.mozilla.gecko.util.MenuUtils;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.widget.themed.ThemedLinearLayout;
import org.mozilla.gecko.widget.themed.ThemedTextView;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.SystemClock;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.ContextMenu;
import android.view.LayoutInflater;
-import android.view.MenuInflater;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.ImageButton;
/**
@@ -141,18 +136,16 @@ public class ToolbarDisplayLayout extend
private PropertyAnimator mForwardAnim;
private final ForegroundColorSpan mUrlColor;
private final ForegroundColorSpan mBlockedColor;
private final ForegroundColorSpan mDomainColor;
private final ForegroundColorSpan mPrivateDomainColor;
- private BrowserToolbar.OnActivateListener mActivateListener;
-
public ToolbarDisplayLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(HORIZONTAL);
mActivity = (BrowserApp) context;
LayoutInflater.from(context).inflate(R.layout.toolbar_display_layout, this);
@@ -234,66 +227,16 @@ public class ToolbarDisplayLayout extend
mTitleSlideRight = new TranslateAnimation(-slideWidth, 0, 0, 0);
mTitleSlideRight.setAnimationListener(this);
final int lockAnimDuration = 300;
mLockFadeIn.setDuration(lockAnimDuration);
mTitleSlideLeft.setDuration(lockAnimDuration);
mTitleSlideRight.setDuration(lockAnimDuration);
-
- // Context menu
- mTitle.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- // NOTE: Use MenuUtils.safeSetVisible because some actions might
- // be on the Page menu
- MenuInflater inflater = mActivity.getMenuInflater();
- inflater.inflate(R.menu.titlebar_contextmenu, menu);
-
- String clipboard = Clipboard.getText();
- if (TextUtils.isEmpty(clipboard)) {
- menu.findItem(R.id.pasteandgo).setVisible(false);
- menu.findItem(R.id.paste).setVisible(false);
- }
-
- Tab tab = Tabs.getInstance().getSelectedTab();
- if (tab != null) {
- String url = tab.getURL();
- if (url == null) {
- menu.findItem(R.id.copyurl).setVisible(false);
- menu.findItem(R.id.add_to_launcher).setVisible(false);
- }
-
- MenuUtils.safeSetVisible(menu, R.id.subscribe, tab.hasFeeds());
- MenuUtils.safeSetVisible(menu, R.id.add_search_engine, tab.hasOpenSearch());
- }
- else {
- // if there is no tab, remove anything tab dependent
- menu.findItem(R.id.copyurl).setVisible(false);
- menu.findItem(R.id.add_to_launcher).setVisible(false);
- MenuUtils.safeSetVisible(menu, R.id.subscribe, false);
- MenuUtils.safeSetVisible(menu, R.id.add_search_engine, false);
- }
- }
- });
-
- // Edit activation
- mTitle.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mActivateListener != null) {
- mActivateListener.onActivate();
- }
- }
- });
- }
-
- public void setOnActivateListener(BrowserToolbar.OnActivateListener listener) {
- mActivateListener = listener;
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
mIsAttached = false;
}
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -67,29 +67,29 @@ class CacheIndexEntryAutoManage
{
public:
CacheIndexEntryAutoManage(const SHA1Sum::Hash *aHash, CacheIndex *aIndex)
: mIndex(aIndex)
, mOldRecord(nullptr)
, mDoNotSearchInIndex(false)
, mDoNotSearchInUpdates(false)
{
- mIndex->AssertOwnsLock();
+ CacheIndex::sLock.AssertCurrentThreadOwns();
mHash = aHash;
const CacheIndexEntry *entry = FindEntry();
mIndex->mIndexStats.BeforeChange(entry);
if (entry && entry->IsInitialized() && !entry->IsRemoved()) {
mOldRecord = entry->mRec;
}
}
~CacheIndexEntryAutoManage()
{
- mIndex->AssertOwnsLock();
+ CacheIndex::sLock.AssertCurrentThreadOwns();
const CacheIndexEntry *entry = FindEntry();
mIndex->mIndexStats.AfterChange(entry);
if (!entry || !entry->IsInitialized() || entry->IsRemoved()) {
entry = nullptr;
}
if (entry && !mOldRecord) {
@@ -158,17 +158,17 @@ public:
NS_DECL_THREADSAFE_ISUPPORTS
explicit FileOpenHelper(CacheIndex* aIndex)
: mIndex(aIndex)
, mCanceled(false)
{}
void Cancel() {
- mIndex->AssertOwnsLock();
+ CacheIndex::sLock.AssertCurrentThreadOwns();
mCanceled = true;
}
private:
virtual ~FileOpenHelper() {}
NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) override;
NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
@@ -196,17 +196,17 @@ private:
RefPtr<CacheIndex> mIndex;
bool mCanceled;
};
NS_IMETHODIMP FileOpenHelper::OnFileOpened(CacheFileHandle *aHandle,
nsresult aResult)
{
- CacheIndexAutoLock lock(mIndex);
+ StaticMutexAutoLock lock(CacheIndex::sLock);
if (mCanceled) {
if (aHandle) {
CacheFileIOManager::DoomFile(aHandle, nullptr);
}
return NS_OK;
}
@@ -215,95 +215,74 @@ NS_IMETHODIMP FileOpenHelper::OnFileOpen
return NS_OK;
}
NS_IMPL_ISUPPORTS(FileOpenHelper, CacheFileIOListener);
CacheIndex * CacheIndex::gInstance = nullptr;
+StaticMutex CacheIndex::sLock;
NS_IMPL_ADDREF(CacheIndex)
NS_IMPL_RELEASE(CacheIndex)
NS_INTERFACE_MAP_BEGIN(CacheIndex)
NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
NS_INTERFACE_MAP_END_THREADSAFE
CacheIndex::CacheIndex()
- : mLock("CacheIndex.mLock")
- , mState(INITIAL)
+ : mState(INITIAL)
, mShuttingDown(false)
, mIndexNeedsUpdate(false)
, mRemovingAll(false)
, mIndexOnDiskIsValid(false)
, mDontMarkIndexClean(false)
, mIndexTimeStamp(0)
, mUpdateEventPending(false)
, mSkipEntries(0)
, mProcessEntries(0)
, mRWBuf(nullptr)
, mRWBufSize(0)
, mRWBufPos(0)
, mJournalReadSuccessfully(false)
{
+ sLock.AssertCurrentThreadOwns();
LOG(("CacheIndex::CacheIndex [this=%p]", this));
MOZ_COUNT_CTOR(CacheIndex);
MOZ_ASSERT(!gInstance, "multiple CacheIndex instances!");
}
CacheIndex::~CacheIndex()
{
LOG(("CacheIndex::~CacheIndex [this=%p]", this));
MOZ_COUNT_DTOR(CacheIndex);
ReleaseBuffer();
}
-void
-CacheIndex::Lock()
-{
- mLock.Lock();
-
- MOZ_ASSERT(!mIndexStats.StateLogged());
-}
-
-void
-CacheIndex::Unlock()
-{
- MOZ_ASSERT(!mIndexStats.StateLogged());
-
- mLock.Unlock();
-}
-
-inline void
-CacheIndex::AssertOwnsLock()
-{
- mLock.AssertCurrentThreadOwns();
-}
-
// static
nsresult
CacheIndex::Init(nsIFile *aCacheDirectory)
{
LOG(("CacheIndex::Init()"));
MOZ_ASSERT(NS_IsMainThread());
+ StaticMutexAutoLock lock(sLock);
+
if (gInstance) {
return NS_ERROR_ALREADY_INITIALIZED;
}
RefPtr<CacheIndex> idx = new CacheIndex();
- CacheIndexAutoLock lock(idx);
-
nsresult rv = idx->InitInternal(aCacheDirectory);
NS_ENSURE_SUCCESS(rv, rv);
idx.swap(gInstance);
return NS_OK;
}
nsresult
@@ -320,29 +299,29 @@ CacheIndex::InitInternal(nsIFile *aCache
return NS_OK;
}
// static
nsresult
CacheIndex::PreShutdown()
{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ StaticMutexAutoLock lock(sLock);
+
LOG(("CacheIndex::PreShutdown() [gInstance=%p]", gInstance));
- MOZ_ASSERT(NS_IsMainThread());
-
nsresult rv;
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- CacheIndexAutoLock lock(index);
-
LOG(("CacheIndex::PreShutdown() - [state=%d, indexOnDiskIsValid=%d, "
"dontMarkIndexClean=%d]", index->mState, index->mIndexOnDiskIsValid,
index->mDontMarkIndexClean));
LOG(("CacheIndex::PreShutdown() - Closing iterators."));
for (uint32_t i = 0; i < index->mIterators.Length(); ) {
rv = index->mIterators[i]->CloseInternal(NS_ERROR_FAILURE);
if (NS_FAILED(rv)) {
@@ -377,17 +356,17 @@ CacheIndex::PreShutdown()
}
return NS_OK;
}
void
CacheIndex::PreShutdownInternal()
{
- CacheIndexAutoLock lock(this);
+ StaticMutexAutoLock lock(sLock);
LOG(("CacheIndex::PreShutdownInternal() - [state=%d, indexOnDiskIsValid=%d, "
"dontMarkIndexClean=%d]", mState, mIndexOnDiskIsValid,
mDontMarkIndexClean));
MOZ_ASSERT(mShuttingDown);
if (mUpdateTimer) {
@@ -415,29 +394,29 @@ CacheIndex::PreShutdownInternal()
// We should end up in READY state
MOZ_ASSERT(mState == READY);
}
// static
nsresult
CacheIndex::Shutdown()
{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ StaticMutexAutoLock lock(sLock);
+
LOG(("CacheIndex::Shutdown() [gInstance=%p]", gInstance));
- MOZ_ASSERT(NS_IsMainThread());
-
RefPtr<CacheIndex> index;
index.swap(gInstance);
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- CacheIndexAutoLock lock(index);
-
bool sanitize = CacheObserver::ClearCacheOnShutdown();
LOG(("CacheIndex::Shutdown() - [state=%d, indexOnDiskIsValid=%d, "
"dontMarkIndexClean=%d, sanitize=%d]", index->mState,
index->mIndexOnDiskIsValid, index->mDontMarkIndexClean, sanitize));
MOZ_ASSERT(index->mShuttingDown);
@@ -481,26 +460,26 @@ CacheIndex::Shutdown()
}
// static
nsresult
CacheIndex::AddEntry(const SHA1Sum::Hash *aHash)
{
LOG(("CacheIndex::AddEntry() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash)));
+ MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
// Getters in CacheIndexStats assert when mStateLogged is true since the
// information is incomplete between calls to BeforeChange() and AfterChange()
// (i.e. while CacheIndexEntryAutoManage exists). We need to check whether
// non-fresh entries exists outside the scope of CacheIndexEntryAutoManage.
@@ -596,26 +575,26 @@ CacheIndex::AddEntry(const SHA1Sum::Hash
// static
nsresult
CacheIndex::EnsureEntryExists(const SHA1Sum::Hash *aHash)
{
LOG(("CacheIndex::EnsureEntryExists() [hash=%08x%08x%08x%08x%08x]",
LOGSHA1(aHash)));
+ MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
{
CacheIndexEntryAutoManage entryMng(aHash, index);
CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
@@ -707,26 +686,26 @@ CacheIndex::InitEntry(const SHA1Sum::Has
bool aAnonymous,
bool aInBrowser,
bool aPinned)
{
LOG(("CacheIndex::InitEntry() [hash=%08x%08x%08x%08x%08x, appId=%u, "
"anonymous=%d, inBrowser=%d, pinned=%d]", LOGSHA1(aHash), aAppId,
aAnonymous, aInBrowser, aPinned));
+ MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
{
CacheIndexEntryAutoManage entryMng(aHash, index);
CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
@@ -816,26 +795,26 @@ CacheIndex::InitEntry(const SHA1Sum::Has
// static
nsresult
CacheIndex::RemoveEntry(const SHA1Sum::Hash *aHash)
{
LOG(("CacheIndex::RemoveEntry() [hash=%08x%08x%08x%08x%08x]",
LOGSHA1(aHash)));
+ MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
{
CacheIndexEntryAutoManage entryMng(aHash, index);
CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
@@ -917,26 +896,26 @@ CacheIndex::UpdateEntry(const SHA1Sum::H
const uint32_t *aSize)
{
LOG(("CacheIndex::UpdateEntry() [hash=%08x%08x%08x%08x%08x, "
"frecency=%s, expirationTime=%s, size=%s]", LOGSHA1(aHash),
aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : "",
aSize ? nsPrintfCString("%u", *aSize).get() : ""));
+ MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
{
CacheIndexEntryAutoManage entryMng(aHash, index);
CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
@@ -1014,28 +993,28 @@ CacheIndex::UpdateEntry(const SHA1Sum::H
}
// static
nsresult
CacheIndex::RemoveAll()
{
LOG(("CacheIndex::RemoveAll()"));
- RefPtr<CacheIndex> index = gInstance;
-
- if (!index) {
- return NS_ERROR_NOT_INITIALIZED;
- }
-
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
nsCOMPtr<nsIFile> file;
{
- CacheIndexAutoLock lock(index);
+ StaticMutexAutoLock lock(sLock);
+
+ RefPtr<CacheIndex> index = gInstance;
+
+ if (!index) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
MOZ_ASSERT(!index->mRemovingAll);
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
AutoRestore<bool> saveRemovingAll(index->mRemovingAll);
@@ -1120,24 +1099,24 @@ CacheIndex::HasEntry(const nsACString &a
return HasEntry(hash, _retval, _pinned);
}
// static
nsresult
CacheIndex::HasEntry(const SHA1Sum::Hash &hash, EntryStatus *_retval, bool *_pinned)
{
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (_pinned) {
*_pinned = false;
}
@@ -1186,25 +1165,25 @@ CacheIndex::HasEntry(const SHA1Sum::Hash
}
// static
nsresult
CacheIndex::GetEntryForEviction(bool aIgnoreEmptyEntries, SHA1Sum::Hash *aHash, uint32_t *aCnt)
{
LOG(("CacheIndex::GetEntryForEviction()"));
+ MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index)
return NS_ERROR_NOT_INITIALIZED;
- MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
SHA1Sum::Hash hash;
bool foundEntry = false;
uint32_t i;
@@ -1262,69 +1241,69 @@ bool CacheIndex::IsForcedValidEntry(cons
// static
nsresult
CacheIndex::GetCacheSize(uint32_t *_retval)
{
LOG(("CacheIndex::GetCacheSize()"));
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index)
return NS_ERROR_NOT_INITIALIZED;
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
*_retval = index->mIndexStats.Size();
LOG(("CacheIndex::GetCacheSize() - returning %u", *_retval));
return NS_OK;
}
// static
nsresult
CacheIndex::GetEntryFileCount(uint32_t *_retval)
{
LOG(("CacheIndex::GetEntryFileCount()"));
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
*_retval = index->mIndexStats.ActiveEntriesCount();
LOG(("CacheIndex::GetEntryFileCount() - returning %u", *_retval));
return NS_OK;
}
// static
nsresult
CacheIndex::GetCacheStats(nsILoadContextInfo *aInfo, uint32_t *aSize, uint32_t *aCount)
{
LOG(("CacheIndex::GetCacheStats() [info=%p]", aInfo));
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!aInfo) {
return NS_ERROR_INVALID_ARG;
}
@@ -1344,24 +1323,24 @@ CacheIndex::GetCacheStats(nsILoadContext
}
// static
nsresult
CacheIndex::AsyncGetDiskConsumption(nsICacheStorageConsumptionObserver* aObserver)
{
LOG(("CacheIndex::AsyncGetDiskConsumption()"));
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<DiskConsumptionObserver> observer =
DiskConsumptionObserver::Init(aObserver);
NS_ENSURE_ARG(observer);
@@ -1383,24 +1362,24 @@ CacheIndex::AsyncGetDiskConsumption(nsIC
// static
nsresult
CacheIndex::GetIterator(nsILoadContextInfo *aInfo, bool aAddNew,
CacheIndexIterator **_retval)
{
LOG(("CacheIndex::GetIterator() [info=%p, addNew=%d]", aInfo, aAddNew));
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<CacheIndexIterator> iter;
if (aInfo) {
iter = new CacheIndexContextIterator(index, aAddNew, aInfo);
} else {
@@ -1416,24 +1395,24 @@ CacheIndex::GetIterator(nsILoadContextIn
}
// static
nsresult
CacheIndex::IsUpToDate(bool *_retval)
{
LOG(("CacheIndex::IsUpToDate()"));
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
- CacheIndexAutoLock lock(index);
-
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
*_retval = (index->mState == READY || index->mState == WRITING) &&
!index->mIndexNeedsUpdate && !index->mShuttingDown;
LOG(("CacheIndex::IsUpToDate() - returning %p", *_retval));
@@ -1508,17 +1487,17 @@ CacheIndex::HasEntryChanged(CacheIndexEn
return false;
}
void
CacheIndex::ProcessPendingOperations()
{
LOG(("CacheIndex::ProcessPendingOperations()"));
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
for (auto iter = mPendingUpdates.Iter(); !iter.Done(); iter.Next()) {
CacheIndexEntryUpdate* update = iter.Get();
LOG(("CacheIndex::ProcessPendingOperations() [hash=%08x%08x%08x%08x%08x]",
LOGSHA1(update->Hash())));
MOZ_ASSERT(update->IsFresh());
@@ -1591,17 +1570,17 @@ CacheIndex::WriteIndexToDiskIfNeeded()
void
CacheIndex::WriteIndexToDisk()
{
LOG(("CacheIndex::WriteIndexToDisk()"));
mIndexStats.Log();
nsresult rv;
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
MOZ_ASSERT(mState == READY);
MOZ_ASSERT(!mRWBuf);
MOZ_ASSERT(!mRWHash);
ChangeState(WRITING);
mProcessEntries = mIndexStats.ActiveEntriesCount();
@@ -1633,17 +1612,17 @@ CacheIndex::WriteIndexToDisk()
void
CacheIndex::WriteRecords()
{
LOG(("CacheIndex::WriteRecords()"));
nsresult rv;
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
MOZ_ASSERT(mState == WRITING);
int64_t fileOffset;
if (mSkipEntries) {
MOZ_ASSERT(mRWBufPos == 0);
fileOffset = sizeof(CacheIndexHeader);
fileOffset += sizeof(CacheIndexRecord) * mSkipEntries;
@@ -1723,17 +1702,17 @@ CacheIndex::WriteRecords()
void
CacheIndex::FinishWrite(bool aSucceeded)
{
LOG(("CacheIndex::FinishWrite() [succeeded=%d]", aSucceeded));
MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == WRITING);
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
mIndexHandle = nullptr;
mRWHash = nullptr;
ReleaseBuffer();
if (aSucceeded) {
// Opening of the file must not be in progress if writing succeeded.
MOZ_ASSERT(!mIndexFileOpener);
@@ -2002,17 +1981,17 @@ CacheIndex::WriteLogToDisk()
void
CacheIndex::ReadIndexFromDisk()
{
LOG(("CacheIndex::ReadIndexFromDisk()"));
nsresult rv;
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
MOZ_ASSERT(mState == INITIAL);
ChangeState(READING);
mIndexFileOpener = new FileOpenHelper(this);
rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(INDEX_NAME),
CacheFileIOManager::SPECIAL_FILE |
CacheFileIOManager::OPEN,
@@ -2049,17 +2028,17 @@ CacheIndex::ReadIndexFromDisk()
void
CacheIndex::StartReadingIndex()
{
LOG(("CacheIndex::StartReadingIndex()"));
nsresult rv;
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
MOZ_ASSERT(mIndexHandle);
MOZ_ASSERT(mState == READING);
MOZ_ASSERT(!mIndexOnDiskIsValid);
MOZ_ASSERT(!mDontMarkIndexClean);
MOZ_ASSERT(!mJournalReadSuccessfully);
MOZ_ASSERT(mIndexHandle->FileSize() >= 0);
@@ -2089,17 +2068,17 @@ CacheIndex::StartReadingIndex()
void
CacheIndex::ParseRecords()
{
LOG(("CacheIndex::ParseRecords()"));
nsresult rv;
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
uint32_t entryCnt = (mIndexHandle->FileSize() - sizeof(CacheIndexHeader) -
sizeof(CacheHash::Hash32_t)) / sizeof(CacheIndexRecord);
uint32_t pos = 0;
if (!mSkipEntries) {
CacheIndexHeader *hdr = reinterpret_cast<CacheIndexHeader *>(
moz_xmalloc(sizeof(CacheIndexHeader)));
@@ -2217,17 +2196,17 @@ CacheIndex::ParseRecords()
void
CacheIndex::StartReadingJournal()
{
LOG(("CacheIndex::StartReadingJournal()"));
nsresult rv;
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
MOZ_ASSERT(mJournalHandle);
MOZ_ASSERT(mIndexOnDiskIsValid);
MOZ_ASSERT(mTmpJournal.Count() == 0);
MOZ_ASSERT(mJournalHandle->FileSize() >= 0);
int64_t entriesSize = mJournalHandle->FileSize() -
sizeof(CacheHash::Hash32_t);
@@ -2254,17 +2233,17 @@ CacheIndex::StartReadingJournal()
void
CacheIndex::ParseJournal()
{
LOG(("CacheIndex::ParseJournal()"));
nsresult rv;
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
uint32_t entryCnt = (mJournalHandle->FileSize() -
sizeof(CacheHash::Hash32_t)) / sizeof(CacheIndexRecord);
uint32_t pos = 0;
while (pos + sizeof(CacheIndexRecord) <= mRWBufPos &&
mSkipEntries != entryCnt) {
@@ -2329,17 +2308,17 @@ CacheIndex::ParseJournal()
}
}
void
CacheIndex::MergeJournal()
{
LOG(("CacheIndex::MergeJournal()"));
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
for (auto iter = mTmpJournal.Iter(); !iter.Done(); iter.Next()) {
CacheIndexEntry* entry = iter.Get();
LOG(("CacheIndex::MergeJournal() [hash=%08x%08x%08x%08x%08x]",
LOGSHA1(entry->Hash())));
CacheIndexEntry* entry2 = mIndex.GetEntry(*entry->Hash());
@@ -2393,17 +2372,17 @@ CacheIndex::EnsureCorrectStats()
MOZ_ASSERT(debugStats == mIndexStats);
#endif
}
void
CacheIndex::FinishRead(bool aSucceeded)
{
LOG(("CacheIndex::FinishRead() [succeeded=%d]", aSucceeded));
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == READING);
MOZ_ASSERT(
// -> rebuild
(!aSucceeded && !mIndexOnDiskIsValid && !mJournalReadSuccessfully) ||
// -> update
(!aSucceeded && mIndexOnDiskIsValid && !mJournalReadSuccessfully) ||
@@ -2474,24 +2453,24 @@ CacheIndex::FinishRead(bool aSucceeded)
// static
void
CacheIndex::DelayedUpdate(nsITimer *aTimer, void *aClosure)
{
LOG(("CacheIndex::DelayedUpdate()"));
nsresult rv;
+ StaticMutexAutoLock lock(sLock);
+
RefPtr<CacheIndex> index = gInstance;
if (!index) {
return;
}
- CacheIndexAutoLock lock(index);
-
index->mUpdateTimer = nullptr;
if (!index->IsIndexUsable()) {
return;
}
if (index->mState == READY && index->mShuttingDown) {
return;
@@ -2608,40 +2587,40 @@ CacheIndex::InitEntryFromDiskData(CacheI
aEntry->SetFileSize(static_cast<uint32_t>(
std::min(static_cast<int64_t>(PR_UINT32_MAX),
(aFileSize + 0x3FF) >> 10)));
}
bool
CacheIndex::IsUpdatePending()
{
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
if (mUpdateTimer || mUpdateEventPending) {
return true;
}
return false;
}
void
CacheIndex::BuildIndex()
{
LOG(("CacheIndex::BuildIndex()"));
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
MOZ_ASSERT(mPendingUpdates.Count() == 0);
nsresult rv;
if (!mDirEnumerator) {
{
// Do not do IO under the lock.
- CacheIndexAutoUnlock unlock(this);
+ StaticMutexAutoUnlock unlock(sLock);
rv = SetupDirectoryEnumerator();
}
if (mState == SHUTDOWN) {
// The index was shut down while we released the lock. FinishUpdate() was
// already called from Shutdown(), so just simply return here.
return;
}
@@ -2656,17 +2635,17 @@ CacheIndex::BuildIndex()
LOG(("CacheIndex::BuildIndex() - Breaking loop for higher level events."));
mUpdateEventPending = true;
return;
}
nsCOMPtr<nsIFile> file;
{
// Do not do IO under the lock.
- CacheIndexAutoUnlock unlock(this);
+ StaticMutexAutoUnlock unlock(sLock);
rv = mDirEnumerator->GetNextFile(getter_AddRefs(file));
}
if (mState == SHUTDOWN) {
return;
}
if (!file) {
FinishUpdate(NS_SUCCEEDED(rv));
return;
@@ -2718,17 +2697,17 @@ CacheIndex::BuildIndex()
MOZ_ASSERT(!handle);
RefPtr<CacheFileMetadata> meta = new CacheFileMetadata();
int64_t size = 0;
{
// Do not do IO under the lock.
- CacheIndexAutoUnlock unlock(this);
+ StaticMutexAutoUnlock unlock(sLock);
rv = meta->SyncReadMetadata(file);
if (NS_SUCCEEDED(rv)) {
rv = file->GetFileSize(&size);
if (NS_FAILED(rv)) {
LOG(("CacheIndex::BuildIndex() - Cannot get filesize of file that was"
" successfully parsed. [name=%s]", leaf.get()));
}
@@ -2777,17 +2756,17 @@ CacheIndex::StartUpdatingIndexIfNeeded(b
return false;
}
void
CacheIndex::StartUpdatingIndex(bool aRebuild)
{
LOG(("CacheIndex::StartUpdatingIndex() [rebuild=%d]", aRebuild));
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
nsresult rv;
mIndexStats.Log();
ChangeState(aRebuild ? BUILDING : UPDATING);
mDontMarkIndexClean = false;
@@ -2833,26 +2812,26 @@ CacheIndex::StartUpdatingIndex(bool aReb
}
}
void
CacheIndex::UpdateIndex()
{
LOG(("CacheIndex::UpdateIndex()"));
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
MOZ_ASSERT(mPendingUpdates.Count() == 0);
nsresult rv;
if (!mDirEnumerator) {
{
// Do not do IO under the lock.
- CacheIndexAutoUnlock unlock(this);
+ StaticMutexAutoUnlock unlock(sLock);
rv = SetupDirectoryEnumerator();
}
if (mState == SHUTDOWN) {
// The index was shut down while we released the lock. FinishUpdate() was
// already called from Shutdown(), so just simply return here.
return;
}
@@ -2868,17 +2847,17 @@ CacheIndex::UpdateIndex()
"events."));
mUpdateEventPending = true;
return;
}
nsCOMPtr<nsIFile> file;
{
// Do not do IO under the lock.
- CacheIndexAutoUnlock unlock(this);
+ StaticMutexAutoUnlock unlock(sLock);
rv = mDirEnumerator->GetNextFile(getter_AddRefs(file));
}
if (mState == SHUTDOWN) {
return;
}
if (!file) {
FinishUpdate(NS_SUCCEEDED(rv));
return;
@@ -2929,17 +2908,17 @@ CacheIndex::UpdateIndex()
}
MOZ_ASSERT(!handle);
if (entry) {
PRTime lastModifiedTime;
{
// Do not do IO under the lock.
- CacheIndexAutoUnlock unlock(this);
+ StaticMutexAutoUnlock unlock(sLock);
rv = file->GetLastModifiedTime(&lastModifiedTime);
}
if (mState == SHUTDOWN) {
return;
}
if (NS_FAILED(rv)) {
LOG(("CacheIndex::UpdateIndex() - Cannot get lastModifiedTime. "
"[name=%s]", leaf.get()));
@@ -2958,17 +2937,17 @@ CacheIndex::UpdateIndex()
}
}
RefPtr<CacheFileMetadata> meta = new CacheFileMetadata();
int64_t size = 0;
{
// Do not do IO under the lock.
- CacheIndexAutoUnlock unlock(this);
+ StaticMutexAutoUnlock unlock(sLock);
rv = meta->SyncReadMetadata(file);
if (NS_SUCCEEDED(rv)) {
rv = file->GetFileSize(&size);
if (NS_FAILED(rv)) {
LOG(("CacheIndex::UpdateIndex() - Cannot get filesize of file that "
"was successfully parsed. [name=%s]", leaf.get()));
}
@@ -3009,17 +2988,17 @@ CacheIndex::UpdateIndex()
void
CacheIndex::FinishUpdate(bool aSucceeded)
{
LOG(("CacheIndex::FinishUpdate() [succeeded=%d]", aSucceeded));
MOZ_ASSERT(mState == UPDATING || mState == BUILDING ||
(!aSucceeded && mState == SHUTDOWN));
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
if (mDirEnumerator) {
if (NS_IsMainThread()) {
LOG(("CacheIndex::FinishUpdate() - posting of PreShutdownInternal failed?"
" Cannot safely release mDirEnumerator, leaking it!"));
NS_WARNING(("CacheIndex::FinishUpdate() - Leaking mDirEnumerator!"));
// This can happen only in case dispatching event to IO thread failed in
// CacheIndex::PreShutdown().
@@ -3193,61 +3172,61 @@ CacheIndex::RemoveRecordFromFrecencyArra
DebugOnly<bool> removed;
removed = mFrecencyArray.RemoveElement(aRecord);
MOZ_ASSERT(removed);
}
void
CacheIndex::AddRecordToIterators(CacheIndexRecord *aRecord)
{
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
for (uint32_t i = 0; i < mIterators.Length(); ++i) {
// Add a new record only when iterator is supposed to be updated.
if (mIterators[i]->ShouldBeNewAdded()) {
mIterators[i]->AddRecord(aRecord);
}
}
}
void
CacheIndex::RemoveRecordFromIterators(CacheIndexRecord *aRecord)
{
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
for (uint32_t i = 0; i < mIterators.Length(); ++i) {
// Remove the record from iterator always, it makes no sence to return
// non-existing entries. Also the pointer to the record is no longer valid
// once the entry is removed from index.
mIterators[i]->RemoveRecord(aRecord);
}
}
void
CacheIndex::ReplaceRecordInIterators(CacheIndexRecord *aOldRecord,
CacheIndexRecord *aNewRecord)
{
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
for (uint32_t i = 0; i < mIterators.Length(); ++i) {
// We have to replace the record always since the pointer is no longer
// valid after this point. NOTE: Replacing the record doesn't mean that
// a new entry was added, it just means that the data in the entry was
// changed (e.g. a file size) and we had to track this change in
// mPendingUpdates since mIndex was read-only.
mIterators[i]->ReplaceRecord(aOldRecord, aNewRecord);
}
}
nsresult
CacheIndex::Run()
{
LOG(("CacheIndex::Run()"));
- CacheIndexAutoLock lock(this);
+ StaticMutexAutoLock lock(sLock);
if (!IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (mState == READY && mShuttingDown) {
return NS_OK;
}
@@ -3272,17 +3251,17 @@ nsresult
CacheIndex::OnFileOpenedInternal(FileOpenHelper *aOpener,
CacheFileHandle *aHandle, nsresult aResult)
{
LOG(("CacheIndex::OnFileOpenedInternal() [opener=%p, handle=%p, "
"result=0x%08x]", aOpener, aHandle, aResult));
nsresult rv;
- AssertOwnsLock();
+ sLock.AssertCurrentThreadOwns();
if (!IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (mState == READY && mShuttingDown) {
return NS_OK;
}
@@ -3382,17 +3361,17 @@ nsresult
CacheIndex::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
nsresult aResult)
{
LOG(("CacheIndex::OnDataWritten() [handle=%p, result=0x%08x]", aHandle,
aResult));
nsresult rv;
- CacheIndexAutoLock lock(this);
+ StaticMutexAutoLock lock(sLock);
if (!IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (mState == READY && mShuttingDown) {
return NS_OK;
}
@@ -3432,17 +3411,17 @@ CacheIndex::OnDataWritten(CacheFileHandl
}
nsresult
CacheIndex::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
{
LOG(("CacheIndex::OnDataRead() [handle=%p, result=0x%08x]", aHandle,
aResult));
- CacheIndexAutoLock lock(this);
+ StaticMutexAutoLock lock(sLock);
if (!IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
switch (mState) {
case READING:
MOZ_ASSERT(mIndexHandle == aHandle || mJournalHandle == aHandle);
@@ -3481,17 +3460,17 @@ CacheIndex::OnEOFSet(CacheFileHandle *aH
}
nsresult
CacheIndex::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
{
LOG(("CacheIndex::OnFileRenamed() [handle=%p, result=0x%08x]", aHandle,
aResult));
- CacheIndexAutoLock lock(this);
+ StaticMutexAutoLock lock(sLock);
if (!IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (mState == READY && mShuttingDown) {
return NS_OK;
}
@@ -3536,17 +3515,17 @@ CacheIndex::OnFileRenamed(CacheFileHandl
return NS_OK;
}
// Memory reporting
size_t
CacheIndex::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const
{
- CacheIndexAutoLock lock(const_cast<CacheIndex*>(this));
+ sLock.AssertCurrentThreadOwns();
size_t n = 0;
nsCOMPtr<nsISizeOf> sizeOf;
// mIndexHandle and mJournalHandle are reported via SizeOfHandlesRunnable
// in CacheFileIOManager::SizeOfExcludingThisInternal as part of special
// handles array.
@@ -3573,26 +3552,30 @@ CacheIndex::SizeOfExcludingThisInternal(
return n;
}
// static
size_t
CacheIndex::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
+ sLock.AssertCurrentThreadOwns();
+
if (!gInstance)
return 0;
return gInstance->SizeOfExcludingThisInternal(mallocSizeOf);
}
// static
size_t
CacheIndex::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
+ StaticMutexAutoLock lock(sLock);
+
return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
}
namespace {
class HashComparator
{
public:
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -11,17 +11,17 @@
#include "CacheHashUtils.h"
#include "nsICacheStorageService.h"
#include "nsICacheEntry.h"
#include "nsILoadContextInfo.h"
#include "nsTHashtable.h"
#include "nsThreadUtils.h"
#include "nsWeakReference.h"
#include "mozilla/SHA1.h"
-#include "mozilla/Mutex.h"
+#include "mozilla/StaticMutex.h"
#include "mozilla/Endian.h"
#include "mozilla/TimeStamp.h"
class nsIFile;
class nsIDirectoryEnumerator;
class nsITimer;
@@ -691,37 +691,31 @@ public:
static nsresult IsUpToDate(bool *_retval);
// Memory reporting
static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
private:
friend class CacheIndexEntryAutoManage;
- friend class CacheIndexAutoLock;
- friend class CacheIndexAutoUnlock;
friend class FileOpenHelper;
friend class CacheIndexIterator;
virtual ~CacheIndex();
NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) override;
nsresult OnFileOpenedInternal(FileOpenHelper *aOpener,
CacheFileHandle *aHandle, nsresult aResult);
NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
nsresult aResult) override;
NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) override;
NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override;
NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override;
NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override;
- void Lock();
- void Unlock();
- void AssertOwnsLock();
-
nsresult InitInternal(nsIFile *aCacheDirectory);
void PreShutdownInternal();
// This method returns false when index is not initialized or is shut down.
bool IsIndexUsable();
// This method checks whether the entry has the same values of appId,
// isAnonymous and isInBrowser. We don't expect to find a collision since
@@ -926,20 +920,20 @@ private:
CacheIndexRecord *aNewRecord);
// Memory reporting (private part)
size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
void ReportHashStats();
static CacheIndex *gInstance;
+ static StaticMutex sLock;
nsCOMPtr<nsIFile> mCacheDirectory;
- mozilla::Mutex mLock;
EState mState;
// Timestamp of time when the index was initialized. We use it to delay
// initial update or build of index.
TimeStamp mStartTime;
// Set to true in PreShutdown(), it is checked on variaous places to prevent
// starting any process (write, update, etc.) during shutdown.
bool mShuttingDown;
// When set to true, update process should start as soon as possible. This
@@ -1084,76 +1078,12 @@ private:
nsWeakPtr mObserver;
int64_t mSize;
};
// List of async observers that want to get disk consumption information
nsTArray<RefPtr<DiskConsumptionObserver> > mDiskConsumptionObservers;
};
-class CacheIndexAutoLock {
-public:
- explicit CacheIndexAutoLock(CacheIndex *aIndex)
- : mIndex(aIndex)
- , mLocked(true)
- {
- mIndex->Lock();
- }
- ~CacheIndexAutoLock()
- {
- if (mLocked) {
- mIndex->Unlock();
- }
- }
- void Lock()
- {
- MOZ_ASSERT(!mLocked);
- mIndex->Lock();
- mLocked = true;
- }
- void Unlock()
- {
- MOZ_ASSERT(mLocked);
- mIndex->Unlock();
- mLocked = false;
- }
-
-private:
- RefPtr<CacheIndex> mIndex;
- bool mLocked;
-};
-
-class CacheIndexAutoUnlock {
-public:
- explicit CacheIndexAutoUnlock(CacheIndex *aIndex)
- : mIndex(aIndex)
- , mLocked(false)
- {
- mIndex->Unlock();
- }
- ~CacheIndexAutoUnlock()
- {
- if (!mLocked) {
- mIndex->Lock();
- }
- }
- void Lock()
- {
- MOZ_ASSERT(!mLocked);
- mIndex->Lock();
- mLocked = true;
- }
- void Unlock()
- {
- MOZ_ASSERT(mLocked);
- mIndex->Unlock();
- mLocked = false;
- }
-
-private:
- RefPtr<CacheIndex> mIndex;
- bool mLocked;
-};
-
} // namespace net
} // namespace mozilla
#endif
--- a/netwerk/cache2/CacheIndexIterator.cpp
+++ b/netwerk/cache2/CacheIndexIterator.cpp
@@ -27,17 +27,17 @@ CacheIndexIterator::~CacheIndexIterator(
Close();
}
nsresult
CacheIndexIterator::GetNextHash(SHA1Sum::Hash *aHash)
{
LOG(("CacheIndexIterator::GetNextHash() [this=%p]", this));
- CacheIndexAutoLock lock(mIndex);
+ StaticMutexAutoLock lock(CacheIndex::sLock);
if (NS_FAILED(mStatus)) {
return mStatus;
}
if (!mRecords.Length()) {
CloseInternal(NS_ERROR_NOT_AVAILABLE);
return mStatus;
@@ -49,17 +49,17 @@ CacheIndexIterator::GetNextHash(SHA1Sum:
return NS_OK;
}
nsresult
CacheIndexIterator::Close()
{
LOG(("CacheIndexIterator::Close() [this=%p]", this));
- CacheIndexAutoLock lock(mIndex);
+ StaticMutexAutoLock lock(CacheIndex::sLock);
return CloseInternal(NS_ERROR_NOT_AVAILABLE);
}
nsresult
CacheIndexIterator::CloseInternal(nsresult aStatus)
{
LOG(("CacheIndexIterator::CloseInternal() [this=%p, status=0x%08x]", this,
--- a/testing/marionette/client/docs/basics.rst
+++ b/testing/marionette/client/docs/basics.rst
@@ -1,9 +1,9 @@
-.. py:currentmodule:: marionette
+.. py:currentmodule:: marionette_driver.marionette
Marionette Python Client
========================
The Marionette python client library allows you to remotely control a
Gecko-based browser or device which is running a Marionette_
server. This includes desktop Firefox and FirefoxOS (support for
Firefox for Android is planned, but not yet fully implemented).
--- a/testing/marionette/client/docs/reference.rst
+++ b/testing/marionette/client/docs/reference.rst
@@ -34,8 +34,13 @@ Wait
:special-members:
.. autoattribute marionette_driver.wait.DEFAULT_TIMEOUT
.. autoattribute marionette_driver.wait.DEFAULT_INTERVAL
Built-in Conditions
^^^^^^^^^^^^^^^^^^^
.. automodule:: marionette_driver.expected
:members:
+
+Addons
+------
+.. autoclass:: marionette_driver.addons.Addons
+ :members:
new file mode 100644
index 0000000000000000000000000000000000000000..c8877b55dec371fa6417fc626ef1a6f3113cc2bc
GIT binary patch
literal 1552
zc$^FHW@Zs#U|`^2xLcX!&XKXBU5t@|;S&(^Fvu_@<>!|amlP!y=w%g$hHx@4I|ev~
zfpBRBHv=Qf3uXoeFtN7(bk<=50oU*Uy1c{uJeFQcbqa5EQfzhD{W56U@r_Mu3@@!v
z<o)^ml;)SM@4Q}|n_Dcg_kj0%p?Q|>-Zu)oD;`)ZNYONB-^7&Y;V;nT%v65;yzqYO
z_q=60Kb{MVNO-N&>f*ge^UliX*5iC^(~e)helD+ju_uR4M(m^)vvjUJda*>mHR;Wq
z<xg3vpYmig?GSu?N|?j#LU)h$+IIi#k9J&dUD@rcty2;j$>&+%CieU?*X$ndcc%jn
zM{qb^+O=wz%o+<mkEBIUHqLt-voK_;_F9dvXTD8jpJnq<?b6m~nyRr6f)sYCzmJr?
zWH5XE>1R(;{7-$}x3ABu>gUUso43^P{MWi=y!~wFzZEe%?^W2()VrU_xBnA=05EL1
z7(hTQBFmit39tjhHaVjxKQ~n$9IR_ZoWis)gEasiIUob#4KJhw0(lugEPyZ|BQ+-{
z9~eKmaFak7%_MDaouGnVjt~4?g{7VReP2pHedi3@5ZV!T%H!<Gli`;F1H8_k_xD`o
z6WX=)nHIAaTQ_s4Mr(SAlclQUO4pTLUILXXiY9PvKeFgb(E_dsA`A$>1&3$3Prjz3
zFArpcFhAUBdbx>tnQ5uTC19870bQks>9XYfypq(s5{2A6-6EjU#G;a%)Z$_VsOPv4
zL68Ov0{z15Lsh`EX#mv74YVgS52zz2N3SR)4Qz*ls8bjSquG%=`LrK6dH&?meltO6
zd86;*E4o4oVmbk;3AvLpH+)duw0z6c*M&ZDtecA7&9N@F4LtQV(DM`LHO|H1AC=BN
zGVNYeR9)>~<GMTaPu~r(DRO-6Ukav5-M(<NXy=khzt1)szv>mL_x_oYQ7-7dq`GM%
zL+P{7XWkwsotte=uhU#{u2%4OVat^5545zSz8G@U8&}61tzO-IFO=i$1n-#|>yGlt
zRE3+bWC`t<r8zf3zo=Vi&TXE>T8q7YIro&*hfZ7C9Uy(r-JzH@$GWtdLG$eDjhpyt
zH2Mo{m(Hu^kP$4oKK)IPYFBhUi;Kbw#f7skWaY_OhlyE8oqkuMY|`-6pmNi)d*K>(
ztn-?R8x~#smM5}hPvi5q%CD!}MOPS{op|xNuz7=duD0vZ<toqRQ!X5<U2OQt`N6aA
z9f=p2SGCl={9ZkG`Siw_na_3X`KtK+4o&F%$0>TEUe7T0!xg5}^&8$<h~4XPY@fEB
z=dB!*f#=2fiK<`svvluc4De=Tl4HhIz({}-5Rl2Rq!GkIEpAvL#SKQD0GWs@TS-7!
z0t{~*V}J(3GZ`x+YhX1DR~AEd-du!XNEr^Fskm|nvZ)qK448R^6_Qu*I2AK1ARD>>
cXedg?V1;B13<Fu&K$=*Aa5hk9JPU{i03S06YXATM
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_addons.py
@@ -0,0 +1,50 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+
+from marionette import MarionetteTestCase
+from marionette_driver.addons import Addons, AddonInstallException
+
+here = os.path.abspath(os.path.dirname(__file__))
+
+
+class TestAddons(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.addons = Addons(self.marionette)
+ self.addon_path = os.path.join(here, 'mn-restartless.xpi')
+
+
+ @property
+ def all_addon_ids(self):
+ with self.marionette.using_context('chrome'):
+ addons = self.marionette.execute_async_script("""
+ Components.utils.import("resource://gre/modules/AddonManager.jsm");
+ AddonManager.getAllAddons(function(addons){
+ let ids = addons.map(function(x) {
+ return x.id;
+ });
+ marionetteScriptFinished(ids);
+ });
+ """)
+
+ return addons
+
+ def test_install_and_remove_unsigned_addon(self):
+ self.addons.signature_required = False
+
+ addon_id = self.addons.install(self.addon_path)
+ self.assertIn(addon_id, self.all_addon_ids)
+
+ self.addons.uninstall(addon_id)
+ self.assertNotIn(addon_id, self.all_addon_ids)
+
+ self.addons.signature_required = True
+
+ def test_install_unsigned_addon_with_signature_required(self):
+ self.signature_required = True
+
+ with self.assertRaises(AddonInstallException):
+ self.addons.install(self.addon_path)
--- a/testing/marionette/client/marionette/tests/unit/test_chrome_element_css.py
+++ b/testing/marionette/client/marionette/tests/unit/test_chrome_element_css.py
@@ -5,17 +5,17 @@
from marionette import MarionetteTestCase
class TestChromeElementCSS(MarionetteTestCase):
def test_we_can_get_css_value_on_chrome_element(self):
self.marionette.navigate("about:blank")
with self.marionette.using_context("chrome"):
- element = self.marionette.find_element("id", "page-proxy-favicon")
+ element = self.marionette.find_element("id", "identity-icon")
favicon_image = element.value_of_css_property("list-style-image")
- self.assertIn("identity-not-secure.svg", favicon_image)
+ self.assertIn("identity-icon.svg", favicon_image)
element = self.marionette.find_element("id", "identity-box")
background_colour = element.value_of_css_property("background-color")
self.assertEqual("transparent", background_colour)
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini
@@ -157,8 +157,10 @@ skip-if = os == "win" # http://bugs.pyth
[test_execute_sandboxes.py]
[test_using_permissions.py]
[test_using_prefs.py]
[test_shadow_dom.py]
[test_chrome.py]
b2g = false
+
+[test_addons.py]
--- a/testing/marionette/driver/marionette_driver/__init__.py
+++ b/testing/marionette/driver/marionette_driver/__init__.py
@@ -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/.
__version__ = '1.1.0'
from marionette_driver import (
+ addons,
by,
date_time_value,
decorators,
errors,
expected,
geckoinstance,
gestures,
keys,
new file mode 100644
--- /dev/null
+++ b/testing/marionette/driver/marionette_driver/addons.py
@@ -0,0 +1,120 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from .errors import MarionetteException
+
+__all__ = ['Addons', 'AddonInstallException']
+
+
+# From https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager/AddonManager#AddonInstall_errors
+ADDON_INSTALL_ERRORS = {
+ -1: "ERROR_NETWORK_FAILURE: A network error occured.",
+ -2: "ERROR_INCORECT_HASH: The downloaded file did not match the expected hash.",
+ -3: "ERROR_CORRUPT_FILE: The file appears to be corrupt.",
+ -4: "ERROR_FILE_ACCESS: There was an error accessing the filesystem.",
+ -5: "ERROR_SIGNEDSTATE_REQUIRED: The addon must be signed and isn't.",
+}
+
+
+class AddonInstallException(MarionetteException):
+ pass
+
+
+class Addons(object):
+ """
+ An API for installing and inspecting addons during Gecko runtime. This
+ is a partially implemented wrapper around Gecko's `AddonManager API`_.
+
+ For example::
+
+ from marionette_driver.addons import Addons
+ addons = Addons(marionette)
+ addons.install('path/to/extension.xpi')
+
+ .. _AddonManager API: https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager
+ """
+
+ def __init__(self, marionette):
+ self._mn = marionette
+ self._signature_required = True
+
+ def on_restart():
+ self.signature_required = self._signature_required
+ self._mn.restart_handlers.append(on_restart)
+
+ @property
+ def signature_required(self):
+ """
+ Whether or not addons must be signed.
+ """
+ return self._signature_required
+
+ @signature_required.setter
+ def signature_required(self, val):
+ self._mn.set_pref('xpinstall.signatures.required', val)
+ self._signature_required = val
+
+ def install(self, path):
+ """Install an addon.
+
+ If the addon is restartless, it can be used right away. Otherwise
+ a restart using :func:`~marionette_driver.marionette.Marionette.restart`
+ will be needed.
+
+ :param path: A file path to the extension to be installed.
+ :returns: The addon ID string of the newly installed addon.
+ :raises: :exc:`AddonInstallException`
+ """
+ with self._mn.using_context('chrome'):
+ addon_id, status = self._mn.execute_async_script("""
+ let FileUtils = Components.utils.import("resource://gre/modules/FileUtils.jsm").FileUtils;
+ Components.utils.import("resource://gre/modules/AddonManager.jsm");
+ let listener = {
+ onInstallEnded: function(install, addon) {
+ marionetteScriptFinished([addon.id, 0]);
+ },
+
+ onInstallFailed: function(install) {
+ marionetteScriptFinished([null, install.error]);
+ }
+ }
+
+ let file = new FileUtils.File(arguments[0]);
+ AddonManager.getInstallForFile(file, function(aInstall) {
+ if (aInstall.error != 0) {
+ marionetteScriptFinished([null, aInstall.error]);
+ }
+ aInstall.addListener(listener);
+ aInstall.install();
+ });
+ """, script_args=[path], debug_script=True)
+
+ if status:
+ if status in ADDON_INSTALL_ERRORS:
+ raise AddonInstallException(ADDON_INSTALL_ERRORS[status])
+ raise AddonInstallException("Addon failed to install with return code: %d" % status)
+ return addon_id
+
+ def uninstall(self, addon_id):
+ """Uninstall an addon.
+
+ If the addon is restartless, it will be uninstalled right away.
+ Otherwise a restart using :func:`~marionette_driver.marionette.Marionette.restart`
+ will be needed.
+
+ If the call to uninstall is resulting in a `ScriptTimeoutException`,
+ an invalid ID is likely being passed in. Unfortunately due to
+ AddonManager's implementation, it's hard to retrieve this error from
+ Python.
+
+ :param addon_id: The addon ID string to uninstall.
+ """
+ with self._mn.using_context('chrome'):
+ return self._mn.execute_async_script("""
+ Components.utils.import("resource://gre/modules/AddonManager.jsm");
+ AddonManager.getAddonByID(arguments[0], function(addon) {
+ addon.uninstall();
+ marionetteScriptFinished(0);
+ });
+ """, script_args=[addon_id])
--- a/testing/marionette/driver/marionette_driver/marionette.py
+++ b/testing/marionette/driver/marionette_driver/marionette.py
@@ -559,16 +559,17 @@ class Marionette(object):
self.baseurl = baseurl
self.no_window = no_window
self._test_name = None
self.timeout = timeout
self.socket_timeout = socket_timeout
self.device_serial = device_serial
self.adb_host = adb_host
self.adb_port = adb_port
+ self.restart_handlers = []
startup_timeout = startup_timeout or self.DEFAULT_STARTUP_TIMEOUT
if bin:
port = int(self.port)
if not Marionette.is_port_available(port, host=self.host):
ex_msg = "%s:%d is unavailable." % (self.host, port)
raise errors.MarionetteException(message=ex_msg)
@@ -1132,16 +1133,21 @@ class Marionette(object):
self.instance.detached = True
else:
self.delete_session()
self.instance.restart(clean=clean)
assert(self.wait_for_port()), "Timed out waiting for port!"
self.start_session(session_id=self.session_id)
self._reset_timeouts()
+ # Give consumers who depended on the old session a
+ # chance to re-initialize and/or restore state.
+ for handler in self.restart_handlers:
+ handler()
+
def absolute_url(self, relative_url):
'''
Returns an absolute url for files served from Marionette's www directory.
:param relative_url: The url of a static file, relative to Marionette's www directory.
'''
return "%s%s" % (self.baseurl, relative_url)
--- a/tools/docs/mach_commands.py
+++ b/tools/docs/mach_commands.py
@@ -1,98 +1,117 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, # You can obtain one at http://mozilla.org/MPL/2.0/.
-from __future__ import absolute_import, unicode_literals
+from __future__ import absolute_import, print_function, unicode_literals
import os
+import sys
from mach.decorators import (
+ Command,
CommandArgument,
CommandProvider,
- Command,
)
+import mozhttpd
+
from mozbuild.base import MachCommandBase
@CommandProvider
class Documentation(MachCommandBase):
"""Helps manage in-tree documentation."""
@Command('doc', category='devenv',
description='Generate and display documentation from the tree.')
- @CommandArgument('what', default=None, nargs='*',
+ @CommandArgument('what', nargs='*', metavar='DIRECTORY [, DIRECTORY]',
help='Path(s) to documentation to build and display.')
@CommandArgument('--format', default='html',
help='Documentation format to write.')
- @CommandArgument('--outdir', default=None,
+ @CommandArgument('--outdir', default=None, metavar='DESTINATION',
help='Where to write output.')
@CommandArgument('--no-open', dest='auto_open', default=True, action='store_false',
- help='Don\'t automatically open HTML docs in a browser.')
- def build_docs(self, what=None, format=None, outdir=None, auto_open=True):
+ help="Don't automatically open HTML docs in a browser.")
+ @CommandArgument('--http', const=':6666', metavar='ADDRESS', nargs='?',
+ help='Serve documentation on an HTTP server, e.g. ":6666".')
+ def build_docs(self, what=None, format=None, outdir=None, auto_open=True, http=None):
self._activate_virtualenv()
self.virtualenv_manager.install_pip_package('sphinx_rtd_theme==0.1.6')
import sphinx
import webbrowser
if not outdir:
outdir = os.path.join(self.topobjdir, 'docs')
-
if not what:
what = [os.path.join(self.topsrcdir, 'tools')]
+ outdir = os.path.join(outdir, format)
generated = []
failed = []
for path in what:
path = os.path.normpath(os.path.abspath(path))
docdir = self._find_doc_dir(path)
if not docdir:
failed.append((path, 'could not find docs at this location'))
continue
# find project name to use as a namespace within `outdir`
project = self._find_project_name(docdir)
- savedir = os.path.join(outdir, format, project)
+ savedir = os.path.join(outdir, project)
args = [
'sphinx',
'-b', format,
docdir,
savedir,
]
result = sphinx.build_main(args)
if result != 0:
failed.append((path, 'sphinx return code %d' % result))
else:
generated.append(savedir)
- # attempt to open html docs in a browser
index_path = os.path.join(savedir, 'index.html')
- if auto_open and os.path.isfile(index_path):
+ if not http and auto_open and os.path.isfile(index_path):
webbrowser.open(index_path)
if generated:
- print("\nGenerated documentation:\n%s\n" % '\n'.join(generated))
+ print('\nGenerated documentation:\n%s\n' % '\n'.join(generated))
if failed:
- failed = ['%s - %s' % (f[0], f[1]) for f in failed]
- print("ERROR failed to generate documentation:\n%s" % '\n'.join(failed))
- return 1
+ failed = ['%s: %s' % (f[0], f[1]) for f in failed]
+ return die('failed to generate documentation:\n%s' % '\n'.join(failed))
+
+ if http is not None:
+ host, port = http.split(':', 1)
+ addr = (host, int(port))
+ if len(addr) != 2:
+ return die('invalid address: %s' % http)
+
+ httpd = mozhttpd.MozHttpd(host=addr[0], port=addr[1], docroot=outdir)
+ print('listening on %s:%d' % addr)
+ httpd.start(block=True)
def _find_project_name(self, path):
import imp
path = os.path.join(path, 'conf.py')
with open(path, 'r') as fh:
conf = imp.load_module('doc_conf', fh, path,
('.py', 'r', imp.PY_SOURCE))
return conf.project.replace(' ', '_')
def _find_doc_dir(self, path):
search_dirs = ('doc', 'docs')
for d in search_dirs:
p = os.path.join(path, d)
if os.path.isfile(os.path.join(p, 'conf.py')):
return p
+
+
+def die(msg, exit_code=1):
+ msg = '%s: %s' % (sys.argv[0], msg)
+ print(msg, file=sys.stderr)
+ return exit_code
--- a/tools/power/rapl.cpp
+++ b/tools/power/rapl.cpp
@@ -833,17 +833,19 @@ main(int argc, char** argv)
Abort("new RAPL() failed");
}
// Install the signal handlers.
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_RESTART | SA_SIGINFO;
- if (sigemptyset(&sa.sa_mask) < 0) {
+ // The extra parens around (0) suppress a -Wunreachable-code warning on OS X
+ // where sigemptyset() is a macro that can never fail and always returns 0.
+ if (sigemptyset(&sa.sa_mask) < (0)) {
Abort("sigemptyset() failed");
}
sa.sa_sigaction = SigAlrmHandler;
if (sigaction(SIGALRM, &sa, NULL) < 0) {
Abort("sigaction(SIGALRM) failed");
}
sa.sa_sigaction = SigIntHandler;
if (sigaction(SIGINT, &sa, NULL) < 0) {
@@ -872,9 +874,8 @@ main(int argc, char** argv)
pause();
}
}
Finish();
return 0;
}
-
--- a/tools/profiler/core/platform-linux.cc
+++ b/tools/profiler/core/platform-linux.cc
@@ -354,18 +354,19 @@ static void* SignalSender(void* arg) {
// Profile from the signal handler for information which is signal safe
// and needs to be precise too, such as the stack of the interrupted
// thread.
if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) {
printf_stderr("profiler failed to signal tid=%d\n", threadId);
#ifdef DEBUG
abort();
+#else
+ continue;
#endif
- continue;
}
// Wait for the signal handler to run before moving on to the next one
sem_wait(&sSignalHandlingDone);
isFirstProfiledThread = false;
// The LUL unwind object accumulates frame statistics.
// Periodically we should poke it to give it a chance to print
--- a/webapprt/locales/en-US/webapprt/overrides/dom.properties
+++ b/webapprt/locales/en-US/webapprt/overrides/dom.properties
@@ -146,8 +146,9 @@ Window_ContentWarning=window._content is
# LOCALIZATION NOTE: Do not translate "XMLHttpRequest"
SyncXMLHttpRequestWarning=Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help http://xhr.spec.whatwg.org/
# LOCALIZATION NOTE: Do not translate "DataContainerEvent" or "CustomEvent"
DataContainerEventWarning=Use of DataContainerEvent is deprecated. Use CustomEvent instead.
# LOCALIZATION NOTE: Do not translate "Worker".
EmptyWorkerSourceWarning=Attempting to create a Worker from an empty source. This is probably unintentional.
WebrtcDeprecatedPrefixWarning=WebRTC interfaces with the "moz" prefix (mozRTCPeerConnection, mozRTCSessionDescription, mozRTCIceCandidate) have been deprecated.
NavigatorGetUserMediaWarning=navigator.mozGetUserMedia has been replaced by navigator.mediaDevices.getUserMedia
+ExecCommandCutCopyDeniedNotInputDriven=document.execCommand('cut'/'copy') was denied because it was not called from inside a short running user-generated event handler.