--- a/browser/base/content/browser-media.js
+++ b/browser/base/content/browser-media.js
@@ -114,17 +114,17 @@ var gEMEHandler = {
let btnAccessKeyId = msgPrefix + "button.accesskey";
buttons.push({
label: gNavigatorBundle.getString(btnLabelId),
accessKey: gNavigatorBundle.getString(btnAccessKeyId),
callback
});
}
- let iconURL = "chrome://browser/skin/drm-icon.svg#chains-black";
+ let iconURL = "chrome://browser/skin/drm-icon.svg";
// Do a little dance to get rich content into the notification:
let fragment = document.createDocumentFragment();
let descriptionContainer = document.createElement("description");
// eslint-disable-next-line no-unsanitized/property
descriptionContainer.innerHTML = message;
while (descriptionContainer.childNodes.length) {
fragment.appendChild(descriptionContainer.childNodes[0]);
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -498,20 +498,19 @@ toolbar:not(#TabsToolbar) > #personal-bo
/* Hide menu elements intended for keyboard access support */
#main-menubar[openedwithkey=false] .show-only-for-keyboard {
display: none;
}
/* ::::: location bar & search bar ::::: */
-#urlbar-container {
- min-width: 50ch;
-}
-
+/* url bar min-width is defined further down, together with the maximum size
+ * of the identity icon block, for different window sizes.
+ */
#search-container {
min-width: 25ch;
}
#urlbar,
.searchbar-textbox {
/* Setting a width and min-width to let the location & search bars maintain
a constant width in case they haven't be resized manually. (bug 965772) */
@@ -680,51 +679,79 @@ html|input.urlbar-input[textoverflow]:no
-moz-user-focus: normal;
}
#urlbar[pageproxystate="invalid"] > #identity-box {
pointer-events: none;
-moz-user-focus: ignore;
}
+
+/* We leave 49ch plus whatever space the download button will need when it
+ * appears. Normally this should be 16px for the icon, plus 2 * 2px padding
+ * plus the toolbarbutton-inner-padding. We're adding 4px to ensure things
+ * like rounding on hidpi don't accidentally result in the button going
+ * into overflow.
+ */
+#urlbar-container {
+ min-width: calc(49ch + 24px + 2 * var(--toolbarbutton-inner-padding));
+}
+
+#nav-bar[hasdownloads] #urlbar-container {
+ min-width: 49ch;
+}
+
#identity-icon-labels {
- max-width: 18em;
+ max-width: 17em;
}
@media (max-width: 700px) {
#urlbar-container {
- min-width: 45ch;
+ min-width: calc(44ch + 24px + 2 * var(--toolbarbutton-inner-padding));
}
+ #nav-bar[hasdownloads] #urlbar-container {
+ min-width: 44ch;
+ }
+
#identity-icon-labels {
- max-width: 70px;
+ max-width: 60px;
}
}
@media (max-width: 600px) {
#urlbar-container {
- min-width: 40ch;
- }
- #identity-icon-labels {
- max-width: 60px;
+ min-width: calc(39ch + 24px + 2 * var(--toolbarbutton-inner-padding));
}
-}
-@media (max-width: 500px) {
- #urlbar-container {
- min-width: 35ch;
+ #nav-bar[hasdownloads] #urlbar-container {
+ min-width: 39ch;
}
#identity-icon-labels {
max-width: 50px;
}
}
-@media (max-width: 400px) {
+@media (max-width: 500px) {
#urlbar-container {
- min-width: 28ch;
+ min-width: calc(34ch + 24px + 2 * var(--toolbarbutton-inner-padding));
+ }
+ #nav-bar[hasdownloads] #urlbar-container {
+ min-width: 34ch;
}
#identity-icon-labels {
max-width: 40px;
}
}
+@media (max-width: 400px) {
+ #urlbar-container {
+ min-width: calc(27ch + 24px + 2 * var(--toolbarbutton-inner-padding));
+ }
+ #nav-bar[hasdownloads] #urlbar-container {
+ min-width: 27ch;
+ }
+ #identity-icon-labels {
+ max-width: 30px;
+ }
+}
#identity-icon-country-label {
direction: ltr;
}
#identity-box.verifiedIdentity > #identity-icon-labels > #identity-icon-label {
margin-inline-end: 0.25em !important;
}
@@ -1057,30 +1084,17 @@ notification[value="translation"] {
/** See bug 872317 for why the following rule is necessary. */
#downloads-button {
-moz-binding: url("chrome://browser/content/downloads/download.xml#download-toolbarbutton");
}
/*** Visibility of downloads indicator controls ***/
-
-/* Bug 924050: If we've loaded the indicator, for now we hide it in the menu panel,
- and just show the icon. This is a hack to side-step very weird layout bugs that
- seem to be caused by the indicator stack interacting with the menu panel. */
-#downloads-button[indicator]:not([cui-areatype="menu-panel"]) > .toolbarbutton-badge-stack > image.toolbarbutton-icon,
-#downloads-button[indicator][cui-areatype="menu-panel"] > #downloads-indicator-anchor {
- display: none;
-}
-
-toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > .toolbarbutton-badge-stack > image.toolbarbutton-icon {
- display: -moz-box;
-}
-
-toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > #downloads-indicator-anchor {
+#downloads-button[indicator] > .toolbarbutton-badge-stack > image.toolbarbutton-icon {
display: none;
}
/* Combobox dropdown renderer */
#ContentSelectDropdown > menupopup {
/* The menupopup itself should always be rendered LTR to ensure the scrollbar aligns with
* the dropdown arrow on the dropdown widget. If a menuitem is RTL, its style will be set accordingly */
direction: ltr;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -668,17 +668,17 @@ var gPopupBlockerObserver = {
label: popupButtonText,
accessKey: popupButtonAccesskey,
popup: "blockedPopupOptions",
callback: null
}];
const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
notificationBox.appendNotification(message, "popup-blocked",
- "chrome://browser/skin/Info.png",
+ "chrome://browser/skin/notification-icons/popup.svg",
priority, buttons);
}
}
// Record the fact that we've reported this blocked popup, so we don't
// show it again.
gBrowser.selectedBrowser.blockedPopups.reported = true;
}
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -898,27 +898,28 @@
<toolbaritem id="search-container" title="&searchItem.title;"
align="center" class="chromeclass-toolbar-additional panel-wide-item"
cui-areatype="toolbar"
flex="100" persist="width" removable="true">
<searchbar id="searchbar" flex="1"/>
</toolbaritem>
<!-- This is a placeholder for the Downloads Indicator. It is visible
- during the customization of the toolbar, in the palette, and before
- the Downloads Indicator overlay is loaded. -->
+ before the Downloads Indicator overlay is loaded. -->
<toolbarbutton id="downloads-button"
+ hidden="true"
class="toolbarbutton-1 chromeclass-toolbar-additional badged-button"
+ skipintoolbarset="true"
+ overflows="false"
key="key_openDownloads"
oncommand="DownloadsIndicatorView.onCommand(event);"
ondrop="DownloadsIndicatorView.onDrop(event);"
ondragover="DownloadsIndicatorView.onDragOver(event);"
ondragenter="DownloadsIndicatorView.onDragOver(event);"
label="&downloads.label;"
- removable="true"
cui-areatype="toolbar"
tooltip="dynamic-shortcut-tooltip"/>
<toolbarbutton id="library-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
removable="true"
oncommand="PanelUI.showSubView('appMenu-libraryView', this, null, event);"
closemenu="none"
cui-areatype="toolbar"
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5377,17 +5377,17 @@
callback() {
if (browser.messageManager) {
browser.messageManager.sendAsyncMessage("RefreshBlocker:Refresh", data);
}
}
}];
notificationBox.appendNotification(message, "refresh-blocked",
- "chrome://browser/skin/Info.png",
+ "chrome://browser/skin/notification-icons/popup.svg",
notificationBox.PRIORITY_INFO_MEDIUM,
buttons);
}
break;
}
case "Prerender:Request": {
let sendCancelPrerendering = () => {
--- a/browser/base/content/test/general/browser_documentnavigation.js
+++ b/browser/base/content/test/general/browser_documentnavigation.js
@@ -69,17 +69,17 @@ async function expectFocusOnF6(backward,
}
if (gMultiProcessBrowser && onContent) {
expectedDocument = "main-window";
expectedElement = gBrowser.selectedBrowser;
}
is(fm.focusedWindow.document.documentElement.id, expectedDocument, desc + " document matches");
- is(fm.focusedElement, expectedElement, desc + " element matches");
+ is(fm.focusedElement, expectedElement, desc + " element matches (wanted: " + expectedElement.id + " got: " + fm.focusedElement.id + ")");
if (onContent) {
window.messageManager.removeMessageListener("BrowserTest:FocusChanged", focusChangedListener);
}
}
// Load a page and navigate between it and the chrome window.
add_task(async function() {
@@ -166,35 +166,52 @@ add_task(async function() {
false, "back focus with sidebar open sidebar");
await expectFocusOnF6(true, "main-window", gURLBar.inputField,
false, "back focus with sidebar urlbar");
SidebarUI.toggle("viewBookmarksSidebar");
});
// Navigate when the downloads panel is open
-add_task(async function() {
+add_task(async function test_download_focus() {
await pushPrefs(["accessibility.tabfocus", 7]);
+ let testTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
+ testTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+ registerCleanupFunction(() => testTargetFile.remove(false));
+ let downloadsList = await Downloads.getList(Downloads.PUBLIC);
+ let download = {
+ source: "http://www.example.com/test-download.txt",
+ target: testTargetFile.path,
+ succeeded: true,
+ startTime: new Date(),
+ };
+ await downloadsList.add(await Downloads.createDownload(download));
+
+ await BrowserTestUtils.waitForCondition(() => {
+ let btn = document.getElementById("downloads-button");
+ return !btn.hidden && btn.getBoundingClientRect().width > 0;
+ });
+
let popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown", true);
EventUtils.synthesizeMouseAtCenter(document.getElementById("downloads-button"), { });
await popupShownPromise;
gURLBar.focus();
- await expectFocusOnF6(false, "main-window", document.getElementById("downloadsHistory"),
+ await expectFocusOnF6(false, "main-window", document.getElementById("downloadsListBox"),
false, "focus with downloads panel open panel");
await expectFocusOnF6(false, "html1", "html1",
true, "focus with downloads panel open");
await expectFocusOnF6(false, "main-window", gURLBar.inputField,
false, "focus downloads panel open urlbar");
// Now go backwards
await expectFocusOnF6(true, "html1", "html1",
true, "back focus with downloads panel open");
- await expectFocusOnF6(true, "main-window", document.getElementById("downloadsHistory"),
+ await expectFocusOnF6(true, "main-window", document.getElementById("downloadsListBox"),
false, "back focus with downloads panel open");
await expectFocusOnF6(true, "main-window", gURLBar.inputField,
false, "back focus downloads panel open urlbar");
let downloadsPopup = document.getElementById("downloadsPanel");
let popupHiddenPromise = BrowserTestUtils.waitForEvent(downloadsPopup, "popuphidden", true);
downloadsPopup.hidePopup();
await popupHiddenPromise;
--- a/browser/base/content/test/performance/browser_startup_images.js
+++ b/browser/base/content/test/performance/browser_startup_images.js
@@ -43,16 +43,20 @@ const whitelist = [
},
// Shared entries
{
file: "chrome://browser/skin/arrow-left.svg",
platforms: ["linux", "win", "macosx"],
},
{
+ file: "chrome://browser/skin/fxa/sync-illustration.svg",
+ platforms: ["linux", "win", "macosx"],
+ },
+ {
file: "chrome://browser/skin/tabbrowser/tab-overflow-indicator.png",
platforms: ["linux", "win", "macosx"],
},
{
file: "chrome://browser/skin/places/toolbarDropMarker.png",
platforms: ["linux", "win", "macosx"],
},
--- a/browser/base/content/test/urlbar/browser_dragdropURL.js
+++ b/browser/base/content/test/urlbar/browser_dragdropURL.js
@@ -4,35 +4,35 @@ const TEST_URL = "data:text/html,a test
const DRAG_URL = "http://www.example.com/";
const DRAG_FORBIDDEN_URL = "chrome://browser/content/aboutDialog.xul";
const DRAG_TEXT = "Firefox is awesome";
const DRAG_WORD = "Firefox";
add_task(async function checkDragURL() {
await BrowserTestUtils.withNewTab(TEST_URL, function(browser) {
// Have to use something other than the URL bar as a source, so picking the
- // downloads button somewhat arbitrarily:
- EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
+ // home button somewhat arbitrarily:
+ EventUtils.synthesizeDrop(document.getElementById("home-button"), gURLBar,
[[{type: "text/plain", data: DRAG_URL}]], "copy", window);
is(gURLBar.value, TEST_URL, "URL bar value should not have changed");
is(gBrowser.selectedBrowser.userTypedValue, null, "Stored URL bar value should not have changed");
});
});
add_task(async function checkDragForbiddenURL() {
await BrowserTestUtils.withNewTab(TEST_URL, function(browser) {
- EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
+ EventUtils.synthesizeDrop(document.getElementById("home-button"), gURLBar,
[[{type: "text/plain", data: DRAG_FORBIDDEN_URL}]], "copy", window);
isnot(gURLBar.value, DRAG_FORBIDDEN_URL, "Shouldn't be allowed to drop forbidden URL on URL bar");
});
});
add_task(async function checkDragText() {
await BrowserTestUtils.withNewTab(TEST_URL, function(browser) {
- EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
+ EventUtils.synthesizeDrop(document.getElementById("home-button"), gURLBar,
[[{type: "text/plain", data: DRAG_TEXT}]], "copy", window);
is(gURLBar.value, DRAG_TEXT, "Dragging normal text should replace the URL bar value");
- EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
+ EventUtils.synthesizeDrop(document.getElementById("home-button"), gURLBar,
[[{type: "text/plain", data: DRAG_WORD}]], "copy", window);
is(gURLBar.value, DRAG_WORD, "Dragging a single word should replace the URL bar value");
});
});
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -52,17 +52,17 @@ const kSubviewEvents = [
"ViewShowing",
"ViewHiding"
];
/**
* The current version. We can use this to auto-add new default widgets as necessary.
* (would be const but isn't because of testing purposes)
*/
-var kVersion = 10;
+var kVersion = 11;
/**
* Buttons removed from built-ins by version they were removed. kVersion must be
* bumped any time a new id is added to this. Use the button id as key, and
* version the button is removed in as the value. e.g. "pocket-button": 5
*/
var ObsoleteBuiltinButtons = {
};
@@ -172,17 +172,17 @@ XPCOMUtils.defineLazyGetter(this, "log",
var CustomizableUIInternal = {
initialize() {
log.debug("Initializing");
this.addListener(this);
this._defineBuiltInWidgets();
this.loadSavedState();
- this._introduceNewBuiltinWidgets();
+ this._updateForNewVersion();
this._markObsoleteBuiltinButtonsSeen();
this.registerArea(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL, {
type: CustomizableUI.TYPE_MENU_PANEL,
defaultPlacements: [],
anchor: "nav-bar-overflow-button",
}, true);
@@ -190,17 +190,16 @@ var CustomizableUIInternal = {
"back-button",
"forward-button",
"stop-reload-button",
"home-button",
"spring",
"urlbar-container",
"search-container",
"spring",
- "downloads-button",
"library-button",
"sidebar-button",
];
if (AppConstants.MOZ_DEV_EDITION) {
navbarPlacements.splice(2, 0, "developer-button");
}
@@ -258,17 +257,17 @@ var CustomizableUIInternal = {
},
_defineBuiltInWidgets() {
for (let widgetDefinition of CustomizableWidgets) {
this.createBuiltinWidget(widgetDefinition);
}
},
- _introduceNewBuiltinWidgets() {
+ _updateForNewVersion() {
// We should still enter even if gSavedState.currentVersion >= kVersion
// because the per-widget pref facility is independent of versioning.
if (!gSavedState) {
// Flip all the prefs so we don't try to re-introduce later:
for (let [, widget] of gPalette) {
if (widget.defaultArea && widget._introducedInVersion === "pref") {
let prefId = "browser.toolbarbuttons.introduced." + widget.id;
Services.prefs.setBoolPref(prefId, true);
@@ -411,16 +410,25 @@ var CustomizableUIInternal = {
if (currentVersion < 10 && gSavedState && gSavedState.placements) {
for (let placements of Object.values(gSavedState.placements)) {
if (placements.includes("webcompat-reporter-button")) {
placements.splice(placements.indexOf("webcompat-reporter-button"), 1);
break;
}
}
}
+
+ if (currentVersion < 11 && gSavedState && gSavedState.placements) {
+ for (let placements of Object.values(gSavedState.placements)) {
+ if (placements.includes("downloads-button")) {
+ placements.splice(placements.indexOf("downloads-button"), 1);
+ break;
+ }
+ }
+ }
},
/**
* _markObsoleteBuiltinButtonsSeen
* when upgrading, ensure obsoleted buttons are in seen state.
*/
_markObsoleteBuiltinButtonsSeen() {
if (!gSavedState)
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -161,17 +161,17 @@
showMoreTooltipText="&appMenuRemoteTabs.showMore.tooltip;"
notabsforclientlabel="&appMenuRemoteTabs.notabs.label;"
/>
</vbox>
<!-- Sync is ready to Sync but the "tabs" engine isn't enabled-->
<hbox id="PanelUI-remotetabs-tabsdisabledpane" pack="center" flex="1">
<vbox class="PanelUI-remotetabs-instruction-box" align="center">
<hbox pack="center">
- <image class="fxaSyncIllustration"/>
+ <html:img class="fxaSyncIllustration" src="chrome://browser/skin/fxa/sync-illustration.svg"/>
</hbox>
<label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.tabsnotsyncing.label;</label>
<hbox pack="center">
<toolbarbutton class="PanelUI-remotetabs-prefs-button"
label="&appMenuRemoteTabs.openprefs.label;"
oncommand="gSync.openPrefs('synced-tabs');"/>
</hbox>
</vbox>
@@ -179,17 +179,17 @@
<!-- Sync is ready to Sync but we are still fetching the tabs to show -->
<vbox id="PanelUI-remotetabs-fetching">
<!-- Show intentionally blank panel, see bug 1239845 -->
</vbox>
<!-- Sync has only 1 (ie, this) device connected -->
<hbox id="PanelUI-remotetabs-nodevicespane" pack="center" flex="1">
<vbox class="PanelUI-remotetabs-instruction-box">
<hbox pack="center">
- <image class="fxaSyncIllustration"/>
+ <html:img class="fxaSyncIllustration" src="chrome://browser/skin/fxa/sync-illustration.svg"/>
</hbox>
<label class="PanelUI-remotetabs-instruction-title">&appMenuRemoteTabs.noclients.title;</label>
<label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.noclients.subtitle;</label>
<!-- The inner HTML for PanelUI-remotetabs-mobile-promo is built at runtime -->
<label id="PanelUI-remotetabs-mobile-promo" fxAccountsBrand="&syncBrand.fxAccount.label;"/>
</vbox>
</hbox>
</deck>
@@ -197,31 +197,31 @@
<!-- a box to ensure contained boxes are centered horizonally -->
<hbox pack="center" flex="1">
<!-- When Sync is not configured -->
<vbox id="PanelUI-remotetabs-setupsync"
flex="1"
align="center"
class="PanelUI-remotetabs-instruction-box"
observes="sync-setup-state">
- <image class="fxaSyncIllustration"/>
+ <html:img class="fxaSyncIllustration" src="chrome://browser/skin/fxa/sync-illustration.svg"/>
<label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.notsignedin.label;</label>
<toolbarbutton class="PanelUI-remotetabs-prefs-button"
label="&appMenuRemoteTabs.signin.label;"
oncommand="gSync.openPrefs('synced-tabs');"/>
</vbox>
<!-- When Sync needs re-authentication. This uses the exact same messaging
as "Sync is not configured" but remains a separate box so we get
the goodness of observing broadcasters to manage the hidden states -->
<vbox id="PanelUI-remotetabs-reauthsync"
flex="1"
align="center"
class="PanelUI-remotetabs-instruction-box"
observes="sync-reauth-state">
- <image class="fxaSyncIllustration"/>
+ <html:img class="fxaSyncIllustration" src="chrome://browser/skin/fxa/sync-illustration.svg"/>
<label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.notsignedin.label;</label>
<toolbarbutton class="PanelUI-remotetabs-prefs-button"
label="&appMenuRemoteTabs.signin.label;"
oncommand="gSync.openPrefs('synced-tabs');"/>
</vbox>
</hbox>
</vbox>
</panelview>
--- a/browser/components/customizableui/test/browser_1042100_default_placements_update.js
+++ b/browser/components/customizableui/test/browser_1042100_default_placements_update.js
@@ -14,17 +14,17 @@ function test() {
let oldState = CustomizableUIBSPass.gSavedState;
registerCleanupFunction(() => CustomizableUIBSPass.gSavedState = oldState );
is(CustomizableUIBSPass.gFuturePlacements.size, 0,
"All future placements should be dealt with by now.");
let {CustomizableUIInternal, gFuturePlacements, gPalette} = CustomizableUIBSPass;
- CustomizableUIInternal._introduceNewBuiltinWidgets();
+ CustomizableUIInternal._updateForNewVersion();
is(gFuturePlacements.size, 0,
"No change to future placements initially.");
let currentVersion = CustomizableUIBSPass.kVersion;
// Add our widget to the defaults:
let testWidgetNew = {
@@ -62,17 +62,17 @@ function test() {
CustomizableUIBSPass.kVersion++;
let hadSavedState = !!CustomizableUIBSPass.gSavedState
if (!hadSavedState) {
CustomizableUIBSPass.gSavedState = {currentVersion: CustomizableUIBSPass.kVersion - 1};
}
// Then call the re-init routine so we re-add the builtin widgets
- CustomizableUIInternal._introduceNewBuiltinWidgets();
+ CustomizableUIInternal._updateForNewVersion();
is(gFuturePlacements.size, 1,
"Should have 1 more future placement");
let haveNavbarPlacements = gFuturePlacements.has(CustomizableUI.AREA_NAVBAR);
ok(haveNavbarPlacements, "Should have placements for nav-bar");
if (haveNavbarPlacements) {
let placements = [...gFuturePlacements.get(CustomizableUI.AREA_NAVBAR)];
// Ignore widgets that are placed using the pref facility and not the
@@ -101,17 +101,17 @@ function test() {
CustomizableUIBSPass.gSavedState = {
currentVersion: 6,
placements: {
"nav-bar": ["urlbar-container", "bookmarks-menu-button"],
"PanelUI-contents": ["panic-button", "edit-controls"],
},
};
- CustomizableUIInternal._introduceNewBuiltinWidgets();
+ CustomizableUIInternal._updateForNewVersion();
let navbarPlacements = CustomizableUIBSPass.gSavedState.placements["nav-bar"];
let springs = navbarPlacements.filter(id => id.includes("spring"));
is(springs.length, 2, "Should have 2 toolbarsprings in placements now");
navbarPlacements = navbarPlacements.filter(id => !id.includes("spring"));
is(navbarPlacements[0], "back-button", "Back button is in the right place.");
is(navbarPlacements[1], "forward-button", "Fwd button is in the right place.");
is(navbarPlacements[2], "stop-reload-button", "Stop/reload button is in the right place.");
is(navbarPlacements[3], "home-button", "Home button is in the right place.");
--- a/browser/components/customizableui/test/browser_1161838_inserted_new_default_buttons.js
+++ b/browser/components/customizableui/test/browser_1161838_inserted_new_default_buttons.js
@@ -12,17 +12,17 @@ function test() {
"All future placements should be dealt with by now.");
let {CustomizableUIInternal, gFuturePlacements, gPalette} = CustomizableUIBSPass;
// Force us to have a saved state:
CustomizableUIInternal.saveState();
CustomizableUIInternal.loadSavedState();
- CustomizableUIInternal._introduceNewBuiltinWidgets();
+ CustomizableUIInternal._updateForNewVersion();
is(gFuturePlacements.size, 0,
"No change to future placements initially.");
// Add our widget to the defaults:
let testWidgetNew = {
id: "test-messing-with-default-placements-new-pref",
label: "Test messing with default placements - pref-based",
defaultArea: CustomizableUI.AREA_NAVBAR,
@@ -39,17 +39,17 @@ function test() {
// Now adjust default placements for area:
let navbarArea = CustomizableUIBSPass.gAreas.get(CustomizableUI.AREA_NAVBAR);
let navbarPlacements = navbarArea.get("defaultPlacements");
navbarPlacements.splice(navbarPlacements.indexOf("bookmarks-menu-button") + 1, 0, testWidgetNew.id);
let savedPlacements = CustomizableUIBSPass.gSavedState.placements[CustomizableUI.AREA_NAVBAR];
// Then call the re-init routine so we re-add the builtin widgets
- CustomizableUIInternal._introduceNewBuiltinWidgets();
+ CustomizableUIInternal._updateForNewVersion();
is(gFuturePlacements.size, 1,
"Should have 1 more future placement");
let futureNavbarPlacements = gFuturePlacements.get(CustomizableUI.AREA_NAVBAR);
ok(futureNavbarPlacements, "Should have placements for nav-bar");
if (futureNavbarPlacements) {
ok(futureNavbarPlacements.has(testWidgetNew.id), "widget should be in future placements");
}
CustomizableUIInternal._placeNewDefaultWidgetsInArea(CustomizableUI.AREA_NAVBAR);
--- a/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
+++ b/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
@@ -11,26 +11,26 @@ var skippedItem;
add_task(async function() {
navbar = document.getElementById("nav-bar");
skippedItem = document.createElement("toolbarbutton");
skippedItem.id = "test-skipintoolbarset-item";
skippedItem.setAttribute("label", "Test");
skippedItem.setAttribute("skipintoolbarset", "true");
skippedItem.setAttribute("removable", "true");
navbar.customizationTarget.appendChild(skippedItem);
- let downloadsButton = document.getElementById("downloads-button");
+ let libraryButton = document.getElementById("library-button");
await startCustomizing();
ok(CustomizableUI.inDefaultState, "Should still be in default state");
- simulateItemDrag(skippedItem, downloadsButton);
+ simulateItemDrag(skippedItem, libraryButton);
ok(CustomizableUI.inDefaultState, "Should still be in default state");
let skippedItemWrapper = skippedItem.parentNode;
is(skippedItemWrapper.nextSibling && skippedItemWrapper.nextSibling.id,
- downloadsButton.parentNode.id, "Should be next to downloads button");
- simulateItemDrag(downloadsButton, skippedItem);
- let downloadWrapper = downloadsButton.parentNode;
+ libraryButton.parentNode.id, "Should be next to downloads button");
+ simulateItemDrag(libraryButton, skippedItem);
+ let downloadWrapper = libraryButton.parentNode;
is(downloadWrapper.nextSibling && downloadWrapper.nextSibling.id,
skippedItem.parentNode.id, "Should be next to skipintoolbarset item");
ok(CustomizableUI.inDefaultState, "Should still be in default state");
});
add_task(async function asyncCleanup() {
await endCustomizing();
skippedItem.remove();
--- a/browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js
+++ b/browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js
@@ -3,21 +3,21 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Customize mode reset button should revert correctly
add_task(async function() {
await startCustomizing();
let devButton = document.getElementById("developer-button");
- let downloadsButton = document.getElementById("downloads-button");
+ let libraryButton = document.getElementById("library-button");
let searchBox = document.getElementById("search-container");
let palette = document.getElementById("customization-palette");
- ok(devButton && downloadsButton && searchBox && palette, "Stuff should exist");
- simulateItemDrag(devButton, downloadsButton);
+ ok(devButton && libraryButton && searchBox && palette, "Stuff should exist");
+ simulateItemDrag(devButton, libraryButton);
simulateItemDrag(searchBox, palette);
await gCustomizeMode.reset();
ok(CustomizableUI.inDefaultState, "Should be back in default state");
await endCustomizing();
});
add_task(async function asyncCleanup() {
await resetCustomization();
--- a/browser/components/customizableui/test/browser_927717_customize_drag_empty_toolbar.js
+++ b/browser/components/customizableui/test/browser_927717_customize_drag_empty_toolbar.js
@@ -5,21 +5,21 @@
"use strict";
const kTestToolbarId = "test-empty-drag";
// Attempting to drag an item to an empty container should work.
add_task(async function() {
await createToolbarWithPlacements(kTestToolbarId, []);
await startCustomizing();
- let downloadButton = document.getElementById("downloads-button");
+ let libraryButton = document.getElementById("library-button");
let customToolbar = document.getElementById(kTestToolbarId);
- simulateItemDrag(downloadButton, customToolbar);
- assertAreaPlacements(kTestToolbarId, ["downloads-button"]);
- ok(downloadButton.parentNode && downloadButton.parentNode.parentNode == customToolbar,
+ simulateItemDrag(libraryButton, customToolbar);
+ assertAreaPlacements(kTestToolbarId, ["library-button"]);
+ ok(libraryButton.parentNode && libraryButton.parentNode.parentNode == customToolbar,
"Button should really be in toolbar");
await endCustomizing();
removeCustomToolbars();
});
add_task(async function asyncCleanup() {
await endCustomizing();
await resetCustomization();
--- a/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
+++ b/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
@@ -39,18 +39,18 @@ add_task(async function() {
// Make sure we have some hidden items at the end of the nav-bar.
navbar.insertItem(hidden1.id);
navbar.insertItem(hidden2.id);
// Drag an item and drop it onto the nav-bar customization target, but
// not over a particular item.
await startCustomizing();
- let downloadsButton = document.getElementById("downloads-button");
- simulateItemDrag(downloadsButton, navbar.customizationTarget);
+ let homeButton = document.getElementById("home-button");
+ simulateItemDrag(homeButton, navbar.customizationTarget);
await endCustomizing();
- is(downloadsButton.previousSibling.id, lastVisible.id,
+ is(homeButton.previousSibling.id, lastVisible.id,
"The downloads button should be placed after the last visible item.");
await resetCustomization();
});
--- a/browser/components/customizableui/test/browser_overflow_use_subviews.js
+++ b/browser/components/customizableui/test/browser_overflow_use_subviews.js
@@ -38,33 +38,8 @@ add_task(async function check_developer_
button.click();
await subviewShownPromise;
let hasSubviews = !!kOverflowPanel.querySelector("photonpanelmultiview,panelmultiview");
let expectedPanel = hasSubviews ? kOverflowPanel : document.getElementById("customizationui-widget-panel");
is(developerView.closest("panel"), expectedPanel, "Should be inside the panel");
expectedPanel.hidePopup();
await Promise.resolve(); // wait for popup to hide fully.
});
-
-/**
- * This checks that non-subview-compatible items still work correctly.
- * Ideally we should make the downloads panel and bookmarks/library item
- * proper subview items, then this test can go away, and potentially we can
- * simplify some of the subview anchoring code.
- */
-add_task(async function check_downloads_panel_in_overflow() {
- let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
- ok(navbar.hasAttribute("overflowing"), "Should still be overflowing");
- let chevron = document.getElementById("nav-bar-overflow-button");
- let shownPanelPromise = promisePanelElementShown(window, kOverflowPanel);
- chevron.click();
- await shownPanelPromise;
-
- let button = document.getElementById("downloads-button");
- button.click();
- await waitForCondition(() => {
- let panel = document.getElementById("downloadsPanel");
- return panel && panel.state != "closed";
- });
- let downloadsPanel = document.getElementById("downloadsPanel");
- isnot(downloadsPanel.state, "closed", "Should be attempting to show the downloads panel.");
- downloadsPanel.hidePopup();
-});
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -37,17 +37,16 @@ const { classes: Cc, interfaces: Ci, uti
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
NetUtil: "resource://gre/modules/NetUtil.jsm",
PluralForm: "resource://gre/modules/PluralForm.jsm",
AppConstants: "resource://gre/modules/AppConstants.jsm",
AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.jsm",
- CustomizableUI: "resource:///modules/CustomizableUI.jsm",
DownloadHistory: "resource://gre/modules/DownloadHistory.jsm",
Downloads: "resource://gre/modules/Downloads.jsm",
DownloadUIHelper: "resource://gre/modules/DownloadUIHelper.jsm",
DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
FileUtils: "resource://gre/modules/FileUtils.jsm",
OS: "resource://gre/modules/osfile.jsm",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
RecentWindow: "resource:///modules/RecentWindow.jsm",
@@ -1156,27 +1155,16 @@ DownloadsIndicatorDataCtor.prototype = {
_updateViews() {
// Do not update the status indicators during batch loads of download items.
if (this._loading) {
return;
}
this._refreshProperties();
- let widgetGroup = CustomizableUI.getWidget("downloads-button");
- let inMenu = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
- if (inMenu) {
- if (this._attention == DownloadsCommon.ATTENTION_NONE) {
- AppMenuNotifications.removeNotification(/^download-/);
- } else {
- let badgeClass = "download-" + this._attention;
- AppMenuNotifications.showBadgeOnlyNotification(badgeClass);
- }
- }
-
this._views.forEach(this._updateView, this);
},
/**
* Updates the specified view with the current aggregate values.
*
* @param aView
* DownloadsIndicatorView object to be updated.
--- a/browser/components/downloads/content/downloads.css
+++ b/browser/components/downloads/content/downloads.css
@@ -171,23 +171,16 @@ richlistitem.download button {
> toolbarseparator,
/* The "show blocked info" button is shown only in the downloads panel. */
.downloadShowBlockedInfo
{
display: none;
}
-/*** Downloads panel ***/
-
-#downloadsPanel[hasdownloads] #emptyDownloads,
-#downloadsPanel:not([hasdownloads]) #downloadsListBox {
- display: none;
-}
-
/*** Downloads panel multiview (main view and blocked-downloads subview) ***/
/* Hide all the usual buttons. */
#downloadsPanel-mainView .download-state[state="8"] .downloadCancel,
#downloadsPanel-mainView .download-state[state="8"] .downloadConfirmBlock,
#downloadsPanel-mainView .download-state[state="8"] .downloadChooseUnblock,
#downloadsPanel-mainView .download-state[state="8"] .downloadChooseOpen,
#downloadsPanel-mainView .download-state[state="8"] .downloadRetry,
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -697,22 +697,18 @@ var DownloadsView = {
* Called when the number of items in the list changes.
*/
_itemCountChanged() {
DownloadsCommon.log("The downloads item count has changed - we are tracking",
this._downloads.length, "downloads in total.");
let count = this._downloads.length;
let hiddenCount = count - this.kItemCountLimit;
- if (count > 0) {
- DownloadsCommon.log("Setting the panel's hasdownloads attribute to true.");
- DownloadsPanel.panel.setAttribute("hasdownloads", "true");
- } else {
- DownloadsCommon.log("Removing the panel's hasdownloads attribute.");
- DownloadsPanel.panel.removeAttribute("hasdownloads");
+ if (count <= 0) {
+ DownloadsButton.hide();
}
// If we've got some hidden downloads, we should activate the
// DownloadsSummary. The DownloadsSummary will determine whether or not
// it's appropriate to actually display the summary.
DownloadsSummary.active = hiddenCount > 0;
},
--- a/browser/components/downloads/content/downloadsOverlay.xul
+++ b/browser/components/downloads/content/downloadsOverlay.xul
@@ -112,19 +112,16 @@
<panelview id="downloadsPanel-mainView">
<vbox class="panel-view-body-unscrollable">
<richlistbox id="downloadsListBox"
context="downloadsContextMenu"
onmouseover="DownloadsView.onDownloadMouseOver(event);"
onmouseout="DownloadsView.onDownloadMouseOut(event);"
oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
ondragstart="DownloadsView.onDownloadDragStart(event);"/>
- <description id="emptyDownloads"
- mousethrough="always"
- value="&downloadsPanelEmpty.label;"/>
</vbox>
<vbox id="downloadsFooter"
class="downloadsPanelFooter">
<stack>
<hbox id="downloadsSummary"
align="center"
orient="horizontal"
onkeydown="DownloadsSummary.onKeyDown(event);"
--- a/browser/components/downloads/content/indicator.js
+++ b/browser/components/downloads/content/indicator.js
@@ -103,46 +103,25 @@ const DownloadsButton = {
if (!indicator) {
// Exit now if the indicator overlay isn't loaded yet, or if the button
// is not in the document.
return null;
}
indicator.open = this._anchorRequested;
- let widget = CustomizableUI.getWidget("downloads-button")
- .forWindow(window);
- // Determine if the indicator is located on an invisible toolbar.
- if (!isElementVisible(indicator.parentNode) && !widget.overflowed) {
- return null;
- }
+ // Determine if the indicator is located on an invisible toolbar.
+ if (!window.toolbar.visible) {
+ return null;
+ }
return DownloadsIndicatorView.indicatorAnchor;
},
/**
- * Checks whether the indicator is, or will soon be visible in the browser
- * window.
- *
- * @param aCallback
- * Called once the indicator overlay has loaded. Gets a boolean
- * argument representing the indicator visibility.
- */
- checkIsVisible(aCallback) {
- DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay, () => {
- if (!this._placeholder) {
- aCallback(false);
- } else {
- let element = DownloadsIndicatorView.indicator || this._placeholder;
- aCallback(isElementVisible(element.parentNode));
- }
- });
- },
-
- /**
* Indicates whether we should try and show the indicator temporarily as an
* anchor for the panel, even if the indicator would be hidden by default.
*/
_anchorRequested: false,
/**
* Ensures that there is an anchor available for the panel.
*
@@ -159,16 +138,46 @@ const DownloadsButton = {
}
DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay, () => {
this._anchorRequested = true;
aCallback(this._getAnchorInternal());
});
},
+ unhideAndPlace() {
+ if (this._placeholder.hasAttribute("hidden")) {
+ this._navBar.setAttribute("hasdownloads", "true");
+ this._placeholder.removeAttribute("hidden");
+ let insertionPoint = document.getElementById("urlbar-container");
+ while (insertionPoint && insertionPoint.nextElementSibling) {
+ let next = insertionPoint.nextElementSibling;
+ if (!next.hidden &&
+ (next.nodeName == "toolbarspring" ||
+ next.id == "downloads-button" || /* also have to ignore ourselves... */
+ next.id == "search-container" ||
+ next.id == "urlbar-search-splitter")) {
+ insertionPoint = next;
+ } else {
+ break;
+ }
+ }
+ if (insertionPoint && insertionPoint.nextElementSibling != this._placeholder &&
+ insertionPoint.nextElementSibling != this._placeholder.nextElementSibling) {
+ insertionPoint.insertAdjacentElement("afterend", this._placeholder);
+ }
+ }
+ },
+
+ hide() {
+ DownloadsPanel.hidePanel();
+ this._navBar.removeAttribute("hasdownloads");
+ this._placeholder.setAttribute("hidden", "true");
+ },
+
/**
* Allows the temporary anchor to be hidden.
*/
releaseAnchor() {
this._anchorRequested = false;
this._getAnchorInternal();
},
@@ -247,22 +256,16 @@ const DownloadsIndicatorView = {
_ensureOperational(aCallback) {
if (this._operational) {
if (aCallback) {
aCallback();
}
return;
}
- // If we don't have a _placeholder, there's no chance that the overlay
- // will load correctly: bail (and don't set _operational to true!)
- if (!DownloadsButton._placeholder) {
- return;
- }
-
DownloadsOverlayLoader.ensureOverlayLoaded(
DownloadsButton.kIndicatorOverlay,
() => {
this._operational = true;
// If the view is initialized, we need to update the elements now that
// they are finally available in the document.
if (this._initialized) {
@@ -334,42 +337,29 @@ const DownloadsIndicatorView = {
* Set to "start" for new downloads, "finish" for completed downloads.
*/
_showNotification(aType) {
// No need to show visual notification if the panel is visible.
if (DownloadsPanel.isPanelShowing) {
return;
}
- let anchor = DownloadsButton._placeholder;
- let widgetGroup = CustomizableUI.getWidget("downloads-button");
- let widget = widgetGroup.forWindow(window);
- if (widget.overflowed || widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
- if (anchor && this._isAncestorPanelOpen(anchor)) {
- // If the containing panel is open, don't do anything, because the
- // notification would appear under the open panel. See
- // https://bugzilla.mozilla.org/show_bug.cgi?id=984023
- return;
- }
-
- // Otherwise, try to use the anchor of the panel:
- anchor = widget.anchor;
- }
- if (!anchor || !isElementVisible(anchor.parentNode)) {
+ if (!window.toolbar.visible) {
// Our container isn't visible, so can't show the animation:
return;
}
// The notification element is positioned to show in the same location as
// the downloads button. It's not in the downloads button itself in order to
// be able to anchor the notification elsewhere if required, and to ensure
// the notification isn't clipped by overflow properties of the anchor's
// container.
// Note: no notifier animation for download finished in Photon
let notifier = this.notifier;
+ let anchor = DownloadsButton._placeholder;
if (aType == "start") {
// Show the notifier before measuring for size/placement. Being hidden by default
// avoids the interference with scrolling/APZ when the notifier element is
// tall enough to overlap the tabbrowser element
notifier.removeAttribute("hidden");
// the anchor height may vary if font-size is changed or
@@ -418,17 +408,19 @@ const DownloadsIndicatorView = {
* Indicates whether the indicator should be shown because there are some
* downloads to be displayed.
*/
set hasDownloads(aValue) {
if (this._hasDownloads != aValue || (!this._operational && aValue)) {
this._hasDownloads = aValue;
// If there is at least one download, ensure that the view elements are
+ // operational
if (aValue) {
+ DownloadsButton.unhideAndPlace();
this._ensureOperational();
}
}
return aValue;
},
get hasDownloads() {
return this._hasDownloads;
},
@@ -440,17 +432,16 @@ const DownloadsIndicatorView = {
*/
set percentComplete(aValue) {
if (!this._operational) {
return this._percentComplete;
}
if (this._percentComplete !== aValue) {
this._percentComplete = aValue;
- this._refreshAttention();
if (this._percentComplete >= 0) {
this.indicator.setAttribute("progress", "true");
// For arrow type only:
// We set animationDelay to a minus value (0s ~ -100s) to show the
// corresponding frame needed for progress.
this._progressIcon.style.animationDelay = (-this._percentComplete) + "s";
} else {
@@ -466,57 +457,35 @@ const DownloadsIndicatorView = {
* Set when the indicator should draw user attention to itself.
*/
set attention(aValue) {
if (!this._operational) {
return this._attention;
}
if (this._attention != aValue) {
this._attention = aValue;
- this._refreshAttention();
+ if (this._attention == DownloadsCommon.ATTENTION_NONE) {
+ this.indicator.removeAttribute("attention");
+ } else {
+ this.indicator.setAttribute("attention", this._attention);
+ }
}
return this._attention;
},
-
- _refreshAttention() {
- // Check if the downloads button is in the menu panel, to determine which
- // button needs to get a badge.
- let widgetGroup = CustomizableUI.getWidget("downloads-button");
- let inMenu = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
-
- // For arrow-Styled indicator, suppress success attention if we have
- // progress in toolbar
- let suppressAttention = !inMenu &&
- this._attention == DownloadsCommon.ATTENTION_SUCCESS &&
- this._percentComplete >= 0;
-
- if (suppressAttention || this._attention == DownloadsCommon.ATTENTION_NONE) {
- this.indicator.removeAttribute("attention");
- } else {
- this.indicator.setAttribute("attention", this._attention);
- }
- },
_attention: DownloadsCommon.ATTENTION_NONE,
// User interface event functions
onWindowUnload() {
// This function is registered as an event listener, we can't use "this".
DownloadsIndicatorView.ensureTerminated();
},
onCommand(aEvent) {
- // If the downloads button is in the menu panel, open the Library
- let widgetGroup = CustomizableUI.getWidget("downloads-button");
- if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
- DownloadsPanel.showDownloadsHistory();
- } else {
- DownloadsPanel.showPanel();
- }
-
+ DownloadsPanel.showPanel();
aEvent.stopPropagation();
},
onDragOver(aEvent) {
browserDragAndDrop.dragOver(aEvent);
},
onDrop(aEvent) {
@@ -558,21 +527,16 @@ const DownloadsIndicatorView = {
if (!indicator || indicator.getAttribute("indicator") != "true") {
return null;
}
return this._indicator = indicator;
},
get indicatorAnchor() {
- let widget = CustomizableUI.getWidget("downloads-button")
- .forWindow(window);
- if (widget.overflowed) {
- return widget.anchor;
- }
return document.getElementById("downloads-indicator-anchor");
},
get _progressIcon() {
return this.__progressIcon ||
(this.__progressIcon = document.getElementById("downloads-indicator-progress-inner"));
},
--- a/browser/components/downloads/test/browser/browser.ini
+++ b/browser/components/downloads/test/browser/browser.ini
@@ -1,15 +1,13 @@
[DEFAULT]
support-files = head.js
[browser_basic_functionality.js]
[browser_first_download_panel.js]
skip-if = os == "linux" # Bug 949434
-[browser_overflow_anchor.js]
-skip-if = os == "linux" # Bug 952422
[browser_confirm_unblock_download.js]
[browser_iframe_gone_mid_download.js]
[browser_indicatorDrop.js]
[browser_libraryDrop.js]
[browser_downloads_panel_block.js]
skip-if = true # Bug 1352792
[browser_downloads_panel_height.js]
--- a/browser/components/downloads/test/browser/browser_downloads_panel_height.js
+++ b/browser/components/downloads/test/browser/browser_downloads_panel_height.js
@@ -5,25 +5,32 @@
/**
* This test exists because we use a <panelmultiview> element and it handles
* some of the height changes for us. We need to verify that the height is
* updated correctly if downloads are removed while the panel is hidden.
*/
add_task(async function test_height_reduced_after_removal() {
await task_addDownloads([
+ { state: DownloadsCommon.DOWNLOAD_PAUSED },
{ state: DownloadsCommon.DOWNLOAD_FINISHED },
]);
+ await BrowserTestUtils.waitForCondition(() => {
+ let btn = document.getElementById("downloads-button");
+ return !btn.hidden && btn.getBoundingClientRect().width > 0;
+ });
+
await task_openPanel();
let panel = document.getElementById("downloadsPanel");
let heightBeforeRemoval = panel.getBoundingClientRect().height;
- // We want to close the panel before we remove the download from the list.
+ // We want to close the panel before we remove a download from the list.
DownloadsPanel.hidePanel();
- await task_resetState();
+ let publicList = await Downloads.getList(Downloads.PUBLIC);
+ await publicList.removeFinished();
await task_openPanel();
let heightAfterRemoval = panel.getBoundingClientRect().height;
Assert.greater(heightBeforeRemoval, heightAfterRemoval);
await task_resetState();
});
--- a/browser/components/downloads/test/browser/browser_first_download_panel.js
+++ b/browser/components/downloads/test/browser/browser_first_download_panel.js
@@ -27,17 +27,19 @@ add_task(async function test_first_downl
// Ensure that state is reset in case previous tests didn't finish.
await task_resetState();
// With this set to false, we should automatically open the panel the first
// time a download is started.
DownloadsCommon.getData(window).panelHasShownBefore = false;
+ info("waiting for panel open");
let promise = promisePanelOpened();
+ await task_addDownloads([{state: DownloadsCommon.DOWNLOAD_DOWNLOADING}]);
DownloadsCommon.getData(window)._notifyDownloadEvent("start");
await promise;
// If we got here, that means the panel opened.
DownloadsPanel.hidePanel();
ok(DownloadsCommon.getData(window).panelHasShownBefore,
"Should have recorded that the panel was opened on a download.")
deleted file mode 100644
--- a/browser/components/downloads/test/browser/browser_overflow_anchor.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-registerCleanupFunction(async function() {
- // Clean up when the test finishes.
- await task_resetState();
-});
-
-/**
- * Make sure the downloads button and indicator overflows into the nav-bar
- * chevron properly, and then when those buttons are clicked in the overflow
- * panel that the downloads panel anchors to the chevron.
- */
-add_task(async function test_overflow_anchor() {
- // Ensure that state is reset in case previous tests didn't finish.
- await task_resetState();
-
- // Record the original width of the window so we can put it back when
- // this test finishes.
- let oldWidth = window.outerWidth;
-
- // The downloads button should not be overflowed to begin with.
- let button = CustomizableUI.getWidget("downloads-button")
- .forWindow(window);
- ok(!button.overflowed, "Downloads button should not be overflowed.");
-
- // Hack - we lock the size of the default flex-y items in the nav-bar,
- // namely, the URL and search inputs. That way we can resize the
- // window without worrying about them flexing.
- const kFlexyItems = ["urlbar-container", "search-container"];
- registerCleanupFunction(() => unlockWidth(kFlexyItems));
- lockWidth(kFlexyItems);
-
- // Resize the window to half of its original size. That should
- // be enough to overflow the downloads button.
- window.resizeTo(oldWidth / 2, window.outerHeight);
- await waitForOverflowed(button, true);
-
- let promise = promisePanelOpened();
- button.node.doCommand();
- await promise;
-
- let panel = DownloadsPanel.panel;
- let chevron = document.getElementById("nav-bar-overflow-button");
- is(panel.anchorNode, chevron, "Panel should be anchored to the chevron.");
-
- DownloadsPanel.hidePanel();
-
- // Unlock the widths on the flex-y items.
- unlockWidth(kFlexyItems);
-
- // Put the window back to its original dimensions.
- window.resizeTo(oldWidth, window.outerHeight);
-
- // The downloads button should eventually be un-overflowed.
- await waitForOverflowed(button, false);
-
- // Now try opening the panel again.
- promise = promisePanelOpened();
- button.node.doCommand();
- await promise;
-
- is(panel.anchorNode.id, "downloads-indicator-anchor");
-
- DownloadsPanel.hidePanel();
-});
-
-/**
- * For some node IDs, finds the nodes and sets their min-width's to their
- * current width, preventing them from flex-shrinking.
- *
- * @param aItemIDs an array of item IDs to set min-width on.
- */
-function lockWidth(aItemIDs) {
- for (let itemID of aItemIDs) {
- let item = document.getElementById(itemID);
- let curWidth = item.getBoundingClientRect().width + "px";
- item.style.minWidth = curWidth;
- }
-}
-
-/**
- * Clears the min-width's set on a set of IDs by lockWidth.
- *
- * @param aItemIDs an array of ItemIDs to remove min-width on.
- */
-function unlockWidth(aItemIDs) {
- for (let itemID of aItemIDs) {
- let item = document.getElementById(itemID);
- item.style.minWidth = "";
- }
-}
-
-/**
- * Waits for a node to enter or exit the overflowed state.
- *
- * @param aItem the node to wait for.
- * @param aIsOverflowed if we're waiting for the item to be overflowed.
- */
-function waitForOverflowed(aItem, aIsOverflowed) {
- if (aItem.overflowed == aIsOverflowed) {
- return Promise.resolve();
- }
-
- return new Promise(resolve => {
- let observer = new MutationObserver(function(aMutations) {
- if (aItem.overflowed == aIsOverflowed) {
- observer.disconnect();
- resolve();
- }
- });
- observer.observe(aItem.node, {attributes: true});
- });
-}
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -68,17 +68,16 @@ skip-if = (os == 'win' && !debug) # bug
[browser_ext_currentWindow.js]
[browser_ext_devtools_inspectedWindow.js]
[browser_ext_devtools_inspectedWindow_eval_bindings.js]
[browser_ext_devtools_inspectedWindow_reload.js]
[browser_ext_devtools_network.js]
[browser_ext_devtools_page.js]
[browser_ext_devtools_panel.js]
[browser_ext_devtools_panels_elements.js]
-skip-if = true # bug 1382487
[browser_ext_geckoProfiler_symbolicate.js]
[browser_ext_getViews.js]
[browser_ext_identity_indication.js]
[browser_ext_incognito_views.js]
[browser_ext_incognito_popup.js]
[browser_ext_lastError.js]
[browser_ext_menus.js]
[browser_ext_omnibox.js]
--- a/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements.js
@@ -6,35 +6,58 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://devtools/client/framework/gDevTools.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "devtools",
"resource://devtools/shared/Loader.jsm");
add_task(async function test_devtools_panels_elements_onSelectionChanged() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
function devtools_page() {
- let doTabReload = true;
+ let isReloading = false;
+ let collectedEvalResults = [];
browser.devtools.panels.elements.onSelectionChanged.addListener(async () => {
const [
evalResult, exceptionInfo,
] = await browser.devtools.inspectedWindow.eval("$0 && $0.tagName");
if (exceptionInfo) {
browser.test.fail("Unexpected exceptionInfo on inspectedWindow.eval: " +
JSON.stringify(exceptionInfo));
}
- browser.test.sendMessage("devtools_eval_result", evalResult);
+ collectedEvalResults.push(evalResult);
+
+ // The eval results that are happening during the reload are going to
+ // be retrieved all at once using the "collected_devttols_eval_results:request".
+ if (!isReloading) {
+ browser.test.sendMessage("devtools_eval_result", evalResult);
+ }
+ });
- if (doTabReload) {
- // Force a reload to test that the expected onSelectionChanged events are sent
- // while the page is navigating and once it has been fully reloaded.
- doTabReload = false;
- await browser.devtools.inspectedWindow.eval("window.location.reload();");
+ browser.test.onMessage.addListener(msg => {
+ switch (msg) {
+ case "inspectedWindow_reload": {
+ // Force a reload to test that the expected onSelectionChanged events are sent
+ // while the page is navigating and once it has been fully reloaded.
+ isReloading = true;
+ collectedEvalResults = [];
+ browser.devtools.inspectedWindow.eval("window.location.reload();");
+ break;
+ }
+
+ case "collected_devtools_eval_results:request": {
+ browser.test.sendMessage("collected_devtools_eval_results:reply",
+ collectedEvalResults);
+ break;
+ }
+
+ default: {
+ browser.test.fail(`Received unexpected test.onMesssage: ${msg}`);
+ }
}
});
browser.test.sendMessage("devtools_page_loaded");
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
@@ -58,33 +81,42 @@ add_task(async function test_devtools_pa
let target = devtools.TargetFactory.forTab(tab);
const toolbox = await gDevTools.showToolbox(target, "webconsole");
info("developer toolbox opened");
await extension.awaitMessage("devtools_page_loaded");
- toolbox.selectTool("inspector");
+ await toolbox.selectTool("inspector");
+
+ const inspector = toolbox.getPanel("inspector");
const evalResult = await extension.awaitMessage("devtools_eval_result");
is(evalResult, "BODY", "Got the expected onSelectionChanged once the inspector is selected");
- const evalResultNavigating = await extension.awaitMessage("devtools_eval_result");
-
- is(evalResultNavigating, undefined, "Got the expected onSelectionChanged once the tab is navigating");
-
- const evalResultNavigated = await extension.awaitMessage("devtools_eval_result");
+ // Reload the inspected tab and wait for the inspector markup view to have been
+ // fully reloaded.
+ const onceMarkupReloaded = inspector.once("markuploaded");
+ extension.sendMessage("inspectedWindow_reload");
+ await onceMarkupReloaded;
- is(evalResultNavigated, undefined, "Got the expected onSelectionChanged once the tab navigated");
+ // Retrieve the first and last collected eval result (the first is related to the
+ // page navigating away, the last one is related to the updated inspector markup view
+ // fully reloaded and the selection updated).
+ extension.sendMessage("collected_devtools_eval_results:request");
+ const collectedEvalResults = await extension.awaitMessage("collected_devtools_eval_results:reply");
+ const evalResultNavigating = collectedEvalResults.shift();
+ const evalResultOnceMarkupReloaded = collectedEvalResults.pop();
- const evalResultReloaded = await extension.awaitMessage("devtools_eval_result");
+ is(evalResultNavigating, undefined,
+ "Got the expected onSelectionChanged once the tab is navigating");
- is(evalResultReloaded, "BODY",
+ is(evalResultOnceMarkupReloaded, "BODY",
"Got the expected onSelectionChanged once the tab has been completely reloaded");
await gDevTools.closeToolbox(target);
await target.destroy();
await extension.unload();
--- a/browser/extensions/formautofill/content/formfill-anchor.svg
+++ b/browser/extensions/formautofill/content/formfill-anchor.svg
@@ -1,11 +1,9 @@
<!-- 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="context-fill">
- <path d="M7.28 5h1.47A.25.25 0 0 0 9 4.75V.984a.984.984 0 0 0-1.97 0V4.75a.25.25 0 0 0 .25.25z"/>
- <path d="M13.5 2H11a1 1 0 0 0 0 2h2.5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-7a.5.5 0 0 1 .5-.5H5a1 1 0 0 0 0-2H2.5A2.5 2.5 0 0 0 0 4.5v7A2.5 2.5 0 0 0 2.5 14h11a2.5 2.5 0 0 0 2.5-2.5v-7A2.5 2.5 0 0 0 13.5 2z"/>
- <rect x="3" y="6" width="4" height="4" rx=".577" ry=".577"/>
- <path d="M9.5 7h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 0 0 1zM9.5 8a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1z"/>
- </g>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M7.28 5h1.47A.25.25 0 0 0 9 4.75V.984a.984.984 0 0 0-1.97 0V4.75a.25.25 0 0 0 .25.25z"/>
+ <path d="M13.5 2H11a1 1 0 0 0 0 2h2.5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-7a.5.5 0 0 1 .5-.5H5a1 1 0 0 0 0-2H2.5A2.5 2.5 0 0 0 0 4.5v7A2.5 2.5 0 0 0 2.5 14h11a2.5 2.5 0 0 0 2.5-2.5v-7A2.5 2.5 0 0 0 13.5 2z"/>
+ <rect x="3" y="6" width="4" height="4" rx=".577" ry=".577"/>
+ <path d="M9.5 7h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 0 0 1zM9.5 8a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1z"/>
</svg>
--- a/browser/extensions/webcompat-reporter/skin/lightbulb.svg
+++ b/browser/extensions/webcompat-reporter/skin/lightbulb.svg
@@ -1,8 +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"
- width="32" height="32" fill="context-fill">
- <path d="M11.5,8.3h.1l9-1a.8.8,0,0,0-.2-1.5l-9,1a.8.8,0,0,0,.1,1.5Zm2.4,13.5a.3.3,0,0,0,.1.2h.9a.3.3,0,0,0,.1-.2,1.8,1.8,0,0,0-.5-1.1A1.8,1.8,0,0,0,13.9,21.7ZM11.5,6.3h.1l8-1a.7.7,0,0,0,.7-.8.7.7,0,0,0-.3-.5A4.4,4.4,0,0,0,16,1c-3,0-4,3-4,3h5.5l-6,.8a.7.7,0,0,0,.1,1.5ZM17,21.8c0,.1,0,.3.5.3h.4a.4.4,0,0,0,.1-.3,1.8,1.8,0,0,0-.4-1A1.9,1.9,0,0,0,17,21.8Zm5.3-8.9-1.1-2.2a1,1,0,0,0-.9-.6H13.8l6.8-.8a.8.8,0,0,0-.2-1.5l-9,1a.8.8,0,0,0-.7.8.7.7,0,0,0,.4.6l-.4.5L9.7,12.8C8.5,15,6,17,6,21.5A9.6,9.6,0,0,0,16,31a9.6,9.6,0,0,0,10-9.5C26,17,23.5,15,22.3,12.8Zm0,7.7a.5.5,0,0,1-.6,0,3.8,3.8,0,0,0-2.2-.8l-1.1.3a2.7,2.7,0,0,1,.6,1.6,1.4,1.4,0,0,1-.4,1,1.6,1.6,0,0,1-1.1.4A1.3,1.3,0,0,1,16,21.8a2.8,2.8,0,0,1,.8-1.8,1.8,1.8,0,0,0-1.7,0,2.8,2.8,0,0,1,.7,1.7,1.3,1.3,0,0,1-.4.9,1.5,1.5,0,0,1-1,.4,1.8,1.8,0,0,1-1.2-.4,1.3,1.3,0,0,1-.4-1,2.7,2.7,0,0,1,.7-1.7,2.5,2.5,0,0,0-1.2-.3,3.8,3.8,0,0,0-2.1.7.5.5,0,0,1-.7-.6l4.6-8.5a.5.5,0,1,1,.9.5l-3.9,7.2,1.2-.2a3.6,3.6,0,0,1,2,.6,2.8,2.8,0,0,1,3.3,0,3.5,3.5,0,0,1,1.9-.5l1.3.2L17,11.8a.5.5,0,1,1,.9-.5l4.5,8.6A.5.5,0,0,1,22.3,20.6Z"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M8 0C4.3 0 2 2.107 2 5.5c0 2.372 2.065 4.268 3 5V14c0 1.476 1.616 2 3 2s3-.524 3-2v-3.5c.935-.736 3-2.632 3-5C14 2.107 11.7 0 8 0zm1 12H7v-1h2zm-1 2a3.086 3.086 0 0 1-1-.172V13h2v.828A3.047 3.047 0 0 1 8 14zm1.445-4.832A1 1 0 0 0 9 10H7a1 1 0 0 0-.444-.831C5.845 8.691 4 7.1 4 5.5 4 2.607 6.175 2 8 2s4 .607 4 3.5c0 1.6-1.845 3.191-2.555 3.668z"/>
</svg>
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -58,16 +58,20 @@ DEFINES += -DJAREXT=
ifdef MOZ_ANGLE_RENDERER
DEFINES += -DMOZ_ANGLE_RENDERER=$(MOZ_ANGLE_RENDERER)
ifdef MOZ_D3DCOMPILER_VISTA_DLL
DEFINES += -DMOZ_D3DCOMPILER_VISTA_DLL=$(MOZ_D3DCOMPILER_VISTA_DLL)
endif
endif
+ifdef MOZ_ENABLE_SKIA_PDF
+DEFINES += -DMOZ_ENABLE_SKIA_PDF=$(MOZ_ENABLE_SKIA_PDF)
+endif
+
DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME)
# Set MSVC dlls version to package, if any.
ifdef MOZ_NO_DEBUG_RTL
ifdef WIN32_REDIST_DIR
ifndef MOZ_ARTIFACT_BUILDS
DEFINES += -DMOZ_PACKAGE_MSVC_DLLS=1
DEFINES += -DMSVC_C_RUNTIME_DLL=$(MSVC_C_RUNTIME_DLL)
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -600,16 +600,20 @@
@BINPATH@/libEGL.dll
@BINPATH@/libGLESv2.dll
#ifdef MOZ_D3DCOMPILER_VISTA_DLL
@BINPATH@/@MOZ_D3DCOMPILER_VISTA_DLL@
#endif
#endif # MOZ_ANGLE_RENDERER
+#if defined(XP_WIN) && defined(MOZ_ENABLE_SKIA_PDF)
+@BINPATH@/pdfium.dll
+#endif
+
; [Browser Chrome Files]
@RESPATH@/browser/chrome.manifest
@RESPATH@/browser/chrome/browser@JAREXT@
@RESPATH@/browser/chrome/browser.manifest
@RESPATH@/browser/chrome/pdfjs.manifest
@RESPATH@/browser/chrome/pdfjs/*
@RESPATH@/browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.manifest
@RESPATH@/browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf
--- a/browser/installer/windows/nsis/defines.nsi.in
+++ b/browser/installer/windows/nsis/defines.nsi.in
@@ -23,16 +23,18 @@
# These defines should match application.ini settings
!define AppName "Firefox"
!define AppVersion "@APP_VERSION@"
!define GREVersion @MOZILLA_VERSION@
!define AB_CD "@AB_CD@"
!define FileMainEXE "@MOZ_APP_NAME@.exe"
!define WindowClass "FirefoxMessageWindow"
+!define MainWindowClass "MozillaWindowClass"
+!define DialogWindowClass "MozillaDialogClass"
!define DDEApplication "Firefox"
!define AppRegName "Firefox"
!ifndef DEV_EDITION
!define BrandShortName "@MOZ_APP_DISPLAYNAME@"
!endif
!define BrandFullName "${BrandFullNameInternal}"
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -42,28 +42,25 @@ Var FontInstalling
Var FontBlurb
Var FontFooter
Var FontCheckbox
Var CanWriteToInstallDir
Var HasRequiredSpaceAvailable
Var IsDownloadFinished
Var DownloadSizeBytes
-Var HalfOfDownload
Var DownloadReset
Var ExistingTopDir
Var SpaceAvailableBytes
Var InitialInstallDir
Var HandleDownload
Var CanSetAsDefault
Var InstallCounterStep
-Var InstallStepSize
Var InstallTotalSteps
Var ProgressCompleted
-Var ProgressTotal
Var ExitCode
Var FirefoxLaunchCode
Var StartDownloadPhaseTickCount
; Since the Intro and Options pages can be displayed multiple times the total
; seconds spent on each of these pages is reported.
Var IntroPhaseSeconds
@@ -89,16 +86,17 @@ Var DownloadRetryCount
Var OpenedDownloadPage
Var DownloadServerIP
Var PostSigningData
Var PreviousInstallDir
Var PreviousInstallArch
Var ProfileCleanupPromptType
Var ProfileCleanupHeaderString
Var ProfileCleanupButtonString
+Var AppLaunchWaitTickCount
; Uncomment the following to prevent pinging the metrics server when testing
; the stub installer
;!define STUB_DEBUG
!define StubURLVersion "v8"
; Successful install exit code
@@ -154,43 +152,43 @@ Var ProfileCleanupButtonString
!define DownloadRetryIntervalMS 3000
; Interval for the download timer
!define DownloadIntervalMS 200
; Interval for the install timer
!define InstallIntervalMS 100
-; The first step for the install progress bar. By starting with a large step
-; immediate feedback is given to the user.
-!define InstallProgressFirstStep 20
-
-; The finish step size to quickly increment the progress bar after the
-; installation has finished.
-!define InstallProgressFinishStep 40
-
; Number of steps for the install progress.
; This might not be enough when installing on a slow network drive so it will
-; fallback to downloading the full installer if it reaches this number. The size
-; of the install progress step is increased when the full installer finishes
-; instead of waiting.
+; fallback to downloading the full installer if it reaches this number.
-; Approximately 150 seconds with a 100 millisecond timer and a first step of 20
-; as defined by InstallProgressFirstStep.
-!define /math InstallCleanTotalSteps ${InstallProgressFirstStep} + 1500
+; Approximately 150 seconds with a 100 millisecond timer.
+!define InstallCleanTotalSteps 1500
-; Approximately 165 seconds (minus 0.2 seconds for each file that is removed)
-; with a 100 millisecond timer and a first step of 20 as defined by
-; InstallProgressFirstStep .
-!define /math InstallPaveOverTotalSteps ${InstallProgressFirstStep} + 1800
+; Approximately 165 seconds with a 100 millisecond timer.
+!define InstallPaveOverTotalSteps 1650
; Blurb duty cycle
!define BlurbDisplayMS 19500
!define BlurbBlankMS 500
+; Interval between checks for the application window and progress bar updates.
+!define AppLaunchWaitIntervalMS 100
+
+; Total time to wait for the application to start before just exiting.
+!define AppLaunchWaitTimeoutMS 10000
+
+; Maximum value of the download/install/launch progress bar, and the end values
+; for each individual stage.
+!define PROGRESS_BAR_TOTAL_STEPS 500
+!define PROGRESS_BAR_DOWNLOAD_END_STEP 300
+!define PROGRESS_BAR_INSTALL_END_STEP 475
+!define PROGRESS_BAR_APP_LAUNCH_END_STEP 500
+
; Amount of physical memory required for the 64-bit build to be selected (2 GB).
; Machines with this or less RAM get the 32-bit build, even with a 64-bit OS.
!define RAM_NEEDED_FOR_64BIT 0x80000000
; Attempt to elevate Standard Users in addition to users that
; are a member of the Administrators group.
!define NONADMIN_ELEVATE
@@ -543,17 +541,16 @@ Function .onGUIEnd
${UnloadUAC}
FunctionEnd
Function .onUserAbort
${NSD_KillTimer} StartDownload
${NSD_KillTimer} OnDownload
${NSD_KillTimer} CheckInstall
${NSD_KillTimer} FinishInstall
- ${NSD_KillTimer} FinishProgressBar
${NSD_KillTimer} DisplayDownloadError
${NSD_KillTimer} NextBlurb
${NSD_KillTimer} ClearBlurb
${If} "$IsDownloadFinished" != ""
Call DisplayDownloadError
${Else}
Call SendPing
@@ -884,27 +881,17 @@ Function StartDownload
${NSD_CreateTimer} OnDownload ${DownloadIntervalMS}
${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}"
RmDir /r "$INSTDIR\${TO_BE_DELETED}"
${EndIf}
FunctionEnd
Function SetProgressBars
SendMessage $Progressbar ${PBM_SETPOS} $ProgressCompleted 0
- ${ITBL3SetProgressValue} "$ProgressCompleted" "$ProgressTotal"
-FunctionEnd
-
-Function RemoveFileProgressCallback
- IntOp $InstallCounterStep $InstallCounterStep + 2
- System::Int64Op $ProgressCompleted + $InstallStepSize
- Pop $ProgressCompleted
- Call SetProgressBars
- System::Int64Op $ProgressCompleted + $InstallStepSize
- Pop $ProgressCompleted
- Call SetProgressBars
+ ${ITBL3SetProgressValue} "$ProgressCompleted" "${PROGRESS_BAR_TOTAL_STEPS}"
FunctionEnd
Function NextBlurb
${NSD_KillTimer} NextBlurb
IntOp $CurrentBlurbIdx $CurrentBlurbIdx + 1
IntOp $CurrentBlurbIdx $CurrentBlurbIdx % 3
@@ -1000,26 +987,20 @@ Function OnDownload
${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
${Else}
${NSD_CreateTimer} StartDownload ${DownloadIntervalMS}
${EndIf}
Return
${EndIf}
StrCpy $DownloadSizeBytes "$4"
- System::Int64Op $4 / 2
- Pop $HalfOfDownload
- System::Int64Op $HalfOfDownload / $InstallTotalSteps
- Pop $InstallStepSize
SendMessage $Progressbar ${PBM_SETMARQUEE} 0 0 ; start=1|stop=0 interval(ms)=+N
${RemoveStyle} $Progressbar ${PBS_MARQUEE}
- System::Int64Op $HalfOfDownload + $DownloadSizeBytes
- Pop $ProgressTotal
StrCpy $ProgressCompleted 0
- SendMessage $Progressbar ${PBM_SETRANGE32} $ProgressCompleted $ProgressTotal
+ SendMessage $Progressbar ${PBM_SETRANGE32} $ProgressCompleted ${PROGRESS_BAR_TOTAL_STEPS}
${EndIf}
; Don't update the status until after the download starts
${If} $2 != 0
${AndIf} "$4" == ""
Return
${EndIf}
@@ -1037,20 +1018,16 @@ Function OnDownload
${EndIf}
Return
${EndIf}
${If} $IsDownloadFinished != "true"
${If} $2 == 0
${NSD_KillTimer} OnDownload
StrCpy $IsDownloadFinished "true"
- ; The first step of the install progress bar is determined by the
- ; InstallProgressFirstStep define and provides the user with immediate
- ; feedback.
- StrCpy $InstallCounterStep "${InstallProgressFirstStep}"
System::Call "kernel32::GetTickCount()l .s"
Pop $EndDownloadPhaseTickCount
StrCpy $DownloadedBytes "$DownloadSizeBytes"
; When a download has finished handle the case where the downloaded size
; is less than the minimum expected size or greater than the maximum
; expected size during the download.
@@ -1065,23 +1042,19 @@ Function OnDownload
${Else}
${NSD_CreateTimer} StartDownload ${DownloadIntervalMS}
${EndIf}
Return
${EndIf}
; Update the progress bars first in the UI change so they take affect
; before other UI changes.
- StrCpy $ProgressCompleted "$DownloadSizeBytes"
+ StrCpy $ProgressCompleted "${PROGRESS_BAR_DOWNLOAD_END_STEP}"
Call SetProgressBars
- System::Int64Op $InstallStepSize * ${InstallProgressFirstStep}
- Pop $R9
- System::Int64Op $ProgressCompleted + $R9
- Pop $ProgressCompleted
- Call SetProgressBars
+
; Disable the Cancel button during the install
GetDlgItem $5 $HWNDPARENT 2
EnableWindow $5 0
; Open a handle to prevent modification of the full installer
StrCpy $R9 "${INVALID_HANDLE_VALUE}"
System::Call 'kernel32::CreateFileW(w "$PLUGINSDIR\download.exe", \
i ${GENERIC_READ}, \
@@ -1150,18 +1123,17 @@ Function OnDownload
WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
!endif
; Delete the taskbar shortcut history to ensure we do the right thing based on
; the config file above.
${GetShortcutsLogPath} $0
Delete "$0"
- GetFunctionAddress $0 RemoveFileProgressCallback
- ${RemovePrecompleteEntries} $0
+ ${RemovePrecompleteEntries} "false"
; Delete the install.log and let the full installer create it. When the
; installer closes it we can detect that it has completed.
Delete "$INSTDIR\install.log"
; Delete firefox.exe.moz-upgrade and firefox.exe.moz-delete if it exists
; since it being present will require an OS restart for the full
; installer.
@@ -1169,29 +1141,31 @@ Function OnDownload
Delete "$INSTDIR\${FileMainEXE}.moz-delete"
System::Call "kernel32::GetTickCount()l .s"
Pop $EndPreInstallPhaseTickCount
Exec "$\"$PLUGINSDIR\download.exe$\" /INI=$PLUGINSDIR\${CONFIG_INI}"
${NSD_CreateTimer} CheckInstall ${InstallIntervalMS}
${Else}
- ${If} $HalfOfDownload != "true"
- ${AndIf} $3 > $HalfOfDownload
- StrCpy $HalfOfDownload "true"
- ${EndIf}
StrCpy $DownloadedBytes "$3"
- StrCpy $ProgressCompleted "$DownloadedBytes"
+ System::Int64Op $DownloadedBytes * ${PROGRESS_BAR_DOWNLOAD_END_STEP}
+ Pop $ProgressCompleted
+ System::Int64Op $ProgressCompleted / $DownloadSizeBytes
+ Pop $ProgressCompleted
Call SetProgressBars
${EndIf}
${EndIf}
FunctionEnd
Function SendPing
+ ${NSD_KillTimer} NextBlurb
+ ${NSD_KillTimer} ClearBlurb
HideWindow
+
${If} $CheckboxSendPing == 1
; Get the tick count for the completion of all phases.
System::Call "kernel32::GetTickCount()l .s"
Pop $EndFinishPhaseTickCount
; When the value of $IsDownloadFinished is false the download was started
; but didn't finish. In this case the tick count stored in
; $EndFinishPhaseTickCount is used to determine how long the download was
@@ -1457,18 +1431,17 @@ Function CheckInstall
; Close the handle that prevents modification of the full installer
System::Call 'kernel32::CloseHandle(i $HandleDownload)'
StrCpy $ExitCode "${ERR_INSTALL_TIMEOUT}"
; Use a timer so the UI has a chance to update
${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
Return
${EndIf}
- System::Int64Op $ProgressCompleted + $InstallStepSize
- Pop $ProgressCompleted
+ IntOp $ProgressCompleted $ProgressCompleted + 1
Call SetProgressBars
${If} ${FileExists} "$INSTDIR\install.log"
Delete "$INSTDIR\install.tmp"
CopyFiles /SILENT "$INSTDIR\install.log" "$INSTDIR\install.tmp"
; The unfocus and refocus that happens approximately here is caused by the
; installer calling SHChangeNotify to refresh the shortcut icons.
@@ -1481,68 +1454,34 @@ Function CheckInstall
${NSD_KillTimer} CheckInstall
; Close the handle that prevents modification of the full installer
System::Call 'kernel32::CloseHandle(i $HandleDownload)'
Rename "$INSTDIR\install.tmp" "$INSTDIR\install.log"
Delete "$PLUGINSDIR\download.exe"
Delete "$PLUGINSDIR\${CONFIG_INI}"
System::Call "kernel32::GetTickCount()l .s"
Pop $EndInstallPhaseTickCount
- System::Int64Op $InstallStepSize * ${InstallProgressFinishStep}
- Pop $InstallStepSize
- ${NSD_CreateTimer} FinishInstall ${InstallIntervalMS}
+ Call FinishInstall
${EndUnless}
${EndIf}
FunctionEnd
Function FinishInstall
- ; The full installer has completed but the progress bar still needs to finish
- ; so increase the size of the step.
- IntOp $InstallCounterStep $InstallCounterStep + ${InstallProgressFinishStep}
- ${If} $InstallTotalSteps < $InstallCounterStep
- StrCpy $InstallCounterStep "$InstallTotalSteps"
- ${EndIf}
-
- ${If} $InstallTotalSteps != $InstallCounterStep
- System::Int64Op $ProgressCompleted + $InstallStepSize
- Pop $ProgressCompleted
- Call SetProgressBars
- Return
- ${EndIf}
-
- ${NSD_KillTimer} FinishInstall
-
- StrCpy $ProgressCompleted "$ProgressTotal"
+ StrCpy $ProgressCompleted "${PROGRESS_BAR_INSTALL_END_STEP}"
Call SetProgressBars
${If} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-upgrade"
Delete "$INSTDIR\${FileMainEXE}"
Rename "$INSTDIR\${FileMainEXE}.moz-upgrade" "$INSTDIR\${FileMainEXE}"
${EndIf}
StrCpy $ExitCode "${ERR_SUCCESS}"
- StrCpy $InstallCounterStep 0
- ${NSD_CreateTimer} FinishProgressBar ${InstallIntervalMS}
-FunctionEnd
-
-Function FinishProgressBar
- IntOp $InstallCounterStep $InstallCounterStep + 1
-
- ${If} $InstallCounterStep < 10
- Return
- ${EndIf}
-
- ${NSD_KillTimer} FinishProgressBar
- ${NSD_KillTimer} NextBlurb
- ${NSD_KillTimer} ClearBlurb
-
Call CopyPostSigningData
Call LaunchApp
- Call SendPing
FunctionEnd
Function RelativeGotoPage
IntCmp $R9 0 0 Move Move
StrCmp $R9 "X" 0 Move
StrCpy $R9 "120"
Move:
@@ -1651,28 +1590,58 @@ Function LaunchApp
${Else}
Exec "$\"$INSTDIR\${FileMainEXE}$\""
${EndIf}
${Else}
StrCpy $R1 $CheckboxCleanupProfile
GetFunctionAddress $0 LaunchAppFromElevatedProcess
UAC::ExecCodeSegment $0
${EndIf}
+
+ StrCpy $AppLaunchWaitTickCount 0
+ ${NSD_CreateTimer} WaitForAppLaunch ${AppLaunchWaitIntervalMS}
FunctionEnd
Function LaunchAppFromElevatedProcess
; Set the current working directory to the installation directory
SetOutPath "$INSTDIR"
${If} $R1 == 1
Exec "$\"$INSTDIR\${FileMainEXE}$\" -reset-profile -migration"
${Else}
Exec "$\"$INSTDIR\${FileMainEXE}$\""
${EndIf}
FunctionEnd
+Function WaitForAppLaunch
+ FindWindow $0 "${MainWindowClass}"
+ FindWindow $1 "${DialogWindowClass}"
+ ${If} $0 <> 0
+ ${OrIf} $1 <> 0
+ ${NSD_KillTimer} WaitForAppLaunch
+ StrCpy $ProgressCompleted "${PROGRESS_BAR_APP_LAUNCH_END_STEP}"
+ Call SetProgressBars
+ Call SendPing
+ Return
+ ${EndIf}
+
+ IntOp $AppLaunchWaitTickCount $AppLaunchWaitTickCount + 1
+ IntOp $0 $AppLaunchWaitTickCount * ${AppLaunchWaitIntervalMS}
+ ${If} $0 >= ${AppLaunchWaitTimeoutMS}
+ ; We've waited an unreasonably long time, so just exit.
+ ${NSD_KillTimer} WaitForAppLaunch
+ Call SendPing
+ Return
+ ${EndIf}
+
+ ${If} $ProgressCompleted < ${PROGRESS_BAR_APP_LAUNCH_END_STEP}
+ IntOp $ProgressCompleted $ProgressCompleted + 1
+ Call SetProgressBars
+ ${EndIf}
+FunctionEnd
+
Function CopyPostSigningData
${LineRead} "$EXEDIR\postSigningData" "1" $PostSigningData
${If} ${Errors}
ClearErrors
StrCpy $PostSigningData "0"
${Else}
CreateDirectory "$LOCALAPPDATA\Mozilla\Firefox"
CopyFiles /SILENT "$EXEDIR\postSigningData" "$LOCALAPPDATA\Mozilla\Firefox"
--- a/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
+++ b/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
@@ -144,18 +144,13 @@
<!ENTITY clearDownloadsButton.tooltip "Clears completed, canceled and failed downloads">
<!-- LOCALIZATION NOTE (downloadsListEmpty.label):
This string is shown when there are no items in the Downloads view, when it
is displayed inside a browser tab.
-->
<!ENTITY downloadsListEmpty.label "There are no downloads.">
-<!-- LOCALIZATION NOTE (downloadsPanelEmpty.label):
- This string is shown when there are no items in the Downloads Panel.
- -->
-<!ENTITY downloadsPanelEmpty.label "No downloads for this session.">
-
<!-- LOCALIZATION NOTE (downloadsListNoMatch.label):
This string is shown when some search terms are specified, but there are no
results in the Downloads view.
-->
<!ENTITY downloadsListNoMatch.label "Could not find any matching downloads.">
deleted file mode 100644
index d1447985258d7f8a34e385bb81c59f779979fb39..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -568,21 +568,16 @@ html|span.ac-emphasize-text-url {
color: GrayText;
font-size: smaller;
}
.autocomplete-treebody::-moz-tree-cell(suggesthint) {
border-top: 1px solid GrayText;
}
-/* Popup blocker button */
-#page-report-button {
- list-style-image: url("chrome://browser/skin/Info.png");
-}
-
/* Bookmarking panel */
#editBookmarkPanelStarIcon {
list-style-image: url("chrome://browser/skin/places/starred48.png");
width: 48px;
height: 48px;
}
#editBookmarkPanelStarIcon[unstarred] {
@@ -721,18 +716,16 @@ html|span.ac-emphasize-text-url {
border-top-left-radius: .3em;
margin-left: 1em;
}
%include ../shared/fullscreen/warning.inc.css
%include ../shared/ctrlTab.inc.css
%include ../shared/plugin-doorhanger.inc.css
-%include downloads/indicator.css
-
.gcli-panel {
padding: 0;
}
.gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
color: hsl(210,11%,16%);
}
deleted file mode 100644
index b8443f04557ac064b45d2a723f6b2c0123a31615..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/browser/themes/linux/downloads/indicator.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/downloads/indicator.inc.css
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -6,17 +6,16 @@ browser.jar:
% skin browser classic/1.0 %skin/classic/browser/
% override chrome://global/skin/icons/warning-16.png moz-icon://stock/gtk-dialog-warning?size=menu
#include ../shared/jar.inc.mn
skin/classic/browser/sanitizeDialog.css
skin/classic/browser/aboutSessionRestore-window-icon.png
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
* skin/classic/browser/browser.css
* skin/classic/browser/compacttheme.css
- skin/classic/browser/Info.png
skin/classic/browser/menuPanel-customize.png
skin/classic/browser/menuPanel-customize@2x.png
skin/classic/browser/menuPanel-exit.png
skin/classic/browser/menuPanel-exit@2x.png
skin/classic/browser/menuPanel-help.png
skin/classic/browser/menuPanel-help@2x.png
skin/classic/browser/monitor.png
skin/classic/browser/monitor_16-10.png
@@ -28,25 +27,24 @@ browser.jar:
skin/classic/browser/reload-stop-go@2x.png
skin/classic/browser/searchbar.css
skin/classic/browser/setDesktopBackground.css
skin/classic/browser/slowStartup-16.png
skin/classic/browser/webRTC-indicator.css (../shared/webRTC-indicator.css)
* skin/classic/browser/controlcenter/panel.css (controlcenter/panel.css)
* skin/classic/browser/customizableui/panelUI.css (customizableui/panelUI.css)
* skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
- skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
* skin/classic/browser/downloads/downloads.css (downloads/downloads.css)
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
* skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
-* skin/classic/browser/notification-icons/geo-blocked.svg (notification-icons/geo-blocked.svg)
+ skin/classic/browser/notification-icons/geo-blocked.svg (notification-icons/geo-blocked.svg)
skin/classic/browser/notification-icons/geo-detailed.svg (notification-icons/geo-detailed.svg)
-* skin/classic/browser/notification-icons/geo.svg (notification-icons/geo.svg)
+ skin/classic/browser/notification-icons/geo.svg (notification-icons/geo.svg)
skin/classic/browser/places/allBookmarks.png (places/allBookmarks.png)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
skin/classic/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel.png)
skin/classic/browser/places/bookmarks-menu-arrow.png (places/bookmarks-menu-arrow.png)
skin/classic/browser/places/calendar.png (places/calendar.png)
* skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
skin/classic/browser/places/livemark-item.png (places/livemark-item.png)
--- a/browser/themes/linux/notification-icons/geo-blocked.svg
+++ b/browser/themes/linux/notification-icons/geo-blocked.svg
@@ -1,12 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <defs>
-#include ../../shared/notification-icons/clip-path.inc.svg
-#include geo-icon.inc.svg
- </defs>
- <use clip-path="url(#blocked-clipPath)" href="#geo-linux-icon"/>
-#include ../../shared/notification-icons/strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M12.7 6.3a4.953 4.953 0 0 1 .2.7H12l-1 1a1 1 0 0 0 1 1h.9A5.015 5.015 0 0 1 9 12.9V12a1 1 0 0 0-1-1l-1 1v.9a4.953 4.953 0 0 1-.7-.2l-1.512 1.512a6.989 6.989 0 0 0 9.425-9.425zm2.007-5.007a1 1 0 0 0-1.414 0L12.184 2.4A6.986 6.986 0 0 0 2.4 12.184l-1.107 1.109a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414zM3.1 9H4a1 1 0 0 0 0-2h-.9A5.015 5.015 0 0 1 7 3.1V4a1 1 0 0 0 2 0v-.9a4.955 4.955 0 0 1 1.747.738l-6.908 6.909A4.957 4.957 0 0 1 3.1 9z"/>
</svg>
deleted file mode 100644
--- a/browser/themes/linux/notification-icons/geo-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="geo-linux-icon" d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 4,2.1 a 10,10 0 0 0 8,8 l 0,-4 4,0 0,4 a 10,10 0 0 0 8,-8 l -4,0 0,-4 4,0 a 10,10 0 0 0 -8,-8 l 0,4 -4,0 0,-4 a 10,10 0 0 0 -8,8 l 4,0 0,4 z"/>
--- a/browser/themes/linux/notification-icons/geo.svg
+++ b/browser/themes/linux/notification-icons/geo.svg
@@ -1,7 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
-#include geo-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm1 11.9V12a1 1 0 0 0-2 0v.9A5.015 5.015 0 0 1 3.1 9H4a1 1 0 0 0 0-2h-.9A5.015 5.015 0 0 1 7 3.1V4a1 1 0 0 0 2 0v-.9A5.015 5.015 0 0 1 12.9 7H12a1 1 0 0 0 0 2h.9A5.015 5.015 0 0 1 9 12.9z"/>
</svg>
deleted file mode 100644
index 7dd795ac92d4d0c9e84799bf5f864102374d4ab4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -559,39 +559,16 @@ html|span.ac-emphasize-text-url {
}
#BMB_bookmarksPopup[side="left"],
#BMB_bookmarksPopup[side="right"] {
margin-top: -26px;
margin-bottom: -26px;
}
-/* POPUP BLOCKER BUTTON */
-#page-report-button {
- list-style-image: url("chrome://browser/skin/urlbar-popup-blocked.png");
- -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-#page-report-button:hover:active,
-#page-report-button[open="true"] {
- -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
-@media (min-resolution: 2dppx) {
- #page-report-button {
- list-style-image: url("chrome://browser/skin/urlbar-popup-blocked@2x.png");
- -moz-image-region: rect(0, 32px, 32px, 0);
- }
-
- #page-report-button:hover:active,
- #page-report-button[open="true"] {
- -moz-image-region: rect(0, 64px, 32px, 32px);
- }
-}
-
/* BOOKMARKING PANEL */
#editBookmarkPanelStarIcon {
list-style-image: url("chrome://browser/skin/places/starred48.png");
width: 48px;
height: 48px;
}
#editBookmarkPanelStarIcon[unstarred] {
@@ -1255,18 +1232,16 @@ html|*.addon-webext-perm-list {
border-top-left-radius: .3em;
margin-left: 1em;
}
%include ../shared/fullscreen/warning.inc.css
%include ../shared/ctrlTab.inc.css
%include ../shared/plugin-doorhanger.inc.css
-%include downloads/indicator.css
-
/* On mac, the popup notification contents are indented by default and so
the default closebutton margins from notification.css require adjustment */
.click-to-play-plugins-notification-description-box > .popup-notification-closebutton {
margin-inline-end: -6px;
margin-top: -7px;
}
deleted file mode 100644
index 583dac86eba3fcf8f06deec44b17bd30b1abe068..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 9b48fbd120a8f16d91d77d71a40da8791235855a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/browser/themes/osx/downloads/indicator.css
+++ /dev/null
@@ -1,12 +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/. */
-
-%include ../../shared/downloads/indicator.inc.css
-
-
-@media (min-resolution: 2dppx) {
- #downloads-button[cui-areatype="menu-panel"][attention="success"] {
- list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel@2x.png");
- }
-}
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -5,17 +5,16 @@
browser.jar:
% skin browser classic/1.0 %skin/classic/browser/
#include ../shared/jar.inc.mn
skin/classic/browser/sanitizeDialog.css
skin/classic/browser/aboutSessionRestore-window-icon.png
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
* skin/classic/browser/browser.css
* skin/classic/browser/compacttheme.css
- skin/classic/browser/Info.png
skin/classic/browser/subtle-pattern.png
skin/classic/browser/menu-back.png
skin/classic/browser/menu-forward.png
skin/classic/browser/menuPanel-customize.png
skin/classic/browser/menuPanel-customize@2x.png
skin/classic/browser/menuPanel-exit.png
skin/classic/browser/menuPanel-exit@2x.png
skin/classic/browser/menuPanel-help.png
@@ -33,40 +32,30 @@ browser.jar:
skin/classic/browser/privatebrowsing-mask-short.png
skin/classic/browser/privatebrowsing-mask-short@2x.png
skin/classic/browser/reload-stop-go.png
skin/classic/browser/reload-stop-go@2x.png
skin/classic/browser/searchbar.css
skin/classic/browser/slowStartup-16.png
skin/classic/browser/toolbarbutton-dropmarker.png
skin/classic/browser/toolbarbutton-dropmarker@2x.png
- skin/classic/browser/urlbar-popup-blocked.png
- skin/classic/browser/urlbar-popup-blocked@2x.png
- skin/classic/browser/webRTC-sharingDevice-menubar.png
- skin/classic/browser/webRTC-sharingDevice-menubar@2x.png
- skin/classic/browser/webRTC-sharingMicrophone-menubar.png
- skin/classic/browser/webRTC-sharingMicrophone-menubar@2x.png
- skin/classic/browser/webRTC-sharingScreen-menubar.png
- skin/classic/browser/webRTC-sharingScreen-menubar@2x.png
skin/classic/browser/webRTC-indicator.css
* skin/classic/browser/controlcenter/panel.css (controlcenter/panel.css)
* skin/classic/browser/customizableui/panelUI.css (customizableui/panelUI.css)
* skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
- skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
- skin/classic/browser/downloads/download-glow-menuPanel@2x.png (downloads/download-glow-menuPanel@2x.png)
* skin/classic/browser/downloads/downloads.css (downloads/downloads.css)
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
* skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/setDesktopBackground.css
skin/classic/browser/monitor.png
skin/classic/browser/monitor_16-10.png
-* skin/classic/browser/notification-icons/geo-blocked.svg (notification-icons/geo-blocked.svg)
-* skin/classic/browser/notification-icons/geo.svg (notification-icons/geo.svg)
+ skin/classic/browser/notification-icons/geo-blocked.svg (notification-icons/geo-blocked.svg)
+ skin/classic/browser/notification-icons/geo.svg (notification-icons/geo.svg)
skin/classic/browser/places/allBookmarks.png (places/allBookmarks.png)
* skin/classic/browser/places/places.css (places/places.css)
skin/classic/browser/places/organizer.css (places/organizer.css)
skin/classic/browser/places/query.png (places/query.png)
skin/classic/browser/places/query@2x.png (places/query@2x.png)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
skin/classic/browser/places/bookmarksToolbar@2x.png (places/bookmarksToolbar@2x.png)
--- a/browser/themes/osx/notification-icons/geo-blocked.svg
+++ b/browser/themes/osx/notification-icons/geo-blocked.svg
@@ -1,12 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <defs>
-#include ../../shared/notification-icons/clip-path.inc.svg
-#include geo-icon.inc.svg
- </defs>
- <use clip-path="url(#blocked-clipPath)" href="#geo-osx-icon"/>
-#include ../../shared/notification-icons/strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M8 16l3.75-8.75L8 11v5zm6.707-14.707a1 1 0 0 0-1.414 0l-1.768 1.768L0 8h6.586l-5.293 5.293a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
</svg>
deleted file mode 100644
--- a/browser/themes/osx/notification-icons/geo-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="geo-osx-icon" d="m 0,16 16,0 0,16 12,-28 z"/>
--- a/browser/themes/osx/notification-icons/geo.svg
+++ b/browser/themes/osx/notification-icons/geo.svg
@@ -1,7 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
-#include geo-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M0 8l14-6-6 14V8z"/>
</svg>
deleted file mode 100644
index 8c4f4d96f06ed2067f62d012d1f91ab1b98503f8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 4d6aa6f2009e0f025f4b759ed3014c199de36ee0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/osx/webRTC-indicator.css
+++ b/browser/themes/osx/webRTC-indicator.css
@@ -1,35 +1,28 @@
/* 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/. */
+#webRTC-sharingCamera-menu,
+#webRTC-sharingMicrophone-menu,
+#webRTC-sharingScreen-menu {
+ -moz-context-properties: fill;
+ fill: currentColor;
+}
+
#webRTC-sharingCamera-menu {
- list-style-image: url("chrome://browser/skin/webRTC-sharingDevice-menubar.png");
+ list-style-image: url("chrome://browser/skin/notification-icons/camera.svg");
}
#webRTC-sharingMicrophone-menu {
- list-style-image: url("chrome://browser/skin/webRTC-sharingMicrophone-menubar.png");
+ list-style-image: url("chrome://browser/skin/notification-icons/microphone.svg");
}
#webRTC-sharingScreen-menu {
- list-style-image: url("chrome://browser/skin/webRTC-sharingScreen-menubar.png");
-}
-
-@media (min-resolution: 2dppx) {
- #webRTC-sharingCamera-menu {
- list-style-image: url("chrome://browser/skin/webRTC-sharingDevice-menubar@2x.png");
- }
-
- #webRTC-sharingMicrophone-menu {
- list-style-image: url("chrome://browser/skin/webRTC-sharingMicrophone-menubar@2x.png");
- }
-
- #webRTC-sharingScreen-menu {
- list-style-image: url("chrome://browser/skin/webRTC-sharingScreen-menubar@2x.png");
- }
+ list-style-image: url("chrome://browser/skin/notification-icons/screen.svg");
}
#webRTC-sharingCamera-menu > menupopup,
#webRTC-sharingMicrophone-menu > menupopup,
#webRTC-sharingScreen-menu > menupopup {
list-style-image: none; /* don't inherit into menu items */
}
deleted file mode 100644
index fb74738dd14e67d3481a39b0aa6f766f29d59365..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 8f64887a8c5b6cd47ef7f2076bab83f03f307b5b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index c47f5827d753d1da4a84b8795462cff48bd8bde1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 2c45c8bccc3e23c3c0f7a0935556819e69c74793..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 6d9e49cfd6200eacb7df70ba1fe4160ff505e321..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 0f3d62ec5599ee6ab537e6241107727ce3aad823..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/shared/browser.inc.css
+++ b/browser/themes/shared/browser.inc.css
@@ -11,16 +11,18 @@
%else
:root[tabsintitlebar][sizemode=normal] #TabsToolbar
%endif
{
padding-inline-start: 40px;
}
%endif
+%include downloads/indicator.inc.css
+
/* Toolbar / content area border */
#navigator-toolbox::after {
content: "";
display: -moz-box;
border-bottom: 1px solid var(--toolbox-border-bottom-color);
}
--- a/browser/themes/shared/compacttheme.inc.css
+++ b/browser/themes/shared/compacttheme.inc.css
@@ -105,25 +105,16 @@ toolbar[brighttext] .toolbarbutton-1 {
.searchbar-textbox:not([focused="true"]) {
border-color: var(--chrome-nav-bar-controls-border-color);
}
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #identity-icon-labels:-moz-lwtheme-brighttext {
color: #30e60b;
}
-#identity-icon:-moz-lwtheme-brighttext,
-#tracking-protection-icon:-moz-lwtheme-brighttext,
-#connection-icon:-moz-lwtheme-brighttext,
-.notification-anchor-icon:-moz-lwtheme-brighttext,
-#blocked-permissions-container > .blocked-permission-icon:-moz-lwtheme-brighttext,
-#extension-icon:-moz-lwtheme-brighttext {
- fill: rgba(255,255,255,.7);
-}
-
#urlbar-zoom-button:-moz-lwtheme-brighttext:hover {
background-color: rgba(255,255,255,.2);
}
#urlbar-zoom-button:-moz-lwtheme-brighttext:hover:active {
background-color: rgba(255,255,255,.3);
}
--- a/browser/themes/shared/contextmenu.inc.css
+++ b/browser/themes/shared/contextmenu.inc.css
@@ -42,10 +42,12 @@
#context-back:-moz-locale-dir(rtl),
#context-forward:-moz-locale-dir(rtl),
#context-reload:-moz-locale-dir(rtl) {
transform: scaleX(-1);
}
#context-media-eme-learnmore {
- list-style-image: url("chrome://browser/skin/drm-icon.svg#chains");
+ list-style-image: url("chrome://browser/skin/drm-icon.svg");
+ -moz-context-properties: fill;
+ fill: currentColor;
}
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -931,17 +931,16 @@ toolbaritem[cui-areatype="menu-panel"][s
#PanelUI-remotetabs[mainview] .PanelUI-remotetabs-notabsforclient-label {
margin-left: 32px;
}
.fxaSyncIllustration {
width: 180px;
height: var(--panel-ui-sync-illustration-height);
- list-style-image: url(chrome://browser/skin/fxa/sync-illustration.svg);
-moz-context-properties: fill;
fill: #cdcdcd;
}
.PanelUI-remotetabs-prefs-button > .toolbarbutton-text {
/* !important to override ".cui-widget-panel toolbarbutton > .toolbarbutton-text" above. */
text-align: center !important;
text-shadow: none;
--- a/browser/themes/shared/downloads/indicator.inc.css
+++ b/browser/themes/shared/downloads/indicator.inc.css
@@ -1,18 +1,14 @@
/* 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/. */
/*** Status and progress indicator ***/
-#downloads-button {
- --downloads-indicator-image: url("chrome://browser/skin/downloads/download-icons.svg#arrow-with-bar");
-}
-
#downloads-indicator-anchor {
min-width: 16px;
min-height: 16px;
}
#downloads-indicator-progress-outer {
width: 16px;
height: 16px;
@@ -24,21 +20,16 @@
#downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon,
#downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-outer {
fill: var(--toolbarbutton-icon-fill-attention);
}
#downloads-button[progress] > #downloads-indicator-anchor > #downloads-indicator-progress-outer {
background: url("chrome://browser/skin/downloads/download-icons.svg#progress-bar-bg") center no-repeat;
}
-#downloads-button[attention="success"] {
- list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel.png");
- -moz-image-region: auto;
-}
-
#downloads-indicator-icon {
-moz-context-properties: fill;
background-image: url("chrome://browser/skin/downloads/download-icons.svg#arrow");
width: 16px;
height: 16px;
}
#downloads-indicator-progress-inner {
--- a/browser/themes/shared/drm-icon.svg
+++ b/browser/themes/shared/drm-icon.svg
@@ -1,38 +1,8 @@
-<?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" viewBox="0 0 16 16">
- <style>
- #chains {
- fill: url(#baseGradient);
- }
- #chains-pressed {
- fill: url(#pressedGradient);
- }
- #chains-black {
- fill: #000;
- }
- use:not(:target) {
- display: none;
- }
- </style>
- <defs>
- <linearGradient id="baseGradient" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="16" y2="0">
- <stop offset="0" style="stop-color: #808080"/>
- <stop offset="1" style="stop-color: #999"/>
- </linearGradient>
- <linearGradient id="pressedGradient" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="16" y2="0">
- <stop offset="0" style="stop-color: #4d4d4d"/>
- <stop offset="1" style="stop-color: #808080"/>
- </linearGradient>
- <g id="path">
- <path d="M7.058,9.72c-0.245,0.245-0.62,0.27-0.834,0.056C6.01,9.562,6.035,9.186,6.28,8.942l0.218-0.218 c-0.245-0.245-0.645-0.245-0.89,0L4.496,9.836c-0.245,0.245-0.245,0.645,0,0.89l0.779,0.779c0.245,0.245,0.645,0.245,0.89,0 l1.112-1.112c0.245-0.245,0.245-0.645,0-0.89L7.058,9.72z"/>
- <path d="M10.726,4.496c-0.245-0.245-0.645-0.245-0.89,0L8.723,5.608c-0.245,0.245-0.245,0.645,0,0.89 L8.95,6.272c0.245-0.245,0.62-0.27,0.834-0.056s0.189,0.59-0.056,0.834L9.502,7.277c0.245,0.245,0.645,0.245,0.89,0l1.112-1.112 c0.245-0.245,0.245-0.645,0-0.89L10.726,4.496z"/>
- <path d="M8,0C3.582,0,0,3.582,0,8s3.582,8,8,8s8-3.582,8-8S12.418,0,8,0z M12.527,6.81l-1.489,1.489 c-0.631,0.631-1.663,0.631-2.293,0L8.612,8.167L8.167,8.612l0.133,0.133c0.631,0.631,0.631,1.663,0,2.293L6.81,12.527 c-0.631,0.631-1.663,0.631-2.293,0l-1.044-1.044c-0.631-0.631-0.631-1.663,0-2.293l1.489-1.489c0.631-0.631,1.663-0.631,2.293,0 l0.133,0.133l0.445-0.445L7.701,7.255c-0.631-0.631-0.631-1.663,0-2.293L9.19,3.473c0.631-0.631,1.663-0.631,2.293,0l1.044,1.044 C13.158,5.148,13.158,6.18,12.527,6.81z"/>
- </g>
- </defs>
- <use xlink:href="#path" id="chains"/>
- <use xlink:href="#path" id="chains-pressed"/>
- <use xlink:href="#path" id="chains-black"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M7.058,9.72c-0.245,0.245-0.62,0.27-0.834,0.056C6.01,9.562,6.035,9.186,6.28,8.942l0.218-0.218 c-0.245-0.245-0.645-0.245-0.89,0L4.496,9.836c-0.245,0.245-0.245,0.645,0,0.89l0.779,0.779c0.245,0.245,0.645,0.245,0.89,0 l1.112-1.112c0.245-0.245,0.245-0.645,0-0.89L7.058,9.72z"/>
+ <path d="M10.726,4.496c-0.245-0.245-0.645-0.245-0.89,0L8.723,5.608c-0.245,0.245-0.245,0.645,0,0.89 L8.95,6.272c0.245-0.245,0.62-0.27,0.834-0.056s0.189,0.59-0.056,0.834L9.502,7.277c0.245,0.245,0.645,0.245,0.89,0l1.112-1.112 c0.245-0.245,0.245-0.645,0-0.89L10.726,4.496z"/>
+ <path d="M8,0C3.582,0,0,3.582,0,8s3.582,8,8,8s8-3.582,8-8S12.418,0,8,0z M12.527,6.81l-1.489,1.489 c-0.631,0.631-1.663,0.631-2.293,0L8.612,8.167L8.167,8.612l0.133,0.133c0.631,0.631,0.631,1.663,0,2.293L6.81,12.527 c-0.631,0.631-1.663,0.631-2.293,0l-1.044-1.044c-0.631-0.631-0.631-1.663,0-2.293l1.489-1.489c0.631-0.631,1.663-0.631,2.293,0 l0.133,0.133l0.445-0.445L7.701,7.255c-0.631-0.631-0.631-1.663,0-2.293L9.19,3.473c0.631-0.631,1.663-0.631,2.293,0l1.044,1.044 C13.158,5.148,13.158,6.18,12.527,6.81z"/>
</svg>
--- a/browser/themes/shared/incontentprefs-old/containers.css
+++ b/browser/themes/shared/incontentprefs-old/containers.css
@@ -18,15 +18,15 @@
background: transparent;
}
#containersView richlistitem {
margin: 0px;
margin-inline-end: 8px;
padding: 0;
padding-block-end: 8px;
- border-block-end: 1px solid var(--in-content-header-border-color);
+ border-block-end: 1px solid var(--in-content-box-border-color);
}
#containersView richlistitem:last-of-type {
border-block-end: 0 none;
margin-block-end: 8px;
}
--- a/browser/themes/shared/incontentprefs-old/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs-old/preferences.inc.css
@@ -460,17 +460,17 @@ description > html|a {
margin-bottom: 27.5px;
}
#noFxaDescription {
margin-bottom: 20px !important;
}
.separator {
- border-bottom: 1px solid var(--in-content-header-border-color);
+ border-bottom: 1px solid var(--in-content-box-border-color);
}
.fxaAccountBox {
border: 1px solid #D1D2D3;
border-radius: 5px;
padding: 14px 20px 14px 14px;
}
--- a/browser/themes/shared/incontentprefs/containers.css
+++ b/browser/themes/shared/incontentprefs/containers.css
@@ -18,15 +18,15 @@
background: transparent;
}
#containersView richlistitem {
margin: 0px;
margin-inline-end: 8px;
padding: 0;
padding-block-end: 8px;
- border-block-end: 1px solid var(--in-content-header-border-color);
+ border-block-end: 1px solid var(--in-content-box-border-color);
}
#containersView richlistitem:last-of-type {
border-block-end: 0 none;
margin-block-end: 8px;
}
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -540,17 +540,17 @@ separator.thin:not([orient="vertical"])
height: 136px;
}
#noFxaDescription {
padding-inline-end: 52px;
}
.separator {
- border-bottom: 1px solid var(--in-content-header-border-color);
+ border-bottom: 1px solid var(--in-content-box-border-color);
}
#fxaGroup {
margin-top: 16px;
margin-bottom: 32px;
}
#signedOutAccountBoxTitle {
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -51,37 +51,37 @@
skin/classic/browser/fullscreen/secure.svg (../shared/fullscreen/secure.svg)
skin/classic/browser/connection-secure.svg (../shared/identity-block/connection-secure.svg)
skin/classic/browser/connection-mixed-passive-loaded.svg (../shared/identity-block/connection-mixed-passive-loaded.svg)
skin/classic/browser/connection-mixed-active-loaded.svg (../shared/identity-block/connection-mixed-active-loaded.svg)
skin/classic/browser/identity-icon.svg (../shared/identity-block/identity-icon.svg)
skin/classic/browser/identity-icon-notice.svg (../shared/identity-block/identity-icon-notice.svg)
skin/classic/browser/info.svg (../shared/info.svg)
-* skin/classic/browser/notification-icons/camera-blocked.svg (../shared/notification-icons/camera-blocked.svg)
-* skin/classic/browser/notification-icons/camera.svg (../shared/notification-icons/camera.svg)
+ skin/classic/browser/notification-icons/camera-blocked.svg (../shared/notification-icons/camera-blocked.svg)
+ skin/classic/browser/notification-icons/camera.svg (../shared/notification-icons/camera.svg)
skin/classic/browser/notification-icons/default-info.svg (../shared/notification-icons/default-info.svg)
-* skin/classic/browser/notification-icons/desktop-notification-blocked.svg (../shared/notification-icons/desktop-notification-blocked.svg)
-* skin/classic/browser/notification-icons/desktop-notification.svg (../shared/notification-icons/desktop-notification.svg)
+ skin/classic/browser/notification-icons/desktop-notification-blocked.svg (../shared/notification-icons/desktop-notification-blocked.svg)
+ skin/classic/browser/notification-icons/desktop-notification.svg (../shared/notification-icons/desktop-notification.svg)
skin/classic/browser/notification-icons/focus-tab-by-prompt.svg (../shared/notification-icons/focus-tab-by-prompt.svg)
-* skin/classic/browser/notification-icons/indexedDB-blocked.svg (../shared/notification-icons/indexedDB-blocked.svg)
-* skin/classic/browser/notification-icons/indexedDB.svg (../shared/notification-icons/indexedDB.svg)
+ skin/classic/browser/notification-icons/indexedDB-blocked.svg (../shared/notification-icons/indexedDB-blocked.svg)
+ skin/classic/browser/notification-icons/indexedDB.svg (../shared/notification-icons/indexedDB.svg)
skin/classic/browser/notification-icons/login-detailed.svg (../shared/notification-icons/login-detailed.svg)
skin/classic/browser/notification-icons/login.svg (../shared/notification-icons/login.svg)
-* skin/classic/browser/notification-icons/microphone-blocked.svg (../shared/notification-icons/microphone-blocked.svg)
+ skin/classic/browser/notification-icons/microphone-blocked.svg (../shared/notification-icons/microphone-blocked.svg)
skin/classic/browser/notification-icons/microphone-detailed.svg (../shared/notification-icons/microphone-detailed.svg)
-* skin/classic/browser/notification-icons/microphone.svg (../shared/notification-icons/microphone.svg)
-* skin/classic/browser/notification-icons/persistent-storage-blocked.svg (../shared/notification-icons/persistent-storage-blocked.svg)
-* skin/classic/browser/notification-icons/persistent-storage.svg (../shared/notification-icons/persistent-storage.svg)
+ skin/classic/browser/notification-icons/microphone.svg (../shared/notification-icons/microphone.svg)
+ skin/classic/browser/notification-icons/persistent-storage-blocked.svg (../shared/notification-icons/persistent-storage-blocked.svg)
+ skin/classic/browser/notification-icons/persistent-storage.svg (../shared/notification-icons/persistent-storage.svg)
skin/classic/browser/notification-icons/plugin-badge.svg (../shared/notification-icons/plugin-badge.svg)
-* skin/classic/browser/notification-icons/plugin-blocked.svg (../shared/notification-icons/plugin-blocked.svg)
-* skin/classic/browser/notification-icons/plugin.svg (../shared/notification-icons/plugin.svg)
+ skin/classic/browser/notification-icons/plugin-blocked.svg (../shared/notification-icons/plugin-blocked.svg)
+ skin/classic/browser/notification-icons/plugin.svg (../shared/notification-icons/plugin.svg)
skin/classic/browser/notification-icons/popup.svg (../shared/notification-icons/popup.svg)
-* skin/classic/browser/notification-icons/screen-blocked.svg (../shared/notification-icons/screen-blocked.svg)
-* skin/classic/browser/notification-icons/screen.svg (../shared/notification-icons/screen.svg)
+ skin/classic/browser/notification-icons/screen-blocked.svg (../shared/notification-icons/screen-blocked.svg)
+ skin/classic/browser/notification-icons/screen.svg (../shared/notification-icons/screen.svg)
skin/classic/browser/notification-icons/update.svg (../shared/notification-icons/update.svg)
skin/classic/browser/tracking-protection-16.svg (../shared/identity-block/tracking-protection-16.svg)
skin/classic/browser/newtab/close.png (../shared/newtab/close.png)
skin/classic/browser/newtab/controls.svg (../shared/newtab/controls.svg)
skin/classic/browser/panel-icon-arrow-left.svg (../shared/panel-icon-arrow-left.svg)
skin/classic/browser/panel-icon-arrow-right.svg (../shared/panel-icon-arrow-right.svg)
skin/classic/browser/panel-icon-cancel.svg (../shared/panel-icon-cancel.svg)
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -149,21 +149,17 @@ html|*#webRTC-previewVideo {
.popup-icon {
list-style-image: url("chrome://browser/skin/notification-icons/popup.svg");
}
/* EME */
.popup-notification-icon[popupid="drmContentPlaying"],
.drm-icon {
- list-style-image: url("chrome://browser/skin/drm-icon.svg#chains");
-}
-
-.drm-icon:hover:active {
- list-style-image: url("chrome://browser/skin/drm-icon.svg#chains-pressed");
+ list-style-image: url("chrome://browser/skin/drm-icon.svg");
}
#eme-notification-icon[firstplay=true] {
animation: emeTeachingMoment 0.2s linear 0s 5 normal;
}
@keyframes emeTeachingMoment {
0% {transform: translateX(0); }
--- a/browser/themes/shared/notification-icons/camera-blocked.svg
+++ b/browser/themes/shared/notification-icons/camera-blocked.svg
@@ -1,12 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <defs>
-#include clip-path.inc.svg
-#include camera-icon.inc.svg
- </defs>
- <use clip-path="url(#blocked-clipPath)" href="#camera-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M9.7 13a1.345 1.345 0 0 0 1.3-1.389V9.552l3.037 2.648a1.007 1.007 0 0 0 .963.285V4l-9 9zm5.007-11.707a1 1 0 0 0-1.414 0l-2.452 2.452A1.284 1.284 0 0 0 9.7 3H2.3A1.345 1.345 0 0 0 1 4.389v7.222a1.4 1.4 0 0 0 .731 1.244l-.438.438a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
</svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/camera-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="camera-icon" d="m 2,23 a 3,3 0 0 0 3,3 l 14,0 a 3,3 0 0 0 3,-3 l 0,-4 6,5.5 c 0.5,0.5 1,0.7 2,0.5 l 0,-18 c -1,-0.2 -1.5,0 -2,0.5 l -6,5.5 0,-4 a 3,3 0 0 0 -3,-3 l -14,0 a 3,3 0 0 0 -3,3 z"/>
--- a/browser/themes/shared/notification-icons/camera.svg
+++ b/browser/themes/shared/notification-icons/camera.svg
@@ -1,7 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
-#include camera-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M14.037 3.828L11 6.479v-2.09A1.345 1.345 0 0 0 9.7 3H2.3A1.345 1.345 0 0 0 1 4.389v7.222A1.345 1.345 0 0 0 2.3 13h7.4a1.345 1.345 0 0 0 1.3-1.389V9.552l3.037 2.648a1.007 1.007 0 0 0 .963.285V3.542a1.007 1.007 0 0 0-.963.286z"/>
</svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/clip-path.inc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<clipPath id="blocked-clipPath">
- <path d="m 0,0 0,31 31,-31 z m 6,32 26,0 0,-26 z"/>
-</clipPath>
--- a/browser/themes/shared/notification-icons/default-info.svg
+++ b/browser/themes/shared/notification-icons/default-info.svg
@@ -1,16 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <defs>
- <mask id="i-mask" fill-opacity="1">
- <rect fill="white" width="32" height="32"/>
- <circle fill="black" cx="16" cy="9" r="2.5"/>
- <rect fill="black" x="14" y="14" width="4" height="10" rx="2" ry="2"/>
- </mask>
- </defs>
- <g>
- <circle cx="16" cy="16" r="14" mask="url(#i-mask)"/>
- </g>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path fill-rule="evenodd" d="M8 1a7 7 0 1 1-7 7 7 7 0 0 1 7-7zm0 3a1 1 0 1 1-1 1 1 1 0 0 1 1-1zm0 3a1 1 0 0 1 1 1v3a1 1 0 0 1-2 0V8a1 1 0 0 1 1-1z"/>
</svg>
--- a/browser/themes/shared/notification-icons/desktop-notification-blocked.svg
+++ b/browser/themes/shared/notification-icons/desktop-notification-blocked.svg
@@ -1,12 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <defs>
-#include clip-path.inc.svg
-#include desktop-notification-icon.inc.svg
- </defs>
- <use clip-path="url(#blocked-clipPath)" href="#desktop-notification-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M15.961 3.039L14 5v4.577a.423.423 0 0 1-.423.423H12a1 1 0 0 0-1 1v1.194l-1.491-1.827a.978.978 0 0 0-.558-.318L7 12h1.26l2.966 3.633A1 1 0 0 0 13 15v-3h.577A2.426 2.426 0 0 0 16 9.577V3.423a2.4 2.4 0 0 0-.039-.384zm-1.254-1.746A1.013 1.013 0 0 0 14 1H2.423A2.426 2.426 0 0 0 0 3.423v6.154A2.426 2.426 0 0 0 2.423 12h.163l-1.293 1.293a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414zM2.423 10A.423.423 0 0 1 2 9.577V3.423A.423.423 0 0 1 2.423 3h9.163l-2 2H4.5a.5.5 0 0 0 0 1h4.086l-1 1H4.5a.5.5 0 0 0 0 1h2.086l-2 2z"/>
</svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/desktop-notification-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="desktop-notification-icon" d="m 2,20 a 4,4 0 0 0 4,4 l 13,0 7,7 0,-7 a 4,4 0 0 0 4,-4 l 0,-12 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 5,-2 a 1,1 0 1 1 0,-2 l 10,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 14,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 18,0 a 1,1 0 1 1 0,2 z"/>
--- a/browser/themes/shared/notification-icons/desktop-notification.svg
+++ b/browser/themes/shared/notification-icons/desktop-notification.svg
@@ -1,7 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
-#include desktop-notification-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M13.577 1H2.423A2.426 2.426 0 0 0 0 3.423v6.154A2.426 2.426 0 0 0 2.423 12H8.26l2.966 3.633A1 1 0 0 0 13 15v-3h.577A2.426 2.426 0 0 0 16 9.577V3.423A2.426 2.426 0 0 0 13.577 1zM14 9.577a.423.423 0 0 1-.423.423H12a1 1 0 0 0-1 1v1.194l-1.491-1.827A1 1 0 0 0 8.734 10H2.423A.423.423 0 0 1 2 9.577V3.423A.423.423 0 0 1 2.423 3h11.154a.423.423 0 0 1 .423.423z"/>
+ <path d="M11.5 5h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm0 2h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1z"/>
</svg>
--- a/browser/themes/shared/notification-icons/indexedDB-blocked.svg
+++ b/browser/themes/shared/notification-icons/indexedDB-blocked.svg
@@ -1,12 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <defs>
-#include clip-path.inc.svg
-#include indexedDB-icon.inc.svg
- </defs>
- <use clip-path="url(#blocked-clipPath)" href="#indexedDB-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M10.293 11.293L9 12.586V10l-2 2v.586l-.293-.293-1.414 1.414 2 2a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414zM13 8h-1a1 1 0 0 0 0 2h2a1 1 0 0 0 1-1V4l-2 2zm1.707-6.707A1.016 1.016 0 0 0 14 1H2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h2a1 1 0 0 0 0-2H3V3h8.586L1.293 13.293a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
</svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/indexedDB-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="indexedDB-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 2,0 0,-4 -2,0 0,-16 20,0 0,16 -2,0 0,4 2,0 a 4,4 0 0 0 4,-4 l 0,-16 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 8,-2 6,7 6,-7 -4,0 0,-8 -4,0 0,8 z"/>
--- a/browser/themes/shared/notification-icons/indexedDB.svg
+++ b/browser/themes/shared/notification-icons/indexedDB.svg
@@ -1,7 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
-#include indexedDB-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M14 1H2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h2a1 1 0 0 0 0-2H3V3h10v5h-1a1 1 0 0 0 0 2h2a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1z"/>
+ <path d="M10.293 11.293L9 12.586V8a1 1 0 0 0-2 0v4.586l-1.293-1.293a1 1 0 0 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414z"/>
</svg>
--- a/browser/themes/shared/notification-icons/login.svg
+++ b/browser/themes/shared/notification-icons/login.svg
@@ -1,7 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <path d="m 2,26 0,4 6,0 0,-2 2,0 0,-2 1,0 0,-1 2,0 0,-3 2,0 2.5,-2.5 1.5,1.5 3,-3 a 8,8 0 1 0 -8,-8 l -3,3 2,2 z m 20,-18.1 a 2,2 0 1 1 0,0.2 z"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M10.992 1a4.009 4.009 0 0 0-4.009 4.008c0 .1.022.187.028.282-.059.05-.119.087-.178.143L5.667 6.6a.366.366 0 0 0 0 .467A1.878 1.878 0 0 0 6 7.5.353.353 0 0 1 6 8l-5 5v1.767a.229.229 0 0 0 .233.233H3.77a.229.229 0 0 0 .23-.233v-.778h.75a.227.227 0 0 0 .233-.228v-.768H5.2s.28 0 .28-.235V12.5h.779s.233-.1.233-.244v-1.271h.855l1.12-1.118H8.7l.467.467c.233.233.233.233.365.233a.437.437 0 0 0 .275-.127l.993-1.273c.034-.053.054-.107.084-.161.036 0 .07.011.107.011a4.008 4.008 0 1 0 0-8.017zM12.5 4.489a1 1 0 1 1 1-1 1 1 0 0 1-1 1z"/>
</svg>
--- a/browser/themes/shared/notification-icons/microphone-blocked.svg
+++ b/browser/themes/shared/notification-icons/microphone-blocked.svg
@@ -1,12 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <defs>
-#include clip-path.inc.svg
-#include microphone-icon.inc.svg
- </defs>
- <use clip-path="url(#blocked-clipPath)" href="#microphone-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M11 8v1a3 3 0 0 1-3 3 2.958 2.958 0 0 1-.859-.141l-.775.775a4.012 4.012 0 0 0 .634.224V14H5.5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1H9v-1.142A4 4 0 0 0 12 9V7.5a.5.5 0 0 0-.146-.354zm3.707-6.707a1 1 0 0 0-1.414 0L10 4.586V3a2 2 0 1 0-4 0v5.586l-.945.945A2.856 2.856 0 0 1 5 9V7.5a.5.5 0 0 0-1 0V9a3.843 3.843 0 0 0 .248 1.338l-2.955 2.955a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
</svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/microphone-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="microphone-icon" d="m 8,14 0,4 a 8,8 0 0 0 6,7.7 l 0,2.3 -2,0 a 2,2 0 0 0 -2,2 l 12,0 a 2,2 0 0 0 -2,-2 l -2,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 -2,0 0,4 a 6,6 0 0 1 -12,0 l 0,-4 z m 4,4 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z"/>
--- a/browser/themes/shared/notification-icons/microphone.svg
+++ b/browser/themes/shared/notification-icons/microphone.svg
@@ -1,7 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
-#include microphone-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M8 11a2 2 0 0 0 2-2V3a2 2 0 1 0-4 0v6a2 2 0 0 0 2 2z"/>
+ <path d="M12 9V7.5a.5.5 0 0 0-1 0V9a3 3 0 0 1-6 0V7.5a.5.5 0 0 0-1 0V9a4 4 0 0 0 3 3.858V14H5.5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1H9v-1.142A4 4 0 0 0 12 9z"/>
</svg>
--- a/browser/themes/shared/notification-icons/persistent-storage-blocked.svg
+++ b/browser/themes/shared/notification-icons/persistent-storage-blocked.svg
@@ -1,12 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <defs>
-#include clip-path.inc.svg
-#include persistent-storage-icon.inc.svg
- </defs>
- <use clip-path="url(#blocked-clipPath)" href="#persistent-storage-icon"/>
-#include strikeout.inc.svg
+<svg width="32" height="32" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
+ <path d="M4 24.172V23.1c0-1.1.9-2 2-2h1.072l2.1-2.1H6c-.7 0-1.4.2-2 .5V6.2C4 4.4 5.3 3 7 3h18c.056 0 .112.002.167.005l1.42-1.42c.78-.78 2.046-.78 2.827 0 .78.782.78 2.048 0 2.83l-24 24c-.78.78-2.047.78-2.828 0-.78-.782-.78-2.048 0-2.83L4 24.173zM7.828 29l7.9-7.9H26c1.1 0 2 .9 2 2V27c0 1.1-.9 2-2 2H7.828zM28 8.828V19.5c-.6-.3-1.3-.5-2-.5h-8.172L28 8.828zM24.1 27c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z" fill-rule="nonzero"/>
</svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/persistent-storage-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="persistent-storage-icon" d="M26 21.1H6c-1.1 0-2 .9-2 2V27c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2v-3.9c0-1.1-.9-2-2-2zM24.1 27c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM25 3H7C5.3 3 4 4.4 4 6.2v13.3c.6-.3 1.3-.5 2-.5h20c.7 0 1.4.2 2 .5V6.2C28 4.4 26.7 3 25 3z"/>
--- a/browser/themes/shared/notification-icons/persistent-storage.svg
+++ b/browser/themes/shared/notification-icons/persistent-storage.svg
@@ -1,7 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
-#include persistent-storage-icon.inc.svg
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg" width="32" height="32">
+ <path d="M26 21.1H6c-1.1 0-2 .9-2 2V27c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2v-3.9c0-1.1-.9-2-2-2zM24.1 27c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM25 3H7C5.3 3 4 4.4 4 6.2v13.3c.6-.3 1.3-.5 2-.5h20c.7 0 1.4.2 2 .5V6.2C28 4.4 26.7 3 25 3z"/>
</svg>
--- a/browser/themes/shared/notification-icons/plugin-blocked.svg
+++ b/browser/themes/shared/notification-icons/plugin-blocked.svg
@@ -1,12 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <defs>
-#include clip-path.inc.svg
-#include plugin-icon.inc.svg
- </defs>
- <use clip-path="url(#blocked-clipPath)" href="#plugin-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M3 3h3a1 1 0 0 0 0-2H3a1 1 0 0 0 0 2zm11.707 1.293L5 14h9a1 1 0 0 0 1-1V5a1 1 0 0 0-.293-.707z"/>
+ <path d="M14.707 1.293a.986.986 0 0 0-1.207-.139A.973.973 0 0 0 13 1h-3a1 1 0 0 0 0 2h1.586l-1 1H2a1 1 0 0 0-1 1v8a.971.971 0 0 0 .154.5.986.986 0 0 0 .139 1.205 1 1 0 0 0 1.414 0l12-12a1 1 0 0 0 0-1.412z"/>
</svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/plugin-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="plugin-icon" d="m 2,26 a 2,2 0 0 0 2,2 l 24,0 a 2,2 0 0 0 2,-2 l 0,-16 a 2,2 0 0 0 -2,-2 l -24,0 a 2,2 0 0 0 -2,2 z m 2,-20 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z m 14,0 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z"/>
--- a/browser/themes/shared/notification-icons/plugin.svg
+++ b/browser/themes/shared/notification-icons/plugin.svg
@@ -1,7 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
-#include plugin-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <rect x="1" y="4" width="14" height="10" rx="1" ry="1"/>
+ <path d="M6 1H3a1 1 0 0 0 0 2h3a1 1 0 0 0 0-2zm7 0h-3a1 1 0 0 0 0 2h3a1 1 0 0 0 0-2z"/>
</svg>
--- a/browser/themes/shared/notification-icons/popup.svg
+++ b/browser/themes/shared/notification-icons/popup.svg
@@ -1,7 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <path d="m 2,24 a 4,4 0 0 0 4,4 l 8,0 a 10,10 0 0 1 -2,-4 l -4,0 a 2,2 0 0 1 -2,-2 l 0,-12 18,0 0,2 a 10,10 0 0 1 4,2 l 0,-8 a 4,4 0 0 0 -4,-4 l -18,0 a 4,4 0 0 0 -4,4 z m 12,-2.1 a 8,8 0 1 1 0,0.2 m 10.7,-4.3 a 5,5 0 0 0 -6.9,6.9 z m -5.4,8.4 a 5,5 0 0 0 6.9,-6.9 z"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M12 8a4 4 0 1 0 4 4 4 4 0 0 0-4-4zm0 1a2.975 2.975 0 0 1 1.733.56L9.56 13.733A2.991 2.991 0 0 1 12 9zm0 6a2.975 2.975 0 0 1-1.733-.56l4.173-4.173A2.991 2.991 0 0 1 12 15z"/>
+ <path d="M13 1H3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h3a1 1 0 0 0 0-2H3a1 1 0 0 1-1-1V6h12a1 1 0 0 0 2 0V4a3 3 0 0 0-3-3zM2 5V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1z"/>
</svg>
--- a/browser/themes/shared/notification-icons/screen-blocked.svg
+++ b/browser/themes/shared/notification-icons/screen-blocked.svg
@@ -1,12 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <defs>
-#include clip-path.inc.svg
-#include screen-icon.inc.svg
- </defs>
- <use clip-path="url(#blocked-clipPath)" href="#screen-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M4 10a1 1 0 0 0 0-2H2V3h6v1a1 1 0 0 0 2 0V2a1 1 0 0 0-1-1H1a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1zm11-4h-2l-2 2h3v5H8v-2l-2 2v1a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1z"/>
+ <path d="M14.707 1.293a1 1 0 0 0-1.414 0L8.586 6H7a1 1 0 0 0-1 1v1.586l-4.707 4.707a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
</svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/screen-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="screen-icon" d="m 2,18 a 2,2 0 0 0 2,2 l 2,0 0,-6 a 4,4 0 0 1 4,-4 l 14,0 0,-6 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z m 6,10 a 2,2 0 0 0 2,2 l 18,0 a 2,2 0 0 0 2,-2 l 0,-14 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z"/>
--- a/browser/themes/shared/notification-icons/screen.svg
+++ b/browser/themes/shared/notification-icons/screen.svg
@@ -1,7 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
-#include screen-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M15 6H7a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1zm-1 7H8V8h6z"/>
+ <path d="M4 8H2V3h6v1a1 1 0 0 0 2 0V2a1 1 0 0 0-1-1H1a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2z"/>
</svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/strikeout.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="strikeout" d="m 2,28 2,2 26,-26 -2,-2 z"/>
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -228,16 +228,22 @@
list-style-image: url(chrome://browser/skin/readerMode.svg);
}
#reader-mode-button[readeractive] {
fill: var(--toolbarbutton-icon-fill-attention);
fill-opacity: 1;
}
+/* Blocked popup icon */
+
+#page-report-button {
+ list-style-image: url(chrome://browser/skin/notification-icons/popup.svg);
+}
+
/* Zoom button */
#urlbar-zoom-button {
margin: 0 3px;
font-size: .8em;
padding: 0 8px;
border-radius: 1em;
background-color: hsla(0,0%,0%,.05);
deleted file mode 100644
index f5dfb65a3f0c7ba09cdb64b5e1518b388445dc23..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -745,32 +745,16 @@ treechildren.searchbar-treebody::-moz-tr
color: GrayText;
font-size: smaller;
}
.autocomplete-treebody::-moz-tree-cell(suggesthint) {
border-top: 1px solid GrayText;
}
-/* popup blocker button */
-
-#page-report-button {
- list-style-image: url("chrome://browser/skin/urlbar-popup-blocked.png");
- -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-#page-report-button:hover {
- -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
-#page-report-button:hover:active,
-#page-report-button[open="true"] {
- -moz-image-region: rect(0, 48px, 16px, 32px);
-}
-
/* bookmarking panel */
#editBookmarkPanelStarIcon {
list-style-image: url("chrome://browser/skin/places/starred48.png");
width: 48px;
height: 48px;
}
@@ -1085,18 +1069,16 @@ notification[value="translation"] {
*/
margin-left: 1em;
}
%include ../shared/fullscreen/warning.inc.css
%include ../shared/ctrlTab.inc.css
%include ../shared/plugin-doorhanger.inc.css
-%include downloads/indicator.css
-
/* Error counter */
#developer-toolbar-toolbox-button[error-count]:before {
color: #FDF3DE;
min-width: 16px;
text-shadow: none;
background-image: linear-gradient(#B4211B, #8A1915);
border-radius: 1px;
deleted file mode 100644
index 7ff7e6a0330012061e5cf4477c196fdc766cecf0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index d05b07b57fcc74749e7a04f0a39427a1f60763fe..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index e7415e83d8bbad00fcde9fb3926a2e51638eb6be..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 1239ada5273ee855ab939da76d6cad11583b24be..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/browser/themes/windows/downloads/indicator.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/downloads/indicator.inc.css
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -5,17 +5,16 @@
browser.jar:
% skin browser classic/1.0 %skin/classic/browser/
#include ../shared/jar.inc.mn
skin/classic/browser/sanitizeDialog.css
skin/classic/browser/aboutSessionRestore-window-icon.png
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
* skin/classic/browser/browser.css
* skin/classic/browser/compacttheme.css
- skin/classic/browser/Info.png
skin/classic/browser/livemark-folder.png
skin/classic/browser/menu-back.png
skin/classic/browser/menu-forward.png
skin/classic/browser/menuPanel-customize.png
skin/classic/browser/menuPanel-customize@2x.png
skin/classic/browser/menuPanel-exit.png
skin/classic/browser/menuPanel-exit@2x.png
skin/classic/browser/menuPanel-help.png
@@ -34,32 +33,29 @@ browser.jar:
skin/classic/browser/reload-stop-go-win7.png
skin/classic/browser/reload-stop-go-win7@2x.png
skin/classic/browser/searchbar.css
skin/classic/browser/setDesktopBackground.css
skin/classic/browser/slowStartup-16.png
skin/classic/browser/sync-desktopIcon.svg (../shared/sync-desktopIcon.svg)
skin/classic/browser/sync-mobileIcon.svg (../shared/sync-mobileIcon.svg)
skin/classic/browser/toolbarbutton-dropdown-arrow-win7.png
- skin/classic/browser/urlbar-popup-blocked.png
skin/classic/browser/webRTC-indicator.css (../shared/webRTC-indicator.css)
* skin/classic/browser/controlcenter/panel.css (controlcenter/panel.css)
skin/classic/browser/customizableui/menu-arrow.svg (customizableui/menu-arrow.svg)
* skin/classic/browser/customizableui/panelUI.css (customizableui/panelUI.css)
* skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
- skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
- skin/classic/browser/downloads/download-glow-menuPanel-win7.png (downloads/download-glow-menuPanel-win7.png)
* skin/classic/browser/downloads/downloads.css (downloads/downloads.css)
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
* skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
-* skin/classic/browser/notification-icons/geo-blocked.svg (notification-icons/geo-blocked.svg)
+ skin/classic/browser/notification-icons/geo-blocked.svg (notification-icons/geo-blocked.svg)
skin/classic/browser/notification-icons/geo-detailed.svg (notification-icons/geo-detailed.svg)
-* skin/classic/browser/notification-icons/geo.svg (notification-icons/geo.svg)
+ skin/classic/browser/notification-icons/geo.svg (notification-icons/geo.svg)
* skin/classic/browser/places/places.css (places/places.css)
* skin/classic/browser/places/organizer.css (places/organizer.css)
skin/classic/browser/places/query.png (places/query.png)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
skin/classic/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel.png)
skin/classic/browser/places/calendar.png (places/calendar.png)
skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png)
@@ -106,16 +102,15 @@ browser.jar:
% override chrome://browser/skin/feeds/videoFeedIcon.png chrome://browser/skin/feeds/feedIcon.png
% override chrome://browser/skin/feeds/videoFeedIcon16.png chrome://browser/skin/feeds/feedIcon16.png
% override chrome://browser/skin/privatebrowsing-mask-tabstrip.png chrome://browser/skin/privatebrowsing-mask-tabstrip-win7.png os=WINNT osversion<=6.1
% override chrome://browser/skin/privatebrowsing-mask-titlebar.png chrome://browser/skin/privatebrowsing-mask-titlebar-win7.png os=WINNT osversion<=6.1
% override chrome://browser/skin/reload-stop-go.png chrome://browser/skin/reload-stop-go-win7.png os=WINNT osversion<=6.1
% override chrome://browser/skin/reload-stop-go@2x.png chrome://browser/skin/reload-stop-go-win7@2x.png os=WINNT osversion<=6.1
% override chrome://browser/skin/toolbarbutton-dropdown-arrow.png chrome://browser/skin/toolbarbutton-dropdown-arrow-win7.png os=WINNT osversion<=6.1
-% override chrome://browser/skin/downloads/download-glow-menuPanel.png chrome://browser/skin/downloads/download-glow-menuPanel-win7.png os=WINNT osversion<=6.1
% override chrome://browser/skin/tabbrowser/tab-background-start.png chrome://browser/skin/tabbrowser/tab-background-start-preWin10.png os=WINNT osversion<=6.3
% override chrome://browser/skin/tabbrowser/tab-background-start@2x.png chrome://browser/skin/tabbrowser/tab-background-start-preWin10@2x.png os=WINNT osversion<=6.3
% override chrome://browser/skin/tabbrowser/tab-background-middle.png chrome://browser/skin/tabbrowser/tab-background-middle-preWin10.png os=WINNT osversion<=6.3
% override chrome://browser/skin/tabbrowser/tab-background-middle@2x.png chrome://browser/skin/tabbrowser/tab-background-middle-preWin10@2x.png os=WINNT osversion<=6.3
% override chrome://browser/skin/tabbrowser/tab-background-end.png chrome://browser/skin/tabbrowser/tab-background-end-preWin10.png os=WINNT osversion<=6.3
% override chrome://browser/skin/tabbrowser/tab-background-end@2x.png chrome://browser/skin/tabbrowser/tab-background-end-preWin10@2x.png os=WINNT osversion<=6.3
--- a/browser/themes/windows/notification-icons/geo-blocked.svg
+++ b/browser/themes/windows/notification-icons/geo-blocked.svg
@@ -1,12 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
- <defs>
-#include ../../shared/notification-icons/clip-path.inc.svg
-#include geo-icon.inc.svg
- </defs>
- <use clip-path="url(#blocked-clipPath)" href="#geo-windows-icon"/>
-#include ../../shared/notification-icons/strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M14 7h-.091a5.931 5.931 0 0 0-.458-1.451l-1.559 1.559a3.972 3.972 0 0 1-4.784 4.784l-1.56 1.56A5.923 5.923 0 0 0 7 13.91V14a1 1 0 0 0 2 0v-.09A6 6 0 0 0 13.909 9H14a1 1 0 0 0 0-2zm.707-5.707a1 1 0 0 0-1.414 0l-1.826 1.826A5.955 5.955 0 0 0 9 2.09V2a1 1 0 0 0-2 0v.09A6 6 0 0 0 2.091 7H2a1 1 0 0 0 0 2h.091a5.963 5.963 0 0 0 1.029 2.467l-1.827 1.826a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414zM4 8a3.976 3.976 0 0 1 6.02-3.434L8.512 6.074a1.974 1.974 0 0 0-2.438 2.438L4.566 10.02A3.961 3.961 0 0 1 4 8z"/>
</svg>
deleted file mode 100644
--- a/browser/themes/windows/notification-icons/geo-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="geo-windows-icon" d="m 2,14 0,4 2,0 a 12,12 0 0 0 10,10 l 0,2 4,0 0,-2 a 12,12 0 0 0 10,-10 l 2,0 0,-4 -2,0 a 12,12 0 0 0 -10,-10 l 0,-2 -4,0 0,2 a 12,12 0 0 0 -10,10 z m 4,1.9 a 10,10 0 1 1 0,0.2 z m 4,0 a 6,6 0 1 1 0,0.2 z"/>
--- a/browser/themes/windows/notification-icons/geo.svg
+++ b/browser/themes/windows/notification-icons/geo.svg
@@ -1,7 +1,6 @@
<!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
- width="32" height="32" viewBox="0 0 32 32">
-#include geo-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="M14 7h-.091A6 6 0 0 0 9 2.09V2a1 1 0 0 0-2 0v.09A6 6 0 0 0 2.091 7H2a1 1 0 0 0 0 2h.091A6 6 0 0 0 7 13.91V14a1 1 0 0 0 2 0v-.09A6 6 0 0 0 13.909 9H14a1 1 0 0 0 0-2zm-6 5a4 4 0 1 1 4-4 4 4 0 0 1-4 4zm0-6a2 2 0 1 0 2 2 2 2 0 0 0-2-2z"/>
</svg>
deleted file mode 100644
index 56dbd2d0d2e266f864bf055cc2ab65383e790bc2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -78,17 +78,16 @@ skip-if = os == "mac" # Full keyboard na
skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
[browser_markup_accessibility_semantics.js]
[browser_markup_anonymous_01.js]
[browser_markup_anonymous_02.js]
skip-if = e10s # scratchpad.xul is not loading in e10s window
[browser_markup_anonymous_03.js]
skip-if = stylo # Stylo doesn't support shadow DOM yet, bug 1293844
[browser_markup_anonymous_04.js]
-skip-if = stylo && debug # Bug 1386865 - Triggers assertion
[browser_markup_copy_image_data.js]
subsuite = clipboard
skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
[browser_markup_css_completion_style_attribute_01.js]
[browser_markup_css_completion_style_attribute_02.js]
[browser_markup_css_completion_style_attribute_03.js]
[browser_markup_dragdrop_autoscroll_01.js]
[browser_markup_dragdrop_autoscroll_02.js]
--- a/devtools/client/locales/en-US/webconsole.properties
+++ b/devtools/client/locales/en-US/webconsole.properties
@@ -292,8 +292,20 @@ webconsole.cssFilterButton.label=CSS
# a fetch call.
webconsole.xhrFilterButton.label=XHR
# LOCALIZATION NOTE (webconsole.requestsFilterButton.label)
# Label used as the text of the "Requests" button in the additional filter toolbar.
# It shows or hides messages displayed when the page makes a network call, for example
# when an image or a scripts is requested.
webconsole.requestsFilterButton.label=Requests
+
+# LOCALIZATION NOTE (webconsole.filteredMessages.label)
+# Text of the "filtered messages" bar, shown when console messages are hidden
+# because the user has set non-default filters in the filter bar.
+# This is a semi-colon list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# example: 345 items hidden by filters.
+webconsole.filteredMessages.label=#1 item hidden by filters;#1 items hidden by filters
+
+# Label used as the text of the "Reset filters" button in the "filtered messages" bar.
+# It resets the default filters of the console to their original values.
+webconsole.resetFiltersButton.label=Reset filters
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -728,37 +728,86 @@ a.learn-more-link.webconsole-learn-more-
.webconsole-output-wrapper {
display: flex;
flex-direction: column;
height: 100%;
-moz-user-focus: normal;
}
-.webconsole-filterbar-wrapper {
- flex-grow: 0;
+/*
+ This element contains the different toolbars in the console
+ - primary, containing the clear messages button and the text search input.
+ It can expand as much as it need.
+ - filtered messages, containing the "X items hidden by filters" and the reset filters button.
+ It should be on the same row than the primary bar if it fits there, or on its own 100% row if it is wrapped.
+ - secondary, containing the filter buttons (Error, Warning, …).
+ It should be on its own 100% row.
+
+ Basically here's what we can have :
+
+ -----------------------------------------------------------------------------------------------------------
+ | Clear button - Open filter bar button - Filter Input | X items hidden by filters - Reset Filters button |
+ -----------------------------------------------------------------------------------------------------------
+ | Error - Warning - Log - Info - Debug - CSS - Network - XHR |
+ -----------------------------------------------------------------------------------------------------------
+
+ or
+
+ ------------------------------------------------------------------------------------
+ | Clear button - Open filter bar button - Filter Input |
+ ------------------------------------------------------------------------------------
+ | X items hidden by filters - Reset Filters button |
+ ------------------------------------------------------------------------------------
+ | Error - Warning - Log - Info - Debug - CSS - Network - XHR |
+ ------------------------------------------------------------------------------------
+*/
+.webconsole-filteringbar-wrapper {
+ display: flex;
+ /* Wrap so the "Hidden messages" bar can go to its own row if needed */
+ flex-wrap: wrap;
+}
+
+.webconsole-filterbar-primary {
+ display: flex;
+ /* We want the toolbar (which contain the text search input) to be at least 200px
+ so we don't allow to shrink it */
+ flex: 1 0 200px;
+}
+
+.devtools-toolbar.webconsole-filterbar-secondary {
+ height: initial;
+ /* This toolbar should always take the whole horizontal space */
+ width: 100%;
+}
+
+.webconsole-filterbar-primary .devtools-plaininput {
+ flex: 1 1 100%;
+}
+
+.webconsole-filterbar-filtered-messages {
+ /* Needed so the bar takes the whole horizontal space when it is wrapped */
+ flex-grow: 1;
+ color: var(--theme-comment);
+ text-align: end;
+}
+
+.webconsole-filterbar-filtered-messages .filter-message-text {
+ font-style: italic;
+}
+
+.webconsole-filterbar-filtered-messages .reset-filters-button {
+ margin-inline-start: 0.5em;
}
.webconsole-output {
flex: 1;
overflow: auto;
}
-.webconsole-filterbar-primary {
- display: flex;
-}
-
-.devtools-toolbar.webconsole-filterbar-secondary {
- height: initial;
-}
-
-.webconsole-filterbar-primary .devtools-plaininput {
- flex: 1 1 100%;
-}
-
.webconsole-output-wrapper .message {
--border-size: 3px;
border-inline-start: var(--border-size) solid transparent;
}
.webconsole-output-wrapper .message:hover {
border-inline-start-color: #0a84ff; /* Photon Design System Blue 50 */
}
--- a/devtools/client/webconsole/new-console-output/actions/filters.js
+++ b/devtools/client/webconsole/new-console-output/actions/filters.js
@@ -8,17 +8,20 @@
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const Services = require("Services");
const {
FILTER_TEXT_SET,
FILTER_TOGGLE,
FILTERS_CLEAR,
+ DEFAULT_FILTERS_RESET,
PREFS,
+ FILTERS,
+ DEFAULT_FILTERS,
} = require("devtools/client/webconsole/new-console-output/constants");
function filterTextSet(text) {
return {
type: FILTER_TEXT_SET,
text
};
}
@@ -38,18 +41,42 @@ function filterToggle(filter) {
function filtersClear() {
return (dispatch, getState) => {
dispatch({
type: FILTERS_CLEAR,
});
const filterState = getAllFilters(getState());
for (let filter in filterState) {
- Services.prefs.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
+ if (filter !== FILTERS.TEXT) {
+ Services.prefs.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
+ }
}
};
}
+/**
+ * Set the default filters to their original values.
+ * This is different than filtersClear where we reset
+ * all the filters to their original values. Here we want
+ * to keep non-default filters the user might have set.
+ */
+function defaultFiltersReset() {
+ return (dispatch, getState) => {
+ dispatch({
+ type: DEFAULT_FILTERS_RESET,
+ });
+
+ const filterState = getAllFilters(getState());
+ DEFAULT_FILTERS.forEach(filter => {
+ if (filterState[filter] === false) {
+ Services.prefs.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
+ }
+ });
+ };
+}
+
module.exports = {
filterTextSet,
filterToggle,
- filtersClear
+ filtersClear,
+ defaultFiltersReset,
};
--- a/devtools/client/webconsole/new-console-output/components/filter-bar.js
+++ b/devtools/client/webconsole/new-console-output/components/filter-bar.js
@@ -5,139 +5,243 @@
const {
createClass,
DOM: dom,
PropTypes
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
+const { getFilteredMessagesCount } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
const {
+ filterBarToggle,
+ defaultFiltersReset,
filterTextSet,
- filterBarToggle,
- messagesClear
+ messagesClear,
} = require("devtools/client/webconsole/new-console-output/actions/index");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
+const { PluralForm } = require("devtools/shared/plural-form");
const {
- MESSAGE_LEVEL
+ DEFAULT_FILTERS,
+ FILTERS,
} = require("../constants");
+
const FilterButton = require("devtools/client/webconsole/new-console-output/components/filter-button");
const FilterBar = createClass({
displayName: "FilterBar",
propTypes: {
dispatch: PropTypes.func.isRequired,
filter: PropTypes.object.isRequired,
serviceContainer: PropTypes.shape({
attachRefToHud: PropTypes.func.isRequired,
}).isRequired,
filterBarVisible: PropTypes.bool.isRequired,
+ filteredMessagesCount: PropTypes.object.isRequired,
+ },
+
+ shouldComponentUpdate(nextProps, nextState) {
+ if (nextProps.filter !== this.props.filter) {
+ return true;
+ }
+
+ if (nextProps.filterBarVisible !== this.props.filterBarVisible) {
+ return true;
+ }
+
+ if (
+ JSON.stringify(nextProps.filteredMessagesCount)
+ !== JSON.stringify(this.props.filteredMessagesCount)
+ ) {
+ return true;
+ }
+
+ return false;
},
componentDidMount() {
this.props.serviceContainer.attachRefToHud("filterBox",
this.wrapperNode.querySelector(".text-filter"));
},
onClickMessagesClear: function () {
this.props.dispatch(messagesClear());
},
onClickFilterBarToggle: function () {
this.props.dispatch(filterBarToggle());
},
+ onClickRemoveAllFilters: function () {
+ this.props.dispatch(defaultFiltersReset());
+ },
+
+ onClickRemoveTextFilter: function () {
+ this.props.dispatch(filterTextSet(""));
+ },
+
onSearchInput: function (e) {
this.props.dispatch(filterTextSet(e.target.value));
},
- render() {
- const {dispatch, filter, filterBarVisible} = this.props;
- let children = [];
+ renderFiltersConfigBar() {
+ const {
+ dispatch,
+ filter,
+ filteredMessagesCount,
+ } = this.props;
+
+ const getLabel = (baseLabel, filterKey) => {
+ const count = filteredMessagesCount[filterKey];
+ if (filter[filterKey] || count === 0) {
+ return baseLabel;
+ }
+ return `${baseLabel} (${count})`;
+ };
- children.push(dom.div({className: "devtools-toolbar webconsole-filterbar-primary"},
- dom.button({
- className: "devtools-button devtools-clear-icon",
- title: l10n.getStr("webconsole.clearButton.tooltip"),
- onClick: this.onClickMessagesClear
+ return dom.div({
+ className: "devtools-toolbar webconsole-filterbar-secondary",
+ key: "config-bar",
+ },
+ FilterButton({
+ active: filter[FILTERS.ERROR],
+ label: getLabel(
+ l10n.getStr("webconsole.errorsFilterButton.label"),
+ FILTERS.ERROR
+ ),
+ filterKey: FILTERS.ERROR,
+ dispatch
+ }),
+ FilterButton({
+ active: filter[FILTERS.WARN],
+ label: getLabel(
+ l10n.getStr("webconsole.warningsFilterButton.label"),
+ FILTERS.WARN
+ ),
+ filterKey: FILTERS.WARN,
+ dispatch
+ }),
+ FilterButton({
+ active: filter[FILTERS.LOG],
+ label: getLabel(l10n.getStr("webconsole.logsFilterButton.label"), FILTERS.LOG),
+ filterKey: FILTERS.LOG,
+ dispatch
+ }),
+ FilterButton({
+ active: filter[FILTERS.INFO],
+ label: getLabel(l10n.getStr("webconsole.infoFilterButton.label"), FILTERS.INFO),
+ filterKey: FILTERS.INFO,
+ dispatch
+ }),
+ FilterButton({
+ active: filter[FILTERS.DEBUG],
+ label: getLabel(l10n.getStr("webconsole.debugFilterButton.label"), FILTERS.DEBUG),
+ filterKey: FILTERS.DEBUG,
+ dispatch
+ }),
+ dom.span({
+ className: "devtools-separator",
}),
+ FilterButton({
+ active: filter[FILTERS.CSS],
+ label: l10n.getStr("webconsole.cssFilterButton.label"),
+ filterKey: FILTERS.CSS,
+ dispatch
+ }),
+ FilterButton({
+ active: filter[FILTERS.NETXHR],
+ label: l10n.getStr("webconsole.xhrFilterButton.label"),
+ filterKey: FILTERS.NETXHR,
+ dispatch
+ }),
+ FilterButton({
+ active: filter[FILTERS.NET],
+ label: l10n.getStr("webconsole.requestsFilterButton.label"),
+ filterKey: FILTERS.NET,
+ dispatch
+ })
+ );
+ },
+
+ renderFilteredMessagesBar() {
+ const {
+ filteredMessagesCount
+ } = this.props;
+ const {
+ global,
+ } = filteredMessagesCount;
+
+ let label = l10n.getStr("webconsole.filteredMessages.label");
+ label = PluralForm.get(global, label).replace("#1", global);
+
+ // Include all default filters that are hiding messages.
+ let title = DEFAULT_FILTERS.reduce((res, filter) => {
+ if (filteredMessagesCount[filter] > 0) {
+ return res.concat(`${filter}: ${filteredMessagesCount[filter]}`);
+ }
+ return res;
+ }, []).join(", ");
+
+ return dom.div({
+ className: "devtools-toolbar webconsole-filterbar-filtered-messages",
+ key: "filtered-messages-bar",
+ },
+ dom.span({
+ className: "filter-message-text",
+ title,
+ }, label),
dom.button({
- className: "devtools-button devtools-filter-icon" + (
- filterBarVisible ? " checked" : ""),
- title: l10n.getStr("webconsole.toggleFilterButton.tooltip"),
- onClick: this.onClickFilterBarToggle
- }),
- dom.input({
- className: "devtools-plaininput text-filter",
- type: "search",
- value: filter.text,
- placeholder: l10n.getStr("webconsole.filterInput.placeholder"),
- onInput: this.onSearchInput
- })
- ));
+ className: "devtools-button reset-filters-button",
+ onClick: this.onClickRemoveAllFilters
+ }, l10n.getStr("webconsole.resetFiltersButton.label"))
+ );
+ },
+
+ render() {
+ const {
+ filter,
+ filterBarVisible,
+ filteredMessagesCount,
+ } = this.props;
+
+ let children = [
+ dom.div({
+ className: "devtools-toolbar webconsole-filterbar-primary",
+ key: "primary-bar",
+ },
+ dom.button({
+ className: "devtools-button devtools-clear-icon",
+ title: l10n.getStr("webconsole.clearButton.tooltip"),
+ onClick: this.onClickMessagesClear
+ }),
+ dom.button({
+ className: "devtools-button devtools-filter-icon" + (
+ filterBarVisible ? " checked" : ""),
+ title: l10n.getStr("webconsole.toggleFilterButton.tooltip"),
+ onClick: this.onClickFilterBarToggle
+ }),
+ dom.input({
+ className: "devtools-plaininput text-filter",
+ type: "search",
+ value: filter.text,
+ placeholder: l10n.getStr("webconsole.filterInput.placeholder"),
+ onInput: this.onSearchInput
+ })
+ )
+ ];
+
+ if (filteredMessagesCount.global > 0) {
+ children.push(this.renderFilteredMessagesBar());
+ }
if (filterBarVisible) {
- children.push(
- dom.div({className: "devtools-toolbar webconsole-filterbar-secondary"},
- FilterButton({
- active: filter.error,
- label: l10n.getStr("webconsole.errorsFilterButton.label"),
- filterKey: MESSAGE_LEVEL.ERROR,
- dispatch
- }),
- FilterButton({
- active: filter.warn,
- label: l10n.getStr("webconsole.warningsFilterButton.label"),
- filterKey: MESSAGE_LEVEL.WARN,
- dispatch
- }),
- FilterButton({
- active: filter.log,
- label: l10n.getStr("webconsole.logsFilterButton.label"),
- filterKey: MESSAGE_LEVEL.LOG,
- dispatch
- }),
- FilterButton({
- active: filter.info,
- label: l10n.getStr("webconsole.infoFilterButton.label"),
- filterKey: MESSAGE_LEVEL.INFO,
- dispatch
- }),
- FilterButton({
- active: filter.debug,
- label: l10n.getStr("webconsole.debugFilterButton.label"),
- filterKey: MESSAGE_LEVEL.DEBUG,
- dispatch
- }),
- dom.span({
- className: "devtools-separator",
- }),
- FilterButton({
- active: filter.css,
- label: l10n.getStr("webconsole.cssFilterButton.label"),
- filterKey: "css",
- dispatch
- }),
- FilterButton({
- active: filter.netxhr,
- label: l10n.getStr("webconsole.xhrFilterButton.label"),
- filterKey: "netxhr",
- dispatch
- }),
- FilterButton({
- active: filter.net,
- label: l10n.getStr("webconsole.requestsFilterButton.label"),
- filterKey: "net",
- dispatch
- })
- )
- );
+ children.push(this.renderFiltersConfigBar());
}
return (
dom.div({
className: "webconsole-filteringbar-wrapper",
ref: node => {
this.wrapperNode = node;
}
@@ -146,12 +250,13 @@ const FilterBar = createClass({
);
}
});
function mapStateToProps(state) {
return {
filter: getAllFilters(state),
filterBarVisible: getAllUi(state).filterBarVisible,
+ filteredMessagesCount: getFilteredMessagesCount(state),
};
}
module.exports = connect(mapStateToProps)(FilterBar);
--- a/devtools/client/webconsole/new-console-output/constants.js
+++ b/devtools/client/webconsole/new-console-output/constants.js
@@ -15,16 +15,17 @@ const actionTypes = {
MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE",
MESSAGE_OBJECT_PROPERTIES_RECEIVE: "MESSAGE_OBJECT_PROPERTIES_RECEIVE",
MESSAGE_OBJECT_ENTRIES_RECEIVE: "MESSAGE_OBJECT_ENTRIES_RECEIVE",
REMOVED_ACTORS_CLEAR: "REMOVED_ACTORS_CLEAR",
TIMESTAMPS_TOGGLE: "TIMESTAMPS_TOGGLE",
FILTER_TOGGLE: "FILTER_TOGGLE",
FILTER_TEXT_SET: "FILTER_TEXT_SET",
FILTERS_CLEAR: "FILTERS_CLEAR",
+ DEFAULT_FILTERS_RESET: "DEFAULT_FILTERS_RESET",
FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE",
};
const prefs = {
PREFS: {
FILTER: {
ERROR: "devtools.webconsole.filter.error",
WARN: "devtools.webconsole.filter.warn",
@@ -36,16 +37,43 @@ const prefs = {
NETXHR: "devtools.webconsole.filter.netxhr",
},
UI: {
FILTER_BAR: "devtools.webconsole.ui.filterbar"
}
}
};
+const FILTERS = {
+ CSS: "css",
+ DEBUG: "debug",
+ ERROR: "error",
+ INFO: "info",
+ LOG: "log",
+ NET: "net",
+ NETXHR: "netxhr",
+ TEXT: "text",
+ WARN: "warn",
+};
+
+const DEFAULT_FILTERS_VALUES = {
+ [FILTERS.TEXT]: "",
+ [FILTERS.ERROR]: true,
+ [FILTERS.WARN]: true,
+ [FILTERS.LOG]: true,
+ [FILTERS.INFO]: true,
+ [FILTERS.DEBUG]: true,
+ [FILTERS.CSS]: false,
+ [FILTERS.NET]: false,
+ [FILTERS.NETXHR]: false,
+};
+
+const DEFAULT_FILTERS = Object.keys(DEFAULT_FILTERS_VALUES)
+ .filter(filter => DEFAULT_FILTERS_VALUES[filter] !== false);
+
const chromeRDPEnums = {
MESSAGE_SOURCE: {
XML: "xml",
CSS: "css",
JAVASCRIPT: "javascript",
NETWORK: "network",
CONSOLE_API: "console-api",
STORAGE: "storage",
@@ -87,14 +115,18 @@ const chromeRDPEnums = {
const jstermCommands = {
JSTERM_COMMANDS: {
INSPECT: "inspectObject"
}
};
// Combine into a single constants object
-module.exports = Object.assign({},
+module.exports = Object.assign({
+ FILTERS,
+ DEFAULT_FILTERS,
+ DEFAULT_FILTERS_VALUES,
+},
actionTypes,
chromeRDPEnums,
jstermCommands,
prefs,
);
--- a/devtools/client/webconsole/new-console-output/reducers/filters.js
+++ b/devtools/client/webconsole/new-console-output/reducers/filters.js
@@ -3,38 +3,34 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const Immutable = require("devtools/client/shared/vendor/immutable");
const constants = require("devtools/client/webconsole/new-console-output/constants");
-const FilterState = Immutable.Record({
- css: false,
- debug: true,
- error: true,
- info: true,
- log: true,
- net: false,
- netxhr: false,
- text: "",
- warn: true,
-});
+const FilterState = Immutable.Record(constants.DEFAULT_FILTERS_VALUES);
function filters(state = new FilterState(), action) {
switch (action.type) {
case constants.FILTER_TOGGLE:
const {filter} = action;
const active = !state.get(filter);
return state.set(filter, active);
case constants.FILTERS_CLEAR:
return new FilterState();
+ case constants.DEFAULT_FILTERS_RESET:
+ return state.withMutations(record => {
+ constants.DEFAULT_FILTERS.forEach(filterName => {
+ record.set(filterName, constants.DEFAULT_FILTERS_VALUES[filterName]);
+ });
+ });
case constants.FILTER_TEXT_SET:
let {text} = action;
- return state.set("text", text);
+ return state.set(constants.FILTERS.TEXT, text);
}
return state;
}
exports.FilterState = FilterState;
exports.filters = filters;
--- a/devtools/client/webconsole/new-console-output/reducers/messages.js
+++ b/devtools/client/webconsole/new-console-output/reducers/messages.js
@@ -1,32 +1,39 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const Immutable = require("devtools/client/shared/vendor/immutable");
-const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
+
+const {
+ isGroupType,
+ l10n,
+} = require("devtools/client/webconsole/new-console-output/utils/messages");
const constants = require("devtools/client/webconsole/new-console-output/constants");
-const {isGroupType} = require("devtools/client/webconsole/new-console-output/utils/messages");
const {
+ DEFAULT_FILTERS,
+ FILTERS,
MESSAGE_TYPE,
- MESSAGE_SOURCE
-} = require("devtools/client/webconsole/new-console-output/constants");
+ MESSAGE_SOURCE,
+} = constants;
const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const MessageState = Immutable.Record({
// List of all the messages added to the console.
messagesById: Immutable.OrderedMap(),
// Array of the visible messages.
visibleMessages: [],
+ // Object for the filtered messages.
+ filteredMessagesCount: getDefaultFiltersCounter(),
// List of the message ids which are opened.
messagesUiById: Immutable.List(),
// Map of the form {messageId : tableData}, which represent the data passed
// as an argument in console.table calls.
messagesTableDataById: Immutable.Map(),
// Map of the form {messageId : {[actor]: properties}}, where `properties` is
// a RDP packet containing the properties of the ${actor} grip.
// This map is consumed by the ObjectInspector so we only load properties once,
@@ -60,16 +67,17 @@ function messages(state = new MessageSta
messagesTableDataById,
messagesObjectPropertiesById,
messagesObjectEntriesById,
networkMessagesUpdateById,
groupsById,
currentGroup,
repeatById,
visibleMessages,
+ filteredMessagesCount,
} = state;
const {logLimit} = prefsState;
switch (action.type) {
case constants.MESSAGE_ADD:
let newMessage = action.message;
@@ -118,18 +126,28 @@ function messages(state = new MessageSta
record.set("groupsById", groupsById.set(newMessage.id, parentGroups));
if (newMessage.type === constants.MESSAGE_TYPE.START_GROUP) {
// We want the group to be open by default.
record.set("messagesUiById", messagesUiById.push(newMessage.id));
}
}
- if (shouldMessageBeVisible(addedMessage, record, filtersState)) {
+ const {
+ visible,
+ cause
+ } = getMessageVisibility(addedMessage, record, filtersState);
+
+ if (visible) {
record.set("visibleMessages", [...visibleMessages, newMessage.id]);
+ } else if (DEFAULT_FILTERS.includes(cause)) {
+ record.set("filteredMessagesCount", Object.assign({}, filteredMessagesCount, {
+ global: filteredMessagesCount.global + 1,
+ [cause]: filteredMessagesCount[cause] + 1
+ }));
}
// Remove top level message if the total count of top level messages
// exceeds the current limit.
if (record.messagesById.size > logLimit) {
limitTopLevelMessageCount(state, record, logLimit);
}
});
@@ -150,24 +168,24 @@ function messages(state = new MessageSta
// If the message is a group
if (isGroupType(messagesById.get(action.id).type)) {
// We want to make its children visible
const messagesToShow = [...messagesById].reduce((res, [id, message]) => {
if (
!visibleMessages.includes(message.id)
&& getParentGroups(message.groupId, groupsById).includes(action.id)
- && shouldMessageBeVisible(
+ && getMessageVisibility(
message,
record,
filtersState,
// We want to check if the message is in an open group
// only if it is not a direct child of the group we're opening.
message.groupId !== action.id
- )
+ ).visible
) {
res.push(id);
}
return res;
}, []);
// We can then insert the messages ids right after the one of the group.
const insertIndex = visibleMessages.indexOf(action.id) + 1;
@@ -232,25 +250,37 @@ function messages(state = new MessageSta
})
);
case constants.REMOVED_ACTORS_CLEAR:
return state.set("removedActors", []);
case constants.FILTER_TOGGLE:
case constants.FILTER_TEXT_SET:
- return state.set(
- "visibleMessages",
- [...messagesById].reduce((res, [messageId, message]) => {
- if (shouldMessageBeVisible(message, state, filtersState)) {
- res.push(messageId);
+ case constants.FILTERS_CLEAR:
+ case constants.DEFAULT_FILTERS_RESET:
+ return state.withMutations(function (record) {
+ const messagesToShow = [];
+ const filtered = getDefaultFiltersCounter();
+ messagesById.forEach((message, messageId) => {
+ const {
+ visible,
+ cause
+ } = getMessageVisibility(message, state, filtersState);
+ if (visible) {
+ messagesToShow.push(messageId);
+ } else if (DEFAULT_FILTERS.includes(cause)) {
+ filtered.global = filtered.global + 1;
+ filtered[cause] = filtered[cause] + 1;
}
- return res;
- }, [])
- );
+ });
+
+ record.set("visibleMessages", messagesToShow);
+ record.set("filteredMessagesCount", filtered);
+ });
}
return state;
}
function getNewCurrentGroup(currentGoup, groupsById) {
let newCurrentGroup = null;
if (currentGoup) {
@@ -427,66 +457,83 @@ function getAllActorsInMessage(message,
* Returns total count of top level messages (those which are not
* within a group).
*/
function getToplevelMessageCount(record) {
return record.messagesById.count(message => !message.groupId);
}
/**
- * Returns true if given message should be visible, false otherwise.
+ * Check if a message should be visible in the console output, and if not, what
+ * causes it to be hidden.
+ *
+ * @return {Object} An object of the following form:
+ * - visible {Boolean}: true if the message should be visible
+ * - cause {String}: if visible is false, what causes the message to be hidden.
*/
-function shouldMessageBeVisible(message, messagesState, filtersState, checkGroup = true) {
+function getMessageVisibility(message, messagesState, filtersState, checkGroup = true) {
// Do not display the message if it's in closed group.
if (
checkGroup
&& !isInOpenedGroup(message, messagesState.groupsById, messagesState.messagesUiById)
) {
- return false;
+ return {
+ visible: false,
+ cause: "closedGroup"
+ };
}
// Some messages can't be filtered out (e.g. groups).
- // So, always return true for those.
+ // So, always return visible: true for those.
if (isUnfilterable(message)) {
- return true;
+ return {
+ visible: true
+ };
}
- // Let's check all filters and hide the message if they say so.
- if (!matchFilters(message, filtersState)) {
- return false;
+ if (!passSearchFilters(message, filtersState)) {
+ return {
+ visible: false,
+ cause: FILTERS.TEXT
+ };
}
- // If there is a free text available for filtering use it to decide
- // whether the message is displayed or not.
- let text = (filtersState.text || "").trim();
- if (text) {
- return matchSearchFilters(message, text);
+ // Let's check all level filters (error, warn, log, …) and return visible: false
+ // and the message level as a cause if the function returns false.
+ if (!passLevelFilters(message, filtersState)) {
+ return {
+ visible: false,
+ cause: message.level
+ };
}
- return true;
-}
-
-function matchFilters(message, filtersState) {
- if (matchLevelFilters(message, filtersState)) {
- return true;
+ if (!passCssFilters(message, filtersState)) {
+ return {
+ visible: false,
+ cause: FILTERS.CSS
+ };
}
- // Return true if the message source is 'css'
- // and CSS filter is enabled.
- if (matchCssFilters(message, filtersState)) {
- return true;
+ if (!passNetworkFilter(message, filtersState)) {
+ return {
+ visible: false,
+ cause: FILTERS.NET
+ };
}
- // Return true if the message is network event
- // and Network and/or XHR filter is enabled.
- if (matchNetworkFilters(message, filtersState)) {
- return true;
+ if (!passXhrFilter(message, filtersState)) {
+ return {
+ visible: false,
+ cause: FILTERS.NETXHR
+ };
}
- return false;
+ return {
+ visible: true
+ };
}
function isUnfilterable(message) {
return [
MESSAGE_TYPE.COMMAND,
MESSAGE_TYPE.RESULT,
MESSAGE_TYPE.START_GROUP,
MESSAGE_TYPE.START_GROUP_COLLAPSED,
@@ -504,40 +551,100 @@ function isInOpenedGroup(message, groups
function hasClosedParentGroup(group, messagesUI) {
return group.some(groupId => isGroupClosed(groupId, messagesUI));
}
function isGroupClosed(groupId, messagesUI) {
return messagesUI.includes(groupId) === false;
}
-function matchNetworkFilters(message, filters) {
+/**
+ * Returns true if the message shouldn't be hidden because of the network filter state.
+ *
+ * @param {Object} message - The message to check the filter against.
+ * @param {FilterState} filters - redux "filters" state.
+ * @returns {Boolean}
+ */
+function passNetworkFilter(message, filters) {
+ // The message passes the filter if it is not a network message,
+ // or if it is an xhr one,
+ // or if the network filter is on.
return (
- message.source === MESSAGE_SOURCE.NETWORK &&
- (filters.get("net") === true && message.isXHR === false) ||
- (filters.get("netxhr") === true && message.isXHR === true)
+ message.source !== MESSAGE_SOURCE.NETWORK ||
+ message.isXHR === true ||
+ filters.get(FILTERS.NET) === true
);
}
-function matchLevelFilters(message, filters) {
+/**
+ * Returns true if the message shouldn't be hidden because of the xhr filter state.
+ *
+ * @param {Object} message - The message to check the filter against.
+ * @param {FilterState} filters - redux "filters" state.
+ * @returns {Boolean}
+ */
+function passXhrFilter(message, filters) {
+ // The message passes the filter if it is not a network message,
+ // or if it is a non-xhr one,
+ // or if the xhr filter is on.
return (
- (message.source === MESSAGE_SOURCE.CONSOLE_API ||
- message.source === MESSAGE_SOURCE.JAVASCRIPT) &&
+ message.source !== MESSAGE_SOURCE.NETWORK ||
+ message.isXHR === false ||
+ filters.get(FILTERS.NETXHR) === true
+ );
+}
+
+/**
+ * Returns true if the message shouldn't be hidden because of levels filter state.
+ *
+ * @param {Object} message - The message to check the filter against.
+ * @param {FilterState} filters - redux "filters" state.
+ * @returns {Boolean}
+ */
+function passLevelFilters(message, filters) {
+ // The message passes the filter if it is not a console call,
+ // or if its level matches the state of the corresponding filter.
+ return (
+ (message.source !== MESSAGE_SOURCE.CONSOLE_API &&
+ message.source !== MESSAGE_SOURCE.JAVASCRIPT) ||
filters.get(message.level) === true
);
}
-function matchCssFilters(message, filters) {
+/**
+ * Returns true if the message shouldn't be hidden because of the CSS filter state.
+ *
+ * @param {Object} message - The message to check the filter against.
+ * @param {FilterState} filters - redux "filters" state.
+ * @returns {Boolean}
+ */
+function passCssFilters(message, filters) {
+ // The message passes the filter if it is not a CSS message,
+ // or if the CSS filter is on.
return (
- message.source === MESSAGE_SOURCE.CSS &&
+ message.source !== MESSAGE_SOURCE.CSS ||
filters.get("css") === true
);
}
-function matchSearchFilters(message, text) {
+/**
+ * Returns true if the message shouldn't be hidden because of search filter state.
+ *
+ * @param {Object} message - The message to check the filter against.
+ * @param {FilterState} filters - redux "filters" state.
+ * @returns {Boolean}
+ */
+function passSearchFilters(message, filters) {
+ let text = (filters.get("text") || "").trim();
+
+ // If there is no search, the message passes the filter.
+ if (!text) {
+ return true;
+ }
+
return (
// Look for a match in parameters.
isTextInParameters(text, message.parameters)
// Look for a match in location.
|| isTextInFrame(text, message.frame)
// Look for a match in net events.
|| isTextInNetEvent(text, message.request)
// Look for a match in stack-trace.
@@ -665,9 +772,18 @@ function getAllProps(grips) {
result = result.filter(grip =>
typeof grip != "object" &&
typeof grip != "undefined"
);
return [...new Set(result)];
}
+function getDefaultFiltersCounter() {
+ const count = DEFAULT_FILTERS.reduce((res, filter) => {
+ res[filter] = 0;
+ return res;
+ }, {});
+ count.global = 0;
+ return count;
+}
+
exports.messages = messages;
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js
+++ b/devtools/client/webconsole/new-console-output/selectors/messages.js
@@ -36,29 +36,34 @@ function getAllGroupsById(state) {
function getCurrentGroup(state) {
return state.messages.currentGroup;
}
function getVisibleMessages(state) {
return state.messages.visibleMessages;
}
+function getFilteredMessagesCount(state) {
+ return state.messages.filteredMessagesCount;
+}
+
function getAllRepeatById(state) {
return state.messages.repeatById;
}
function getAllNetworkMessagesUpdateById(state) {
return state.messages.networkMessagesUpdateById;
}
module.exports = {
getMessage,
getAllMessagesById,
getAllMessagesUiById,
getAllMessagesTableDataById,
getAllGroupsById,
getCurrentGroup,
getVisibleMessages,
+ getFilteredMessagesCount,
getAllRepeatById,
getAllNetworkMessagesUpdateById,
getAllMessagesObjectPropertiesById,
getAllMessagesObjectEntriesById,
};
--- a/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js
@@ -4,22 +4,24 @@
const expect = require("expect");
const sinon = require("sinon");
const { render, mount, shallow } = require("enzyme");
const { createFactory, DOM } = require("devtools/client/shared/vendor/react");
const Provider = createFactory(require("react-redux").Provider);
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const FilterButton = require("devtools/client/webconsole/new-console-output/components/filter-button");
const FilterBar = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar"));
const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
+const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const {
MESSAGES_CLEAR,
- MESSAGE_LEVEL
+ FILTERS,
} = require("devtools/client/webconsole/new-console-output/constants");
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
describe("FilterBar component:", () => {
it("initial render", () => {
const store = setupStore([]);
@@ -42,16 +44,150 @@ describe("FilterBar component:", () => {
// Text filter
expect(toolbar.children().eq(2).attr("class"))
.toBe("devtools-plaininput text-filter");
expect(toolbar.children().eq(2).attr("placeholder")).toBe("Filter output");
expect(toolbar.children().eq(2).attr("type")).toBe("search");
expect(toolbar.children().eq(2).attr("value")).toBe("");
});
+ it("displays the number of hidden messages when there are one hidden message", () => {
+ const store = setupStore([
+ "console.log('foobar', 'test')"
+ ]);
+ // Filter-out LOG messages
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+
+ const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+ const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+ expect(toolbar.exists()).toBeTruthy();
+
+ const message = toolbar.find(".filter-message-text");
+ expect(message.text()).toBe("1 item hidden by filters");
+ expect(message.prop("title")).toBe("log: 1");
+ });
+
+ it("Reset filters when the Reset filters button is clicked.", () => {
+ const store = setupStore([
+ "console.log('foobar', 'test')"
+ ]);
+ // Filter-out LOG messages
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+ const wrapper = mount(Provider({store}, FilterBar({serviceContainer})));
+
+ const resetFiltersButton = wrapper.find(
+ ".webconsole-filterbar-filtered-messages .devtools-button");
+ resetFiltersButton.simulate("click");
+
+ // Toolbar is now hidden
+ const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+ expect(toolbar.exists()).toBeFalsy();
+ expect(getAllFilters(store.getState()).get(FILTERS.LOG)).toBeTruthy();
+ });
+
+ it("displays the number of hidden messages when a search hide messages", () => {
+ const store = setupStore([
+ "console.log('foobar', 'test')",
+ "console.info('info message');",
+ "console.warn('danger, will robinson!')",
+ "console.debug('debug message');",
+ "console.error('error message');",
+ ]);
+ store.dispatch(actions.filterTextSet("qwerty"));
+
+ const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+ const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+ expect(toolbar.exists()).toBeTruthy();
+
+ const message = toolbar.find(".filter-message-text");
+ expect(message.text()).toBe("5 items hidden by filters");
+ expect(message.prop("title")).toBe("text: 5");
+ });
+
+ it("displays the number of hidden messages when there are multiple ones", () => {
+ const store = setupStore([
+ "console.log('foobar', 'test')",
+ "console.info('info message');",
+ "console.warn('danger, will robinson!')",
+ "console.debug('debug message');",
+ "console.error('error message');",
+ "console.log('foobar', 'test')",
+ "console.info('info message');",
+ "console.warn('danger, will robinson!')",
+ "console.debug('debug message');",
+ "console.error('error message');",
+ ]);
+
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+ store.dispatch(actions.filterToggle(FILTERS.WARN));
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+ store.dispatch(actions.filterToggle(FILTERS.INFO));
+ store.dispatch(actions.filterToggle(FILTERS.DEBUG));
+ store.dispatch(actions.filterTextSet("qwerty"));
+
+ const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+ const message = wrapper.find(".filter-message-text");
+
+ expect(message.prop("title")).toBe("text: 10");
+ });
+
+ it("displays expected tooltip when there is text & level hidden-messages", () => {
+ const store = setupStore([
+ "console.log('foobar', 'test')",
+ "console.info('info message');",
+ "console.warn('danger, will robinson!')",
+ "console.debug('debug message');",
+ "console.error('error message');",
+ "console.log('foobar', 'test')",
+ "console.info('info message');",
+ "console.warn('danger, will robinson!')",
+ "console.debug('debug message');",
+ "console.error('error message');",
+ ]);
+
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+ store.dispatch(actions.filterToggle(FILTERS.WARN));
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+ store.dispatch(actions.filterToggle(FILTERS.INFO));
+ store.dispatch(actions.filterToggle(FILTERS.DEBUG));
+
+ const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+ const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+ expect(toolbar.exists()).toBeTruthy();
+
+ const message = toolbar.find(".filter-message-text");
+ expect(message.text()).toBe("10 items hidden by filters");
+ expect(message.prop("title")).toBe("error: 2, warn: 2, log: 2, info: 2, debug: 2");
+ });
+
+ it("does not display the number of hidden messages when there are no messages", () => {
+ const store = setupStore([]);
+ const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+ const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+ expect(toolbar.exists()).toBeFalsy();
+ });
+
+ it("does not display the number of hidden non-default filters (CSS, Network,…)", () => {
+ const store = setupStore([
+ "Unknown property ‘such-unknown-property’. Declaration dropped.",
+ "GET request",
+ "XHR GET request"
+ ]);
+ const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+
+ // Let's make sure those non-default filters are off.
+ const filters = getAllFilters(store.getState());
+ expect(filters.get(FILTERS.CSS)).toBe(false);
+ expect(filters.get(FILTERS.NET)).toBe(false);
+ expect(filters.get(FILTERS.NETXHR)).toBe(false);
+
+ const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+ expect(toolbar.exists()).toBeFalsy();
+ });
+
it("displays filter bar when button is clicked", () => {
const store = setupStore([]);
expect(getAllUi(store.getState()).filterBarVisible).toBe(false);
const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
wrapper.find(".devtools-filter-icon").simulate("click");
@@ -64,25 +200,25 @@ describe("FilterBar component:", () => {
const filterBtn = props => FilterButton(
Object.assign({}, {
active: true,
dispatch: store.dispatch
}, props)
);
let buttons = [
- filterBtn({ label: "Errors", filterKey: MESSAGE_LEVEL.ERROR }),
- filterBtn({ label: "Warnings", filterKey: MESSAGE_LEVEL.WARN }),
- filterBtn({ label: "Logs", filterKey: MESSAGE_LEVEL.LOG }),
- filterBtn({ label: "Info", filterKey: MESSAGE_LEVEL.INFO }),
- filterBtn({ label: "Debug", filterKey: MESSAGE_LEVEL.DEBUG }),
+ filterBtn({ label: "Errors", filterKey: FILTERS.ERROR }),
+ filterBtn({ label: "Warnings", filterKey: FILTERS.WARN }),
+ filterBtn({ label: "Logs", filterKey: FILTERS.LOG }),
+ filterBtn({ label: "Info", filterKey: FILTERS.INFO }),
+ filterBtn({ label: "Debug", filterKey: FILTERS.DEBUG }),
DOM.span({
className: "devtools-separator",
}),
- filterBtn({ label: "CSS", filterKey: "css" }),
+ filterBtn({ label: "CSS", filterKey: "css", active: false }),
filterBtn({ label: "XHR", filterKey: "netxhr", active: false }),
filterBtn({ label: "Requests", filterKey: "net", active: false }),
];
secondaryBar.children().forEach((child, index) => {
expect(child.html()).toEqual(shallow(buttons[index]).html());
});
});
--- a/devtools/client/webconsole/new-console-output/test/fixtures/L10n.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/L10n.js
@@ -32,16 +32,20 @@ class L10n {
case "webconsole.debugFilterButton.label":
return "Debug";
case "webconsole.cssFilterButton.label":
return "CSS";
case "webconsole.xhrFilterButton.label":
return "XHR";
case "webconsole.requestsFilterButton.label":
return "Requests";
+ case "messageRepeats.tooltip2":
+ return "#1 repeat;#1 repeats";
+ case "webconsole.filteredMessages.label":
+ return "#1 item hidden by filters;#1 items hidden by filters";
default:
return str;
}
}
getFormatStr(str) {
return this.getStr(str);
}
--- a/devtools/client/webconsole/new-console-output/test/fixtures/PluralForm.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/PluralForm.js
@@ -1,18 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
module.exports = {
PluralForm: {
get: function (occurence, str) {
- // @TODO Remove when loading the actual strings from webconsole.properties
- // is done in the L10n fixture.
- if (str === "messageRepeats.tooltip2") {
- return `${occurence} repeats`;
+ if (str.includes(";")) {
+ const [singular, plural] = str.split(";");
+ return occurence > 1 ? plural : singular;
}
return str;
}
}
};
--- a/devtools/client/webconsole/new-console-output/test/fixtures/Services.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/Services.js
@@ -11,16 +11,17 @@ module.exports = {
switch (pref) {
case "devtools.hud.loglimit":
return 1000;
}
return null;
},
getBoolPref: pref => {
const falsey = [
+ PREFS.FILTER.CSS,
PREFS.FILTER.NET,
PREFS.FILTER.NETXHR,
PREFS.UI.FILTER_BAR,
];
return !falsey.includes(pref);
},
setBoolPref: () => {},
clearUserPref: () => {},
--- a/devtools/client/webconsole/new-console-output/test/helpers.js
+++ b/devtools/client/webconsole/new-console-output/test/helpers.js
@@ -29,17 +29,17 @@ function setupActions() {
};
return wrappedActions;
}
/**
* Prepare the store for use in testing.
*/
-function setupStore(input, hud, options) {
+function setupStore(input = [], hud, options) {
const store = configureStore(hud, options);
// Add the messages from the input commands to the store.
input.forEach((cmd) => {
store.dispatch(actions.messageAdd(stubPackets.get(cmd)));
});
return store;
--- a/devtools/client/webconsole/new-console-output/test/store/filters.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/filters.test.js
@@ -6,17 +6,17 @@
const expect = require("expect");
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const { messageAdd } = require("devtools/client/webconsole/new-console-output/actions/index");
const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
const { getVisibleMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
-const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
+const { FILTERS } = require("devtools/client/webconsole/new-console-output/constants");
const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
describe("Filtering", () => {
let store;
let numMessages;
// Number of messages in prepareBaseStore which are not filtered out, i.e. Evaluation
// Results, console commands and console.groups .
@@ -32,56 +32,56 @@ describe("Filtering", () => {
* Tests for filter buttons in Console toolbar. The test switches off
* all filters and consequently tests one by one on the list of messages
* created in `prepareBaseStore` method.
*/
describe("Level filter", () => {
beforeEach(() => {
// Switch off all filters (include those which are on by default).
store.dispatch(actions.filtersClear());
- store.dispatch(actions.filterToggle(MESSAGE_LEVEL.DEBUG));
- store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
- store.dispatch(actions.filterToggle(MESSAGE_LEVEL.INFO));
- store.dispatch(actions.filterToggle(MESSAGE_LEVEL.LOG));
- store.dispatch(actions.filterToggle(MESSAGE_LEVEL.WARN));
+ store.dispatch(actions.filterToggle(FILTERS.DEBUG));
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+ store.dispatch(actions.filterToggle(FILTERS.INFO));
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+ store.dispatch(actions.filterToggle(FILTERS.WARN));
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numUnfilterableMessages);
});
it("filters log messages", () => {
- store.dispatch(actions.filterToggle(MESSAGE_LEVEL.LOG));
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numUnfilterableMessages + 5);
});
it("filters debug messages", () => {
- store.dispatch(actions.filterToggle(MESSAGE_LEVEL.DEBUG));
+ store.dispatch(actions.filterToggle(FILTERS.DEBUG));
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numUnfilterableMessages + 1);
});
it("filters info messages", () => {
- store.dispatch(actions.filterToggle(MESSAGE_LEVEL.INFO));
+ store.dispatch(actions.filterToggle(FILTERS.INFO));
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numUnfilterableMessages + 1);
});
it("filters warning messages", () => {
- store.dispatch(actions.filterToggle(MESSAGE_LEVEL.WARN));
+ store.dispatch(actions.filterToggle(FILTERS.WARN));
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numUnfilterableMessages + 1);
});
it("filters error messages", () => {
- store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numUnfilterableMessages + 3);
});
it("filters css messages", () => {
let message = stubPreparedMessages.get(
"Unknown property ‘such-unknown-property’. Declaration dropped."
@@ -201,30 +201,34 @@ describe("Filtering", () => {
});
});
describe("Clear filters", () => {
it("clears all filters", () => {
const store = setupStore([]);
// Setup test case
- store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
- store.dispatch(actions.filterToggle("netxhr"));
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+ store.dispatch(actions.filterToggle(FILTERS.CSS));
+ store.dispatch(actions.filterToggle(FILTERS.NET));
+ store.dispatch(actions.filterToggle(FILTERS.NETXHR));
store.dispatch(actions.filterTextSet("foobar"));
let filters = getAllFilters(store.getState());
expect(filters.toJS()).toEqual({
- "css": true,
- "debug": true,
- "error": false,
+ // default
+ "warn": true,
+ "log": true,
"info": true,
- "log": true,
- "net": false,
+ "debug": true,
+ "css": true,
+ // changed
+ "error": false,
+ "net": true,
"netxhr": true,
- "warn": true,
"text": "foobar",
});
store.dispatch(actions.filtersClear());
filters = getAllFilters(store.getState());
expect(filters.toJS()).toEqual({
"css": false,
@@ -235,16 +239,62 @@ describe("Clear filters", () => {
"net": false,
"netxhr": false,
"warn": true,
"text": "",
});
});
});
+describe("Resets filters", () => {
+ it("resets default filters value to their original one.", () => {
+ const store = setupStore([]);
+
+ // Setup test case
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+ store.dispatch(actions.filterToggle(FILTERS.CSS));
+ store.dispatch(actions.filterToggle(FILTERS.NET));
+ store.dispatch(actions.filterToggle(FILTERS.NETXHR));
+ store.dispatch(actions.filterTextSet("foobar"));
+
+ let filters = getAllFilters(store.getState());
+ expect(filters.toJS()).toEqual({
+ // default
+ "warn": true,
+ "info": true,
+ "debug": true,
+ // changed
+ "error": false,
+ "log": false,
+ "css": true,
+ "net": true,
+ "netxhr": true,
+ "text": "foobar",
+ });
+
+ store.dispatch(actions.defaultFiltersReset());
+
+ filters = getAllFilters(store.getState());
+ expect(filters.toJS()).toEqual({
+ // default
+ "error": true,
+ "warn": true,
+ "log": true,
+ "info": true,
+ "debug": true,
+ "text": "",
+ // non-default filters weren't changed
+ "css": true,
+ "net": true,
+ "netxhr": true,
+ });
+ });
+});
+
function prepareBaseStore() {
const store = setupStore([
// Console API
"console.log('foobar', 'test')",
"console.warn('danger, will robinson!')",
"console.log(undefined)",
"console.count('bar')",
"console.log('鼬')",
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/store/hidden-messages.test.js
@@ -0,0 +1,173 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const expect = require("expect");
+
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
+const { getFilteredMessagesCount } = require("devtools/client/webconsole/new-console-output/selectors/messages");
+const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
+const { FILTERS } = require("devtools/client/webconsole/new-console-output/constants");
+const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
+
+describe("Filtering - Hidden messages", () => {
+ let store;
+
+ beforeEach(() => {
+ store = prepareBaseStore();
+ // Switch off all filters (include those which are on by default).
+ store.dispatch(actions.filtersClear());
+ store.dispatch(actions.filterToggle(FILTERS.DEBUG));
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+ store.dispatch(actions.filterToggle(FILTERS.INFO));
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+ store.dispatch(actions.filterToggle(FILTERS.WARN));
+ });
+
+ it("has the expected numbers", () => {
+ let counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual(BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT);
+ });
+
+ it("has the expected numbers when there is a text search", () => {
+ store.dispatch(actions.filterTextSet("danger, will robinson!"));
+ let counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual({
+ [FILTERS.ERROR]: 0,
+ // The warning message matches the text search.
+ [FILTERS.WARN]: 1,
+ [FILTERS.LOG]: 0,
+ [FILTERS.INFO]: 0,
+ [FILTERS.DEBUG]: 0,
+ [FILTERS.TEXT]: 10,
+ global: 11
+ });
+
+ // Numbers update if the text search is cleared.
+ store.dispatch(actions.filterTextSet(""));
+ counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual(BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT);
+ });
+
+ it("updates when messages are added", () => {
+ MESSAGES.forEach(message =>
+ store.dispatch(actions.messageAdd(stubPackets.get(message))));
+
+ let counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual({
+ [FILTERS.ERROR]: 6,
+ [FILTERS.WARN]: 2,
+ [FILTERS.LOG]: 10,
+ [FILTERS.INFO]: 2,
+ [FILTERS.DEBUG]: 2,
+ [FILTERS.TEXT]: 0,
+ global: 22
+ });
+ });
+
+ it("updates when filters are toggled", () => {
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+
+ let counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual(Object.assign({}, BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT, {
+ [FILTERS.LOG]: 0,
+ global: 6
+ }));
+
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+
+ counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual(Object.assign({}, BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT, {
+ [FILTERS.ERROR]: 0,
+ [FILTERS.LOG]: 0,
+ global: 3
+ }));
+
+ store.dispatch(actions.filterToggle(FILTERS.LOG));
+ store.dispatch(actions.filterToggle(FILTERS.ERROR));
+ counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual(BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT);
+ });
+
+ it("has the expected numbers after message clear", () => {
+ // Add a text search to make sure it is handled as well.
+ store.dispatch(actions.filterTextSet("danger, will robinson!"));
+ store.dispatch(actions.messagesClear());
+ let counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual({
+ [FILTERS.ERROR]: 0,
+ [FILTERS.WARN]: 0,
+ [FILTERS.LOG]: 0,
+ [FILTERS.INFO]: 0,
+ [FILTERS.DEBUG]: 0,
+ [FILTERS.TEXT]: 0,
+ global: 0,
+ });
+ });
+
+ it("has the expected numbers after filter reset", () => {
+ // Add a text search to make sure it is handled as well.
+ store.dispatch(actions.filterTextSet("danger, will robinson!"));
+ store.dispatch(actions.defaultFiltersReset());
+ let counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual({
+ [FILTERS.ERROR]: 0,
+ [FILTERS.WARN]: 0,
+ [FILTERS.LOG]: 0,
+ [FILTERS.INFO]: 0,
+ [FILTERS.DEBUG]: 0,
+ [FILTERS.TEXT]: 0,
+ global: 0,
+ });
+ });
+
+ it("has the expected numbers after filter clear", () => {
+ // Add a text search to make sure it is handled as well.
+ store.dispatch(actions.filterTextSet("danger, will robinson!"));
+ store.dispatch(actions.filtersClear());
+ let counter = getFilteredMessagesCount(store.getState());
+ expect(counter).toEqual({
+ [FILTERS.ERROR]: 0,
+ [FILTERS.WARN]: 0,
+ [FILTERS.LOG]: 0,
+ [FILTERS.INFO]: 0,
+ [FILTERS.DEBUG]: 0,
+ [FILTERS.TEXT]: 0,
+ global: 0,
+ });
+ });
+});
+
+const MESSAGES = [
+ // Error
+ "ReferenceError: asdf is not defined",
+ "console.error('error message');",
+ "console.assert(false, {message: 'foobar'})",
+ // Warning
+ "console.warn('danger, will robinson!')",
+ // Log
+ "console.log('foobar', 'test')",
+ "console.log(undefined)",
+ "console.count('bar')",
+ "console.log('鼬')",
+ "console.table(['red', 'green', 'blue']);",
+ // Info
+ "console.info('info message');",
+ // Debug
+ "console.debug('debug message');",
+];
+
+const BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT = {
+ [FILTERS.ERROR]: 3,
+ [FILTERS.WARN]: 1,
+ [FILTERS.LOG]: 5,
+ [FILTERS.INFO]: 1,
+ [FILTERS.DEBUG]: 1,
+ [FILTERS.TEXT]: 0,
+ global: 11
+};
+
+function prepareBaseStore() {
+ return setupStore(MESSAGES);
+}
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -191,16 +191,17 @@
#include "mozilla/dom/nsCSPUtils.h"
#include "nsHTMLStyleSheet.h"
#include "nsHTMLCSSStyleSheet.h"
#include "mozilla/dom/DOMImplementation.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/Comment.h"
#include "nsTextNode.h"
#include "mozilla/dom/Link.h"
+#include "mozilla/dom/HTMLCollectionBinding.h"
#include "mozilla/dom/HTMLElementBinding.h"
#include "nsXULAppAPI.h"
#include "mozilla/dom/Touch.h"
#include "mozilla/dom/TouchEvent.h"
#include "mozilla/Preferences.h"
#include "imgILoader.h"
@@ -507,21 +508,140 @@ nsIdentifierMapEntry::SetImageElement(El
Element* oldElement = GetImageIdElement();
mImageElement = aElement;
Element* newElement = GetImageIdElement();
if (oldElement != newElement) {
FireChangeCallbacks(oldElement, newElement, true);
}
}
+namespace mozilla {
+namespace dom {
+class SimpleHTMLCollection final : public nsSimpleContentList
+ , public nsIHTMLCollection
+{
+public:
+ explicit SimpleHTMLCollection(nsINode* aRoot) : nsSimpleContentList(aRoot) {}
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // nsIDOMHTMLCollection
+ NS_DECL_NSIDOMHTMLCOLLECTION
+
+ virtual nsINode* GetParentObject() override
+ {
+ return nsSimpleContentList::GetParentObject();
+ }
+ virtual Element* GetElementAt(uint32_t aIndex) override
+ {
+ return mElements.SafeElementAt(aIndex)->AsElement();
+ }
+
+ virtual Element* GetFirstNamedElement(const nsAString& aName,
+ bool& aFound) override
+ {
+ aFound = false;
+ nsCOMPtr<nsIAtom> name = NS_Atomize(aName);
+ for (uint32_t i = 0; i < mElements.Length(); i++) {
+ MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
+ Element* element = mElements[i]->AsElement();
+ if (element->GetID() == name ||
+ (element->HasName() &&
+ element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == name)) {
+ aFound = true;
+ return element;
+ }
+ }
+ return nullptr;
+ }
+
+ virtual void GetSupportedNames(nsTArray<nsString>& aNames) override
+ {
+ AutoTArray<nsIAtom*, 8> atoms;
+ for (uint32_t i = 0; i < mElements.Length(); i++) {
+ MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
+ Element* element = mElements[i]->AsElement();
+
+ nsIAtom* id = element->GetID();
+ MOZ_ASSERT(id != nsGkAtoms::_empty);
+ if (id && !atoms.Contains(id)) {
+ atoms.AppendElement(id);
+ }
+
+ if (element->HasName()) {
+ nsIAtom* name = element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
+ MOZ_ASSERT(name && name != nsGkAtoms::_empty);
+ if (name && !atoms.Contains(name)) {
+ atoms.AppendElement(name);
+ }
+ }
+ }
+
+ nsString* names = aNames.AppendElements(atoms.Length());
+ for (uint32_t i = 0; i < atoms.Length(); i++) {
+ atoms[i]->ToString(names[i]);
+ }
+ }
+
+ virtual JSObject* GetWrapperPreserveColorInternal() override
+ {
+ return nsWrapperCache::GetWrapperPreserveColor();
+ }
+ virtual void PreserveWrapperInternal(nsISupports* aScriptObjectHolder) override
+ {
+ nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
+ }
+ virtual JSObject* WrapObject(JSContext *aCx,
+ JS::Handle<JSObject*> aGivenProto) override
+ {
+ return HTMLCollectionBinding::Wrap(aCx, this, aGivenProto);
+ }
+
+ using nsBaseContentList::Length;
+ using nsBaseContentList::Item;
+ using nsIHTMLCollection::NamedItem;
+
+private:
+ virtual ~SimpleHTMLCollection() {}
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection, nsSimpleContentList,
+ nsIHTMLCollection, nsIDOMHTMLCollection)
+
+NS_IMETHODIMP
+SimpleHTMLCollection::GetLength(uint32_t* aLength)
+{
+ *aLength = Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SimpleHTMLCollection::Item(uint32_t aIdx, nsIDOMNode** aRetVal)
+{
+ nsCOMPtr<nsIDOMNode> retVal = Item(aIdx)->AsDOMNode();
+ retVal.forget(aRetVal);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SimpleHTMLCollection::NamedItem(const nsAString& aName, nsIDOMNode** aRetVal)
+{
+ nsCOMPtr<nsIDOMNode> retVal = NamedItem(aName)->AsDOMNode();
+ retVal.forget(aRetVal);
+ return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
+
void
nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
{
if (!mNameContentList) {
- mNameContentList = new nsSimpleContentList(aNode);
+ mNameContentList = new SimpleHTMLCollection(aNode);
}
mNameContentList->AppendElement(aElement);
}
void
nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
{
--- a/dom/base/nsTextFragment.cpp
+++ b/dom/base/nsTextFragment.cpp
@@ -332,31 +332,31 @@ nsTextFragment::Append(const char16_t* a
// This is a common case because some callsites create a textnode
// with a value by creating the node and then calling AppendData.
if (mState.mLength == 0) {
return SetTo(aBuffer, aLength, aUpdateBidi, aForce2b);
}
// Should we optimize for aData.Length() == 0?
- CheckedUint32 length = mState.mLength;
- length += aLength;
-
- if (!length.isValid()) {
- return false;
+ // FYI: Don't use CheckedInt in this method since here is very hot path
+ // in some performance tests.
+ if (NS_MAX_TEXT_FRAGMENT_LENGTH - mState.mLength < aLength) {
+ return false; // Would be overflown if we'd keep handling.
}
if (mState.mIs2b) {
- length *= sizeof(char16_t);
- if (!length.isValid()) {
- return false;
+ size_t size = mState.mLength + aLength;
+ if (SIZE_MAX / sizeof(char16_t) < size) {
+ return false; // Would be overflown if we'd keep handling.
}
+ size *= sizeof(char16_t);
// Already a 2-byte string so the result will be too
- char16_t* buff = static_cast<char16_t*>(realloc(m2b, length.value()));
+ char16_t* buff = static_cast<char16_t*>(realloc(m2b, size));
if (!buff) {
return false;
}
memcpy(buff + mState.mLength, aBuffer, aLength * sizeof(char16_t));
mState.mLength += aLength;
m2b = buff;
@@ -366,24 +366,25 @@ nsTextFragment::Append(const char16_t* a
return true;
}
// Current string is a 1-byte string, check if the new data fits in one byte too.
int32_t first16bit = aForce2b ? 0 : FirstNon8Bit(aBuffer, aBuffer + aLength);
if (first16bit != -1) { // aBuffer contains no non-8bit character
- length *= sizeof(char16_t);
- if (!length.isValid()) {
- return false;
+ size_t size = mState.mLength + aLength;
+ if (SIZE_MAX / sizeof(char16_t) < size) {
+ return false; // Would be overflown if we'd keep handling.
}
+ size *= sizeof(char16_t);
// The old data was 1-byte, but the new is not so we have to expand it
// all to 2-byte
- char16_t* buff = static_cast<char16_t*>(malloc(length.value()));
+ char16_t* buff = static_cast<char16_t*>(malloc(size));
if (!buff) {
return false;
}
// Copy data into buff
LossyConvertEncoding8to16 converter(buff);
copy_string(m1b, m1b+mState.mLength, converter);
@@ -401,25 +402,27 @@ nsTextFragment::Append(const char16_t* a
if (aUpdateBidi) {
UpdateBidiFlag(aBuffer + first16bit, aLength - first16bit);
}
return true;
}
// The new and the old data is all 1-byte
+ size_t size = mState.mLength + aLength;
+ MOZ_ASSERT(sizeof(char) == 1);
char* buff;
if (mState.mInHeap) {
- buff = static_cast<char*>(realloc(const_cast<char*>(m1b), length.value()));
+ buff = static_cast<char*>(realloc(const_cast<char*>(m1b), size));
if (!buff) {
return false;
}
}
else {
- buff = static_cast<char*>(malloc(length.value()));
+ buff = static_cast<char*>(malloc(size));
if (!buff) {
return false;
}
memcpy(buff, m1b, mState.mLength);
mState.mInHeap = true;
}
--- a/dom/base/nsTextFragment.h
+++ b/dom/base/nsTextFragment.h
@@ -216,19 +216,23 @@ public:
// uint32_t to ensure that the values are unsigned, because we
// want 0/1, not 0/-1!
// Making these bool causes Windows to not actually pack them,
// which causes crashes because we assume this structure is no more than
// 32 bits!
uint32_t mInHeap : 1;
uint32_t mIs2b : 1;
uint32_t mIsBidi : 1;
+ // Note that when you change the bits of mLength, you also need to change
+ // NS_MAX_TEXT_FRAGMENT_LENGTH.
uint32_t mLength : 29;
};
+#define NS_MAX_TEXT_FRAGMENT_LENGTH (static_cast<uint32_t>(0x1FFFFFFF))
+
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
private:
void ReleaseText();
/**
* Scan the contents of the fragment and turn on mState.mIsBidi if it
* includes any Bidi characters.
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -6297,16 +6297,26 @@ CanvasRenderingContext2D::InitializeCanv
CanvasInitializeData data;
data.mSize = GetSize();
data.mHasAlpha = !mOpaque;
data.mPreTransCallback = CanvasRenderingContext2DUserData::PreTransactionCallback;
data.mPreTransCallbackData = this;
data.mDidTransCallback = CanvasRenderingContext2DUserData::DidTransactionCallback;
data.mDidTransCallbackData = this;
+ if (!mBufferProvider) {
+ // Force the creation of a buffer provider.
+ EnsureTarget();
+ ReturnTarget();
+ if (!mBufferProvider) {
+ MarkContextClean();
+ return false;
+ }
+ }
+
if (mIsSkiaGL) {
GLuint skiaGLTex = SkiaGLTex();
if (skiaGLTex) {
SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
MOZ_ASSERT(glue);
data.mGLContext = glue->GetGLContext();
data.mFrontbufferGLTex = skiaGLTex;
}
--- a/dom/html/HTMLLinkElement.h
+++ b/dom/html/HTMLLinkElement.h
@@ -102,16 +102,24 @@ public:
SetOrRemoveNullableStringAttr(nsGkAtoms::crossorigin, aCrossOrigin, aError);
}
// XPCOM GetRel is fine.
void SetRel(const nsAString& aRel, ErrorResult& aRv)
{
SetHTMLAttr(nsGkAtoms::rel, aRel, aRv);
}
nsDOMTokenList* RelList();
+ void GetNonce(nsAString& aNonce) const
+ {
+ GetHTMLAttr(nsGkAtoms::nonce, aNonce);
+ }
+ void SetNonce(const nsAString& aNonce, ErrorResult& aRv)
+ {
+ SetHTMLAttr(nsGkAtoms::nonce, aNonce, aRv);
+ }
// XPCOM GetMedia is fine.
void SetMedia(const nsAString& aMedia, ErrorResult& aRv)
{
SetHTMLAttr(nsGkAtoms::media, aMedia, aRv);
}
// XPCOM GetHreflang is fine.
void SetHreflang(const nsAString& aHreflang, ErrorResult& aRv)
{
--- a/dom/html/HTMLScriptElement.h
+++ b/dom/html/HTMLScriptElement.h
@@ -76,16 +76,24 @@ public:
// always parse to an enum value, so we don't need an invalid
// default, and we _want_ the missing default to be null.
GetEnumAttr(nsGkAtoms::crossorigin, nullptr, aResult);
}
void SetCrossOrigin(const nsAString& aCrossOrigin, ErrorResult& aError)
{
SetOrRemoveNullableStringAttr(nsGkAtoms::crossorigin, aCrossOrigin, aError);
}
+ void GetNonce(nsAString& aNonce) const
+ {
+ GetHTMLAttr(nsGkAtoms::nonce, aNonce);
+ }
+ void SetNonce(const nsAString& aNonce, ErrorResult& aRv)
+ {
+ SetHTMLAttr(nsGkAtoms::nonce, aNonce, aRv);
+ }
void GetIntegrity(nsAString& aIntegrity)
{
GetHTMLAttr(nsGkAtoms::integrity, aIntegrity);
}
void SetIntegrity(const nsAString& aIntegrity, ErrorResult& rv)
{
SetHTMLAttr(nsGkAtoms::integrity, aIntegrity, rv);
}
--- a/dom/html/HTMLStyleElement.h
+++ b/dom/html/HTMLStyleElement.h
@@ -57,16 +57,24 @@ public:
// nsIMutationObserver
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
bool Disabled();
void SetDisabled(bool aDisabled);
+ void GetNonce(nsAString& aNonce) const
+ {
+ GetHTMLAttr(nsGkAtoms::nonce, aNonce);
+ }
+ void SetNonce(const nsAString& aNonce, ErrorResult& aRv)
+ {
+ SetHTMLAttr(nsGkAtoms::nonce, aNonce, aRv);
+ }
void SetMedia(const nsAString& aMedia, ErrorResult& aError)
{
SetHTMLAttr(nsGkAtoms::media, aMedia, aError);
}
void SetType(const nsAString& aType, ErrorResult& aError)
{
SetHTMLAttr(nsGkAtoms::type, aType, aError);
}
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -170,27 +170,16 @@ public:
virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
uint32_t aCount, uint32_t* aBytes) = 0;
// Indicate whether caching data in advance of reads is worth it.
// E.g. Caching lockless and memory-based MediaResource subclasses would be a
// waste, but caching lock/IO-bound resources means reducing the impact of
// each read.
virtual bool ShouldCacheReads() = 0;
- already_AddRefed<MediaByteBuffer> CachedReadAt(int64_t aOffset, uint32_t aCount)
- {
- RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
- bool ok = bytes->SetLength(aCount, fallible);
- NS_ENSURE_TRUE(ok, nullptr);
- char* curr = reinterpret_cast<char*>(bytes->Elements());
- nsresult rv = ReadFromCache(curr, aOffset, aCount);
- NS_ENSURE_SUCCESS(rv, nullptr);
- return bytes.forget();
- }
-
// Report the current offset in bytes from the start of the stream.
// This is used to approximate where we currently are in the playback of a
// media.
// A call to ReadAt will update this position.
virtual int64_t Tell() = 0;
// These can be called on any thread.
// Cached blocks associated with this stream will not be evicted
@@ -772,16 +761,29 @@ public:
break;
}
aCount -= bytesRead;
curr += bytesRead;
}
bytes->SetLength(curr - start);
return bytes.forget();
}
+
+ already_AddRefed<MediaByteBuffer> CachedMediaReadAt(int64_t aOffset,
+ uint32_t aCount) const
+ {
+ RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
+ bool ok = bytes->SetLength(aCount, fallible);
+ NS_ENSURE_TRUE(ok, nullptr);
+ char* curr = reinterpret_cast<char*>(bytes->Elements());
+ nsresult rv = mResource->ReadFromCache(curr, aOffset, aCount);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ return bytes.forget();
+ }
+
// Get the length of the stream in bytes. Returns -1 if not known.
// This can change over time; after a seek operation, a misbehaving
// server may give us a resource of a different length to what it had
// reported previously --- or it may just lie in its Content-Length
// header and give us more or less data than it reported. We will adjust
// the result of GetLength to reflect the data that's actually arriving.
int64_t GetLength() const { return mResource->GetLength(); }
--- a/dom/media/webm/WebMBufferedParser.cpp
+++ b/dom/media/webm/WebMBufferedParser.cpp
@@ -441,20 +441,22 @@ void WebMBufferedState::UpdateIndex(cons
length -= uint32_t(adjust);
} else {
mRangeParsers.InsertElementAt(idx, WebMBufferedParser(offset));
if (idx) {
mRangeParsers[idx].SetTimecodeScale(mRangeParsers[0].GetTimecodeScale());
}
}
}
+
+ MediaResourceIndex res(aResource);
while (length > 0) {
static const uint32_t BLOCK_SIZE = 1048576;
uint32_t block = std::min(length, BLOCK_SIZE);
- RefPtr<MediaByteBuffer> bytes = aResource->CachedReadAt(offset, block);
+ RefPtr<MediaByteBuffer> bytes = res.CachedMediaReadAt(offset, block);
if (!bytes) {
break;
}
NotifyDataArrived(bytes->Elements(), bytes->Length(), offset);
length -= bytes->Length();
offset += bytes->Length();
}
}
--- a/dom/webidl/HTMLLinkElement.webidl
+++ b/dom/webidl/HTMLLinkElement.webidl
@@ -22,16 +22,18 @@ interface HTMLLinkElement : HTMLElement
attribute DOMString? crossOrigin;
[CEReactions, SetterThrows, Pure]
attribute DOMString rel;
[PutForwards=value]
readonly attribute DOMTokenList relList;
[CEReactions, SetterThrows, Pure]
attribute DOMString media;
[CEReactions, SetterThrows, Pure]
+ attribute DOMString nonce;
+ [CEReactions, SetterThrows, Pure]
attribute DOMString hreflang;
[CEReactions, SetterThrows, Pure]
attribute DOMString type;
[CEReactions, SetterThrows, Pure]
attribute DOMString referrerPolicy;
[PutForwards=value] readonly attribute DOMTokenList sizes;
};
HTMLLinkElement implements LinkStyle;
--- a/dom/webidl/HTMLScriptElement.webidl
+++ b/dom/webidl/HTMLScriptElement.webidl
@@ -21,23 +21,21 @@ interface HTMLScriptElement : HTMLElemen
[CEReactions, SetterThrows]
attribute boolean async;
[CEReactions, SetterThrows]
attribute boolean defer;
[CEReactions, SetterThrows]
attribute DOMString? crossOrigin;
[CEReactions, SetterThrows]
attribute DOMString text;
+ [CEReactions, SetterThrows, Pure]
+ attribute DOMString nonce;
+ [CEReactions, SetterThrows, Pure]
+ attribute DOMString integrity;
};
// http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
partial interface HTMLScriptElement {
[CEReactions, SetterThrows]
attribute DOMString event;
[CEReactions, SetterThrows]
attribute DOMString htmlFor;
};
-
-// https://w3c.github.io/webappsec/specs/subresourceintegrity/#htmlscriptelement-1
-partial interface HTMLScriptElement {
- [CEReactions, SetterThrows]
- attribute DOMString integrity;
-};
--- a/dom/webidl/HTMLStyleElement.webidl
+++ b/dom/webidl/HTMLStyleElement.webidl
@@ -10,14 +10,16 @@
[HTMLConstructor]
interface HTMLStyleElement : HTMLElement {
[Pure]
attribute boolean disabled;
[CEReactions, SetterThrows, Pure]
attribute DOMString media;
[CEReactions, SetterThrows, Pure]
+ attribute DOMString nonce;
+ [CEReactions, SetterThrows, Pure]
attribute DOMString type;
[SetterThrows, Pure, Pref="layout.css.scoped-style.enabled"]
attribute boolean scoped;
};
HTMLStyleElement implements LinkStyle;
--- a/dom/xbl/nsXBLPrototypeResources.cpp
+++ b/dom/xbl/nsXBLPrototypeResources.cpp
@@ -174,16 +174,21 @@ nsXBLPrototypeResources::ComputeServoSty
"This should only be called with Servo-flavored style backend!");
// The XBL style sheets aren't document level sheets, but we need to
// decide a particular SheetType to add them to style set. This type
// doesn't affect the place where we pull those rules from
// stylist::push_applicable_declarations_as_xbl_only_stylist().
mServoStyleSet->AppendStyleSheet(SheetType::Doc, sheet->AsServo());
}
mServoStyleSet->UpdateStylistIfNeeded();
+
+ // The PresContext of the bound document could be destroyed anytime later,
+ // which shouldn't be used for XBL styleset, so we clear it here to avoid
+ // dangling pointer.
+ mServoStyleSet->ClearPresContext();
}
void
nsXBLPrototypeResources::AppendStyleSheet(StyleSheet* aSheet)
{
mStyleSheetList.AppendElement(aSheet);
}
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -74,17 +74,17 @@ StackingContextHelper::StackingContextHe
float* aOpacityPtr,
gfx::Matrix4x4* aTransformPtr,
gfx::Matrix4x4* aPerspectivePtr,
const nsTArray<wr::WrFilterOp>& aFilters,
const gfx::CompositionOp& aMixBlendMode)
: mBuilder(&aBuilder)
{
nsRect visibleRect;
- bool is2d = aTransformPtr && aTransformPtr->Is2D() && !aPerspectivePtr;
+ bool is2d = !aTransformPtr || (aTransformPtr->Is2D() && !aPerspectivePtr);
if (is2d) {
nsRect itemBounds = aDisplayList->GetClippedBoundsWithRespectToASR(aDisplayListBuilder, aItem->GetActiveScrolledRoot());
nsRect childrenVisible = aItem->GetVisibleRectForChildren();
visibleRect = itemBounds.Intersect(childrenVisible);
} else {
visibleRect = aDisplayList->GetBounds(aDisplayListBuilder);
// The position of bounds are calculated by transform and perspective matrix in 3d case. reset it to (0, 0)
visibleRect.MoveTo(0, 0);
--- a/intl/unicharutil/util/nsBidiUtils.cpp
+++ b/intl/unicharutil/util/nsBidiUtils.cpp
@@ -1,15 +1,19 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsBidiUtils.h"
+namespace mozilla {
+static const uint32_t kMinRTLChar = 0x0590;
+} // namespace mozilla;
+
#define ARABIC_TO_HINDI_DIGIT_INCREMENT (START_HINDI_DIGITS - START_ARABIC_DIGITS)
#define PERSIAN_TO_HINDI_DIGIT_INCREMENT (START_HINDI_DIGITS - START_FARSI_DIGITS)
#define ARABIC_TO_PERSIAN_DIGIT_INCREMENT (START_FARSI_DIGITS - START_ARABIC_DIGITS)
#define NUM_TO_ARABIC(c) \
((((c)>=START_HINDI_DIGITS) && ((c)<=END_HINDI_DIGITS)) ? \
((c) - (uint16_t)ARABIC_TO_HINDI_DIGIT_INCREMENT) : \
((((c)>=START_FARSI_DIGITS) && ((c)<=END_FARSI_DIGITS)) ? \
((c) - (uint16_t)ARABIC_TO_PERSIAN_DIGIT_INCREMENT) : \
@@ -85,16 +89,19 @@ nsresult HandleNumbers(char16_t* aBuffer
bool HasRTLChars(const char16_t* aText, uint32_t aLength)
{
// This is used to determine whether a string has right-to-left characters
// that mean it will require bidi processing.
const char16_t* cp = aText;
const char16_t* end = cp + aLength;
while (cp < end) {
uint32_t ch = *cp++;
+ if (ch < mozilla::kMinRTLChar) {
+ continue;
+ }
if (NS_IS_HIGH_SURROGATE(ch) && cp < end && NS_IS_LOW_SURROGATE(*cp)) {
ch = SURROGATE_TO_UCS4(ch, *cp++);
}
if (UTF32_CHAR_IS_BIDI(ch) || IsBidiControlRTL(ch)) {
return true;
}
}
return false;
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -39,16 +39,18 @@ interface nsIXPCComponents_Interfaces :
/**
* interface of Components.classes
* (interesting stuff only reflected into JavaScript)
*/
[scriptable, uuid(978ff520-d26c-11d2-9842-006008962422)]
interface nsIXPCComponents_Classes : nsISupports
{
+ // Make it so that |cid| gets mapped to |idString|.
+ void initialize(in nsIJSCID cid, in string idString);
};
/**
* interface of Components.classesByID
* (interesting stuff only reflected into JavaScript)
*/
[scriptable, uuid(336a9590-4d19-11d3-9893-006008962422)]
interface nsIXPCComponents_ClassesByID : nsISupports
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -749,16 +749,24 @@ nsXPCComponents_Classes::Resolve(nsIXPCo
JSPROP_RESOLVING);
}
}
}
}
return NS_OK;
}
+NS_IMETHODIMP
+nsXPCComponents_Classes::Initialize(nsIJSCID* cid,
+ const char* str)
+{
+ return cid->Initialize(str);
+}
+
+
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
class nsXPCComponents_ClassesByID final :
public nsIXPCComponents_ClassesByID,
public nsIXPCScriptable,
public nsIClassInfo
--- a/js/xpconnect/src/XPCJSID.cpp
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -565,18 +565,36 @@ NS_IMETHODIMP_(const nsID*) nsJSCID::Get
{return &mDetails->ID();}
NS_IMETHODIMP nsJSCID::GetValid(bool* aValid)
{return mDetails->GetValid(aValid);}
NS_IMETHODIMP nsJSCID::Equals(nsIJSID* other, bool* _retval)
{return mDetails->Equals(other, _retval);}
-NS_IMETHODIMP nsJSCID::Initialize(const char* idString)
- {return mDetails->Initialize(idString);}
+NS_IMETHODIMP
+nsJSCID::Initialize(const char* str)
+{
+ if (str[0] == '{') {
+ NS_ENSURE_SUCCESS(mDetails->Initialize(str), NS_ERROR_FAILURE);
+ } else {
+ nsCOMPtr<nsIComponentRegistrar> registrar;
+ NS_GetComponentRegistrar(getter_AddRefs(registrar));
+ NS_ENSURE_TRUE(registrar, NS_ERROR_FAILURE);
+
+ nsCID* cid;
+ if (NS_FAILED(registrar->ContractIDToCID(str, &cid)))
+ return NS_ERROR_FAILURE;
+ bool success = mDetails->InitWithName(*cid, str);
+ free(cid);
+ if (!success)
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
NS_IMETHODIMP nsJSCID::ToString(char** _retval)
{ResolveName(); return mDetails->ToString(_retval);}
void
nsJSCID::ResolveName()
{
if (!mDetails->NameIsSet())
@@ -588,31 +606,18 @@ already_AddRefed<nsJSCID>
nsJSCID::NewID(const char* str)
{
if (!str) {
NS_ERROR("no string");
return nullptr;
}
RefPtr<nsJSCID> idObj = new nsJSCID();
- if (str[0] == '{') {
- NS_ENSURE_SUCCESS(idObj->Initialize(str), nullptr);
- } else {
- nsCOMPtr<nsIComponentRegistrar> registrar;
- NS_GetComponentRegistrar(getter_AddRefs(registrar));
- NS_ENSURE_TRUE(registrar, nullptr);
-
- nsCID* cid;
- if (NS_FAILED(registrar->ContractIDToCID(str, &cid)))
- return nullptr;
- bool success = idObj->mDetails->InitWithName(*cid, str);
- free(cid);
- if (!success)
- return nullptr;
- }
+ if (NS_FAILED(idObj->Initialize(str)))
+ return nullptr;
return idObj.forget();
}
static const nsID*
GetIIDArg(uint32_t argc, const JS::Value& val, JSContext* cx)
{
const nsID* iid;
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -135,17 +135,17 @@ public:
case CanvasContextType::WebGL2:
{
bool isRecycled;
RefPtr<WebRenderCanvasData> canvasData =
aManager->CreateOrRecycleWebRenderUserData<WebRenderCanvasData>(this, &isRecycled);
WebRenderCanvasRendererAsync* data =
static_cast<WebRenderCanvasRendererAsync*>(canvasData->GetCanvasRenderer());
- if (isRecycled) {
+ if (!isRecycled) {
nsHTMLCanvasFrame* canvasFrame = static_cast<nsHTMLCanvasFrame*>(mFrame);
if (!canvasFrame->InitializeCanvasRenderer(aDisplayListBuilder, data)) {
return true;
}
}
data->UpdateCompositableClient();
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5145,28 +5145,30 @@ nsDisplayText::nsDisplayText(nsDisplayLi
if (gfxPrefs::LayersAllowTextLayers() &&
CanUseAdvancedLayer(aBuilder->GetWidgetLayerManager())) {
mTextDrawer = new TextDrawTarget();
RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(mTextDrawer);
// TODO: Paint() checks mDisableSubpixelAA, we should too.
RenderToContext(captureCtx, mTextDrawer, aBuilder, true);
+
+ if (!mTextDrawer->CanSerializeFonts()) {
+ mTextDrawer = nullptr;
+ }
}
}
LayerState
nsDisplayText::GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters)
{
// Basic things that all advanced backends need
- if (!mTextDrawer ||
- !mTextDrawer->CanSerializeFonts() ||
- XRE_IsParentProcess()) {
+ if (!mTextDrawer) {
return mozilla::LAYER_NONE;
}
// If we're using the webrender backend, then we're good to go!
if (aManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
return mozilla::LAYER_ACTIVE;
}
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -1450,19 +1450,21 @@ ServoStyleSet::RunPostTraversalTasks()
}
}
ServoStyleRuleMap*
ServoStyleSet::StyleRuleMap()
{
if (!mStyleRuleMap) {
mStyleRuleMap = new ServoStyleRuleMap(this);
- nsIDocument* doc = mPresContext->Document();
- doc->AddObserver(mStyleRuleMap);
- doc->CSSLoader()->AddObserver(mStyleRuleMap);
+ if (mPresContext) {
+ nsIDocument* doc = mPresContext->Document();
+ doc->AddObserver(mStyleRuleMap);
+ doc->CSSLoader()->AddObserver(mStyleRuleMap);
+ }
}
return mStyleRuleMap;
}
bool
ServoStyleSet::MightHaveAttributeDependency(const Element& aElement,
nsIAtom* aAttribute) const
{
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -416,16 +416,21 @@ public:
// its inner.
void SetNeedsRestyleAfterEnsureUniqueInner() {
mNeedsRestyleAfterEnsureUniqueInner = true;
}
// Returns the style rule map.
ServoStyleRuleMap* StyleRuleMap();
+ // Clear mPresContext. This is needed after XBL ServoStyleSet is created.
+ void ClearPresContext() {
+ mPresContext = nullptr;
+ }
+
/**
* Returns true if a modification to an an attribute with the specified
* local name might require us to restyle the element.
*
* This function allows us to skip taking a an attribute snapshot when
* the modified attribute doesn't appear in an attribute selector in
* a style sheet.
*/
--- a/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_device_template.h
+++ b/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_device_template.h
@@ -99,17 +99,17 @@ class AudioDeviceTemplate : public Audio
LOG(INFO) << __FUNCTION__;
return 1;
}
int32_t PlayoutDeviceName(
uint16_t index,
char name[kAdmMaxDeviceNameSize],
char guid[kAdmMaxGuidSize]) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t RecordingDeviceName(
uint16_t index,
char name[kAdmMaxDeviceNameSize],
char guid[kAdmMaxGuidSize]) override {
LOG(INFO) << __FUNCTION__;
@@ -120,30 +120,30 @@ class AudioDeviceTemplate : public Audio
// OK to use but it has no effect currently since device selection is
// done using Andoid APIs instead.
LOG(INFO) << __FUNCTION__;
return 0;
}
int32_t SetPlayoutDevice(
AudioDeviceModule::WindowsDeviceType device) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t SetRecordingDevice(uint16_t index) override {
// OK to use but it has no effect currently since device selection is
// done using Andoid APIs instead.
LOG(INFO) << __FUNCTION__;
return 0;
}
int32_t SetRecordingDevice(
AudioDeviceModule::WindowsDeviceType device) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t PlayoutIsAvailable(bool& available) override {
LOG(INFO) << __FUNCTION__;
available = true;
return 0;
}
@@ -215,36 +215,34 @@ class AudioDeviceTemplate : public Audio
return err;
}
bool Recording() const override {
return input_.Recording() ;
}
int32_t SetAGC(bool enable) override {
- if (enable) {
- FATAL() << "Should never be called";
- }
+ LOG(INFO) << __FUNCTION__;
return -1;
}
bool AGC() const override {
LOG(INFO) << __FUNCTION__;
return false;
}
int32_t SetWaveOutVolume(
uint16_t volumeLeft, uint16_t volumeRight) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t WaveOutVolume(
uint16_t& volumeLeft, uint16_t& volumeRight) const override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t InitSpeaker() override {
LOG(INFO) << __FUNCTION__;
return 0;
}
@@ -284,92 +282,98 @@ class AudioDeviceTemplate : public Audio
}
int32_t MinSpeakerVolume(uint32_t& minVolume) const override {
LOG(INFO) << __FUNCTION__;
return output_.MinSpeakerVolume(minVolume);
}
int32_t SpeakerVolumeStepSize(uint16_t& stepSize) const override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t MicrophoneVolumeIsAvailable(bool& available) override{
available = false;
return -1;
}
int32_t SetMicrophoneVolume(uint32_t volume) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t MicrophoneVolume(uint32_t& volume) const override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t MaxMicrophoneVolume(uint32_t& maxVolume) const override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t MinMicrophoneVolume(uint32_t& minVolume) const override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t MicrophoneVolumeStepSize(uint16_t& stepSize) const override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t SpeakerMuteIsAvailable(bool& available) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
+ available = false;
return -1;
}
int32_t SetSpeakerMute(bool enable) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t SpeakerMute(bool& enabled) const override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
+ enabled = false;
return -1;
}
int32_t MicrophoneMuteIsAvailable(bool& available) override {
- FATAL() << "Not implemented";
+ LOG(INFO) << __FUNCTION__;
+ available = false;
return -1;
}
int32_t SetMicrophoneMute(bool enable) override {
- FATAL() << "Not implemented";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t MicrophoneMute(bool& enabled) const override {
- FATAL() << "Not implemented";
+ LOG(INFO) << __FUNCTION__;
+ enabled = false;
return -1;
}
int32_t MicrophoneBoostIsAvailable(bool& available) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
+ available = false;
return -1;
}
int32_t SetMicrophoneBoost(bool enable) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t MicrophoneBoost(bool& enabled) const override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
+ enabled = false;
return -1;
}
int32_t StereoPlayoutIsAvailable(bool& available) override {
LOG(INFO) << __FUNCTION__;
available = false;
return 0;
}
@@ -378,17 +382,17 @@ class AudioDeviceTemplate : public Audio
int32_t SetStereoPlayout(bool enable) override {
LOG(INFO) << __FUNCTION__;
return -1;
}
// TODO(henrika): add support.
int32_t StereoPlayout(bool& enabled) const override {
enabled = false;
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t StereoRecordingIsAvailable(bool& available) override {
LOG(INFO) << __FUNCTION__;
available = false;
return 0;
}
@@ -401,23 +405,23 @@ class AudioDeviceTemplate : public Audio
int32_t StereoRecording(bool& enabled) const override {
LOG(INFO) << __FUNCTION__;
enabled = false;
return 0;
}
int32_t SetPlayoutBuffer(
const AudioDeviceModule::BufferType type, uint16_t sizeMS) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t PlayoutBuffer(
AudioDeviceModule::BufferType& type, uint16_t& sizeMS) const override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t PlayoutDelay(uint16_t& delay_ms) const override {
// Best guess we can do is to use half of the estimated total delay.
delay_ms = audio_manager_->GetDelayEstimateInMilliseconds() / 2;
RTC_DCHECK_GT(delay_ms, 0);
return 0;
@@ -427,17 +431,17 @@ class AudioDeviceTemplate : public Audio
// Best guess we can do is to use half of the estimated total delay.
LOG(INFO) << __FUNCTION__;
delay_ms = audio_manager_->GetDelayEstimateInMilliseconds() / 2;
RTC_DCHECK_GT(delay_ms, 0);
return 0;
}
int32_t CPULoad(uint16_t& load) const override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
bool PlayoutWarning() const override {
return false;
}
bool PlayoutError() const override {
@@ -463,27 +467,27 @@ class AudioDeviceTemplate : public Audio
void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override {
LOG(INFO) << __FUNCTION__;
output_.AttachAudioBuffer(audioBuffer);
input_.AttachAudioBuffer(audioBuffer);
}
// TODO(henrika): remove
int32_t SetPlayoutSampleRate(const uint32_t samplesPerSec) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t SetLoudspeakerStatus(bool enable) override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
int32_t GetLoudspeakerStatus(bool& enable) const override {
- FATAL() << "Should never be called";
+ LOG(INFO) << __FUNCTION__;
return -1;
}
// Returns true if the device both supports built in AEC and the device
// is not blacklisted.
// Currently, if OpenSL ES is used in both directions, this method will still
// report the correct value and it has the correct effect. As an example:
// a device supports built in AEC and this method returns true. Libjingle
--- a/mobile/android/components/extensions/ext-browserAction.js
+++ b/mobile/android/components/extensions/ext-browserAction.js
@@ -79,17 +79,17 @@ class BrowserAction {
let properties = this.tabContext.get(tab.id);
if (value) {
properties[prop] = value;
} else {
delete properties[prop];
}
}
- if (!tab || tab.selected) {
+ if (!tab || tab.getActive()) {
BrowserActions.update(this.uuid, {[prop]: value});
}
}
/**
* Retreives a property of the browser action for the specified tab.
*
* @param {Object} tab The tab to retrieve the property from. If null, the default value is returned.
--- a/mobile/android/components/extensions/test/mochitest/test_ext_browserAction_getTitle_setTitle.html
+++ b/mobile/android/components/extensions/test/mochitest/test_ext_browserAction_getTitle_setTitle.html
@@ -135,12 +135,43 @@ add_task(async function test_setTitle_an
await checkTab(tabs.tab3, "tab 3");
await checkTab(tabs.tab1, "Updated Title");
extension.sendMessage("finish");
await extension.awaitFinish("browserAction.setTitleAndGetTitle");
await extension.unload();
});
+
+add_task(async function test_setTitle_activeTab() {
+ async function background() {
+ const tabs = await browser.tabs.query({active: true});
+ const tabId = tabs[0].id;
+
+ const title = "Customized browserAction title";
+ await browser.browserAction.setTitle({tabId, title});
+
+ browser.test.notifyPass("browserAction_setTitle_activeTab.done");
+ }
+
+ const extension = ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ "browser_action": {
+ "default_title": "Browser Action default title",
+ },
+ },
+ });
+
+ await extension.startup();
+
+ await extension.awaitFinish("browserAction_setTitle_activeTab.done");
+
+ is(BrowserActions.getNameForActiveTab(`{${extension.uuid}}`),
+ "Customized browserAction title",
+ "The browserAction title has been updated on the currently activeTab");
+
+ await extension.unload();
+});
</script>
</body>
</html>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3673,25 +3673,19 @@ pref("font.name-list.sans-serif.el", "Ar
pref("font.name-list.monospace.el", "Courier New");
pref("font.name-list.cursive.el", "Comic Sans MS");
pref("font.name-list.serif.he", "Narkisim, David");
pref("font.name-list.sans-serif.he", "Arial");
pref("font.name-list.monospace.he", "Fixed Miriam Transparent, Miriam Fixed, Rod, Courier New");
pref("font.name-list.cursive.he", "Guttman Yad, Ktav, Arial");
-#ifdef EARLY_BETA_OR_EARLIER
pref("font.name-list.serif.ja", "Yu Mincho, MS PMincho, MS Mincho, Meiryo, Yu Gothic, MS PGothic, MS Gothic");
pref("font.name-list.sans-serif.ja", "Meiryo, Yu Gothic, MS PGothic, MS Gothic, Yu Mincho, MS PMincho, MS Mincho");
pref("font.name-list.monospace.ja", "MS Gothic, MS Mincho, Meiryo, Yu Gothic, Yu Mincho, MS PGothic, MS PMincho");
-#else
-pref("font.name-list.serif.ja", "MS PMincho, MS Mincho, MS PGothic, MS Gothic,Meiryo");
-pref("font.name-list.sans-serif.ja", "MS PGothic, MS Gothic, MS PMincho, MS Mincho,Meiryo");
-pref("font.name-list.monospace.ja", "MS Gothic, MS Mincho, MS PGothic, MS PMincho,Meiryo");
-#endif
pref("font.name-list.serif.ko", "Batang, Gulim");
pref("font.name-list.sans-serif.ko", "Gulim");
pref("font.name-list.monospace.ko", "GulimChe");
pref("font.name-list.cursive.ko", "Gungsuh");
pref("font.name-list.serif.th", "Tahoma");
pref("font.name-list.sans-serif.th", "Tahoma");
--- a/modules/pdfium/moz.build
+++ b/modules/pdfium/moz.build
@@ -554,9 +554,12 @@ LOCAL_INCLUDES += [
]
USE_LIBS += [
'media_libjpeg',
]
# We allow warnings for third-party code that can be updated from upstream.
ALLOW_COMPILER_WARNINGS = True
-FINAL_LIBRARY = 'xul'
+GeckoSharedLibrary('pdfium', linkage=None)
+
+if CONFIG['OS_TARGET'] == 'WINNT':
+ DEFFILE = SRCDIR + '/pdfium.def'
new file mode 100644
--- /dev/null
+++ b/modules/pdfium/pdfium.def
@@ -0,0 +1,14 @@
+; 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/.
+
+LIBRARY pdfium
+EXPORTS
+ FPDF_InitLibrary
+ FPDF_DestroyLibrary
+ FPDF_LoadDocument
+ FPDF_CloseDocument
+ FPDF_GetPageCount
+ FPDF_LoadPage
+ FPDF_ClosePage
+ FPDF_RenderPage
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -1710,27 +1710,32 @@ dependencies = [
"toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "metrics"
version = "0.0.1"
dependencies = [
"gfx 0.0.1",
+ "gfx_traits 0.0.1",
+ "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg 0.0.1",
"profile_traits 0.0.1",
+ "script_traits 0.0.1",
"servo_config 0.0.1",
- "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "metrics_tests"
version = "0.0.1"
dependencies = [
"euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx 0.0.1",
+ "gfx_traits 0.0.1",
"ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"metrics 0.0.1",
"msg 0.0.1",
"net_traits 0.0.1",
"profile_traits 0.0.1",
"style 0.0.1",
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2592,17 +2597,16 @@ dependencies = [
"euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx_traits 0.0.1",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.10 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper_serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
- "metrics 0.0.1",
"msg 0.0.1",
"net_traits 0.0.1",
"profile_traits 0.0.1",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_atoms 0.0.1",
"servo_url 0.0.1",
"style_traits 0.0.1",
--- a/servo/components/compositing/compositor.rs
+++ b/servo/components/compositing/compositor.rs
@@ -181,16 +181,23 @@ pub struct IOCompositor<Window: WindowMe
/// The active webrender document.
webrender_document: webrender_api::DocumentId,
/// The webrender interface, if enabled.
webrender_api: webrender_api::RenderApi,
/// GL functions interface (may be GL or GLES)
gl: Rc<gl::Gl>,
+
+ /// Map of the pending paint metrics per layout thread.
+ /// The layout thread for each specific pipeline expects the compositor to
+ /// paint frames with specific given IDs (epoch). Once the compositor paints
+ /// these frames, it records the paint time for each of them and sends the
+ /// metric to the corresponding layout thread.
+ pending_paint_metrics: HashMap<PipelineId, Epoch>,
}
#[derive(Copy, Clone)]
struct ScrollZoomEvent {
/// Change the pinch zoom level by this factor
magnification: f32,
/// Scroll by this offset, or to Start or End
scroll_location: ScrollLocation,
@@ -366,16 +373,17 @@ impl<Window: WindowMethods> IOCompositor
time_profiler_chan: state.time_profiler_chan,
last_composite_time: 0,
ready_to_save_state: ReadyState::Unknown,
scroll_in_progress: false,
in_scroll_transaction: None,
webrender: state.webrender,
webrender_document: state.webrender_document,
webrender_api: state.webrender_api,
+ pending_paint_metrics: HashMap::new(),
}
}
pub fn create(window: Rc<Window>, state: InitialCompositorState) -> IOCompositor<Window> {
let mut compositor = IOCompositor::new(window, state);
let compositor_proxy_for_webrender = compositor.channel_to_self
.clone_compositor_proxy();
@@ -588,16 +596,20 @@ impl<Window: WindowMethods> IOCompositor
// But if we start running more complex code here, we should really catch panic here.
func();
}
(Msg::SetFullscreenState(top_level_browsing_context_id, state), ShutdownState::NotShuttingDown) => {
self.window.set_fullscreen_state(top_level_browsing_context_id, state);
}
+ (Msg::PendingPaintMetric(pipeline_id, epoch), _) => {
+ self.pending_paint_metrics.insert(pipeline_id, epoch);
+ }
+
// When we are shutting_down, we need to avoid performing operations
// such as Paint that may crash because we have begun tearing down
// the rest of our resources.
(_, ShutdownState::ShuttingDown) => { }
}
true
}
@@ -1422,16 +1434,48 @@ impl<Window: WindowMethods> IOCompositor
profile(ProfilerCategory::Compositing, None, self.time_profiler_chan.clone(), || {
debug!("compositor: compositing");
// Paint the scene.
self.webrender.render(self.frame_size);
});
+ // If there are pending paint metrics, we check if any of the painted epochs is
+ // one of the ones that the paint metrics recorder is expecting . In that case,
+ // we get the current time, inform the layout thread about it and remove the
+ // pending metric from the list.
+ if !self.pending_paint_metrics.is_empty() {
+ let paint_time = precise_time_ns() as f64;
+ let mut to_remove = Vec::new();
+ // For each pending paint metrics pipeline id
+ for (id, pending_epoch) in &self.pending_paint_metrics {
+ // we get the last painted frame id from webrender
+ if let Some(webrender_api::Epoch(epoch)) = self.webrender.current_epoch(id.to_webrender()) {
+ // and check if it is the one the layout thread is expecting,
+ let epoch = Epoch(epoch);
+ if *pending_epoch != epoch {
+ continue;
+ }
+ // in which case, we remove it from the list of pending metrics,
+ to_remove.push(id.clone());
+ if let Some(pipeline) = self.pipeline(*id) {
+ // and inform the layout thread with the measured paint time.
+ let msg = LayoutControlMsg::PaintMetric(epoch, paint_time);
+ if let Err(e) = pipeline.layout_chan.send(msg) {
+ warn!("Sending PaintMetric message to layout failed ({}).", e);
+ }
+ }
+ }
+ }
+ for id in to_remove.iter() {
+ self.pending_paint_metrics.remove(id);
+ }
+ }
+
let rv = match target {
CompositeTarget::Window => None,
CompositeTarget::WindowAndPng => {
let img = self.draw_img(render_target_info,
width,
height);
Some(Image {
width: img.width(),
--- a/servo/components/compositing/compositor_thread.rs
+++ b/servo/components/compositing/compositor_thread.rs
@@ -2,16 +2,17 @@
* 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/. */
//! Communication with the compositor thread.
use SendableFrameTree;
use compositor::CompositingReason;
use euclid::{Point2D, Size2D};
+use gfx_traits::Epoch;
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::{Key, KeyModifiers, KeyState, PipelineId, TopLevelBrowsingContextId};
use net_traits::image::base::Image;
use profile_traits::mem;
use profile_traits::time;
use script_traits::{AnimationState, ConstellationMsg, EventResult, LoadData};
use servo_url::ServoUrl;
use std::fmt::{Debug, Error, Formatter};
@@ -138,16 +139,20 @@ pub enum Msg {
// tear down the other threads associated with this pipeline.
PipelineExited(PipelineId, IpcSender<()>),
/// Runs a closure in the compositor thread.
/// It's used to dispatch functions from webrender to the main thread's event loop.
/// Required to allow WGL GLContext sharing in Windows.
Dispatch(Box<Fn() + Send>),
/// Enter or exit fullscreen
SetFullscreenState(TopLevelBrowsingContextId, bool),
+ /// Indicates to the compositor that it needs to record the time when the frame with
+ /// the given ID (epoch) is painted and report it to the layout thread of the given
+ /// pipeline ID.
+ PendingPaintMetric(PipelineId, Epoch),
}
impl Debug for Msg {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match *self {
Msg::Exit => write!(f, "Exit"),
Msg::ShutdownComplete => write!(f, "ShutdownComplete"),
Msg::ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
@@ -171,16 +176,17 @@ impl Debug for Msg {
Msg::GetClientWindow(..) => write!(f, "GetClientWindow"),
Msg::MoveTo(..) => write!(f, "MoveTo"),
Msg::ResizeTo(..) => write!(f, "ResizeTo"),
Msg::PipelineVisibilityChanged(..) => write!(f, "PipelineVisibilityChanged"),
Msg::PipelineExited(..) => write!(f, "PipelineExited"),
Msg::NewScrollFrameReady(..) => write!(f, "NewScrollFrameReady"),
Msg::Dispatch(..) => write!(f, "Dispatch"),
Msg::SetFullscreenState(..) => write!(f, "SetFullscreenState"),
+ Msg::PendingPaintMetric(..) => write!(f, "PendingPaintMetric"),
}
}
}
/// Data used to construct a compositor.
pub struct InitialCompositorState {
/// A channel to the compositor.
pub sender: CompositorProxy,
--- a/servo/components/constellation/constellation.rs
+++ b/servo/components/constellation/constellation.rs
@@ -1250,16 +1250,20 @@ impl<Message, LTF, STF> Constellation<Me
self.handle_change_running_animations_state(pipeline_id, animation_state)
}
// Layout sends new sizes for all subframes. This needs to be reflected by all
// frame trees in the navigation context containing the subframe.
FromLayoutMsg::IFrameSizes(iframe_sizes) => {
debug!("constellation got iframe size message");
self.handle_iframe_size_msg(iframe_sizes);
}
+ FromLayoutMsg::PendingPaintMetric(pipeline_id, epoch) => {
+ debug!("constellation got a pending paint metric message");
+ self.handle_pending_paint_metric(pipeline_id, epoch);
+ }
FromLayoutMsg::SetCursor(cursor) => {
self.handle_set_cursor_msg(cursor)
}
FromLayoutMsg::ViewportConstrained(pipeline_id, constraints) => {
debug!("constellation got viewport-constrained event message");
self.handle_viewport_constrained_msg(pipeline_id, constraints);
}
}
@@ -1695,16 +1699,20 @@ impl<Message, LTF, STF> Constellation<Me
top_level_browsing_context_id: top_level_browsing_context_id,
browsing_context_id: browsing_context_id,
new_pipeline_id: new_pipeline_id,
load_data: load_data,
replace_instant: replace_instant,
});
}
+ fn handle_pending_paint_metric(&self, pipeline_id: PipelineId, epoch: Epoch) {
+ self.compositor_proxy.send(ToCompositorMsg::PendingPaintMetric(pipeline_id, epoch))
+ }
+
fn handle_set_cursor_msg(&mut self, cursor: Cursor) {
self.compositor_proxy.send(ToCompositorMsg::SetCursor(cursor))
}
fn handle_change_running_animations_state(&mut self,
pipeline_id: PipelineId,
animation_state: AnimationState) {
self.compositor_proxy.send(ToCompositorMsg::ChangeRunningAnimationsState(pipeline_id,
--- a/servo/components/constellation/pipeline.rs
+++ b/servo/components/constellation/pipeline.rs
@@ -481,17 +481,19 @@ pub struct UnprivilegedPipelineContent {
}
impl UnprivilegedPipelineContent {
pub fn start_all<Message, LTF, STF>(self, wait_for_completion: bool)
where LTF: LayoutThreadFactory<Message=Message>,
STF: ScriptThreadFactory<Message=Message>
{
let image_cache = Arc::new(ImageCacheImpl::new(self.webrender_api_sender.create_api()));
- let paint_time_metrics = PaintTimeMetrics::new(self.time_profiler_chan.clone());
+ let paint_time_metrics = PaintTimeMetrics::new(self.id,
+ self.time_profiler_chan.clone(),
+ self.layout_to_constellation_chan.clone());
let layout_pair = STF::create(InitialScriptState {
id: self.id,
browsing_context_id: self.browsing_context_id,
top_level_browsing_context_id: self.top_level_browsing_context_id,
parent_info: self.parent_info,
control_chan: self.script_chan.clone(),
control_port: self.script_port,
script_to_constellation_chan: self.script_to_constellation_chan.clone(),
--- a/servo/components/gfx_traits/lib.rs
+++ b/servo/components/gfx_traits/lib.rs
@@ -13,17 +13,17 @@ extern crate heapsize;
#[macro_use] extern crate serde;
pub mod print_tree;
use range::RangeIndex;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
/// A newtype struct for denoting the age of messages; prevents race conditions.
-#[derive(PartialEq, Eq, Debug, Copy, Clone, PartialOrd, Ord, Deserialize, Serialize)]
+#[derive(PartialEq, Eq, Debug, Copy, Clone, PartialOrd, Ord, Deserialize, Serialize, Hash)]
pub struct Epoch(pub u32);
impl Epoch {
pub fn next(&mut self) {
self.0 += 1;
}
}
--- a/servo/components/layout_thread/lib.rs
+++ b/servo/components/layout_thread/lib.rs
@@ -124,80 +124,46 @@ use std::thread;
use style::animation::Animation;
use style::context::{QuirksMode, ReflowGoal, SharedStyleContext};
use style::context::{StyleSystemOptions, ThreadLocalStyleContextCreationInfo};
use style::context::RegisteredSpeculativePainter;
use style::context::RegisteredSpeculativePainters;
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
use style::error_reporting::{NullReporter, RustLogReporter};
use style::invalidation::element::restyle_hints::RestyleHint;
-use style::invalidation::media_queries::{MediaListKey, ToMediaListKey};
use style::logical_geometry::LogicalPoint;
use style::media_queries::{Device, MediaList, MediaType};
use style::properties::PropertyId;
use style::selector_parser::SnapshotMap;
use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW};
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
-use style::stylesheet_set::StylesheetSet;
-use style::stylesheets::{Origin, Stylesheet, StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
+use style::stylesheets::{Origin, OriginSet, Stylesheet, DocumentStyleSheet, StylesheetInDocument, UserAgentStylesheets};
use style::stylist::Stylist;
use style::thread_state;
use style::timer::Timer;
use style::traversal::{DomTraversal, TraversalDriver};
use style::traversal_flags::TraversalFlags;
use style_traits::CSSPixel;
use style_traits::DevicePixel;
use style_traits::SpeculativePainter;
-#[derive(Clone)]
-struct DocumentStyleSheet(ServoArc<Stylesheet>);
-
-impl PartialEq for DocumentStyleSheet {
- fn eq(&self, other: &Self) -> bool {
- ServoArc::ptr_eq(&self.0, &other.0)
- }
-}
-
-impl ToMediaListKey for DocumentStyleSheet {
- fn to_media_list_key(&self) -> MediaListKey {
- self.0.to_media_list_key()
- }
-}
-
-impl StylesheetInDocument for DocumentStyleSheet {
- fn contents(&self, guard: &SharedRwLockReadGuard) -> &StylesheetContents {
- self.0.contents(guard)
- }
-
- fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
- self.0.media(guard)
- }
-
- fn enabled(&self) -> bool {
- self.0.enabled()
- }
-}
-
/// Information needed by the layout thread.
pub struct LayoutThread {
/// The ID of the pipeline that we belong to.
id: PipelineId,
/// The ID of the top-level browsing context that we belong to.
top_level_browsing_context_id: TopLevelBrowsingContextId,
/// The URL of the pipeline that we belong to.
url: ServoUrl,
/// Performs CSS selector matching and style resolution.
stylist: Stylist,
- /// The list of stylesheets synchronized with the document.
- stylesheets: StylesheetSet<DocumentStyleSheet>,
-
/// Is the current reflow of an iframe, as opposed to a root window?
is_iframe: bool,
/// The port on which we receive messages from the script thread.
port: Receiver<Msg>,
/// The port on which we receive messages from the constellation.
pipeline_port: Receiver<LayoutControlMsg>,
@@ -544,17 +510,16 @@ impl LayoutThread {
document_shared_lock: None,
running_animations: ServoArc::new(RwLock::new(FnvHashMap::default())),
expired_animations: ServoArc::new(RwLock::new(FnvHashMap::default())),
epoch: Cell::new(Epoch(0)),
viewport_size: Size2D::new(Au(0), Au(0)),
webrender_api: webrender_api_sender.create_api(),
webrender_document,
stylist: Stylist::new(device, QuirksMode::NoQuirks),
- stylesheets: StylesheetSet::new(),
rw_data: Arc::new(Mutex::new(
LayoutThreadData {
constellation_chan: constellation_chan,
display_list: None,
content_box_response: None,
content_boxes_response: Vec::new(),
client_rect_response: Rect::zero(),
hit_test_response: (None, false),
@@ -669,16 +634,20 @@ impl LayoutThread {
},
Request::FromPipeline(LayoutControlMsg::GetWebFontLoadState(sender)) => {
self.handle_request_helper(Msg::GetWebFontLoadState(sender),
possibly_locked_rw_data)
},
Request::FromPipeline(LayoutControlMsg::ExitNow) => {
self.handle_request_helper(Msg::ExitNow, possibly_locked_rw_data)
},
+ Request::FromPipeline(LayoutControlMsg::PaintMetric(epoch, paint_time)) => {
+ self.paint_time_metrics.maybe_set_metric(epoch, paint_time);
+ true
+ },
Request::FromScript(msg) => {
self.handle_request_helper(msg, possibly_locked_rw_data)
},
Request::FromFontCache => {
let _rw_data = possibly_locked_rw_data.lock();
self.outstanding_web_fonts.fetch_sub(1, Ordering::SeqCst);
font_context::invalidate_font_caches();
self.script_chan.send(ConstellationControlMsg::WebFontLoaded(self.id)).unwrap();
@@ -695,36 +664,33 @@ impl LayoutThread {
) -> bool {
match request {
Msg::AddStylesheet(stylesheet, before_stylesheet) => {
let guard = stylesheet.shared_lock.read();
self.handle_add_stylesheet(&stylesheet, &guard);
match before_stylesheet {
Some(insertion_point) => {
- self.stylesheets.insert_stylesheet_before(
- Some(self.stylist.device()),
+ self.stylist.insert_stylesheet_before(
DocumentStyleSheet(stylesheet.clone()),
DocumentStyleSheet(insertion_point),
&guard,
)
}
None => {
- self.stylesheets.append_stylesheet(
- Some(self.stylist.device()),
+ self.stylist.append_stylesheet(
DocumentStyleSheet(stylesheet.clone()),
&guard,
)
}
}
}
Msg::RemoveStylesheet(stylesheet) => {
let guard = stylesheet.shared_lock.read();
- self.stylesheets.remove_stylesheet(
- Some(self.stylist.device()),
+ self.stylist.remove_stylesheet(
DocumentStyleSheet(stylesheet.clone()),
&guard,
);
}
Msg::SetQuirksMode(mode) => self.handle_set_quirks_mode(mode),
Msg::GetRPC(response_chan) => {
response_chan.send(box LayoutRPCImpl(self.rw_data.clone()) as
Box<LayoutRPC + Send>).unwrap();
@@ -1074,20 +1040,20 @@ impl LayoutThread {
self.viewport_size.height.to_f32_px());
let mut epoch = self.epoch.get();
epoch.next();
self.epoch.set(epoch);
let viewport_size = webrender_api::LayoutSize::from_untyped(&viewport_size);
- // Set paint metrics if needed right before sending the display list to WebRender.
- // XXX At some point, we may want to set this metric from WebRender itself.
- self.paint_time_metrics.maybe_set_first_paint(self);
- self.paint_time_metrics.maybe_set_first_contentful_paint(self, &display_list);
+ // Observe notifications about rendered frames if needed right before
+ // sending the display list to WebRender in order to set time related
+ // Progressive Web Metrics.
+ self.paint_time_metrics.maybe_observe_paint_time(self, epoch, &display_list);
self.webrender_api.set_display_list(
self.webrender_document,
webrender_api::Epoch(epoch.0),
Some(get_root_flow_background_color(layout_root)),
viewport_size,
builder.finalize(),
true,
@@ -1179,18 +1145,19 @@ impl LayoutThread {
// Calculate the actual viewport as per DEVICE-ADAPT § 6
let document_shared_lock = document.style_shared_lock();
self.document_shared_lock = Some(document_shared_lock.clone());
let author_guard = document_shared_lock.read();
let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio);
let sheet_origins_affected_by_device_change =
- self.stylist.set_device(device, &author_guard, self.stylesheets.iter());
+ self.stylist.set_device(device, &author_guard);
+ self.stylist.force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
self.viewport_size =
self.stylist.viewport_constraints().map_or(current_screen_size, |constraints| {
debug!("Viewport constraints: {:?}", constraints);
// other rules are evaluated against the actual viewport
Size2D::new(Au::from_f32_px(constraints.size.width),
Au::from_f32_px(constraints.size.height))
});
@@ -1198,17 +1165,17 @@ impl LayoutThread {
let viewport_size_changed = self.viewport_size != old_viewport_size;
if viewport_size_changed {
if let Some(constraints) = self.stylist.viewport_constraints() {
// let the constellation know about the viewport constraints
rw_data.constellation_chan
.send(ConstellationMsg::ViewportConstrained(self.id, constraints.clone()))
.unwrap();
}
- if self.stylesheets.iter().any(|sheet| sheet.0.dirty_on_viewport_size_change()) {
+ if self.stylist.iter_stylesheets().any(|sheet| sheet.0.dirty_on_viewport_size_change()) {
let mut iter = element.as_node().traverse_preorder();
let mut next = iter.next();
while let Some(node) = next {
if node.needs_dirty_on_viewport_size_changed() {
let el = node.as_element().unwrap();
if let Some(mut d) = element.mutate_data() {
if d.has_styles() {
@@ -1232,75 +1199,51 @@ impl LayoutThread {
// If the entire flow tree is invalid, then it will be reflowed anyhow.
let ua_stylesheets = &*UA_STYLESHEETS;
let ua_or_user_guard = ua_stylesheets.shared_lock.read();
let guards = StylesheetGuards {
author: &author_guard,
ua_or_user: &ua_or_user_guard,
};
- let needs_dirtying = {
- debug!("Flushing stylist");
-
- let mut origins_dirty = sheet_origins_affected_by_device_change;
-
- debug!("Device changes: {:?}", origins_dirty);
-
+ {
if self.first_reflow.get() {
debug!("First reflow, rebuilding user and UA rules");
-
- origins_dirty |= Origin::User;
- origins_dirty |= Origin::UserAgent;
+ let mut ua_and_user = OriginSet::empty();
+ ua_and_user |= Origin::User;
+ ua_and_user |= Origin::UserAgent;
+ self.stylist.force_stylesheet_origins_dirty(ua_and_user);
for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets {
self.handle_add_stylesheet(stylesheet, &ua_or_user_guard);
}
}
- let (iter, invalidation_origins_dirty) = self.stylesheets.flush(Some(element));
- debug!("invalidation: {:?}", invalidation_origins_dirty);
-
- origins_dirty |= invalidation_origins_dirty;
-
if data.stylesheets_changed {
debug!("Doc sheets changed, flushing author sheets too");
- origins_dirty |= Origin::Author;
+ self.stylist.force_stylesheet_origins_dirty(Origin::Author.into());
}
- if !origins_dirty.is_empty() {
- let mut extra_data = Default::default();
- self.stylist.rebuild(
- iter,
- &guards,
- Some(ua_stylesheets),
- /* author_style_disabled = */ false,
- &mut extra_data,
- origins_dirty,
- );
- }
+ let mut extra_data = Default::default();
+ self.stylist.flush(
+ &guards,
+ Some(ua_stylesheets),
+ &mut extra_data,
+ Some(element),
+ );
+ }
- !origins_dirty.is_empty()
- };
-
- let needs_reflow = viewport_size_changed && !needs_dirtying;
- if needs_dirtying {
- if let Some(mut d) = element.mutate_data() {
- if d.has_styles() {
- d.restyle.hint.insert(RestyleHint::restyle_subtree());
- }
- }
- }
- if needs_reflow {
+ if viewport_size_changed {
if let Some(mut flow) = self.try_get_layout_root(element.as_node()) {
LayoutThread::reflow_all_nodes(FlowRef::deref_mut(&mut flow));
}
}
let restyles = document.drain_pending_restyles();
- debug!("Draining restyles: {} (needs dirtying? {:?})",
- restyles.len(), needs_dirtying);
+ debug!("Draining restyles: {}", restyles.len());
+
let mut map = SnapshotMap::new();
let elements_with_snapshot: Vec<_> =
restyles
.iter()
.filter(|r| r.1.snapshot.is_some())
.map(|r| r.0)
.collect();
--- a/servo/components/metrics/Cargo.toml
+++ b/servo/components/metrics/Cargo.toml
@@ -6,11 +6,15 @@ license = "MPL-2.0"
publish = false
[lib]
name = "metrics"
path = "lib.rs"
[dependencies]
gfx = {path = "../gfx"}
+gfx_traits = {path = "../gfx_traits"}
+ipc-channel = "0.8"
+log = "0.3.5"
+msg = {path = "../msg"}
profile_traits = {path = "../profile_traits"}
+script_traits = {path = "../script_traits"}
servo_config = {path = "../config"}
-time = "0.1.12"
--- a/servo/components/metrics/lib.rs
+++ b/servo/components/metrics/lib.rs
@@ -1,119 +1,161 @@
/* 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/. */
extern crate gfx;
+extern crate gfx_traits;
+extern crate ipc_channel;
+#[macro_use]
+extern crate log;
+extern crate msg;
extern crate profile_traits;
+extern crate script_traits;
extern crate servo_config;
-extern crate time;
use gfx::display_list::{DisplayItem, DisplayList};
+use gfx_traits::Epoch;
+use ipc_channel::ipc::IpcSender;
+use msg::constellation_msg::PipelineId;
use profile_traits::time::{ProfilerChan, ProfilerCategory, send_profile_data};
use profile_traits::time::TimerMetadata;
+use script_traits::LayoutMsg;
use servo_config::opts;
-use std::cell::Cell;
+use std::cell::{Cell, RefCell};
+use std::collections::HashMap;
pub trait ProfilerMetadataFactory {
fn new_metadata(&self) -> Option<TimerMetadata>;
}
macro_rules! make_time_setter(
( $attr:ident, $func:ident, $category:ident, $label:expr ) => (
- fn $func<T>(&self, profiler_metadata_factory: &T)
- where T: ProfilerMetadataFactory {
+ fn $func(&self, profiler_metadata: Option<TimerMetadata>, paint_time: f64) {
+ if self.$attr.get().is_some() {
+ return;
+ }
+
let navigation_start = match self.navigation_start {
Some(time) => time,
None => {
- println!("Trying to set metric before navigation start");
+ warn!("Trying to set metric before navigation start");
return;
}
};
- let now = time::precise_time_ns() as f64;
- let time = now - navigation_start;
+ let time = paint_time - navigation_start;
self.$attr.set(Some(time));
// Send the metric to the time profiler.
send_profile_data(ProfilerCategory::$category,
- profiler_metadata_factory.new_metadata(),
+ profiler_metadata,
&self.time_profiler_chan,
time as u64, time as u64, 0, 0);
// Print the metric to console if the print-pwm option was given.
if opts::get().print_pwm {
println!("{:?} {:?}", $label, time);
}
}
);
);
pub struct PaintTimeMetrics {
+ pending_metrics: RefCell<HashMap<Epoch, (Option<TimerMetadata>, bool)>>,
navigation_start: Option<f64>,
first_paint: Cell<Option<f64>>,
first_contentful_paint: Cell<Option<f64>>,
+ pipeline_id: PipelineId,
time_profiler_chan: ProfilerChan,
+ constellation_chan: IpcSender<LayoutMsg>,
}
impl PaintTimeMetrics {
- pub fn new(time_profiler_chan: ProfilerChan)
+ pub fn new(pipeline_id: PipelineId,
+ time_profiler_chan: ProfilerChan,
+ constellation_chan: IpcSender<LayoutMsg>)
-> PaintTimeMetrics {
PaintTimeMetrics {
+ pending_metrics: RefCell::new(HashMap::new()),
navigation_start: None,
first_paint: Cell::new(None),
first_contentful_paint: Cell::new(None),
- time_profiler_chan: time_profiler_chan,
+ pipeline_id,
+ time_profiler_chan,
+ constellation_chan,
}
}
pub fn set_navigation_start(&mut self, time: f64) {
self.navigation_start = Some(time);
}
make_time_setter!(first_paint, set_first_paint,
TimeToFirstPaint,
"first-paint");
make_time_setter!(first_contentful_paint, set_first_contentful_paint,
TimeToFirstContentfulPaint,
"first-contentful-paint");
- pub fn maybe_set_first_paint<T>(&self, profiler_metadata_factory: &T)
+ pub fn maybe_observe_paint_time<T>(&self,
+ profiler_metadata_factory: &T,
+ epoch: Epoch,
+ display_list: &DisplayList)
where T: ProfilerMetadataFactory {
- {
- if self.first_paint.get().is_some() {
- return;
- }
+ if self.first_paint.get().is_some() && self.first_contentful_paint.get().is_some() {
+ // If we already set all paint metrics, we just bail out.
+ return;
}
- self.set_first_paint(profiler_metadata_factory);
- }
-
- pub fn maybe_set_first_contentful_paint<T>(&self, profiler_metadata_factory: &T,
- display_list: &DisplayList)
- where T: ProfilerMetadataFactory {
- {
- if self.first_contentful_paint.get().is_some() {
- return;
- }
- }
-
- // Analyze display list to figure out if this is the first contentful
- // paint (i.e. the display list contains items of type text, image,
- // non-white canvas or SVG)
+ let mut is_contentful = false;
+ // Analyze the display list to figure out if this may be the first
+ // contentful paint (i.e. the display list contains items of type text,
+ // image, non-white canvas or SVG).
for item in &display_list.list {
match item {
&DisplayItem::Text(_) |
&DisplayItem::Image(_) => {
- self.set_first_contentful_paint(profiler_metadata_factory);
- return;
+ is_contentful = true;
+ break;
},
_ => (),
}
}
+
+ self.pending_metrics.borrow_mut().insert(
+ epoch,
+ (profiler_metadata_factory.new_metadata(), is_contentful)
+ );
+
+ // Send the pending metric information to the compositor thread.
+ // The compositor will record the current time after painting the
+ // frame with the given ID and will send the metric back to us.
+ let msg = LayoutMsg::PendingPaintMetric(self.pipeline_id, epoch);
+ if let Err(e) = self.constellation_chan.send(msg) {
+ warn!("Failed to send PendingPaintMetric {:?}", e);
+ }
+ }
+
+ pub fn maybe_set_metric(&mut self, epoch: Epoch, paint_time: f64) {
+ if (self.first_paint.get().is_some() && self.first_contentful_paint.get().is_some()) ||
+ self.navigation_start.is_none() {
+ // If we already set all paint metrics or we have not set navigation start yet,
+ // we just bail out.
+ return;
+ }
+
+ if let Some(pending_metric) = self.pending_metrics.borrow_mut().remove(&epoch) {
+ let profiler_metadata = pending_metric.0;
+ self.set_first_paint(profiler_metadata.clone(), paint_time);
+ if pending_metric.1 {
+ self.set_first_contentful_paint(profiler_metadata, paint_time);
+ }
+ }
+
}
pub fn get_navigation_start(&self) -> Option<f64> {
self.navigation_start
}
pub fn get_first_paint(&self) -> Option<f64> {
self.first_paint.get()
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -2333,16 +2333,21 @@ impl Document {
pub fn style_shared_lock(&self) -> &StyleSharedRwLock {
&self.style_shared_lock
}
/// Flushes the stylesheet list, and returns whether any stylesheet changed.
pub fn flush_stylesheets_for_reflow(&self) -> bool {
// NOTE(emilio): The invalidation machinery is used on the replicated
// list on the layout thread.
+ //
+ // FIXME(emilio): This really should differentiate between CSSOM changes
+ // and normal stylesheets additions / removals, because in the last case
+ // the layout thread already has that information and we could avoid
+ // dirtying the whole thing.
let mut stylesheets = self.stylesheets.borrow_mut();
let have_changed = stylesheets.has_changed();
stylesheets.flush_without_invalidation();
have_changed
}
/// Returns a `Device` suitable for media query evaluation.
///
@@ -2365,21 +2370,20 @@ impl Document {
#[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) {
self.window()
.layout_chan()
.send(Msg::RemoveStylesheet(s.clone()))
.unwrap();
let guard = s.shared_lock.read();
- let device = self.device();
// FIXME(emilio): Would be nice to remove the clone, etc.
self.stylesheets.borrow_mut().remove_stylesheet(
- device.as_ref(),
+ None,
StyleSheetInDocument {
sheet: s.clone(),
owner: JS::from_ref(owner),
},
&guard,
);
}
@@ -2409,23 +2413,22 @@ impl Document {
let sheet = StyleSheetInDocument {
sheet,
owner: JS::from_ref(owner),
};
let lock = self.style_shared_lock();
let guard = lock.read();
- let device = self.device();
match insertion_point {
Some(ip) => {
- stylesheets.insert_stylesheet_before(device.as_ref(), sheet, ip, &guard);
+ stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
}
None => {
- stylesheets.append_stylesheet(device.as_ref(), sheet, &guard);
+ stylesheets.append_stylesheet(None, sheet, &guard);
}
}
}
/// Returns the number of document stylesheets.
pub fn stylesheet_count(&self) -> usize {
self.stylesheets.borrow().len()
}
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -1475,17 +1475,19 @@ impl ScriptThread {
is_parent: false,
layout_pair: layout_pair,
pipeline_port: pipeline_port,
constellation_chan: self.layout_to_constellation_chan.clone(),
script_chan: self.control_chan.clone(),
image_cache: self.image_cache.clone(),
content_process_shutdown_chan: content_process_shutdown_chan,
layout_threads: layout_threads,
- paint_time_metrics: PaintTimeMetrics::new(self.time_profiler_chan.clone()),
+ paint_time_metrics: PaintTimeMetrics::new(new_pipeline_id,
+ self.time_profiler_chan.clone(),
+ self.layout_to_constellation_chan.clone()),
});
// Pick a layout thread, any layout thread
let current_layout_chan = self.documents.borrow().iter().next()
.map(|(_, document)| document.window().layout_chan().clone())
.or_else(|| self.incomplete_loads.borrow().first().map(|load| load.layout_chan.clone()));
match current_layout_chan {
--- a/servo/components/script_traits/Cargo.toml
+++ b/servo/components/script_traits/Cargo.toml
@@ -17,17 +17,16 @@ devtools_traits = {path = "../devtools_t
euclid = "0.15"
gfx_traits = {path = "../gfx_traits"}
heapsize = "0.4"
heapsize_derive = "0.1"
hyper = "0.10"
hyper_serde = "0.7"
ipc-channel = "0.8"
libc = "0.2"
-metrics = {path = "../metrics"}
msg = {path = "../msg"}
net_traits = {path = "../net_traits"}
profile_traits = {path = "../profile_traits"}
rustc-serialize = "0.3.4"
serde = "1.0"
servo_atoms = {path = "../atoms"}
servo_url = {path = "../url"}
style_traits = {path = "../style_traits", features = ["servo"]}
--- a/servo/components/script_traits/lib.rs
+++ b/servo/components/script_traits/lib.rs
@@ -119,16 +119,18 @@ pub enum LayoutControlMsg {
GetCurrentEpoch(IpcSender<Epoch>),
/// Asks layout to run another step in its animation.
TickAnimations,
/// Tells layout about the new scrolling offsets of each scrollable stacking context.
SetScrollStates(Vec<ScrollState>),
/// Requests the current load state of Web fonts. `true` is returned if fonts are still loading
/// and `false` is returned if all fonts have loaded.
GetWebFontLoadState(IpcSender<bool>),
+ /// Send the paint time for a specific epoch to the layout thread.
+ PaintMetric(Epoch, f64),
}
/// can be passed to `LoadUrl` to load a page with GET/POST
/// parameters or headers
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct LoadData {
/// The URL.
pub url: ServoUrl,
--- a/servo/components/script_traits/script_msg.rs
+++ b/servo/components/script_traits/script_msg.rs
@@ -10,16 +10,17 @@ use IFrameLoadInfoWithData;
use LayoutControlMsg;
use LoadData;
use MozBrowserEvent;
use WorkerGlobalScopeInit;
use WorkerScriptLoadOrigin;
use canvas_traits::canvas::CanvasMsg;
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use euclid::{Point2D, Size2D, TypedSize2D};
+use gfx_traits::Epoch;
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::{BrowsingContextId, FrameType, PipelineId, TraversalDirection};
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
use net_traits::CoreResourceMsg;
use net_traits::request::RequestInit;
use net_traits::storage_thread::StorageType;
use servo_url::ImmutableOrigin;
use servo_url::ServoUrl;
@@ -30,16 +31,19 @@ use webrender_api::ClipId;
/// Messages from the layout to the constellation.
#[derive(Deserialize, Serialize)]
pub enum LayoutMsg {
/// Indicates whether this pipeline is currently running animations.
ChangeRunningAnimationsState(PipelineId, AnimationState),
/// Inform the constellation of the size of the iframe's viewport.
IFrameSizes(Vec<(BrowsingContextId, TypedSize2D<f32, CSSPixel>)>),
+ /// Requests that the constellation inform the compositor that it needs to record
+ /// the time when the frame with the given ID (epoch) is painted.
+ PendingPaintMetric(PipelineId, Epoch),
/// Requests that the constellation inform the compositor of the a cursor change.
SetCursor(Cursor),
/// Notifies the constellation that the viewport has been constrained in some manner
ViewportConstrained(PipelineId, ViewportConstraints),
}
/// Whether a DOM event was prevented by web content
#[derive(Deserialize, Serialize)]
--- a/servo/components/style/data.rs
+++ b/servo/components/style/data.rs
@@ -67,27 +67,29 @@ impl RestyleData {
damage: RestyleDamage::empty(),
}
}
/// Clear all the restyle state associated with this element.
///
/// FIXME(bholley): The only caller of this should probably just assert that
/// the hint is empty and call clear_flags_and_damage().
+ #[inline]
fn clear_restyle_state(&mut self) {
self.clear_restyle_flags_and_damage();
self.hint = RestyleHint::empty();
}
/// Clear restyle flags and damage.
///
/// Note that we don't touch the TRAVERSED_WITHOUT_STYLING bit, which gets
/// set to the correct value on each traversal. There's no reason anyone
/// needs to clear it, and clearing it accidentally mid-traversal could
/// cause incorrect style sharing behavior.
+ #[inline]
fn clear_restyle_flags_and_damage(&mut self) {
self.damage = RestyleDamage::empty();
self.flags = self.flags & TRAVERSED_WITHOUT_STYLING;
}
/// Returns whether this element or any ancestor is going to be
/// reconstructed.
pub fn reconstructed_self_or_ancestor(&self) -> bool {
@@ -119,32 +121,34 @@ impl RestyleData {
/// Mark this element as restyled, which is useful to know whether we need
/// to do a post-traversal.
pub fn set_restyled(&mut self) {
self.flags.insert(WAS_RESTYLED);
self.flags.remove(TRAVERSED_WITHOUT_STYLING);
}
/// Returns true if this element was restyled.
+ #[inline]
pub fn is_restyle(&self) -> bool {
self.flags.contains(WAS_RESTYLED)
}
/// Mark that we traversed this element without computing any style for it.
pub fn set_traversed_without_styling(&mut self) {
self.flags.insert(TRAVERSED_WITHOUT_STYLING);
}
/// Returns whether the element was traversed without computing any style for
/// it.
pub fn traversed_without_styling(&self) -> bool {
self.flags.contains(TRAVERSED_WITHOUT_STYLING)
}
/// Returns whether this element has been part of a restyle.
+ #[inline]
pub fn contains_restyle_data(&self) -> bool {
self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
}
}
/// A lazily-allocated list of styles for eagerly-cascaded pseudo-elements.
///
/// We use an Arc so that sharing these styles via the style sharing cache does
@@ -348,16 +352,17 @@ impl ElementData {
);
invalidator.invalidate();
unsafe { element.set_handled_snapshot() }
debug_assert!(element.handled_snapshot());
}
}
/// Returns true if this element has styles.
+ #[inline]
pub fn has_styles(&self) -> bool {
self.styles.primary.is_some()
}
/// Returns the kind of restyling that we're going to need to do on this
/// element, based of the stored restyle hint.
pub fn restyle_kind(
&self,
@@ -424,21 +429,23 @@ impl ElementData {
debug_assert!(self.has_styles());
let (important_rules, _custom) =
self.styles.primary().rules().get_properties_overriding_animations(&guards);
let (other_important_rules, _custom) = rules.get_properties_overriding_animations(&guards);
important_rules != other_important_rules
}
/// Drops any restyle state from the element.
+ #[inline]
pub fn clear_restyle_state(&mut self) {
self.restyle.clear_restyle_state();
}
/// Drops restyle flags and damage from the element.
+ #[inline]
pub fn clear_restyle_flags_and_damage(&mut self) {
self.restyle.clear_restyle_flags_and_damage();
}
/// Measures memory usage.
#[cfg(feature = "gecko")]
pub fn malloc_size_of_children_excluding_cvs(&self, state: &mut SizeOfState) -> usize {
let n = self.styles.malloc_size_of_children_excluding_cvs(state);
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -11,17 +11,16 @@ use gecko_bindings::structs::{ServoStyle
use gecko_bindings::structs::RawGeckoPresContextOwned;
use gecko_bindings::structs::nsIDocument;
use gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
use invalidation::media_queries::{MediaListKey, ToMediaListKey};
use media_queries::{Device, MediaList};
use properties::ComputedValues;
use servo_arc::Arc;
use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
-use stylesheet_set::StylesheetSet;
use stylesheets::{PerOrigin, StylesheetContents, StylesheetInDocument};
use stylist::{ExtraStyleData, Stylist};
/// Little wrapper to a Gecko style sheet.
#[derive(PartialEq, Eq, Debug)]
pub struct GeckoStyleSheet(*const ServoStyleSheet);
impl ToMediaListKey for ::gecko::data::GeckoStyleSheet {
@@ -109,19 +108,16 @@ impl StylesheetInDocument for GeckoStyle
}
/// The container for data that a Servo-backed Gecko document needs to style
/// itself.
pub struct PerDocumentStyleDataImpl {
/// Rule processor.
pub stylist: Stylist,
- /// List of stylesheets, mirrored from Gecko.
- pub stylesheets: StylesheetSet<GeckoStyleSheet>,
-
/// List of effective @font-face and @counter-style rules.
pub extra_style_data: PerOrigin<ExtraStyleData>,
}
/// The data itself is an `AtomicRefCell`, which guarantees the proper semantics
/// and unexpected races while trying to mutate it.
pub struct PerDocumentStyleData(AtomicRefCell<PerDocumentStyleDataImpl>);
@@ -130,17 +126,16 @@ impl PerDocumentStyleData {
pub fn new(pres_context: RawGeckoPresContextOwned) -> Self {
let device = Device::new(pres_context);
let quirks_mode = unsafe {
(*device.pres_context().mDocument.raw::<nsIDocument>()).mCompatMode
};
PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
stylist: Stylist::new(device, quirks_mode.into()),
- stylesheets: StylesheetSet::new(),
extra_style_data: Default::default(),
}))
}
/// Get an immutable reference to this style data.
pub fn borrow(&self) -> AtomicRef<PerDocumentStyleDataImpl> {
self.0.borrow()
}
@@ -148,36 +143,30 @@ impl PerDocumentStyleData {
/// Get an mutable reference to this style data.
pub fn borrow_mut(&self) -> AtomicRefMut<PerDocumentStyleDataImpl> {
self.0.borrow_mut()
}
}
impl PerDocumentStyleDataImpl {
/// Recreate the style data if the stylesheets have changed.
- pub fn flush_stylesheets<E>(&mut self,
- guard: &SharedRwLockReadGuard,
- document_element: Option<E>)
- where E: TElement,
+ pub fn flush_stylesheets<E>(
+ &mut self,
+ guard: &SharedRwLockReadGuard,
+ document_element: Option<E>,
+ )
+ where
+ E: TElement,
{
- if !self.stylesheets.has_changed() {
- return;
- }
-
- let author_style_disabled = self.stylesheets.author_style_disabled();
-
- let (iter, dirty_origins) = self.stylesheets.flush(document_element);
- self.stylist.rebuild(
- iter,
+ self.stylist.flush(
&StylesheetGuards::same(guard),
/* ua_sheets = */ None,
- author_style_disabled,
&mut self.extra_style_data,
- dirty_origins,
- );
+ document_element,
+ )
}
/// Returns whether private browsing is enabled.
pub fn is_private_browsing_enabled(&self) -> bool {
let doc =
self.stylist.device().pres_context().mDocument.raw::<nsIDocument>();
unsafe { bindings::Gecko_IsPrivateBrowsingEnabled(doc) }
}
--- a/servo/components/style/gecko/restyle_damage.rs
+++ b/servo/components/style/gecko/restyle_damage.rs
@@ -13,31 +13,35 @@ use std::ops::{BitAnd, BitOr, BitOrAssig
/// The representation of Gecko's restyle damage is just a wrapper over
/// `nsChangeHint`.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct GeckoRestyleDamage(nsChangeHint);
impl GeckoRestyleDamage {
/// Trivially construct a new `GeckoRestyleDamage`.
+ #[inline]
pub fn new(raw: nsChangeHint) -> Self {
GeckoRestyleDamage(raw)
}
/// Get the inner change hint for this damage.
+ #[inline]
pub fn as_change_hint(&self) -> nsChangeHint {
self.0
}
/// Get an empty change hint, that is (`nsChangeHint(0)`).
+ #[inline]
pub fn empty() -> Self {
GeckoRestyleDamage(nsChangeHint(0))
}
/// Returns whether this restyle damage represents the empty damage.
+ #[inline]
pub fn is_empty(&self) -> bool {
self.0 == nsChangeHint(0)
}
/// Computes the `StyleDifference` (including the appropriate change hint)
/// given an old style (in the form of a `nsStyleContext`, and a new style
/// (in the form of `ComputedValues`).
///
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -289,16 +289,17 @@ impl<'ln> TNode for GeckoNode<'ln> {
let ptr: usize = self.0 as *const _ as usize;
OpaqueNode(ptr)
}
fn debug_id(self) -> usize {
unimplemented!()
}
+ #[inline]
fn as_element(&self) -> Option<GeckoElement<'ln>> {
if self.is_element() {
unsafe { Some(GeckoElement(&*(self.0 as *const _ as *const RawGeckoElement))) }
} else {
None
}
}
@@ -1014,43 +1015,47 @@ impl<'le> TElement for GeckoElement<'le>
fn each_class<F>(&self, callback: F)
where F: FnMut(&Atom)
{
snapshot_helpers::each_class(self.0,
callback,
Gecko_ClassOrClassList)
}
+ #[inline]
fn has_snapshot(&self) -> bool {
self.flags() & (ELEMENT_HAS_SNAPSHOT as u32) != 0
}
+ #[inline]
fn handled_snapshot(&self) -> bool {
self.flags() & (ELEMENT_HANDLED_SNAPSHOT as u32) != 0
}
unsafe fn set_handled_snapshot(&self) {
debug_assert!(self.get_data().is_some());
self.set_flags(ELEMENT_HANDLED_SNAPSHOT as u32)
}
+ #[inline]
fn has_dirty_descendants(&self) -> bool {
self.flags() & (ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
}
unsafe fn set_dirty_descendants(&self) {
debug_assert!(self.get_data().is_some());
debug!("Setting dirty descendants: {:?}", self);
self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
}
unsafe fn unset_dirty_descendants(&self) {
self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
}
+ #[inline]
fn has_animation_only_dirty_descendants(&self) -> bool {
self.flags() & (ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
}
unsafe fn set_animation_only_dirty_descendants(&self) {
self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32)
}
@@ -1059,21 +1064,23 @@ impl<'le> TElement for GeckoElement<'le>
}
unsafe fn clear_descendants_bits(&self) {
self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32 |
ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32 |
NODE_DESCENDANTS_NEED_FRAMES as u32)
}
+ #[inline]
fn is_visited_link(&self) -> bool {
use element_state::IN_VISITED_STATE;
self.get_state().intersects(IN_VISITED_STATE)
}
+ #[inline]
fn is_native_anonymous(&self) -> bool {
use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS;
self.flags() & (NODE_IS_NATIVE_ANONYMOUS as u32) != 0
}
fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
if !self.is_native_anonymous() {
return None;
@@ -1091,16 +1098,17 @@ impl<'le> TElement for GeckoElement<'le>
fn store_children_to_process(&self, _: isize) {
// This is only used for bottom-up traversal, and is thus a no-op for Gecko.
}
fn did_process_child(&self) -> isize {
panic!("Atomic child count not implemented in Gecko");
}
+ #[inline(always)]
fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
unsafe { self.0.mServoData.get().as_ref() }
}
unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData> {
if self.get_data().is_none() {
debug!("Creating ElementData for {:?}", self);
let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::default())));
@@ -1122,16 +1130,17 @@ impl<'le> TElement for GeckoElement<'le>
// Perform a mutable borrow of the data in debug builds. This
// serves as an assertion that there are no outstanding borrows
// when we destroy the data.
debug_assert!({ let _ = data.borrow_mut(); true });
}
}
+ #[inline]
fn skip_root_and_item_based_display_fixup(&self) -> bool {
// We don't want to fix up display values of native anonymous content.
// Additionally, we want to skip root-based display fixup for document
// level native anonymous content subtree roots, since they're not
// really roots from the style fixup perspective. Checking that we
// are NAC handles both cases.
self.is_native_anonymous()
}
@@ -1396,25 +1405,25 @@ impl<'le> TElement for GeckoElement<'le>
// Check if we have to cancel the running transition because this is not a matching
// transition-property value.
transitions_to_keep.map_or(false, |set| {
existing_transitions.keys().any(|property| !set.contains(property))
})
}
- fn needs_transitions_update_per_property(&self,
- property: &TransitionProperty,
- combined_duration: f32,
- before_change_style: &ComputedValues,
- after_change_style: &ComputedValues,
- existing_transitions: &HashMap<TransitionProperty,
- Arc<AnimationValue>>)
- -> bool {
- use properties::animated_properties::Animatable;
+ fn needs_transitions_update_per_property(
+ &self,
+ property: &TransitionProperty,
+ combined_duration: f32,
+ before_change_style: &ComputedValues,
+ after_change_style: &ComputedValues,
+ existing_transitions: &HashMap<TransitionProperty, Arc<AnimationValue>>,
+ ) -> bool {
+ use values::animated::{Animate, Procedure};
// |property| should be an animatable longhand
let animatable_longhand = AnimatableLonghand::from_transition_property(property).unwrap();
if existing_transitions.contains_key(property) {
// If there is an existing transition, update only if the end value differs.
// If the end value has not changed, we should leave the currently running
// transition as-is since we don't want to interrupt its timing function.
@@ -1426,17 +1435,17 @@ impl<'le> TElement for GeckoElement<'le>
let from = AnimationValue::from_computed_values(&animatable_longhand,
before_change_style);
let to = AnimationValue::from_computed_values(&animatable_longhand,
after_change_style);
combined_duration > 0.0f32 &&
from != to &&
- from.interpolate(&to, 0.5).is_ok()
+ from.animate(&to, Procedure::Interpolate { progress: 0.5 }).is_ok()
}
#[inline]
fn lang_attr(&self) -> Option<AttrValue> {
let ptr = unsafe { bindings::Gecko_LangValue(self.0) };
if ptr.is_null() {
None
} else {
--- a/servo/components/style/macros.rs
+++ b/servo/components/style/macros.rs
@@ -82,20 +82,23 @@ macro_rules! add_impls_for_keyword_enum
macro_rules! define_keyword_type {
($name: ident, $css: expr) => {
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, ComputeSquaredDistance, Copy, PartialEq, ToCss)]
pub struct $name;
- impl $crate::properties::animated_properties::Animatable for $name {
+ impl $crate::values::animated::Animate for $name {
#[inline]
- fn add_weighted(&self, _other: &Self, _self_progress: f64, _other_progress: f64)
- -> Result<Self, ()> {
+ fn animate(
+ &self,
+ _other: &Self,
+ _procedure: $crate::values::animated::Procedure,
+ ) -> Result<Self, ()> {
Ok($name)
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, $css)
}
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -206,17 +206,17 @@ class Longhand(object):
self.is_animatable_with_computed_value = animation_value_type == "ComputedValue" \
or animation_value_type == "discrete"
if self.logical:
# Logical properties will be animatable (i.e. the animation type is
# discrete). For now, it is still non-animatable.
self.animatable = False
self.transitionable = False
self.animation_type = None
- # NB: Animatable implies clone because a property animation requires a
+ # NB: Animate implies clone because a property animation requires a
# copy of the computed value.
#
# See components/style/helpers/animated_properties.mako.rs.
self.need_clone = need_clone or self.animatable
class Shorthand(object):
def __init__(self, name, sub_properties, spec=None, experimental=False, internal=False,
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -128,27 +128,22 @@
% if allow_empty and allow_empty != "NotInitial":
pub Vec<single_value::T>,
% else:
pub SmallVec<[single_value::T; 1]>,
% endif
);
% if need_animatable or animation_value_type == "ComputedValue":
- use properties::animated_properties::Animatable;
- use values::animated::ToAnimatedZero;
+ use values::animated::{Animate, Procedure, ToAnimatedZero};
- impl Animatable for T {
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
- -> Result<Self, ()> {
- self.0.add_weighted(&other.0, self_portion, other_portion).map(T)
- }
-
- fn add(&self, other: &Self) -> Result<Self, ()> {
- self.0.add(&other.0).map(T)
+ impl Animate for T {
+ #[inline]
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ Ok(T(self.0.animate(&other.0, procedure)?))
}
}
impl ToAnimatedZero for T {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
}
% endif
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -3,46 +3,48 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import to_idl_name, SYSTEM_FONT_LONGHANDS %>
use app_units::Au;
use cssparser::Parser;
-use euclid::{Point2D, Point3D, Size2D};
+use euclid::Point3D;
#[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
#[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
#[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
#[cfg(feature = "gecko")] use gecko_string_cache::Atom;
+use itertools::{EitherOrBoth, Itertools};
use properties::{CSSWideKeyword, PropertyDeclaration};
use properties::longhands;
use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
use properties::longhands::border_spacing::computed_value::T as BorderSpacing;
use properties::longhands::font_weight::computed_value::T as FontWeight;
use properties::longhands::font_stretch::computed_value::T as FontStretch;
use properties::longhands::line_height::computed_value::T as LineHeight;
use properties::longhands::transform::computed_value::ComputedMatrix;
use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
use properties::longhands::transform::computed_value::T as TransformList;
use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
use properties::longhands::visibility::computed_value::T as Visibility;
#[cfg(feature = "gecko")] use properties::{PropertyId, PropertyDeclarationId, LonghandId};
#[cfg(feature = "gecko")] use properties::{ShorthandId};
use selectors::parser::SelectorParseError;
use smallvec::SmallVec;
+use std::borrow::Cow;
use std::cmp;
#[cfg(feature = "gecko")] use fnv::FnvHashMap;
use style_traits::ParseError;
use super::ComputedValues;
#[cfg(feature = "gecko")]
use values::Auto;
use values::{CSSFloat, CustomIdent, Either};
-use values::animated::{ToAnimatedValue, ToAnimatedZero};
+use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA};
use values::animated::effects::BoxShadowList as AnimatedBoxShadowList;
use values::animated::effects::Filter as AnimatedFilter;
use values::animated::effects::FilterList as AnimatedFilterList;
use values::animated::effects::TextShadowList as AnimatedTextShadowList;
use values::computed::{Angle, BorderCornerRadius, CalcLengthOrPercentage};
use values::computed::{ClipRect, Context, ComputedUrl, ComputedValueAsSpecified};
use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
@@ -53,49 +55,18 @@ use values::computed::length::{NonNegati
use values::computed::length::NonNegativeLengthOrPercentage;
use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::generics::{GreaterThanOrEqualToOne, NonNegative};
use values::generics::effects::Filter;
use values::generics::position as generic_position;
use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber, SVGPaint};
use values::generics::svg::{SVGPaintKind, SVGStrokeDashArray, SVGOpacity};
-/// A trait used to implement various procedures used during animation.
-pub trait Animatable: Sized {
- /// Performs a weighted sum of this value and |other|. This is used for
- /// interpolation and addition of animation values.
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
- -> Result<Self, ()>;
-
- /// [Interpolates][interpolation] a value with another for a given property.
- ///
- /// [interpolation]: https://w3c.github.io/web-animations/#animation-interpolation
- fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
- self.add_weighted(other, 1.0 - progress, progress)
- }
-
- /// Returns the [sum][animation-addition] of this value and |other|.
- ///
- /// [animation-addition]: https://w3c.github.io/web-animations/#animation-addition
- fn add(&self, other: &Self) -> Result<Self, ()> {
- self.add_weighted(other, 1.0, 1.0)
- }
-
- /// [Accumulates][animation-accumulation] this value onto itself (|count| - 1) times then
- /// accumulates |other| onto the result.
- /// If |count| is zero, the result will be |other|.
- ///
- /// [animation-accumulation]: https://w3c.github.io/web-animations/#animation-accumulation
- fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
- self.add_weighted(other, count as f64, 1.0)
- }
-}
-
/// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list
-pub trait RepeatableListAnimatable: Animatable {}
+pub trait RepeatableListAnimatable: Animate {}
/// A longhand property whose animation type is not "none".
///
/// NOTE: This includes the 'display' property since it is animatable from SMIL even though it is
/// not animatable from CSS animations or Web Animations. CSS transitions also does not allow
/// animating 'display', but for CSS transitions we have the separate TransitionProperty type.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -444,17 +415,17 @@ impl AnimatedProperty {
match *self {
% for prop in data.longhands:
% if prop.animatable:
AnimatedProperty::${prop.camel_case}(ref from, ref to) => {
// https://w3c.github.io/web-animations/#discrete-animation-type
% if prop.animation_value_type == "discrete":
let value = if progress < 0.5 { from.clone() } else { to.clone() };
% else:
- let value = match from.interpolate(to, progress) {
+ let value = match from.animate(to, Procedure::Interpolate { progress }) {
Ok(value) => value,
Err(()) => return,
};
% endif
% if not prop.is_animatable_with_computed_value:
let value: longhands::${prop.ident}::computed_value::T =
ToAnimatedValue::from_animated_value(value);
% endif
@@ -655,88 +626,48 @@ impl AnimationValue {
)
}
% endif
% endfor
}
}
}
-impl Animatable for AnimationValue {
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
- -> Result<Self, ()> {
- match (self, other) {
- % for prop in data.longhands:
- % if prop.animatable:
- (&AnimationValue::${prop.camel_case}(ref from),
- &AnimationValue::${prop.camel_case}(ref to)) => {
- % if prop.animation_value_type == "discrete":
- if self_portion > other_portion {
- Ok(AnimationValue::${prop.camel_case}(from.clone()))
- } else {
- Ok(AnimationValue::${prop.camel_case}(to.clone()))
- }
- % else:
- from.add_weighted(to, self_portion, other_portion)
- .map(AnimationValue::${prop.camel_case})
- % endif
- }
- % endif
- % endfor
- _ => {
- panic!("Expected weighted addition of computed values of the same \
- property, got: {:?}, {:?}", self, other);
- }
- }
- }
-
- fn add(&self, other: &Self) -> Result<Self, ()> {
+impl Animate for AnimationValue {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
match (self, other) {
% for prop in data.longhands:
- % if prop.animatable:
- % if prop.animation_value_type == "discrete":
- (&AnimationValue::${prop.camel_case}(_),
- &AnimationValue::${prop.camel_case}(_)) => {
- Err(())
- }
- % else:
- (&AnimationValue::${prop.camel_case}(ref from),
- &AnimationValue::${prop.camel_case}(ref to)) => {
- from.add(to).map(AnimationValue::${prop.camel_case})
- }
- % endif
- % endif
+ % if prop.animatable:
+ % if prop.animation_value_type != "discrete":
+ (
+ &AnimationValue::${prop.camel_case}(ref this),
+ &AnimationValue::${prop.camel_case}(ref other),
+ ) => {
+ Ok(AnimationValue::${prop.camel_case}(
+ this.animate(other, procedure)?,
+ ))
+ },
+ % else:
+ (
+ &AnimationValue::${prop.camel_case}(ref this),
+ &AnimationValue::${prop.camel_case}(ref other),
+ ) => {
+ if let Procedure::Interpolate { progress } = procedure {
+ Ok(AnimationValue::${prop.camel_case}(
+ if progress < 0.5 { this.clone() } else { other.clone() },
+ ))
+ } else {
+ Err(())
+ }
+ },
+ % endif
+ % endif
% endfor
_ => {
- panic!("Expected addition of computed values of the same \
- property, got: {:?}, {:?}", self, other);
- }
- }
- }
-
- fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
- match (self, other) {
- % for prop in data.longhands:
- % if prop.animatable:
- % if prop.animation_value_type == "discrete":
- (&AnimationValue::${prop.camel_case}(_),
- &AnimationValue::${prop.camel_case}(_)) => {
- Err(())
- }
- % else:
- (&AnimationValue::${prop.camel_case}(ref from),
- &AnimationValue::${prop.camel_case}(ref to)) => {
- from.accumulate(to, count).map(AnimationValue::${prop.camel_case})
- }
- % endif
- % endif
- % endfor
- _ => {
- panic!("Expected accumulation of computed values of the same \
- property, got: {:?}, {:?}", self, other);
+ panic!("Unexpected AnimationValue::animate call, got: {:?}, {:?}", self, other);
}
}
}
}
impl ComputeSquaredDistance for AnimationValue {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self, other) {
@@ -782,27 +713,29 @@ impl ToAnimatedZero for AnimationValue {
impl RepeatableListAnimatable for LengthOrPercentage {}
impl RepeatableListAnimatable for Either<f32, LengthOrPercentage> {}
impl RepeatableListAnimatable for Either<NonNegativeNumber, NonNegativeLengthOrPercentage> {}
impl RepeatableListAnimatable for SvgLengthOrPercentageOrNumber<NonNegativeLengthOrPercentage, NonNegativeNumber> {}
macro_rules! repeated_vec_impl {
($($ty:ty),*) => {
- $(impl<T: RepeatableListAnimatable> Animatable for $ty {
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
- -> Result<Self, ()> {
+ $(impl<T> Animate for $ty
+ where
+ T: RepeatableListAnimatable,
+ {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
// If the length of either list is zero, the least common multiple is undefined.
if self.is_empty() || other.is_empty() {
return Err(());
}
use num_integer::lcm;
let len = lcm(self.len(), other.len());
- self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(me, you)| {
- me.add_weighted(you, self_portion, other_portion)
+ self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(this, other)| {
+ this.animate(other, procedure)
}).collect()
}
}
impl<T> ComputeSquaredDistance for $ty
where
T: ComputeSquaredDistance + RepeatableListAnimatable,
{
@@ -818,73 +751,27 @@ macro_rules! repeated_vec_impl {
}).sum()
}
})*
};
}
repeated_vec_impl!(SmallVec<[T; 1]>, Vec<T>);
-/// https://drafts.csswg.org/css-transitions/#animtype-number
-impl Animatable for Au {
- #[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- Ok(Au((self.0 as f64 * self_portion + other.0 as f64 * other_portion).round() as i32))
- }
-}
-
-impl <T> Animatable for Option<T>
- where T: Animatable,
-{
- #[inline]
- fn add_weighted(&self, other: &Option<T>, self_portion: f64, other_portion: f64) -> Result<Option<T>, ()> {
- match (self, other) {
- (&Some(ref this), &Some(ref other)) => {
- Ok(this.add_weighted(other, self_portion, other_portion).ok())
- }
- (&None, &None) => Ok(None),
- _ => Err(()),
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-number
-impl Animatable for f32 {
+/// https://drafts.csswg.org/css-transitions/#animtype-visibility
+impl Animate for Visibility {
#[inline]
- fn add_weighted(&self, other: &f32, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- Ok((*self as f64 * self_portion + *other as f64 * other_portion) as f32)
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-number
-impl Animatable for f64 {
- #[inline]
- fn add_weighted(&self, other: &f64, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- Ok(*self * self_portion + *other * other_portion)
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-integer
-impl Animatable for i32 {
- #[inline]
- fn add_weighted(&self, other: &i32, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- Ok((*self as f64 * self_portion + *other as f64 * other_portion + 0.5).floor() as i32)
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-visibility
-impl Animatable for Visibility {
- #[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ let (this_weight, other_weight) = procedure.weights();
match (*self, *other) {
(Visibility::visible, _) => {
- Ok(if self_portion > 0.0 { *self } else { *other })
+ Ok(if this_weight > 0.0 { *self } else { *other })
},
(_, Visibility::visible) => {
- Ok(if other_portion > 0.0 { *other } else { *self })
+ Ok(if other_weight > 0.0 { *other } else { *self })
},
_ => Err(()),
}
}
}
impl ComputeSquaredDistance for Visibility {
#[inline]
@@ -895,47 +782,29 @@ impl ComputeSquaredDistance for Visibili
impl ToAnimatedZero for Visibility {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
Err(())
}
}
-impl<T: Animatable + Copy> Animatable for Size2D<T> {
- #[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- let width = self.width.add_weighted(&other.width, self_portion, other_portion)?;
- let height = self.height.add_weighted(&other.height, self_portion, other_portion)?;
-
- Ok(Size2D::new(width, height))
- }
-}
-
-impl<T: Animatable + Copy> Animatable for Point2D<T> {
+/// https://drafts.csswg.org/css-transitions/#animtype-length
+impl Animate for VerticalAlign {
#[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- let x = self.x.add_weighted(&other.x, self_portion, other_portion)?;
- let y = self.y.add_weighted(&other.y, self_portion, other_portion)?;
-
- Ok(Point2D::new(x, y))
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-length
-impl Animatable for VerticalAlign {
- #[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- match (*self, *other) {
- (VerticalAlign::LengthOrPercentage(ref this),
- VerticalAlign::LengthOrPercentage(ref other)) => {
- this.add_weighted(other, self_portion, other_portion).map(|value| {
- VerticalAlign::LengthOrPercentage(value)
- })
- }
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ match (self, other) {
+ (
+ &VerticalAlign::LengthOrPercentage(ref this),
+ &VerticalAlign::LengthOrPercentage(ref other),
+ ) => {
+ Ok(VerticalAlign::LengthOrPercentage(
+ this.animate(other, procedure)?
+ ))
+ },
_ => Err(()),
}
}
}
impl ComputeSquaredDistance for VerticalAlign {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
@@ -953,112 +822,114 @@ impl ComputeSquaredDistance for Vertical
}
impl ToAnimatedZero for VerticalAlign {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for CalcLengthOrPercentage {
+impl Animate for CalcLengthOrPercentage {
#[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- fn add_weighted_half<T>(this: Option<T>,
- other: Option<T>,
- self_portion: f64,
- other_portion: f64)
- -> Result<Option<T>, ()>
- where T: Default + Animatable,
- {
- match (this, other) {
- (None, None) => Ok(None),
- (this, other) => {
- let this = this.unwrap_or(T::default());
- let other = other.unwrap_or(T::default());
- this.add_weighted(&other, self_portion, other_portion).map(Some)
- }
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ let animate_percentage_half = |this: Option<Percentage>, other: Option<Percentage>| {
+ if this.is_none() && other.is_none() {
+ return Ok(None);
}
- }
+ let this = this.unwrap_or_default();
+ let other = other.unwrap_or_default();
+ Ok(Some(this.animate(&other, procedure)?))
+ };
- let length = self.unclamped_length().add_weighted(&other.unclamped_length(), self_portion, other_portion)?;
- let percentage = add_weighted_half(self.percentage, other.percentage, self_portion, other_portion)?;
+ let length = self.unclamped_length().animate(&other.unclamped_length(), procedure)?;
+ let percentage = animate_percentage_half(self.percentage, other.percentage)?;
Ok(CalcLengthOrPercentage::with_clamping_mode(length, percentage, self.clamping_mode))
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for LengthOrPercentage {
+impl Animate for LengthOrPercentage {
#[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- match (*self, *other) {
- (LengthOrPercentage::Length(ref this),
- LengthOrPercentage::Length(ref other)) => {
- this.add_weighted(other, self_portion, other_portion)
- .map(LengthOrPercentage::Length)
- }
- (LengthOrPercentage::Percentage(ref this),
- LengthOrPercentage::Percentage(ref other)) => {
- this.add_weighted(other, self_portion, other_portion)
- .map(LengthOrPercentage::Percentage)
- }
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ match (self, other) {
+ (
+ &LengthOrPercentage::Length(ref this),
+ &LengthOrPercentage::Length(ref other),
+ ) => {
+ Ok(LengthOrPercentage::Length(this.animate(other, procedure)?))
+ },
+ (
+ &LengthOrPercentage::Percentage(ref this),
+ &LengthOrPercentage::Percentage(ref other),
+ ) => {
+ Ok(LengthOrPercentage::Percentage(this.animate(other, procedure)?))
+ },
(this, other) => {
// Special handling for zero values since these should not require calc().
if this.is_definitely_zero() {
- return other.add_weighted(&other, 0., other_portion)
- } else if other.is_definitely_zero() {
- return this.add_weighted(self, self_portion, 0.)
+ return other.to_animated_zero()?.animate(other, procedure);
+ }
+ if other.is_definitely_zero() {
+ return this.animate(&this.to_animated_zero()?, procedure);
}
- let this: CalcLengthOrPercentage = From::from(this);
- let other: CalcLengthOrPercentage = From::from(other);
- this.add_weighted(&other, self_portion, other_portion)
- .map(LengthOrPercentage::Calc)
+ let this = CalcLengthOrPercentage::from(*this);
+ let other = CalcLengthOrPercentage::from(*other);
+ Ok(LengthOrPercentage::Calc(this.animate(&other, procedure)?))
}
}
}
}
impl ToAnimatedZero for LengthOrPercentage {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
- match self {
- &LengthOrPercentage::Length(_) | &LengthOrPercentage::Calc(_) =>
- Ok(LengthOrPercentage::zero()),
- &LengthOrPercentage::Percentage(_) =>
- Ok(LengthOrPercentage::Percentage(Percentage::zero())),
- }
+ match *self {
+ LengthOrPercentage::Length(ref length) => {
+ Ok(LengthOrPercentage::Length(length.to_animated_zero()?))
+ },
+ LengthOrPercentage::Percentage(ref percentage) => {
+ Ok(LengthOrPercentage::Percentage(percentage.to_animated_zero()?))
+ },
+ LengthOrPercentage::Calc(ref calc) => {
+ Ok(LengthOrPercentage::Calc(calc.to_animated_zero()?))
+ },
+ }
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for LengthOrPercentageOrAuto {
+impl Animate for LengthOrPercentageOrAuto {
#[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- match (*self, *other) {
- (LengthOrPercentageOrAuto::Length(ref this),
- LengthOrPercentageOrAuto::Length(ref other)) => {
- this.add_weighted(other, self_portion, other_portion)
- .map(LengthOrPercentageOrAuto::Length)
- }
- (LengthOrPercentageOrAuto::Percentage(ref this),
- LengthOrPercentageOrAuto::Percentage(ref other)) => {
- this.add_weighted(other, self_portion, other_portion)
- .map(LengthOrPercentageOrAuto::Percentage)
- }
- (LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ match (self, other) {
+ (
+ &LengthOrPercentageOrAuto::Length(ref this),
+ &LengthOrPercentageOrAuto::Length(ref other),
+ ) => {
+ Ok(LengthOrPercentageOrAuto::Length(this.animate(other, procedure)?))
+ },
+ (
+ &LengthOrPercentageOrAuto::Percentage(ref this),
+ &LengthOrPercentageOrAuto::Percentage(ref other),
+ ) => {
+ Ok(LengthOrPercentageOrAuto::Percentage(
+ this.animate(other, procedure)?,
+ ))
+ },
+ (&LengthOrPercentageOrAuto::Auto, &LengthOrPercentageOrAuto::Auto) => {
Ok(LengthOrPercentageOrAuto::Auto)
- }
+ },
(this, other) => {
- let this: Option<CalcLengthOrPercentage> = From::from(this);
- let other: Option<CalcLengthOrPercentage> = From::from(other);
- match this.add_weighted(&other, self_portion, other_portion) {
- Ok(Some(result)) => Ok(LengthOrPercentageOrAuto::Calc(result)),
- _ => Err(()),
- }
- }
+ let this: Option<CalcLengthOrPercentage> = From::from(*this);
+ let other: Option<CalcLengthOrPercentage> = From::from(*other);
+ Ok(LengthOrPercentageOrAuto::Calc(
+ this.animate(&other, procedure)?.ok_or(())?,
+ ))
+ },
}
}
}
impl ToAnimatedZero for LengthOrPercentageOrAuto {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
match *self {
@@ -1068,40 +939,43 @@ impl ToAnimatedZero for LengthOrPercenta
Ok(LengthOrPercentageOrAuto::Length(Au(0)))
},
LengthOrPercentageOrAuto::Auto => Err(()),
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for LengthOrPercentageOrNone {
+impl Animate for LengthOrPercentageOrNone {
#[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- match (*self, *other) {
- (LengthOrPercentageOrNone::Length(ref this),
- LengthOrPercentageOrNone::Length(ref other)) => {
- this.add_weighted(other, self_portion, other_portion)
- .map(LengthOrPercentageOrNone::Length)
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ match (self, other) {
+ (
+ &LengthOrPercentageOrNone::Length(ref this),
+ &LengthOrPercentageOrNone::Length(ref other),
+ ) => {
+ Ok(LengthOrPercentageOrNone::Length(this.animate(other, procedure)?))
+ },
+ (
+ &LengthOrPercentageOrNone::Percentage(ref this),
+ &LengthOrPercentageOrNone::Percentage(ref other),
+ ) => {
+ Ok(LengthOrPercentageOrNone::Percentage(
+ this.animate(other, procedure)?,
+ ))
}
- (LengthOrPercentageOrNone::Percentage(ref this),
- LengthOrPercentageOrNone::Percentage(ref other)) => {
- this.add_weighted(other, self_portion, other_portion)
- .map(LengthOrPercentageOrNone::Percentage)
- }
- (LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => {
+ (&LengthOrPercentageOrNone::None, &LengthOrPercentageOrNone::None) => {
Ok(LengthOrPercentageOrNone::None)
- }
+ },
(this, other) => {
- let this = <Option<CalcLengthOrPercentage>>::from(this);
- let other = <Option<CalcLengthOrPercentage>>::from(other);
- match this.add_weighted(&other, self_portion, other_portion) {
- Ok(Some(result)) => Ok(LengthOrPercentageOrNone::Calc(result)),
- _ => Err(()),
- }
+ let this = <Option<CalcLengthOrPercentage>>::from(*this);
+ let other = <Option<CalcLengthOrPercentage>>::from(*other);
+ Ok(LengthOrPercentageOrNone::Calc(
+ this.animate(&other, procedure)?.ok_or(())?,
+ ))
},
}
}
}
impl ToAnimatedZero for LengthOrPercentageOrNone {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
@@ -1112,24 +986,27 @@ impl ToAnimatedZero for LengthOrPercenta
Ok(LengthOrPercentageOrNone::Length(Au(0)))
},
LengthOrPercentageOrNone::None => Err(()),
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for MozLength {
+impl Animate for MozLength {
#[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- match (*self, *other) {
- (MozLength::LengthOrPercentageOrAuto(ref this),
- MozLength::LengthOrPercentageOrAuto(ref other)) => {
- this.add_weighted(other, self_portion, other_portion)
- .map(MozLength::LengthOrPercentageOrAuto)
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ match (self, other) {
+ (
+ &MozLength::LengthOrPercentageOrAuto(ref this),
+ &MozLength::LengthOrPercentageOrAuto(ref other),
+ ) => {
+ Ok(MozLength::LengthOrPercentageOrAuto(
+ this.animate(other, procedure)?,
+ ))
}
_ => Err(()),
}
}
}
impl ToAnimatedZero for MozLength {
#[inline]
@@ -1139,67 +1016,69 @@ impl ToAnimatedZero for MozLength {
Ok(MozLength::LengthOrPercentageOrAuto(length.to_animated_zero()?))
},
_ => Err(())
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for MaxLength {
+impl Animate for MaxLength {
#[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- match (*self, *other) {
- (MaxLength::LengthOrPercentageOrNone(ref this),
- MaxLength::LengthOrPercentageOrNone(ref other)) => {
- this.add_weighted(other, self_portion, other_portion)
- .map(MaxLength::LengthOrPercentageOrNone)
- }
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ match (self, other) {
+ (
+ &MaxLength::LengthOrPercentageOrNone(ref this),
+ &MaxLength::LengthOrPercentageOrNone(ref other),
+ ) => {
+ Ok(MaxLength::LengthOrPercentageOrNone(
+ this.animate(other, procedure)?,
+ ))
+ },
_ => Err(()),
}
}
}
impl ToAnimatedZero for MaxLength {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
}
/// http://dev.w3.org/csswg/css-transitions/#animtype-font-weight
-impl Animatable for FontWeight {
+impl Animate for FontWeight {
#[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
let a = self.0 as f64;
let b = other.0 as f64;
const NORMAL: f64 = 400.;
- let weight = (a - NORMAL) * self_portion + (b - NORMAL) * other_portion + NORMAL;
+ let (this_weight, other_weight) = procedure.weights();
+ let weight = (a - NORMAL) * this_weight + (b - NORMAL) * other_weight + NORMAL;
let weight = (weight.max(100.).min(900.) / 100.).round() * 100.;
Ok(FontWeight(weight as u16))
}
}
impl ToAnimatedZero for FontWeight {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
Ok(FontWeight::normal())
}
}
/// https://drafts.csswg.org/css-fonts/#font-stretch-prop
-impl Animatable for FontStretch {
+impl Animate for FontStretch {
#[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
- -> Result<Self, ()>
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>
{
let from = f64::from(*self);
let to = f64::from(*other);
- // FIXME: When `const fn` is available in release rust, make |normal|, below, const.
let normal = f64::from(FontStretch::normal);
- let result = (from - normal) * self_portion + (to - normal) * other_portion + normal;
-
+ let (this_weight, other_weight) = procedure.weights();
+ let result = (from - normal) * this_weight + (to - normal) * other_weight + normal;
Ok(result.into())
}
}
impl ComputeSquaredDistance for FontStretch {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
f64::from(*self).compute_squared_distance(&(*other).into())
@@ -1236,23 +1115,26 @@ impl Into<FontStretch> for f64 {
let index = (self + 0.5).floor().min(9.0).max(1.0);
static FONT_STRETCH_ENUM_MAP: [FontStretch; 9] =
[ ultra_condensed, extra_condensed, condensed, semi_condensed, normal,
semi_expanded, expanded, extra_expanded, ultra_expanded ];
FONT_STRETCH_ENUM_MAP[(index - 1.0) as usize]
}
}
-/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
-impl<H: Animatable, V: Animatable> Animatable for generic_position::Position<H, V> {
+impl<H, V> Animate for generic_position::Position<H, V>
+where
+ H: Animate,
+ V: Animate,
+{
#[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(generic_position::Position {
- horizontal: self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?,
- vertical: self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?,
+ horizontal: self.horizontal.animate(&other.horizontal, procedure)?,
+ vertical: self.vertical.animate(&other.vertical, procedure)?,
})
}
}
impl<H, V> ToAnimatedZero for generic_position::Position<H, V>
where
H: ToAnimatedZero,
V: ToAnimatedZero,
@@ -1265,218 +1147,172 @@ where
})
}
}
impl<H, V> RepeatableListAnimatable for generic_position::Position<H, V>
where H: RepeatableListAnimatable, V: RepeatableListAnimatable {}
/// https://drafts.csswg.org/css-transitions/#animtype-rect
-impl Animatable for ClipRect {
+impl Animate for ClipRect {
#[inline]
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
- -> Result<Self, ()> {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(ClipRect {
- top: self.top.add_weighted(&other.top, self_portion, other_portion)?,
- right: self.right.add_weighted(&other.right, self_portion, other_portion)?,
- bottom: self.bottom.add_weighted(&other.bottom, self_portion, other_portion)?,
- left: self.left.add_weighted(&other.left, self_portion, other_portion)?,
+ top: self.top.animate(&other.top, procedure)?,
+ right: self.right.animate(&other.right, procedure)?,
+ bottom: self.bottom.animate(&other.bottom, procedure)?,
+ left: self.left.animate(&other.left, procedure)?,
})
}
}
impl ToAnimatedZero for ClipRect {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
}
-/// Check if it's possible to do a direct numerical interpolation
-/// between these two transform lists.
-/// http://dev.w3.org/csswg/css-transforms/#transform-transform-animation
-fn can_interpolate_list(from_list: &[TransformOperation],
- to_list: &[TransformOperation]) -> bool {
- // Lists must be equal length
- if from_list.len() != to_list.len() {
- return false;
- }
-
- // Each transform operation must match primitive type in other list
- for (from, to) in from_list.iter().zip(to_list) {
- match (from, to) {
- (&TransformOperation::Matrix(..), &TransformOperation::Matrix(..)) |
- (&TransformOperation::Skew(..), &TransformOperation::Skew(..)) |
- (&TransformOperation::Translate(..), &TransformOperation::Translate(..)) |
- (&TransformOperation::Scale(..), &TransformOperation::Scale(..)) |
- (&TransformOperation::Rotate(..), &TransformOperation::Rotate(..)) |
- (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => {}
- _ => {
- return false;
- }
- }
- }
-
- true
-}
-
/// Build an equivalent 'identity transform function list' based
/// on an existing transform list.
/// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
-fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOperation> {
- let mut result = vec!();
-
- for operation in list {
- match *operation {
+impl ToAnimatedZero for TransformOperation {
+ fn to_animated_zero(&self) -> Result<Self, ()> {
+ match *self {
TransformOperation::Matrix(..) => {
- let identity = ComputedMatrix::identity();
- result.push(TransformOperation::Matrix(identity));
- }
- TransformOperation::MatrixWithPercents(..) => {}
- TransformOperation::Skew(..) => {
- result.push(TransformOperation::Skew(Angle::zero(), Angle::zero()))
- }
- TransformOperation::Translate(..) => {
- result.push(TransformOperation::Translate(LengthOrPercentage::zero(),
- LengthOrPercentage::zero(),
- Au(0)));
- }
+ Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
+ },
+ TransformOperation::MatrixWithPercents(..) => {
+ // FIXME(nox): Should be MatrixWithPercents value.
+ Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
+ },
+ TransformOperation::Skew(sx, sy) => {
+ Ok(TransformOperation::Skew(
+ sx.to_animated_zero()?,
+ sy.to_animated_zero()?,
+ ))
+ },
+ TransformOperation::Translate(ref tx, ref ty, ref tz) => {
+ Ok(TransformOperation::Translate(
+ tx.to_animated_zero()?,
+ ty.to_animated_zero()?,
+ tz.to_animated_zero()?,
+ ))
+ },
TransformOperation::Scale(..) => {
- result.push(TransformOperation::Scale(1.0, 1.0, 1.0));
- }
+ Ok(TransformOperation::Scale(1.0, 1.0, 1.0))
+ },
TransformOperation::Rotate(x, y, z, a) => {
let (x, y, z, _) = get_normalized_vector_and_angle(x, y, z, a);
- result.push(TransformOperation::Rotate(x, y, z, Angle::zero()));
- }
+ Ok(TransformOperation::Rotate(x, y, z, Angle::zero()))
+ },
TransformOperation::Perspective(..) |
TransformOperation::AccumulateMatrix { .. } |
TransformOperation::InterpolateMatrix { .. } => {
// Perspective: We convert a perspective function into an equivalent
// ComputedMatrix, and then decompose/interpolate/recompose these matrices.
// AccumulateMatrix/InterpolateMatrix: We do interpolation on
// AccumulateMatrix/InterpolateMatrix by reading it as a ComputedMatrix
// (with layout information), and then do matrix interpolation.
//
// Therefore, we use an identity matrix to represent the identity transform list.
// http://dev.w3.org/csswg/css-transforms/#identity-transform-function
- let identity = ComputedMatrix::identity();
- result.push(TransformOperation::Matrix(identity));
- }
+ //
+ // FIXME(nox): This does not actually work, given the impl of
+ // Animate for TransformOperation bails out if the two given
+ // values are dissimilar.
+ Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
+ },
}
}
-
- result
}
-/// A wrapper for calling add_weighted that interpolates the distance of the two values from
-/// an initial_value and uses that to produce an interpolated value.
-/// This is used for values such as 'scale' where the initial value is 1 and where if we interpolate
-/// the absolute values, we will produce odd results for accumulation.
-fn add_weighted_with_initial_val<T: Animatable>(a: &T,
- b: &T,
- a_portion: f64,
- b_portion: f64,
- initial_val: &T) -> Result<T, ()> {
- let a = a.add_weighted(&initial_val, 1.0, -1.0)?;
- let b = b.add_weighted(&initial_val, 1.0, -1.0)?;
- let result = a.add_weighted(&b, a_portion, b_portion)?;
- result.add_weighted(&initial_val, 1.0, 1.0)
+fn animate_multiplicative_factor(
+ this: CSSFloat,
+ other: CSSFloat,
+ procedure: Procedure,
+) -> Result<CSSFloat, ()> {
+ Ok((this - 1.).animate(&(other - 1.), procedure)? + 1.)
}
-/// Add two transform lists.
/// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
-fn add_weighted_transform_lists(from_list: &[TransformOperation],
- to_list: &[TransformOperation],
- self_portion: f64,
- other_portion: f64) -> TransformList {
- let mut result = vec![];
-
- if can_interpolate_list(from_list, to_list) {
- for (from, to) in from_list.iter().zip(to_list) {
- match (from, to) {
- (&TransformOperation::Matrix(from),
- &TransformOperation::Matrix(_to)) => {
- let sum = from.add_weighted(&_to, self_portion, other_portion).unwrap();
- result.push(TransformOperation::Matrix(sum));
- }
- (&TransformOperation::MatrixWithPercents(_),
- &TransformOperation::MatrixWithPercents(_)) => {
- // We don't add_weighted `-moz-transform` matrices yet.
- // They contain percentage values.
- {}
- }
- (&TransformOperation::Skew(fx, fy),
- &TransformOperation::Skew(tx, ty)) => {
- let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap();
- let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap();
- result.push(TransformOperation::Skew(ix, iy));
- }
- (&TransformOperation::Translate(fx, fy, fz),
- &TransformOperation::Translate(tx, ty, tz)) => {
- let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap();
- let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap();
- let iz = fz.add_weighted(&tz, self_portion, other_portion).unwrap();
- result.push(TransformOperation::Translate(ix, iy, iz));
- }
- (&TransformOperation::Scale(fx, fy, fz),
- &TransformOperation::Scale(tx, ty, tz)) => {
- let ix = add_weighted_with_initial_val(&fx, &tx, self_portion,
- other_portion, &1.0).unwrap();
- let iy = add_weighted_with_initial_val(&fy, &ty, self_portion,
- other_portion, &1.0).unwrap();
- let iz = add_weighted_with_initial_val(&fz, &tz, self_portion,
- other_portion, &1.0).unwrap();
- result.push(TransformOperation::Scale(ix, iy, iz));
+impl Animate for TransformOperation {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ match (self, other) {
+ (
+ &TransformOperation::Matrix(ref this),
+ &TransformOperation::Matrix(ref other),
+ ) => {
+ Ok(TransformOperation::Matrix(
+ this.animate(other, procedure)?,
+ ))
+ },
+ (
+ &TransformOperation::Skew(ref fx, ref fy),
+ &TransformOperation::Skew(ref tx, ref ty),
+ ) => {
+ Ok(TransformOperation::Skew(
+ fx.animate(tx, procedure)?,
+ fy.animate(ty, procedure)?,
+ ))
+ },
+ (
+ &TransformOperation::Translate(ref fx, ref fy, ref fz),
+ &TransformOperation::Translate(ref tx, ref ty, ref tz),
+ ) => {
+ Ok(TransformOperation::Translate(
+ fx.animate(tx, procedure)?,
+ fy.animate(ty, procedure)?,
+ fz.animate(tz, procedure)?,
+ ))
+ },
+ (
+ &TransformOperation::Scale(ref fx, ref fy, ref fz),
+ &TransformOperation::Scale(ref tx, ref ty, ref tz),
+ ) => {
+ Ok(TransformOperation::Scale(
+ animate_multiplicative_factor(*fx, *tx, procedure)?,
+ animate_multiplicative_factor(*fy, *ty, procedure)?,
+ animate_multiplicative_factor(*fz, *tz, procedure)?,
+ ))
+ },
+ (
+ &TransformOperation::Rotate(fx, fy, fz, fa),
+ &TransformOperation::Rotate(tx, ty, tz, ta),
+ ) => {
+ let (fx, fy, fz, fa) = get_normalized_vector_and_angle(fx, fy, fz, fa);
+ let (tx, ty, tz, ta) = get_normalized_vector_and_angle(tx, ty, tz, ta);
+ if (fx, fy, fz) == (tx, ty, tz) {
+ let ia = fa.animate(&ta, procedure)?;
+ Ok(TransformOperation::Rotate(fx, fy, fz, ia))
+ } else {
+ let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
+ let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
+ Ok(TransformOperation::Matrix(
+ matrix_f.animate(&matrix_t, procedure)?,
+ ))
}
- (&TransformOperation::Rotate(fx, fy, fz, fa),
- &TransformOperation::Rotate(tx, ty, tz, ta)) => {
- let (fx, fy, fz, fa) = get_normalized_vector_and_angle(fx, fy, fz, fa);
- let (tx, ty, tz, ta) = get_normalized_vector_and_angle(tx, ty, tz, ta);
- if (fx, fy, fz) == (tx, ty, tz) {
- let ia = fa.add_weighted(&ta, self_portion, other_portion).unwrap();
- result.push(TransformOperation::Rotate(fx, fy, fz, ia));
- } else {
- let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
- let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
- let sum = matrix_f.add_weighted(&matrix_t, self_portion, other_portion)
- .unwrap();
-
- result.push(TransformOperation::Matrix(sum));
- }
+ },
+ (
+ &TransformOperation::Perspective(ref fd),
+ &TransformOperation::Perspective(ref td),
+ ) => {
+ let mut fd_matrix = ComputedMatrix::identity();
+ let mut td_matrix = ComputedMatrix::identity();
+ if fd.0 > 0 {
+ fd_matrix.m34 = -1. / fd.to_f32_px();
}
- (&TransformOperation::Perspective(fd),
- &TransformOperation::Perspective(td)) => {
- let mut fd_matrix = ComputedMatrix::identity();
- let mut td_matrix = ComputedMatrix::identity();
- if fd.0 > 0 {
- fd_matrix.m34 = -1. / fd.to_f32_px();
- }
-
- if td.0 > 0 {
- td_matrix.m34 = -1. / td.to_f32_px();
- }
-
- let sum = fd_matrix.add_weighted(&td_matrix, self_portion, other_portion)
- .unwrap();
- result.push(TransformOperation::Matrix(sum));
+ if td.0 > 0 {
+ td_matrix.m34 = -1. / td.to_f32_px();
}
- _ => {
- // This should be unreachable due to the can_interpolate_list() call.
- unreachable!();
- }
- }
+ Ok(TransformOperation::Matrix(
+ fd_matrix.animate(&td_matrix, procedure)?,
+ ))
+ },
+ _ => Err(()),
}
- } else {
- let from_transform_list = TransformList(Some(from_list.to_vec()));
- let to_transform_list = TransformList(Some(to_list.to_vec()));
- result.push(
- TransformOperation::InterpolateMatrix { from_list: from_transform_list,
- to_list: to_transform_list,
- progress: Percentage(other_portion as f32) });
}
-
- TransformList(Some(result))
}
/// https://www.w3.org/TR/css-transforms-1/#Rotate3dDefined
fn rotate_to_matrix(x: f32, y: f32, z: f32, a: Angle) -> ComputedMatrix {
let half_rad = a.radians() / 2.0;
let sc = (half_rad).sin() * (half_rad).cos();
let sq = (half_rad).sin().powi(2);
@@ -1535,50 +1371,48 @@ pub struct MatrixDecomposed2D {
/// The scale function.
pub scale: Scale2D,
/// The rotation angle.
pub angle: f32,
/// The inner matrix.
pub matrix: InnerMatrix2D,
}
-impl Animatable for InnerMatrix2D {
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for InnerMatrix2D {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(InnerMatrix2D {
- m11: add_weighted_with_initial_val(&self.m11, &other.m11,
- self_portion, other_portion, &1.0)?,
- m12: self.m12.add_weighted(&other.m12, self_portion, other_portion)?,
- m21: self.m21.add_weighted(&other.m21, self_portion, other_portion)?,
- m22: add_weighted_with_initial_val(&self.m22, &other.m22,
- self_portion, other_portion, &1.0)?,
+ m11: animate_multiplicative_factor(self.m11, other.m11, procedure)?,
+ m12: self.m12.animate(&other.m12, procedure)?,
+ m21: self.m21.animate(&other.m21, procedure)?,
+ m22: animate_multiplicative_factor(self.m22, other.m22, procedure)?,
})
}
}
-impl Animatable for Translate2D {
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Translate2D {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(Translate2D(
- self.0.add_weighted(&other.0, self_portion, other_portion)?,
- self.1.add_weighted(&other.1, self_portion, other_portion)?,
+ self.0.animate(&other.0, procedure)?,
+ self.1.animate(&other.1, procedure)?,
))
}
}
-impl Animatable for Scale2D {
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Scale2D {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(Scale2D(
- add_weighted_with_initial_val(&self.0, &other.0, self_portion, other_portion, &1.0)?,
- add_weighted_with_initial_val(&self.1, &other.1, self_portion, other_portion, &1.0)?,
+ animate_multiplicative_factor(self.0, other.0, procedure)?,
+ animate_multiplicative_factor(self.1, other.1, procedure)?,
))
}
}
-impl Animatable for MatrixDecomposed2D {
+impl Animate for MatrixDecomposed2D {
/// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-2d-matrix-values
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
// If x-axis of one is flipped, and y-axis of the other,
// convert to an unflipped rotation.
let mut scale = self.scale;
let mut angle = self.angle;
let mut other_angle = other.angle;
if (scale.0 < 0.0 && other.scale.1 < 0.0) || (scale.1 < 0.0 && other.scale.0 < 0.0) {
scale.0 = -scale.0;
scale.1 = -scale.1;
@@ -1598,20 +1432,20 @@ impl Animatable for MatrixDecomposed2D {
angle -= 360.
}
else{
other_angle -= 360.
}
}
// Interpolate all values.
- let translate = self.translate.add_weighted(&other.translate, self_portion, other_portion)?;
- let scale = scale.add_weighted(&other.scale, self_portion, other_portion)?;
- let angle = angle.add_weighted(&other_angle, self_portion, other_portion)?;
- let matrix = self.matrix.add_weighted(&other.matrix, self_portion, other_portion)?;
+ let translate = self.translate.animate(&other.translate, procedure)?;
+ let scale = scale.animate(&other.scale, procedure)?;
+ let angle = angle.animate(&other_angle, procedure)?;
+ let matrix = self.matrix.animate(&other.matrix, procedure)?;
Ok(MatrixDecomposed2D {
translate: translate,
scale: scale,
angle: angle,
matrix: matrix,
})
}
@@ -1626,36 +1460,35 @@ impl ComputeSquaredDistance for MatrixDe
let angle2 = other.angle as f64 * RAD_PER_DEG;
Ok(self.translate.compute_squared_distance(&other.translate)? +
self.scale.compute_squared_distance(&other.scale)? +
angle1.compute_squared_distance(&angle2)? +
self.matrix.compute_squared_distance(&other.matrix)?)
}
}
-impl Animatable for ComputedMatrix {
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for ComputedMatrix {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
if self.is_3d() || other.is_3d() {
let decomposed_from = decompose_3d_matrix(*self);
let decomposed_to = decompose_3d_matrix(*other);
match (decomposed_from, decomposed_to) {
- (Ok(from), Ok(to)) => {
- let sum = from.add_weighted(&to, self_portion, other_portion)?;
- Ok(ComputedMatrix::from(sum))
+ (Ok(this), Ok(other)) => {
+ Ok(ComputedMatrix::from(this.animate(&other, procedure)?))
},
_ => {
- let result = if self_portion > other_portion {*self} else {*other};
+ let (this_weight, other_weight) = procedure.weights();
+ let result = if this_weight > other_weight { *self } else { *other };
Ok(result)
- }
+ },
}
} else {
- let decomposed_from = MatrixDecomposed2D::from(*self);
- let decomposed_to = MatrixDecomposed2D::from(*other);
- let sum = decomposed_from.add_weighted(&decomposed_to, self_portion, other_portion)?;
- Ok(ComputedMatrix::from(sum))
+ let this = MatrixDecomposed2D::from(*self);
+ let other = MatrixDecomposed2D::from(*other);
+ Ok(ComputedMatrix::from(this.animate(&other, procedure)?))
}
}
}
impl ComputeSquaredDistance for ComputedMatrix {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
if self.is_3d() || other.is_3d() {
@@ -2082,103 +1915,104 @@ fn dot(a: [f32; 3], b: [f32; 3]) -> f32
fn cross(row1: [f32; 3], row2: [f32; 3]) -> [f32; 3] {
[
row1[1] * row2[2] - row1[2] * row2[1],
row1[2] * row2[0] - row1[0] * row2[2],
row1[0] * row2[1] - row1[1] * row2[0]
]
}
-impl Animatable for Translate3D {
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Translate3D {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(Translate3D(
- self.0.add_weighted(&other.0, self_portion, other_portion)?,
- self.1.add_weighted(&other.1, self_portion, other_portion)?,
- self.2.add_weighted(&other.2, self_portion, other_portion)?,
+ self.0.animate(&other.0, procedure)?,
+ self.1.animate(&other.1, procedure)?,
+ self.2.animate(&other.2, procedure)?,
))
}
}
-impl Animatable for Scale3D {
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Scale3D {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(Scale3D(
- add_weighted_with_initial_val(&self.0, &other.0, self_portion, other_portion, &1.0)?,
- add_weighted_with_initial_val(&self.1, &other.1, self_portion, other_portion, &1.0)?,
- add_weighted_with_initial_val(&self.2, &other.2, self_portion, other_portion, &1.0)?,
+ animate_multiplicative_factor(self.0, other.0, procedure)?,
+ animate_multiplicative_factor(self.1, other.1, procedure)?,
+ animate_multiplicative_factor(self.2, other.2, procedure)?,
))
}
}
-impl Animatable for Skew {
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Skew {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(Skew(
- self.0.add_weighted(&other.0, self_portion, other_portion)?,
- self.1.add_weighted(&other.1, self_portion, other_portion)?,
- self.2.add_weighted(&other.2, self_portion, other_portion)?,
+ self.0.animate(&other.0, procedure)?,
+ self.1.animate(&other.1, procedure)?,
+ self.2.animate(&other.2, procedure)?,
))
}
}
impl ComputeSquaredDistance for Skew {
// We have to use atan() to convert the skew factors into skew angles, so implement
// ComputeSquaredDistance manually.
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
Ok(self.0.atan().compute_squared_distance(&other.0.atan())? +
self.1.atan().compute_squared_distance(&other.1.atan())? +
self.2.atan().compute_squared_distance(&other.2.atan())?)
}
}
-impl Animatable for Perspective {
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Perspective {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(Perspective(
- self.0.add_weighted(&other.0, self_portion, other_portion)?,
- self.1.add_weighted(&other.1, self_portion, other_portion)?,
- self.2.add_weighted(&other.2, self_portion, other_portion)?,
- add_weighted_with_initial_val(&self.3, &other.3, self_portion, other_portion, &1.0)?,
+ self.0.animate(&other.0, procedure)?,
+ self.1.animate(&other.1, procedure)?,
+ self.2.animate(&other.2, procedure)?,
+ animate_multiplicative_factor(self.3, other.3, procedure)?,
))
}
}
-impl Animatable for MatrixDecomposed3D {
+impl Animate for MatrixDecomposed3D {
/// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-3d-matrix-values
- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
- -> Result<Self, ()> {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
use std::f64;
- debug_assert!((self_portion + other_portion - 1.0f64).abs() <= f64::EPSILON ||
- other_portion == 1.0f64 || other_portion == 0.0f64,
- "add_weighted should only be used for interpolating or accumulating transforms");
+ let (this_weight, other_weight) = procedure.weights();
+
+ debug_assert!((this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON ||
+ other_weight == 1.0f64 || other_weight == 0.0f64,
+ "animate should only be used for interpolating or accumulating transforms");
let mut sum = *self;
// Add translate, scale, skew and perspective components.
- sum.translate = self.translate.add_weighted(&other.translate, self_portion, other_portion)?;
- sum.scale = self.scale.add_weighted(&other.scale, self_portion, other_portion)?;
- sum.skew = self.skew.add_weighted(&other.skew, self_portion, other_portion)?;
- sum.perspective = self.perspective.add_weighted(&other.perspective, self_portion, other_portion)?;
+ sum.translate = self.translate.animate(&other.translate, procedure)?;
+ sum.scale = self.scale.animate(&other.scale, procedure)?;
+ sum.skew = self.skew.animate(&other.skew, procedure)?;
+ sum.perspective = self.perspective.animate(&other.perspective, procedure)?;
// Add quaternions using spherical linear interpolation (Slerp).
//
- // We take a specialized code path for accumulation (where other_portion is 1)
- if other_portion == 1.0 {
- if self_portion == 0.0 {
+ // We take a specialized code path for accumulation (where other_weight is 1)
+ if other_weight == 1.0 {
+ if this_weight == 0.0 {
return Ok(*other)
}
let clamped_w = self.quaternion.3.min(1.0).max(-1.0);
// Determine the scale factor.
let mut theta = clamped_w.acos();
let mut scale = if theta == 0.0 { 0.0 } else { 1.0 / theta.sin() };
- theta *= self_portion;
+ theta *= this_weight;
scale *= theta.sin();
- // Scale the self matrix by self_portion.
+ // Scale the self matrix by this_weight.
let mut scaled_self = *self;
% for i in range(3):
scaled_self.quaternion.${i} *= scale;
% endfor
scaled_self.quaternion.3 = theta.cos();
// Multiply scaled-self by other.
let a = &scaled_self.quaternion;
@@ -2199,22 +2033,22 @@ impl Animatable for MatrixDecomposed3D {
product = product.min(1.0);
product = product.max(-1.0);
if product == 1.0 {
return Ok(sum);
}
let theta = product.acos();
- let w = (other_portion * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
+ let w = (other_weight * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
let mut a = *self;
let mut b = *other;
% for i in range(4):
- a.quaternion.${i} *= (other_portion * theta).cos() - product * w;
+ a.quaternion.${i} *= (other_weight * theta).cos() - product * w;
b.quaternion.${i} *= w;
sum.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i};
% endfor
}
Ok(sum)
}
}
@@ -2412,233 +2246,242 @@ impl ComputedMatrix {
self.m12*self.m21*self.m33 + self.m11*self.m22*self.m33),
};
Some(x)
}
}
/// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms
-impl Animatable for TransformList {
+impl Animate for TransformList {
#[inline]
- fn add_weighted(&self, other: &TransformList, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
- // http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
- let result = match (&self.0, &other.0) {
- (&Some(ref from_list), &Some(ref to_list)) => {
- // Two lists of transforms
- add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion)
- }
- (&Some(ref from_list), &None) => {
- // http://dev.w3.org/csswg/css-transforms/#none-transform-animation
- let to_list = build_identity_transform_list(from_list);
- add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion)
- }
- (&None, &Some(ref to_list)) => {
- // http://dev.w3.org/csswg/css-transforms/#none-transform-animation
- let from_list = build_identity_transform_list(to_list);
- add_weighted_transform_lists(&from_list, to_list, self_portion, other_portion)
- }
- _ => {
- // http://dev.w3.org/csswg/css-transforms/#none-none-animation
- TransformList(None)
- }
+ fn animate(
+ &self,
+ other: &Self,
+ procedure: Procedure,
+ ) -> Result<Self, ()> {
+ if self.0.is_none() && other.0.is_none() {
+ return Ok(TransformList(None));
+ }
+
+ if procedure == Procedure::Add {
+ let this = self.0.as_ref().map_or(&[][..], |l| l);
+ let other = other.0.as_ref().map_or(&[][..], |l| l);
+ let result = this.iter().chain(other).cloned().collect::<Vec<_>>();
+ return Ok(TransformList(if result.is_empty() {
+ None
+ } else {
+ Some(result)
+ }));
+ }
+
+ let this = if self.0.is_some() {
+ Cow::Borrowed(self)
+ } else {
+ Cow::Owned(other.to_animated_zero()?)
+ };
+ let other = if other.0.is_some() {
+ Cow::Borrowed(other)
+ } else {
+ Cow::Owned(self.to_animated_zero()?)
};
- Ok(result)
- }
-
- fn add(&self, other: &Self) -> Result<Self, ()> {
- match (&self.0, &other.0) {
- (&Some(ref from_list), &Some(ref to_list)) => {
- Ok(TransformList(Some([&from_list[..], &to_list[..]].concat())))
- }
- (&Some(_), &None) => {
- Ok(self.clone())
- }
- (&None, &Some(_)) => {
- Ok(other.clone())
- }
- _ => {
- Ok(TransformList(None))
+ {
+ let this = (*this).0.as_ref().map_or(&[][..], |l| l);
+ let other = (*other).0.as_ref().map_or(&[][..], |l| l);
+ if this.len() == other.len() {
+ let result = this.iter().zip(other).map(|(this, other)| {
+ this.animate(other, procedure)
+ }).collect::<Result<Vec<_>, _>>();
+ if let Ok(list) = result {
+ return Ok(TransformList(if list.is_empty() {
+ None
+ } else {
+ Some(list)
+ }));
+ }
}
}
- }
- #[inline]
- fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
- match (&self.0, &other.0) {
- (&Some(ref from_list), &Some(ref to_list)) => {
- if can_interpolate_list(from_list, to_list) {
- Ok(add_weighted_transform_lists(from_list, &to_list, count as f64, 1.0))
- } else {
- use std::i32;
- let result = vec![TransformOperation::AccumulateMatrix {
- from_list: self.clone(),
- to_list: other.clone(),
- count: cmp::min(count, i32::MAX as u64) as i32
- }];
- Ok(TransformList(Some(result)))
- }
- }
- (&Some(ref from_list), &None) => {
- Ok(add_weighted_transform_lists(from_list, from_list, count as f64, 0.0))
- }
- (&None, &Some(_)) => {
- // If |self| is 'none' then we are calculating:
- //
- // none * |count| + |other|
- // = none + |other|
- // = |other|
- //
- // Hence the result is just |other|.
- Ok(other.clone())
- }
- _ => {
- Ok(TransformList(None))
- }
+ match procedure {
+ Procedure::Add => Err(()),
+ Procedure::Interpolate { progress } => {
+ Ok(TransformList(Some(vec![TransformOperation::InterpolateMatrix {
+ from_list: this.into_owned(),
+ to_list: other.into_owned(),
+ progress: Percentage(progress as f32),
+ }])))
+ },
+ Procedure::Accumulate { count } => {
+ Ok(TransformList(Some(vec![TransformOperation::AccumulateMatrix {
+ from_list: this.into_owned(),
+ to_list: other.into_owned(),
+ count: cmp::min(count, i32::max_value() as u64) as i32,
+ }])))
+ },
}
}
}
-/// A helper function to retrieve the pixel length and percentage value.
-fn extract_pixel_calc_value(lop: &LengthOrPercentage) -> (f64, CSSFloat) {
- match lop {
- &LengthOrPercentage::Length(au) => (au.to_f64_px(), 0.),
- &LengthOrPercentage::Percentage(percent) => (0., percent.0),
- &LengthOrPercentage::Calc(calc) => (calc.length().to_f64_px(), calc.percentage())
- }
-}
-
-/// Compute the squared distance of two transform lists.
// This might not be the most useful definition of distance. It might be better, for example,
// to trace the distance travelled by a point as its transform is interpolated between the two
// lists. That, however, proves to be quite complicated so we take a simple approach for now.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1318591#c0.
-fn compute_transform_lists_squared_distance(from_list: &[TransformOperation],
- to_list: &[TransformOperation])
- -> Result<SquaredDistance, ()> {
- let zero_distance = SquaredDistance::Value(0.);
- let squared_distance = from_list.iter().zip(to_list.iter()).map(|(from, to)| {
- match (from, to) {
- (&TransformOperation::Matrix(from),
- &TransformOperation::Matrix(to)) => {
- from.compute_squared_distance(&to).unwrap_or(zero_distance)
- }
- (&TransformOperation::Skew(fx, fy),
- &TransformOperation::Skew(tx, ty)) => {
- fx.compute_squared_distance(&tx).unwrap_or(zero_distance) +
- fy.compute_squared_distance(&ty).unwrap_or(zero_distance)
- }
- (&TransformOperation::Translate(fx, fy, fz),
- &TransformOperation::Translate(tx, ty, tz)) => {
+impl ComputeSquaredDistance for TransformOperation {
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
+ match (self, other) {
+ (
+ &TransformOperation::Matrix(ref this),
+ &TransformOperation::Matrix(ref other),
+ ) => {
+ this.compute_squared_distance(other)
+ },
+ (
+ &TransformOperation::Skew(ref fx, ref fy),
+ &TransformOperation::Skew(ref tx, ref ty),
+ ) => {
+ Ok(
+ fx.compute_squared_distance(&tx)? +
+ fy.compute_squared_distance(&ty)?,
+ )
+ },
+ (
+ &TransformOperation::Translate(ref fx, ref fy, ref fz),
+ &TransformOperation::Translate(ref tx, ref ty, ref tz),
+ ) => {
// We don't want to require doing layout in order to calculate the result, so
// drop the percentage part. However, dropping percentage makes us impossible to
// compute the distance for the percentage-percentage case, but Gecko uses the
// same formula, so it's fine for now.
// Note: We use pixel value to compute the distance for translate, so we have to
// convert Au into px.
- let diff_x = fx.add_weighted(&tx, 1., -1.).unwrap_or(LengthOrPercentage::zero());
- let diff_y = fy.add_weighted(&ty, 1., -1.).unwrap_or(LengthOrPercentage::zero());
- let (diff_x_length, _) = extract_pixel_calc_value(&diff_x);
- let (diff_y_length, _) = extract_pixel_calc_value(&diff_y);
- SquaredDistance::Value(diff_x_length * diff_x_length) +
- SquaredDistance::Value(diff_y_length * diff_y_length) +
- fz.to_f64_px().compute_squared_distance(&tz.to_f64_px()).unwrap_or(zero_distance)
- }
- (&TransformOperation::Scale(fx, fy, fz),
- &TransformOperation::Scale(tx, ty, tz)) => {
- fx.compute_squared_distance(&tx).unwrap_or(zero_distance) +
- fy.compute_squared_distance(&ty).unwrap_or(zero_distance) +
- fz.compute_squared_distance(&tz).unwrap_or(zero_distance)
- }
- (&TransformOperation::Rotate(fx, fy, fz, fa),
- &TransformOperation::Rotate(tx, ty, tz, ta)) => {
+ let extract_pixel_length = |lop: &LengthOrPercentage| {
+ match *lop {
+ LengthOrPercentage::Length(au) => au.to_f64_px(),
+ LengthOrPercentage::Percentage(_) => 0.,
+ LengthOrPercentage::Calc(calc) => calc.length().to_f64_px(),
+ }
+ };
+
+ let fx = extract_pixel_length(&fx);
+ let fy = extract_pixel_length(&fy);
+ let tx = extract_pixel_length(&tx);
+ let ty = extract_pixel_length(&ty);
+
+ Ok(
+ fx.compute_squared_distance(&tx)? +
+ fy.compute_squared_distance(&ty)? +
+ fz.to_f64_px().compute_squared_distance(&tz.to_f64_px())?,
+ )
+ },
+ (
+ &TransformOperation::Scale(ref fx, ref fy, ref fz),
+ &TransformOperation::Scale(ref tx, ref ty, ref tz),
+ ) => {
+ Ok(
+ fx.compute_squared_distance(&tx)? +
+ fy.compute_squared_distance(&ty)? +
+ fz.compute_squared_distance(&tz)?,
+ )
+ },
+ (
+ &TransformOperation::Rotate(fx, fy, fz, fa),
+ &TransformOperation::Rotate(tx, ty, tz, ta),
+ ) => {
let (fx, fy, fz, angle1) = get_normalized_vector