author | Wes Kocher <wkocher@mozilla.com> |
Wed, 30 Aug 2017 19:52:39 -0700 | |
changeset 377824 | 04b6be50a2526c7a26a63715f441c47e1aa1f9be |
parent 377823 | d9b405d82cffb07343a5f2fd941e029298c7f6c4 (current diff) |
parent 377802 | 9182a041d8891cc61efb3e2cbe35397b3f981364 (diff) |
child 377825 | a81e995c2a6a06447745f22f60fab62ca88f2eb5 |
child 377964 | 9ca18987dabb23b5b2a581af46e3c6969c97ac69 |
push id | 50043 |
push user | kwierso@gmail.com |
push date | Thu, 31 Aug 2017 03:03:23 +0000 |
treeherder | autoland@04b6be50a252 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 57.0a1 |
first release with | nightly linux32
04b6be50a252
/
57.0a1
/
20170831100258
/
files
nightly linux64
04b6be50a252
/
57.0a1
/
20170831100258
/
files
nightly mac
04b6be50a252
/
57.0a1
/
20170831100258
/
files
nightly win32
04b6be50a252
/
57.0a1
/
20170831100258
/
files
nightly win64
04b6be50a252
/
57.0a1
/
20170831100258
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
57.0a1
/
20170831100258
/
pushlog to previous
nightly linux64
57.0a1
/
20170831100258
/
pushlog to previous
nightly mac
57.0a1
/
20170831100258
/
pushlog to previous
nightly win32
57.0a1
/
20170831100258
/
pushlog to previous
nightly win64
57.0a1
/
20170831100258
/
pushlog to previous
|
--- a/accessible/base/MarkupMap.h +++ b/accessible/base/MarkupMap.h @@ -59,22 +59,22 @@ MARKUPMAP(figure, roles::FIGURE, Attr(xmlroles, figure)) MARKUPMAP(form, New_HyperText, roles::FORM) MARKUPMAP(footer, - New_HyperText, - roles::FOOTER) + New_HTMLHeaderOrFooter, + 0) MARKUPMAP(header, - New_HyperText, - roles::HEADER) + New_HTMLHeaderOrFooter, + 0) MARKUPMAP(h1, New_HyperText, roles::HEADING) MARKUPMAP(h2, New_HyperText, roles::HEADING)
--- a/accessible/base/nsAccessibilityService.cpp +++ b/accessible/base/nsAccessibilityService.cpp @@ -156,16 +156,19 @@ static Accessible* New_HyperText(nsICont { return new HyperTextAccessibleWrap(aContent, aContext->Document()); } static Accessible* New_HTMLFigcaption(nsIContent* aContent, Accessible* aContext) { return new HTMLFigcaptionAccessible(aContent, aContext->Document()); } static Accessible* New_HTMLFigure(nsIContent* aContent, Accessible* aContext) { return new HTMLFigureAccessible(aContent, aContext->Document()); } +static Accessible* New_HTMLHeaderOrFooter(nsIContent* aContent, Accessible* aContext) + { return new HTMLHeaderOrFooterAccessible(aContent, aContext->Document()); } + static Accessible* New_HTMLLegend(nsIContent* aContent, Accessible* aContext) { return new HTMLLegendAccessible(aContent, aContext->Document()); } static Accessible* New_HTMLOption(nsIContent* aContent, Accessible* aContext) { return new HTMLSelectOptionAccessible(aContent, aContext->Document()); } static Accessible* New_HTMLOptgroup(nsIContent* aContent, Accessible* aContext) { return new HTMLSelectOptGroupAccessible(aContent, aContext->Document()); }
--- a/accessible/generic/HyperTextAccessible.cpp +++ b/accessible/generic/HyperTextAccessible.cpp @@ -1137,41 +1137,16 @@ HyperTextAccessible::LandmarkRole() cons return nullptr; // For the html landmark elements we expose them like we do ARIA landmarks to // make AT navigation schemes "just work". if (mContent->IsHTMLElement(nsGkAtoms::nav)) { return nsGkAtoms::navigation; } - if (mContent->IsAnyOfHTMLElements(nsGkAtoms::header, - nsGkAtoms::footer)) { - // Only map header and footer if they are not descendants of an article - // or section tag. - nsIContent* parent = mContent->GetParent(); - while (parent) { - if (parent->IsAnyOfHTMLElements(nsGkAtoms::article, nsGkAtoms::section)) { - break; - } - parent = parent->GetParent(); - } - - // No article or section elements found. - if (!parent) { - if (mContent->IsHTMLElement(nsGkAtoms::header)) { - return nsGkAtoms::banner; - } - - if (mContent->IsHTMLElement(nsGkAtoms::footer)) { - return nsGkAtoms::contentinfo; - } - } - return nullptr; - } - if (mContent->IsHTMLElement(nsGkAtoms::aside)) { return nsGkAtoms::complementary; } if (mContent->IsHTMLElement(nsGkAtoms::main)) { return nsGkAtoms::main; }
--- a/accessible/html/HTMLElementAccessibles.cpp +++ b/accessible/html/HTMLElementAccessibles.cpp @@ -197,8 +197,64 @@ HTMLSummaryAccessible::NativeState() //////////////////////////////////////////////////////////////////////////////// // HTMLSummaryAccessible: Widgets bool HTMLSummaryAccessible::IsWidget() const { return true; } + + +//////////////////////////////////////////////////////////////////////////////// +// HTMLHeaderOrFooterAccessible +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS_INHERITED0(HTMLHeaderOrFooterAccessible, HyperTextAccessible) + +role +HTMLHeaderOrFooterAccessible::NativeRole() +{ + // Only map header and footer if they are direct descendants of the body tag. + // If other sectioning or sectioning root elements, they become sections. + nsIContent* parent = mContent->GetParent(); + while (parent) { + if (parent->IsAnyOfHTMLElements(nsGkAtoms::article, nsGkAtoms::aside, + nsGkAtoms::nav, nsGkAtoms::section, + nsGkAtoms::blockquote, nsGkAtoms::details, + nsGkAtoms::dialog, nsGkAtoms::fieldset, + nsGkAtoms::figure, nsGkAtoms::td)) { + break; + } + parent = parent->GetParent(); + } + + // No sectioning or sectioning root elements found. + if (!parent) { + if (mContent->IsHTMLElement(nsGkAtoms::header)) { + return roles::HEADER; + } + + if (mContent->IsHTMLElement(nsGkAtoms::footer)) { + return roles::FOOTER; + } + } + + return roles::SECTION; +} + +nsIAtom* +HTMLHeaderOrFooterAccessible::LandmarkRole() const +{ + if (!HasOwnContent()) + return nullptr; + + a11y::role r = const_cast<HTMLHeaderOrFooterAccessible*>(this)->Role(); + if (r == roles::HEADER) { + return nsGkAtoms::banner; + } + + if (r == roles::FOOTER) { + return nsGkAtoms::contentinfo; + } + + return nullptr; +}
--- a/accessible/html/HTMLElementAccessibles.h +++ b/accessible/html/HTMLElementAccessibles.h @@ -109,12 +109,32 @@ public: virtual uint8_t ActionCount() override; virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; virtual bool DoAction(uint8_t aIndex) override; // Widgets virtual bool IsWidget() const override; }; +/** + * Used for HTML header and footer elements. + */ +class HTMLHeaderOrFooterAccessible : public HyperTextAccessibleWrap +{ +public: + + HTMLHeaderOrFooterAccessible(nsIContent* aContent, DocAccessible* aDoc) : + HyperTextAccessibleWrap(aContent, aDoc) {} + + NS_DECL_ISUPPORTS_INHERITED + + // Accessible + virtual nsIAtom* LandmarkRole() const override; + virtual a11y::role NativeRole() override; + +protected: + virtual ~HTMLHeaderOrFooterAccessible() {} +}; + } // namespace a11y } // namespace mozilla #endif
--- a/accessible/tests/mochitest/elm/test_HTMLSpec.html +++ b/accessible/tests/mochitest/elm/test_HTMLSpec.html @@ -519,22 +519,30 @@ obj = { role: ROLE_FOOTER, attributes: { "xml-roles": "contentinfo" }, interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ] }; testElm("footer", obj); obj = { - role: ROLE_FOOTER, + role: ROLE_SECTION, absentAttributes: { "xml-roles": "contentinfo" }, interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ] }; testElm("footer_in_article", obj); + testElm("footer_in_aside", obj); + testElm("footer_in_nav", obj); testElm("footer_in_section", obj); + testElm("footer_in_blockquote", obj); + testElm("footer_in_details", obj); + testElm("footer_in_dialog", obj); + testElm("footer_in_fieldset", obj); + testElm("footer_in_figure", obj); + testElm("footer_in_td", obj); // //////////////////////////////////////////////////////////////////////// // HTML:form obj = { role: ROLE_FORM }; testElm("form", obj); @@ -604,22 +612,30 @@ obj = { role: ROLE_HEADER, attributes: { "xml-roles": "banner" }, interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ] }; testElm("header", obj); obj = { - role: ROLE_HEADER, + role: ROLE_SECTION, absentAttributes: { "xml-roles": "banner" }, interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ] }; testElm("header_in_article", obj); + testElm("header_in_aside", obj); + testElm("header_in_nav", obj); testElm("header_in_section", obj); + testElm("header_in_blockquote", obj); + testElm("header_in_details", obj); + testElm("header_in_dialog", obj); + testElm("header_in_fieldset", obj); + testElm("header_in_figure", obj); + testElm("header_in_td", obj); // //////////////////////////////////////////////////////////////////////// // HTML:hr obj = { role: ROLE_SEPARATOR, }; testElm("hr", obj); @@ -1466,19 +1482,43 @@ <img src="../moz.png" alt="An awesome picture"> <figcaption id="figcaption">Caption for the awesome picture</figcaption> </figure> <footer id="footer">Some copyright info</footer> <article> <footer id="footer_in_article">Some copyright info</footer> </article> + <aside> + <footer id="footer_in_aside">Some copyright info</footer> + </aside> + <nav> + <footer id="footer_in_nav">Some copyright info</footer> + </nav> <section> <footer id="footer_in_section">Some copyright info</footer> </section> + <blockquote> + <footer id="footer_in_blockquote">Some copyright info</footer> + </blockquote> + <details open="true"> + <footer id="footer_in_details">Some copyright info</footer> + </details> + <dialog open="true"> + <footer id="footer_in_dialog">Some copyright info</footer> + </dialog> + <fieldset> + <footer id="footer_in_fieldset">Some copyright info</footer> + </fieldset> + <figure> + <footer id="footer_in_figure">Some copyright info</footer> + </figure> + <table><tr><td> + <footer id="footer_in_td">Some copyright info</footer> + </td></tr></table> <form id="form"></form> <iframe id="frameset_container" src="data:text/html,<html><frameset><frame src='data:text/html,hi'></frame></frameset></html>"> </iframe> <h1 id="h1">heading1</h1> @@ -1487,19 +1527,43 @@ <h4 id="h4">heading4</h4> <h5 id="h5">heading5</h5> <h6 id="h6">heading6</h6> <header id="header">A logo</header> <article> <header id="header_in_article">Not logo</header> </article> + <aside> + <header id="header_in_aside">Not logo</header> + </aside> + <nav> + <header id="header_in_nav">Not logo</header> + </nav> <section> <header id="header_in_section">Not logo</header> </section> + <blockquote> + <header id="header_in_blockquote">Not logo</header> + </blockquote> + <details open="true"> + <header id="header_in_details">Not logo</header> + </details> + <dialog open="true"> + <header id="header_in_dialog">Not logo</header> + </dialog> + <fieldset> + <header id="header_in_fieldset">Not logo</header> + </fieldset> + <figure> + <header id="header_in_figure">Not logo</header> + </figure> + <table><tr><td> + <header id="header_in_td">Not logo</header> + </td></tr></table> <hr id="hr"> <p id="i_container">normal<i>italic</i></p> <img id="img" src="../moz.png"> <input id="input_button" type="button" value="Button"> <input id="input_checkbox" type="checkbox"> <input id="input_checkbox_true" type="checkbox" checked>
--- a/accessible/tests/mochitest/jsat/test_landmarks.html +++ b/accessible/tests/mochitest/jsat/test_landmarks.html @@ -42,44 +42,44 @@ [{"string": "contentinfo"}, {"string": "footer"}, "a footer"], ["a footer", {"string": "footer"}, {"string": "contentinfo"}]], expectedBraille: [ [{"string": "contentinfo"}, {"string": "footerAbbr"}, "a footer"], ["a footer", {"string": "footerAbbr"}, {"string": "contentinfo"}]] }, { accOrElmOrID: "article_header", expectedUtterance: [ - [{"string": "header"}, "a header within an article"], - ["a header within an article", {"string": "header"}]], + ["a header within an article"], + ["a header within an article"]], expectedBraille: [ - [{"string": "headerAbbr"}, "a header within an article"], - ["a header within an article", {"string": "headerAbbr"}]], + ["a header within an article"], + ["a header within an article"]], }, { accOrElmOrID: "article_footer", expectedUtterance: [ - [{"string": "footer"}, "a footer within an article"], - ["a footer within an article", {"string": "footer"}]], + ["a footer within an article"], + ["a footer within an article"]], expectedBraille: [ - [{"string": "footerAbbr"}, "a footer within an article"], - ["a footer within an article", {"string": "footerAbbr"}]] + ["a footer within an article"], + ["a footer within an article"]] }, { accOrElmOrID: "section_header", - expectedUtterance: [[{"string": "header"}, "a header within a section"], - ["a header within a section", {"string": "header"}]], + expectedUtterance: [["a header within a section"], + ["a header within a section"]], expectedBraille: [ - [{"string": "headerAbbr"}, "a header within a section"], - ["a header within a section", {"string": "headerAbbr"}]] + ["a header within a section"], + ["a header within a section"]] }, { accOrElmOrID: "section_footer", expectedUtterance: [ - [{"string": "footer"}, "a footer within a section"], - ["a footer within a section", {"string": "footer"}]], + ["a footer within a section"], + ["a footer within a section"]], expectedBraille: [ - [{"string": "footerAbbr"}, "a footer within a section"], - ["a footer within a section", {"string": "footerAbbr"}]] + ["a footer within a section"], + ["a footer within a section"]] }, { accOrElmOrID: "aside", expectedUtterance: [ [{"string": "complementary"}, "by the way I am an aside"], ["by the way I am an aside", {"string": "complementary"}]], expectedBraille: [ [{"string": "complementary"}, "by the way I am an aside"], ["by the way I am an aside", {"string": "complementary"}]]
--- a/accessible/tests/mochitest/role/test_general.html +++ b/accessible/tests/mochitest/role/test_general.html @@ -27,19 +27,19 @@ testRole("article", ROLE_ARTICLE); testRole("aside", ROLE_NOTE); testRole("section", ROLE_SECTION); // Bug 996821 // Check that landmark elements get accessibles with styled overflow. testRole("section_overflow", ROLE_SECTION); testRole("nav_overflow", ROLE_SECTION); - testRole("header_overflow", ROLE_HEADER); + testRole("header_overflow", ROLE_SECTION); testRole("aside_overflow", ROLE_NOTE); - testRole("footer_overflow", ROLE_FOOTER); + testRole("footer_overflow", ROLE_SECTION); testRole("article_overflow", ROLE_ARTICLE); // test html:div testRole("sec", ROLE_SECTION); // Test html:blockquote testRole("quote", ROLE_SECTION);
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -455,16 +455,17 @@ pref("browser.tabs.insertRelatedAfterCur pref("browser.tabs.warnOnClose", true); pref("browser.tabs.warnOnCloseOtherTabs", true); pref("browser.tabs.warnOnOpen", true); pref("browser.tabs.maxOpenBeforeWarn", 15); pref("browser.tabs.loadInBackground", true); pref("browser.tabs.opentabfor.middleclick", true); pref("browser.tabs.loadDivertedInBackground", false); pref("browser.tabs.loadBookmarksInBackground", false); +pref("browser.tabs.loadBookmarksInTabs", false); pref("browser.tabs.tabClipWidth", 140); #ifdef UNIX_BUT_NOT_MAC pref("browser.tabs.drawInTitlebar", false); #else pref("browser.tabs.drawInTitlebar", true); #endif // 0 - Disable the tabbar session restore button.
--- a/browser/base/content/aboutDialog-appUpdater.js +++ b/browser/base/content/aboutDialog-appUpdater.js @@ -22,27 +22,28 @@ function onUnload(aEvent) { if (gAppUpdater.isChecking) gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK); // Safe to call even when there isn't a download in progress. gAppUpdater.removeDownloadListener(); gAppUpdater = null; } -function appUpdater() { +function appUpdater(options = {}) { XPCOMUtils.defineLazyServiceGetter(this, "aus", "@mozilla.org/updates/update-service;1", "nsIApplicationUpdateService"); XPCOMUtils.defineLazyServiceGetter(this, "checker", "@mozilla.org/updates/update-checker;1", "nsIUpdateChecker"); XPCOMUtils.defineLazyServiceGetter(this, "um", "@mozilla.org/updates/update-manager;1", "nsIUpdateManager"); + this.options = options; this.updateDeck = document.getElementById("updateDeck"); // Hide the update deck when the update window is already open and it's not // already applied, to avoid syncing issues between them. Applied updates // don't have any information to sync between the windows as they both just // show the "Restart to continue"-type button. if (Services.wm.getMostRecentWindow("Update:Wizard") && !this.isApplied) { @@ -181,19 +182,25 @@ appUpdater.prototype = let year = buildID.slice(0, 4); let month = buildID.slice(4, 6); let day = buildID.slice(6, 8); updateVersion += ` (${year}-${month}-${day})`; } button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1); button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey"); } + this.updateDeck.selectedPanel = panel; + if (this.options.buttonAutoFocus && + (!document.commandDispatcher.focusedElement || // don't steal the focus + document.commandDispatcher.focusedElement.localName == "button")) { // except from the other buttons + button.focus(); + } + } else { + this.updateDeck.selectedPanel = panel; } - - this.updateDeck.selectedPanel = panel; }, /** * Check for updates */ checkForUpdates() { // Clear prefs that could prevent a user from discovering available updates. if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) {
--- a/browser/base/content/aboutDialog.js +++ b/browser/base/content/aboutDialog.js @@ -59,23 +59,17 @@ function init(aEvent) { let relNotesURL = Services.urlFormatter.formatURLPref("app.releaseNotesURL"); if (relNotesURL != "about:blank") { relNotesLink.href = relNotesURL; relNotesLink.hidden = false; } } if (AppConstants.MOZ_UPDATER) { - gAppUpdater = new appUpdater(); - - let button = gAppUpdater.updateDeck.selectedPanel.querySelector("button"); - if (button && (!document.commandDispatcher.focusedElement || // don't steal the focus - document.commandDispatcher.focusedElement.localName == "button")) { // except from the other buttons - button.focus(); - } + gAppUpdater = new appUpdater({ buttonAutoFocus: true }); let channelLabel = document.getElementById("currentChannel"); let currentChannelText = document.getElementById("currentChannelText"); channelLabel.value = UpdateUtils.UpdateChannel; if (/^release($|\-)/.test(channelLabel.value)) currentChannelText.hidden = true; }
--- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -662,16 +662,17 @@ html|input.urlbar-input[textoverflow]:no } #DateTimePickerPanel[active="true"] { -moz-binding: url("chrome://global/content/bindings/datetimepopup.xml#datetime-popup"); } #urlbar[pageproxystate=invalid] > #page-action-buttons > .urlbar-page-action, #identity-box.chromeUI ~ #page-action-buttons > .urlbar-page-action, +#urlbar[usertyping] > .urlbar-textbox-container > .urlbar-history-dropmarker, .urlbar-go-button[pageproxystate="valid"], .urlbar-go-button:not([parentfocused="true"]), #urlbar[pageproxystate="invalid"] > #identity-box > #blocked-permissions-container, #urlbar[pageproxystate="invalid"] > #identity-box > #notification-popup-box, #urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon-labels { visibility: collapse; }
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1374,20 +1374,16 @@ var gBrowserInit = { document.documentElement.setAttribute("darkwindowframe", "true"); } } ToolbarIconColor.init(); gRemoteControl.updateVisualCue(Marionette.running); - this._uriToLoadPromise.then(uriToLoad => { - gIdentityHandler.initIdentityBlock(uriToLoad); - }); - // Wait until chrome is painted before executing code not critical to making the window visible this._boundDelayedStartup = this._delayedStartup.bind(this); window.addEventListener("MozAfterPaint", this._boundDelayedStartup); this._loadHandled = true; }, _cancelDelayedStartup() { @@ -2770,16 +2766,17 @@ function URLBarSetURI(aURI) { } valid = !isBlankPageURL(uri.spec); } let isDifferentValidValue = valid && value != gURLBar.value; gURLBar.value = value; gURLBar.valueIsTyped = !valid; + gURLBar.removeAttribute("usertyping"); if (isDifferentValidValue) { gURLBar.selectionStart = gURLBar.selectionEnd = 0; } SetPageProxyState(valid ? "valid" : "invalid"); } function losslessDecodeURI(aURI) { @@ -7077,17 +7074,17 @@ var gIdentityHandler = { * to be able to focus it on the popupshown event. */ _popupTriggeredByKeyboard: false, /** * RegExp used to decide if an about url should be shown as being part of * the browser UI. */ - _secureInternalUIWhitelist: /^(?:accounts|addons|cache|config|crashes|customizing|downloads|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|searchreset|sessionrestore|support|welcomeback)(?:[?#]|$)/i, + _secureInternalUIWhitelist: /^(?:accounts|addons|cache|config|crashes|customizing|downloads|healthreport|license|newaddon|permissions|preferences|rights|searchreset|sessionrestore|support|welcomeback)(?:[?#]|$)/i, get _isBroken() { return this._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN; }, get _isSecure() { // If a <browser> is included within a chrome document, then this._state // will refer to the security state for the <browser> and not the top level @@ -7707,24 +7704,16 @@ var gIdentityHandler = { this._identityPopupContentSupp.textContent = supplemental; this._identityPopupContentVerif.textContent = verifier; // Update per-site permissions section. this.updateSitePermissions(); }, setURI(uri) { - // Ignore about:blank loads until the window's initial URL has loaded, - // to avoid hiding the UI that initIdentityBlock could have prepared. - if (this._ignoreAboutBlankUntilFirstLoad) { - if (uri.spec == "about:blank") - return; - this._ignoreAboutBlankUntilFirstLoad = false; - } - this._uri = uri; try { this._uri.host; this._uriHasHost = true; } catch (ex) { this._uriHasHost = false; } @@ -7749,40 +7738,16 @@ var gIdentityHandler = { // Check the URI again after resolving. this._isURILoadedFromFile = resolvedURI.schemeIs("file"); } catch (ex) { // NetUtil's methods will throw for malformed URIs and the like } }, /** - * Used to initialize the identity block before first paint to avoid - * flickering when opening a new window showing a secure internal page - * (eg. about:home) - */ - initIdentityBlock(initialURI) { - if (this._uri) { - // Apparently we already loaded something, so there's nothing to do here. - return; - } - - if ((typeof initialURI != "string") || !initialURI.startsWith("about:")) - return; - - let uri = Services.io.newURI(initialURI); - if (this._secureInternalUIWhitelist.test(uri.pathQueryRef)) { - this._isSecureInternalUI = true; - this._ignoreAboutBlankUntilFirstLoad = true; - this.refreshIdentityBlock(); - // The identity label won't be visible without setting this. - gURLBar.setAttribute("pageproxystate", "valid"); - } - }, - - /** * Click handler for the identity-box element in primary chrome. */ handleIdentityButtonEvent(event) { event.stopPropagation(); if ((event.type == "click" && event.button != 0) || (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE && event.keyCode != KeyEvent.DOM_VK_RETURN)) {
--- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -724,16 +724,17 @@ aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) { if (this.mTab.hasAttribute("busy")) { this.mTab.removeAttribute("busy"); // Only animate the "burst" indicating the page has loaded if // the top-level page is the one that finished loading. if (aWebProgress.isTopLevel && !aWebProgress.isLoadingDocument && + Components.isSuccessCode(aStatus) && !this.mTabBrowser.tabAnimationsInProgress && Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) { this.mTab.setAttribute("bursting", "true"); } this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]); if (!this.mTab.selected) this.mTab.setAttribute("unread", "true");
--- a/browser/base/content/test/about/browser_aboutHome.js +++ b/browser/base/content/test/about/browser_aboutHome.js @@ -523,28 +523,27 @@ add_task(async function() { // Skip this test on Mac, because Space doesn't activate the button there. if (AppConstants.platform == "macosx") { return; } await BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, async function(browser) { info("Waiting for about:addons tab to open..."); - let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons"); + let promiseTabLoaded = BrowserTestUtils.browserLoaded(browser, false, "about:addons"); await ContentTask.spawn(browser, null, async function() { let addOnsButton = content.document.getElementById("addons"); addOnsButton.focus(); }); await BrowserTestUtils.synthesizeKey(" ", {}, browser); - let tab = await promiseTabOpened; - is(tab.linkedBrowser.currentURI.spec, "about:addons", + await promiseTabLoaded; + is(browser.currentURI.spec, "about:addons", "Should have seen the about:addons tab"); - await BrowserTestUtils.removeTab(tab); }); }); /** * Cleans up snippets and ensures that by default we don't try to check for * remote snippets since that may cause network bustage or slowness. * * @param aSetupFn
--- a/browser/base/content/test/general/browser_bug882977.js +++ b/browser/base/content/test/general/browser_bug882977.js @@ -1,16 +1,16 @@ "use strict"; /** * Tests that the identity-box shows the chromeUI styling - * when viewing about:home in a new window. + * when viewing such a page in a new window. */ add_task(async function() { - let homepage = "about:home"; + let homepage = "about:preferences"; await SpecialPowers.pushPrefEnv({ "set": [ ["browser.startup.homepage", homepage], ["browser.startup.page", 1], ] }); let win = OpenBrowserWindow();
--- a/browser/base/content/test/static/browser_all_files_referenced.js +++ b/browser/base/content/test/static/browser_all_files_referenced.js @@ -124,18 +124,16 @@ var whitelist = [ // These are used in content processes. They are actually referenced. {file: "resource://shield-recipe-client-content/shield-content-frame.js"}, {file: "resource://shield-recipe-client-content/shield-content-process.js"}, // New L10n API that is not yet used in production {file: "resource://gre/modules/Localization.jsm"}, // Starting from here, files in the whitelist are bugs that need fixing. - // Bug 1339420 - {file: "chrome://branding/content/icon128.png"}, // Bug 1339424 (wontfix?) {file: "chrome://browser/locale/taskbar.properties", platforms: ["linux", "macosx"]}, // Bug 1316187 {file: "chrome://global/content/customizeToolbar.xul"}, // Bug 1343837 {file: "chrome://global/content/findUtils.js"}, // Bug 1343843
--- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -1295,16 +1295,21 @@ file, You can obtain one at http://mozil <method name="onInput"> <parameter name="aEvent"/> <body><![CDATA[ if (!this.mIgnoreInput && this.mController.input == this) { this._value = this.inputField.value; gBrowser.userTypedValue = this.value; this.valueIsTyped = true; + if (this.inputField.value) { + this.setAttribute("usertyping", "true"); + } else { + this.removeAttribute("usertyping"); + } // Only wait for a result when we are sure to get one. In some // cases, like when pasting the same exact text, we may not fire // a new search and we won't get a result. if (this.mController.handleText()) { this.gotResultForCurrentQuery = false; this._searchStartDate = Date.now(); this._deferredKeyEventQueue = []; if (this._deferredKeyEventTimeout) {
--- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -38,17 +38,19 @@ Object.defineProperty(this, "BROWSER_NEW var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab"; var gBidiUI = false; /** * Determines whether the given url is considered a special URL for new tabs. */ function isBlankPageURL(aURL) { - return aURL == "about:blank" || aURL == BROWSER_NEW_TAB_URL; + return aURL == "about:blank" || + aURL == "about:home" || + aURL == BROWSER_NEW_TAB_URL; } function getBrowserURL() { return "chrome://browser/content/browser.xul"; } function getTopWin(skipPopups) { // If this is called in a browser window, use that window regardless of
--- a/browser/components/places/PlacesUIUtils.jsm +++ b/browser/components/places/PlacesUIUtils.jsm @@ -35,49 +35,63 @@ XPCOMUtils.defineLazyModuleGetter(this, const gInContentProcess = Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT; const FAVICON_REQUEST_TIMEOUT = 60 * 1000; // Map from windows to arrays of data about pending favicon loads. let gFaviconLoadDataMap = new Map(); // copied from utilityOverlay.js const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab"; +const PREF_LOAD_BOOKMARKS_IN_BACKGROUND = "browser.tabs.loadBookmarksInBackground"; +const PREF_LOAD_BOOKMARKS_IN_TABS = "browser.tabs.loadBookmarksInTabs"; // This function isn't public both because it's synchronous and because it is // going to be removed in bug 1072833. function IsLivemark(aItemId) { // Since this check may be done on each dragover event, it's worth maintaining // a cache. let self = IsLivemark; if (!("ids" in self)) { const LIVEMARK_ANNO = PlacesUtils.LMANNO_FEEDURI; let idsVec = PlacesUtils.annotations.getItemsWithAnnotation(LIVEMARK_ANNO); self.ids = new Set(idsVec); let obs = Object.freeze({ - QueryInterface: XPCOMUtils.generateQI(Ci.nsIAnnotationObserver), + QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarksObserver]), + + // Ci.nsINavBookmarkObserver items. - onItemAnnotationSet(itemId, annoName) { - if (annoName == LIVEMARK_ANNO) + onItemChanged(itemId, property, isAnnoProperty, newValue, lastModified, + itemType, parentId, guid) { + if (isAnnoProperty && property == LIVEMARK_ANNO) { self.ids.add(itemId); + } }, - onItemAnnotationRemoved(itemId, annoName) { - // If annoName is set to an empty string, the item is gone. - if (annoName == LIVEMARK_ANNO || annoName == "") - self.ids.delete(itemId); + onItemRemoved(itemId) { + // Since the bookmark is removed, we know we can remove any references + // to it from the cache. + self.ids.delete(itemId); }, + onItemAdded() {}, + onBeginUpdateBatch() {}, + onEndUpdateBatch() {}, + onItemVisited() {}, + onItemMoved() {}, onPageAnnotationSet() { }, onPageAnnotationRemoved() { }, + skipDescendantsOnItemRemoval: false, + skipTags: false, }); - PlacesUtils.annotations.addObserver(obs); + + PlacesUtils.bookmarks.addObserver(obs); PlacesUtils.registerShutdownFunction(() => { - PlacesUtils.annotations.removeObserver(obs); + PlacesUtils.bookmarks.removeObserver(obs); }); } return self.ids.has(aItemId); } let InternalFaviconLoader = { /** * This gets called for every inner window that is destroyed. @@ -1013,30 +1027,37 @@ this.PlacesUIUtils = { * An uri result node. * @param aEvent * The DOM mouse/key event with modifier keys set that track the * user's preferred destination window or tab. */ openNodeWithEvent: function PUIU_openNodeWithEvent(aNode, aEvent) { let window = aEvent.target.ownerGlobal; - this._openNodeIn(aNode, window.whereToOpenLink(aEvent, false, true), window); + + let where = window.whereToOpenLink(aEvent, false, true); + if (where == "current" && this.loadBookmarksInTabs && + PlacesUtils.nodeIsBookmark(aNode) && !aNode.uri.startsWith("javascript:")) { + where = "tab"; + } + + this._openNodeIn(aNode, where, window); }, /** * Loads the node's URL in the appropriate tab or window or as a * web panel. * see also openUILinkIn */ openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView, aPrivate) { let window = aView.ownerWindow; this._openNodeIn(aNode, aWhere, window, aPrivate); }, - _openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow, aPrivate = false) { + _openNodeIn: function PUIU__openNodeIn(aNode, aWhere, aWindow, aPrivate = false) { if (aNode && PlacesUtils.nodeIsURI(aNode) && this.checkURLSecurity(aNode, aWindow)) { let isBookmark = PlacesUtils.nodeIsBookmark(aNode); if (!PrivateBrowsingUtils.isWindowPrivate(aWindow)) { if (isBookmark) this.markPageAsFollowedBookmark(aNode.uri); else @@ -1053,17 +1074,17 @@ this.PlacesUIUtils = { browserWin.openWebPanel(aNode.title, aNode.uri); return; } } } aWindow.openUILinkIn(aNode.uri, aWhere, { allowPopups: aNode.uri.startsWith("javascript:"), - inBackground: Services.prefs.getBoolPref("browser.tabs.loadBookmarksInBackground"), + inBackground: this.loadBookmarksInBackground, private: aPrivate, }); } }, /** * Helper for guessing scheme from an url string. * Used to avoid nsIURI overhead in frequently called UI functions. @@ -1520,16 +1541,21 @@ XPCOMUtils.defineLazyGetter(PlacesUIUtil XPCOMUtils.defineLazyGetter(PlacesUIUtils, "useAsyncTransactions", function() { try { return Services.prefs.getBoolPref("browser.places.useAsyncTransactions"); } catch (ex) { } return false; }); +XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "loadBookmarksInBackground", + PREF_LOAD_BOOKMARKS_IN_BACKGROUND, false); +XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "loadBookmarksInTabs", + PREF_LOAD_BOOKMARKS_IN_TABS, false); + XPCOMUtils.defineLazyServiceGetter(this, "URIFixup", "@mozilla.org/docshell/urifixup;1", "nsIURIFixup"); XPCOMUtils.defineLazyGetter(this, "bundle", function() { const PLACES_STRING_BUNDLE_URI = "chrome://browser/locale/places/places.properties"; return Cc["@mozilla.org/intl/stringbundle;1"].
--- a/browser/components/places/tests/browser/browser.ini +++ b/browser/components/places/tests/browser/browser.ini @@ -7,37 +7,35 @@ support-files = head.js framedPage.html frameLeft.html frameRight.html sidebarpanels_click_test_page.html keyword_form.html [browser_0_library_left_pane_migration.js] -[browser_410196_paste_into_tags.js] -subsuite = clipboard -[browser_416459_cut.js] -subsuite = clipboard -[browser_423515.js] -[browser_425884.js] -[browser_435851_copy_query.js] -subsuite = clipboard -[browser_475045.js] -[browser_555547.js] [browser_addBookmarkForFrame.js] +[browser_bookmark_folder_moveability.js] [browser_bookmarklet_windowOpen.js] support-files = pageopeningwindow.html [browser_bookmarkProperties_addFolderDefaultButton.js] [browser_bookmarkProperties_addKeywordForThisSearch.js] [browser_bookmarkProperties_addLivemark.js] [browser_bookmarkProperties_bookmarkAllTabs.js] [browser_bookmarkProperties_editTagContainer.js] [browser_bookmarkProperties_readOnlyRoot.js] [browser_bookmarksProperties.js] +[browser_check_correct_controllers.js] +[browser_click_bookmarks_on_toolbar.js] +[browser_cutting_bookmarks.js] +subsuite = clipboard +[browser_copy_folder_tree.js] +[browser_copy_query_without_tree.js] +subsuite = clipboard [browser_drag_bookmarks_on_toolbar.js] [browser_forgetthissite_single.js] [browser_history_sidebar_search.js] [browser_library_batch_delete.js] [browser_library_commands.js] [browser_library_delete_tags.js] [browser_library_downloads.js] [browser_library_infoBox.js] @@ -46,19 +44,22 @@ support-files = [browser_library_left_pane_select_hierarchy.js] [browser_library_middleclick.js] [browser_library_open_leak.js] [browser_library_openFlatContainer.js] [browser_library_panel_leak.js] [browser_library_search.js] [browser_library_views_liveupdate.js] [browser_markPageAsFollowedLink.js] +[browser_paste_into_tags.js] +subsuite = clipboard [browser_sidebarpanels_click.js] skip-if = true # temporarily disabled for breaking the treeview - bug 658744 [browser_sort_in_library.js] +[browser_toolbar_drop_text.js] [browser_toolbarbutton_menu_context.js] [browser_views_iconsupdate.js] support-files = favicon-normal16.png [browser_views_liveupdate.js] [browser_bookmark_all_tabs.js] support-files = bookmark_dummy_1.html
rename from browser/components/places/tests/browser/browser_423515.js rename to browser/components/places/tests/browser/browser_bookmark_folder_moveability.js
rename from browser/components/places/tests/browser/browser_555547.js rename to browser/components/places/tests/browser/browser_check_correct_controllers.js
new file mode 100644 --- /dev/null +++ b/browser/components/places/tests/browser/browser_click_bookmarks_on_toolbar.js @@ -0,0 +1,130 @@ +/* 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/. */ + +const PREF_LOAD_BOOKMARKS_IN_TABS = "browser.tabs.loadBookmarksInTabs"; +const TEST_PAGES = ["about:mozilla", "about:robots"]; + +var gBookmarkElements = []; + +function getToolbarNodeForItemGuid(aItemGuid) { + var children = document.getElementById("PlacesToolbarItems").childNodes; + for (let child of children) { + if (aItemGuid == child._placesNode.bookmarkGuid) { + return child; + } + } + return null; +} + +function waitForLoad(browser, url) { + return BrowserTestUtils.browserLoaded(browser, false, url).then(() => { + return BrowserTestUtils.loadURI(browser, "about:blank"); + }); +} + +function waitForNewTab(url, inBackground) { + return BrowserTestUtils.waitForNewTab(gBrowser, url).then(tab => { + if (inBackground) { + Assert.notEqual(tab, + gBrowser.selectedTab, `The new tab is in the background`); + } else { + Assert.equal(tab, + gBrowser.selectedTab, `The new tab is in the foreground`); + } + + return BrowserTestUtils.removeTab(tab); + }) +} + +add_task(async function setup() { + let bookmarks = await Promise.all(TEST_PAGES.map((url, index) => { + return PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + title: `Title ${index}`, + url + }); + })); + + let toolbar = document.getElementById("PersonalToolbar"); + let wasCollapsed = toolbar.collapsed; + if (wasCollapsed) { + await promiseSetToolbarVisibility(toolbar, true); + } + + for (let bookmark of bookmarks) { + let element = getToolbarNodeForItemGuid(bookmark.guid); + Assert.notEqual(element, null, "Found node on toolbar"); + + gBookmarkElements.push(element); + } + + registerCleanupFunction(async () => { + gBookmarkElements = []; + + if (wasCollapsed) { + await promiseSetToolbarVisibility(toolbar, false); + } + + await Promise.all(bookmarks.map(bookmark => { + return PlacesUtils.bookmarks.remove(bookmark); + })); + }); +}); + +add_task(async function click() { + let promise = waitForLoad(gBrowser.selectedBrowser, TEST_PAGES[0]); + EventUtils.synthesizeMouseAtCenter(gBookmarkElements[0], { + button: 0 + }); + await promise; + + promise = waitForNewTab(TEST_PAGES[1], false); + EventUtils.synthesizeMouseAtCenter(gBookmarkElements[1], { + button: 0, accelKey: true + }); + await promise; +}); + +add_task(async function middleclick() { + let promise = waitForNewTab(TEST_PAGES[0], true); + EventUtils.synthesizeMouseAtCenter(gBookmarkElements[0], { + button: 1, shiftKey: true + }); + await promise; + + promise = waitForNewTab(TEST_PAGES[1], false); + EventUtils.synthesizeMouseAtCenter(gBookmarkElements[1], { + button: 1 + }); + await promise; +}); + +add_task(async function clickWithPrefSet() { + await SpecialPowers.pushPrefEnv({set: [ + [PREF_LOAD_BOOKMARKS_IN_TABS, true] + ]}) + + let promise = waitForNewTab(TEST_PAGES[0], false); + EventUtils.synthesizeMouseAtCenter(gBookmarkElements[0], { + button: 0 + }); + await promise; + + let placesContext = document.getElementById("placesContext"); + promise = BrowserTestUtils.waitForEvent(placesContext, "popupshown"); + EventUtils.synthesizeMouseAtCenter(gBookmarkElements[1], { + button: 2, + type: "contextmenu" + }); + await promise; + + promise = waitForLoad(gBrowser.selectedBrowser, TEST_PAGES[1]); + let open = document.getElementById("placesContext_open"); + EventUtils.synthesizeMouseAtCenter(open, { + button: 0 + }); + await promise; + + await SpecialPowers.popPrefEnv(); +});
rename from browser/components/places/tests/browser/browser_425884.js rename to browser/components/places/tests/browser/browser_copy_folder_tree.js
rename from browser/components/places/tests/browser/browser_435851_copy_query.js rename to browser/components/places/tests/browser/browser_copy_query_without_tree.js
rename from browser/components/places/tests/browser/browser_416459_cut.js rename to browser/components/places/tests/browser/browser_cutting_bookmarks.js
--- a/browser/components/places/tests/browser/browser_library_views_liveupdate.js +++ b/browser/components/places/tests/browser/browser_library_views_liveupdate.js @@ -60,20 +60,20 @@ function startTest() { PlacesUtils._uri("http://bmf1.mozilla.org/"), bs.DEFAULT_INDEX, "bmf1"); addedBookmarks.push(id); bs.moveItem(id, bs.bookmarksMenuFolder, 0); // TOOLBAR ok(true, "*** Acting on toolbar bookmarks"); - bs.insertBookmark(bs.toolbarFolder, - PlacesUtils._uri("http://tb1.mozilla.org/"), - bs.DEFAULT_INDEX, - "tb1"); + id = bs.insertBookmark(bs.toolbarFolder, + PlacesUtils._uri("http://tb1.mozilla.org/"), + bs.DEFAULT_INDEX, + "tb1"); bs.setItemTitle(id, "tb1_edited"); addedBookmarks.push(id); id = bs.insertBookmark(bs.toolbarFolder, PlacesUtils._uri("place:"), bs.DEFAULT_INDEX, "tb2"); bs.setItemTitle(id, "tb2_edited"); addedBookmarks.push(id);
rename from browser/components/places/tests/browser/browser_410196_paste_into_tags.js rename to browser/components/places/tests/browser/browser_paste_into_tags.js
rename from browser/components/places/tests/browser/browser_475045.js rename to browser/components/places/tests/browser/browser_toolbar_drop_text.js --- a/browser/components/places/tests/browser/browser_475045.js +++ b/browser/components/places/tests/browser/browser_toolbar_drop_text.js @@ -19,32 +19,36 @@ add_task(async function test() { }); } // Setup the node we will use to be dropped. The actual node used does not // matter because we will set its data, effect, and mimeType manually. let placesItems = document.getElementById("PlacesToolbarItems"); ok(placesItems, "PlacesToolbarItems should not be null"); ok(placesItems.localName == "scrollbox", "PlacesToolbarItems should not be null"); - ok(placesItems.childNodes[0], "PlacesToolbarItems must have at least one child"); /** * Simulates a drop of a URI onto the bookmarks bar. * * @param aEffect * The effect to use for the drop operation: move, copy, or link. * @param aMimeType * The mime type to use for the drop operation. */ let simulateDragDrop = async function(aEffect, aMimeType) { const url = "http://www.mozilla.org/D1995729-A152-4e30-8329-469B01F30AA7"; let promiseItemAddedNotification = promiseBookmarksNotification( "onItemAdded", (itemId, parentId, index, type, uri, guid) => uri.spec == url); - EventUtils.synthesizeDrop(placesItems.childNodes[0], + // We use the toolbar as the drag source, as we just need almost any node + // to simulate the drag. The actual data for the drop is passed via the + // drag data. Note: The toolbar is used rather than another bookmark node, + // as we need something that is immovable from a places perspective, as this + // forces the move into a copy. + EventUtils.synthesizeDrop(toolbar, placesItems, [[{type: aMimeType, data: url}]], aEffect, window); await promiseItemAddedNotification; // Verify that the drop produces exactly one bookmark. @@ -75,17 +79,18 @@ add_task(async function test() { if (aMimeType == "text/x-moz-url") data = urls.map(spec => spec + "\n" + spec).join("\n"); else data = urls.join("\n"); let promiseItemAddedNotification = promiseBookmarksNotification( "onItemAdded", (itemId, parentId, index, type, uri, guid) => uri.spec == urls[2]); - EventUtils.synthesizeDrop(placesItems.childNodes[0], + // See notes for EventUtils.synthesizeDrop in simulateDragDrop(). + EventUtils.synthesizeDrop(toolbar, placesItems, [[{type: aMimeType, data}]], aEffect, window); await promiseItemAddedNotification; // Verify that the drop produces exactly one bookmark per each URL.
new file mode 100644 --- /dev/null +++ b/browser/components/places/tests/unit/test_PUIU_livemarksCache.js @@ -0,0 +1,40 @@ +"use strict"; + +let {IsLivemark} = Cu.import("resource:///modules/PlacesUIUtils.jsm", {}); + +add_task(function test_livemark_cache_builtin_folder() { + // This test checks a basic livemark, and also initializes the observer for + // updates to the bookmarks. + Assert.ok(!IsLivemark(PlacesUtils.unfiledBookmarksFolderId), + "unfiled bookmarks should not be seen as a livemark"); +}); + +add_task(async function test_livemark_add_and_remove_items() { + let bookmark = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + title: "Grandsire", + url: "http://example.com", + }); + + let bookmarkId = await PlacesUtils.promiseItemId(bookmark.guid); + + Assert.ok(!IsLivemark(bookmarkId), + "a simple bookmark should not be seen as a livemark"); + + let livemark = await PlacesUtils.livemarks.addLivemark({ + title: "Stedman", + feedURI: Services.io.newURI("http://livemark.com/"), + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + }); + + let livemarkId = await PlacesUtils.promiseItemId(livemark.guid); + + Assert.ok(IsLivemark(livemarkId), + "a livemark should be reported as a livemark"); + + // Now remove the livemark. + await PlacesUtils.livemarks.removeLivemark(livemark); + + Assert.ok(!IsLivemark(livemarkId), + "the livemark should have been removed from the cache"); +});
--- a/browser/components/places/tests/unit/xpcshell.ini +++ b/browser/components/places/tests/unit/xpcshell.ini @@ -16,9 +16,10 @@ support-files = [test_browserGlue_distribution.js] [test_browserGlue_migrate.js] [test_browserGlue_prefs.js] [test_browserGlue_restore.js] [test_browserGlue_smartBookmarks.js] [test_browserGlue_urlbar_defaultbehavior_migration.js] [test_clearHistory_shutdown.js] [test_leftpane_corruption_handling.js] +[test_PUIU_livemarksCache.js] [test_PUIU_makeTransaction.js]
--- a/browser/components/preferences/in-content-new/main.js +++ b/browser/components/preferences/in-content-new/main.js @@ -762,26 +762,26 @@ var gMainPane = { /** * Hide/show the "Show my windows and tabs from last time" option based * on the value of the browser.privatebrowsing.autostart pref. */ updateBrowserStartupLastSession() { let pbAutoStartPref = document.getElementById("browser.privatebrowsing.autostart"); let startupPref = document.getElementById("browser.startup.page"); - let menu = document.getElementById("browserStartupPage"); + let group = document.getElementById("browserStartupPage"); let option = document.getElementById("browserStartupLastSession"); if (pbAutoStartPref.value) { option.setAttribute("disabled", "true"); if (option.selected) { - menu.selectedItem = document.getElementById("browserStartupHomePage"); + group.selectedItem = document.getElementById("browserStartupHomePage"); } } else { option.removeAttribute("disabled"); - startupPref.updateElements(); // select the correct index in the startup menulist + startupPref.updateElements(); // select the correct radio in the startup group } }, // TABS /* * Preferences: *
--- a/browser/components/preferences/in-content-new/main.xul +++ b/browser/components/preferences/in-content-new/main.xul @@ -316,84 +316,77 @@ label="&setAsMyDefaultBrowser3.label;" accesskey="&setAsMyDefaultBrowser3.accesskey;" preference="pref.general.disable_button.default_browser"/> </hbox> <hbox align="center" class="indent"> <image class="face-smile"/> <label id="isDefaultLabel" flex="1">&isDefault.label;</label> </hbox> </deck> - <separator class="thin"/> </vbox> #endif - <html:table id="startupTable"> - <html:tr> - <html:td class="label-cell"> - <label accesskey="&startupPage2.accesskey;" - control="browserStartupPage">&startupPage2.label;</label> - </html:td> - <html:td class="content-cell"> - <menulist id="browserStartupPage" - class="content-cell-item" - preference="browser.startup.page"> - <menupopup> - <menuitem label="&startupUserHomePage.label;" - value="1" - id="browserStartupHomePage"/> - <menuitem label="&startupBlankPage.label;" - value="0" - id="browserStartupBlank"/> - <menuitem label="&startupPrevSession.label;" - value="3" - id="browserStartupLastSession"/> - </menupopup> - </menulist> - </html:td> - </html:tr> - <html:tr class="tableGroup"> - <html:td class="label-cell"> - <label accesskey="&homepage2.accesskey;" - control="browserHomePage">&homepage2.label;</label> - </html:td> - <html:td class="content-cell"> - <textbox id="browserHomePage" - class="padded uri-element content-cell-item" - type="autocomplete" - autocompletesearch="unifiedcomplete" - onsyncfrompreference="return gMainPane.syncFromHomePref();" - onsynctopreference="return gMainPane.syncToHomePref(this.value);" - placeholder="&abouthome.pageTitle;" - preference="browser.startup.homepage"/> - </html:td> - </html:tr> - <html:tr class="tableSubGroup"> - <html:td class="label-cell" /> - <html:td class="content-cell homepage-buttons"> - <button id="useCurrent" - class="content-cell-item" - label="" - accesskey="&useCurrentPage.accesskey;" - label1="&useCurrentPage.label;" - label2="&useMultiple.label;" - preference="pref.browser.homepage.disable_button.current_page"/> - <button id="useBookmark" - class="content-cell-item" - label="&chooseBookmark.label;" - accesskey="&chooseBookmark.accesskey;" - preference="pref.browser.homepage.disable_button.bookmark_page" - searchkeywords="&selectBookmark.title; &selectBookmark.label;"/> - <button id="restoreDefaultHomePage" - class="content-cell-item" - label="&restoreDefault.label;" - accesskey="&restoreDefault.accesskey;" - preference="pref.browser.homepage.disable_button.restore_default"/> - </html:td> - </html:tr> - </html:table> + <vbox id="startupPageBox"> + <label accesskey="&startupPage2.accesskey;" + control="browserStartupPage">&startupPage2.label;</label> + <radiogroup id="browserStartupPage" + preference="browser.startup.page"> + <radio label="&startupUserHomePage.label;" + value="1" + id="browserStartupHomePage"/> + <radio label="&startupBlankPage.label;" + value="0" + id="browserStartupBlank"/> + <radio label="&startupPrevSession.label;" + value="3" + id="browserStartupLastSession"/> + </radiogroup> + </vbox> +</groupbox> + +<!-- Home Page --> +<groupbox id="homepageGroup" + data-category="paneGeneral" + hidden="true"> + <caption><label>&homepage2.label;</label></caption> + + <vbox> + <textbox id="browserHomePage" + class="uri-element" + type="autocomplete" + autocompletesearch="unifiedcomplete" + onsyncfrompreference="return gMainPane.syncFromHomePref();" + onsynctopreference="return gMainPane.syncToHomePref(this.value);" + placeholder="&abouthome.pageTitle;" + preference="browser.startup.homepage"/> + </vbox> + + <hbox class="homepage-buttons"> + <button id="useCurrent" + flex="1" + class="homepage-button" + label="" + accesskey="&useCurrentPage.accesskey;" + label1="&useCurrentPage.label;" + label2="&useMultiple.label;" + preference="pref.browser.homepage.disable_button.current_page"/> + <button id="useBookmark" + flex="1" + class="homepage-button" + label="&chooseBookmark.label;" + accesskey="&chooseBookmark.accesskey;" + preference="pref.browser.homepage.disable_button.bookmark_page" + searchkeywords="&selectBookmark.title; &selectBookmark.label;"/> + <button id="restoreDefaultHomePage" + flex="1" + class="homepage-button" + label="&restoreDefault.label;" + accesskey="&restoreDefault.accesskey;" + preference="pref.browser.homepage.disable_button.restore_default"/> + </hbox> </groupbox> <!-- Tab preferences --> <groupbox data-category="paneGeneral" hidden="true"> <caption><label>&tabsGroup.label;</label></caption> <checkbox id="ctrlTabRecentlyUsedOrder" label="&ctrlTabRecentlyUsedOrder.label;"
--- a/browser/components/preferences/in-content-new/privacy.js +++ b/browser/components/preferences/in-content-new/privacy.js @@ -1097,20 +1097,27 @@ var gPrivacyPane = { }); blockUncommonUnwanted.addEventListener("command", function() { blockUnwantedPref.value = blockUncommonUnwanted.checked; blockUncommonPref.value = blockUncommonUnwanted.checked; let malware = malwareTable.value .split(",") - .filter(x => x !== "goog-unwanted-shavar" && x !== "test-unwanted-simple"); + .filter(x => x !== "goog-unwanted-proto" && + x !== "goog-unwanted-shavar" && + x !== "test-unwanted-simple"); if (blockUncommonUnwanted.checked) { - malware.push("goog-unwanted-shavar"); + if (malware.indexOf("goog-malware-shavar") != -1) { + malware.push("goog-unwanted-shavar"); + } else { + malware.push("goog-unwanted-proto"); + } + malware.push("test-unwanted-simple"); } // sort alphabetically to keep the pref consistent malware.sort(); malwareTable.value = malware.join(","); });
--- a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_1.js +++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_1.js @@ -7,17 +7,17 @@ add_task(async function() { await SpecialPowers.pushPrefEnv({"set": [["browser.preferences.search", true]]}); }); /** * Test for searching for the "Set Home Page" subdialog. */ add_task(async function() { await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true}); - await evaluateSearchResults("Set Home Page", "startupGroup"); + await evaluateSearchResults("Set Home Page", "homepageGroup"); await BrowserTestUtils.removeTab(gBrowser.selectedTab); }); /** * Test for searching for the "Languages" subdialog. */ add_task(async function() { await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
--- a/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_1.js +++ b/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_1.js @@ -125,16 +125,17 @@ add_task(async function() { } await searchCompletedPromise; // Checks if back to generalPane for (let i = 0; i < mainPrefTag.childElementCount; i++) { let child = mainPrefTag.children[i] if (child.id == "paneGeneral" || child.id == "startupGroup" + || child.id == "homepageGroup" || child.id == "languagesGroup" || child.id == "fontsGroup" || child.id == "downloadsGroup" || child.id == "applicationsGroup" || child.id == "drmGroup" || child.id == "updateApp" || child.id == "browsingGroup" || child.id == "performanceGroup"
--- a/browser/components/preferences/in-content-new/tests/browser_security-2.js +++ b/browser/components/preferences/in-content-new/tests/browser_security-2.js @@ -54,21 +54,29 @@ add_task(async function() { is(blockUncommon.hasAttribute("disabled"), val, "block uncommon checkbox is set correctly"); } await checkPrefSwitch(true); await checkPrefSwitch(false); }); +requestLongerTimeout(2); // test the unwanted/uncommon software warning preference add_task(async function() { - async function checkPrefSwitch(val1, val2) { + async function checkPrefSwitch(val1, val2, isV2) { Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", val1); Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_uncommon", val2); + let testMalwareTable = "goog-malware-" + (isV2 ? "shavar" : "proto"); + testMalwareTable += ",test-malware-simple"; + if (val1 && val2) { + testMalwareTable += ",goog-unwanted-" + (isV2 ? "shavar" : "proto"); + testMalwareTable += ",test-unwanted-simple"; + } + Services.prefs.setCharPref("urlclassifier.malwareTable", testMalwareTable); gBrowser.reload(); await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); let doc = gBrowser.selectedBrowser.contentDocument; let checkbox = doc.getElementById("blockUncommonUnwanted"); let checked = checkbox.checked; is(checked, val1 && val2, "unwanted/uncommon preference is initialized correctly"); @@ -80,23 +88,33 @@ add_task(async function() { // check that both settings are now turned on or off is(Services.prefs.getBoolPref("browser.safebrowsing.downloads.remote.block_potentially_unwanted"), !checked, "block_potentially_unwanted is set correctly"); is(Services.prefs.getBoolPref("browser.safebrowsing.downloads.remote.block_uncommon"), !checked, "block_uncommon is set correctly"); // when the preference is on, the malware table should include these ids let malwareTable = Services.prefs.getCharPref("urlclassifier.malwareTable").split(","); - is(malwareTable.includes("goog-unwanted-shavar"), !checked, - "malware table doesn't include goog-unwanted-shavar"); + if (isV2) { + is(malwareTable.includes("goog-unwanted-shavar"), !checked, + "malware table doesn't include goog-unwanted-shavar"); + } else { + is(malwareTable.includes("goog-unwanted-proto"), !checked, + "malware table doesn't include goog-unwanted-proto"); + } is(malwareTable.includes("test-unwanted-simple"), !checked, "malware table doesn't include test-unwanted-simple"); let sortedMalware = malwareTable.slice(0); sortedMalware.sort(); Assert.deepEqual(malwareTable, sortedMalware, "malware table has been sorted"); } - await checkPrefSwitch(true, true); - await checkPrefSwitch(false, true); - await checkPrefSwitch(true, false); - await checkPrefSwitch(false, false); + await checkPrefSwitch(true, true, false); + await checkPrefSwitch(false, true, false); + await checkPrefSwitch(true, false, false); + await checkPrefSwitch(false, false, false); + await checkPrefSwitch(true, true, true); + await checkPrefSwitch(false, true, true); + await checkPrefSwitch(true, false, true); + await checkPrefSwitch(false, false, true); + });
--- a/browser/components/preferences/in-content/security.js +++ b/browser/components/preferences/in-content/security.js @@ -194,20 +194,27 @@ var gSecurityPane = { }); blockUncommonUnwanted.addEventListener("command", function() { blockUnwantedPref.value = blockUncommonUnwanted.checked; blockUncommonPref.value = blockUncommonUnwanted.checked; let malware = malwareTable.value .split(",") - .filter(x => x !== "goog-unwanted-shavar" && x !== "test-unwanted-simple"); + .filter(x => x !== "goog-unwanted-proto" && + x !== "goog-unwanted-shavar" && + x !== "test-unwanted-simple"); if (blockUncommonUnwanted.checked) { - malware.push("goog-unwanted-shavar"); + if (malware.indexOf("goog-malware-shavar") != -1) { + malware.push("goog-unwanted-shavar"); + } else { + malware.push("goog-unwanted-proto"); + } + malware.push("test-unwanted-simple"); } // sort alphabetically to keep the pref consistent malware.sort(); malwareTable.value = malware.join(","); });
--- a/browser/components/preferences/in-content/tests/browser_security.js +++ b/browser/components/preferences/in-content/tests/browser_security.js @@ -92,21 +92,29 @@ add_task(async function() { // check if the uncommon warning checkbox has updated is(blockUncommon.hasAttribute("disabled"), val, "block uncommon checkbox is set correctly"); } await checkPrefSwitch(true); await checkPrefSwitch(false); }); +requestLongerTimeout(2); // test the unwanted/uncommon software warning preference add_task(async function() { - async function checkPrefSwitch(val1, val2) { + async function checkPrefSwitch(val1, val2, isV2) { Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", val1); Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_uncommon", val2); + let testMalwareTable = "goog-malware-" + (isV2 ? "shavar" : "proto"); + testMalwareTable += ",test-malware-simple"; + if (val1 && val2) { + testMalwareTable += ",goog-unwanted-" + (isV2 ? "shavar" : "proto"); + testMalwareTable += ",test-unwanted-simple"; + } + Services.prefs.setCharPref("urlclassifier.malwareTable", testMalwareTable); gBrowser.reload(); await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); let doc = gBrowser.selectedBrowser.contentDocument; let checkbox = doc.getElementById("blockUncommonUnwanted"); let checked = checkbox.checked; is(checked, val1 && val2, "unwanted/uncommon preference is initialized correctly"); @@ -117,22 +125,32 @@ add_task(async function() { // check that both settings are now turned on or off is(Services.prefs.getBoolPref("browser.safebrowsing.downloads.remote.block_potentially_unwanted"), !checked, "block_potentially_unwanted is set correctly"); is(Services.prefs.getBoolPref("browser.safebrowsing.downloads.remote.block_uncommon"), !checked, "block_uncommon is set correctly"); // when the preference is on, the malware table should include these ids let malwareTable = Services.prefs.getCharPref("urlclassifier.malwareTable").split(","); - is(malwareTable.includes("goog-unwanted-shavar"), !checked, - "malware table doesn't include goog-unwanted-shavar"); + if (isV2) { + is(malwareTable.includes("goog-unwanted-shavar"), !checked, + "malware table doesn't include goog-unwanted-shavar"); + } else { + is(malwareTable.includes("goog-unwanted-proto"), !checked, + "malware table doesn't include goog-unwanted-proto"); + } is(malwareTable.includes("test-unwanted-simple"), !checked, "malware table doesn't include test-unwanted-simple"); let sortedMalware = malwareTable.slice(0); sortedMalware.sort(); Assert.deepEqual(malwareTable, sortedMalware, "malware table has been sorted"); } - await checkPrefSwitch(true, true); - await checkPrefSwitch(false, true); - await checkPrefSwitch(true, false); - await checkPrefSwitch(false, false); + await checkPrefSwitch(true, true, false); + await checkPrefSwitch(false, true, false); + await checkPrefSwitch(true, false, false); + await checkPrefSwitch(false, false, false); + await checkPrefSwitch(true, true, true); + await checkPrefSwitch(false, true, true); + await checkPrefSwitch(true, false, true); + await checkPrefSwitch(false, false, true); + });
--- a/browser/extensions/formautofill/FormAutofillContent.jsm +++ b/browser/extensions/formautofill/FormAutofillContent.jsm @@ -121,19 +121,26 @@ AutofillProfileAutoCompleteSearch.protot onSearchResult: (search, result) => { listener.onSearchResult(this, result); ProfileAutocomplete.setProfileAutoCompleteResult(result); }, }); return; } - let collectionName = isAddressField ? ADDRESSES_COLLECTION_NAME : CREDITCARDS_COLLECTION_NAME; + let infoWithoutElement = Object.assign({}, info); + delete infoWithoutElement.elementWeakRef; - this._getRecords({collectionName, info, searchString}).then((records) => { + let data = { + collectionName: isAddressField ? ADDRESSES_COLLECTION_NAME : CREDITCARDS_COLLECTION_NAME, + info: infoWithoutElement, + searchString, + }; + + this._getRecords(data).then((records) => { if (this.forceStop) { return; } // Sort addresses by timeLastUsed for showing the lastest used address at top. records.sort((a, b) => b.timeLastUsed - a.timeLastUsed); let adaptedRecords = handler.getAdaptedProfiles(records); let result = null;
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm +++ b/browser/extensions/formautofill/FormAutofillUtils.jsm @@ -53,16 +53,19 @@ this.FormAutofillUtils = { "tel-national": "tel", "tel-area-code": "tel", "tel-local": "tel", "tel-local-prefix": "tel", "tel-local-suffix": "tel", "tel-extension": "tel", "email": "email", "cc-name": "creditCard", + "cc-given-name": "creditCard", + "cc-additional-name": "creditCard", + "cc-family-name": "creditCard", "cc-number": "creditCard", "cc-exp-month": "creditCard", "cc-exp-year": "creditCard", "cc-exp": "creditCard", }, _addressDataLoaded: false, _collators: {}, _reAlternativeCountryNames: {},
--- a/browser/extensions/formautofill/ProfileStorage.jsm +++ b/browser/extensions/formautofill/ProfileStorage.jsm @@ -64,16 +64,17 @@ * cc-exp-year, // 2-digit year will be converted to 4 digits * // upon saving * * // computed fields (These fields are computed based on the above fields * // and are not allowed to be modified directly.) * cc-given-name, * cc-additional-name, * cc-family-name, + * cc-exp, * * // metadata * timeCreated, // in ms * timeLastUsed, // in ms * timeLastModified, // in ms * timesUsed * _sync: { ... optional sync metadata }, * } @@ -185,16 +186,17 @@ const VALID_CREDIT_CARD_FIELDS = [ "cc-exp-month", "cc-exp-year", ]; const VALID_CREDIT_CARD_COMPUTED_FIELDS = [ "cc-given-name", "cc-additional-name", "cc-family-name", + "cc-exp", ]; const INTERNAL_FIELDS = [ "guid", "version", "timeCreated", "timeLastUsed", "timeLastModified",
--- a/browser/extensions/formautofill/skin/shared/editCreditCard.css +++ b/browser/extensions/formautofill/skin/shared/editCreditCard.css @@ -1,16 +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/. */ form { justify-content: center; - /* Add extra space to ensure invalid input box is displayed properly */ - padding: 2px; } form > label, form > div { flex: 1 0 100%; align-self: center; font-size: 1.3em; margin: 0 0 0.5em !important;
--- a/browser/extensions/formautofill/skin/shared/editDialog.css +++ b/browser/extensions/formautofill/skin/shared/editDialog.css @@ -10,16 +10,18 @@ form, label, div, p { display: flex; } form { flex-wrap: wrap; + /* Add extra space to ensure invalid input box is displayed properly */ + padding: 2px; } form > label, form > p { margin: 0 0 0.5em !important; } label > span,
--- a/browser/extensions/formautofill/test/unit/test_creditCardRecords.js +++ b/browser/extensions/formautofill/test/unit/test_creditCardRecords.js @@ -123,21 +123,23 @@ add_task(async function test_getAll() { do_check_eq(creditCards.length, 2); do_check_credit_card_matches(creditCards[0], TEST_CREDIT_CARD_1); do_check_credit_card_matches(creditCards[1], TEST_CREDIT_CARD_2); // Check computed fields. do_check_eq(creditCards[0]["cc-given-name"], "John"); do_check_eq(creditCards[0]["cc-family-name"], "Doe"); + do_check_eq(creditCards[0]["cc-exp"], "2017-04"); // Test with rawData set. creditCards = profileStorage.creditCards.getAll({rawData: true}); do_check_eq(creditCards[0]["cc-given-name"], undefined); do_check_eq(creditCards[0]["cc-family-name"], undefined); + do_check_eq(creditCards[0]["cc-exp"], undefined); // Modifying output shouldn't affect the storage. creditCards[0]["cc-name"] = "test"; do_check_credit_card_matches(profileStorage.creditCards.getAll()[0], TEST_CREDIT_CARD_1); }); add_task(async function test_get() { let path = getTempFile(TEST_STORE_FILE_NAME).path;
deleted file mode 100644 --- a/browser/extensions/onboarding/content/img/overlay-icon.svg +++ /dev/null @@ -1,2 +0,0 @@ - -<svg width="36" height="29" viewBox="0 0 36 29" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>overlayfox</title><defs><path id="a" d="M.002.058h35.953V27.94H.002z"/><path id="c" d="M0 17.39V.42h35.957v16.97H0z"/></defs><g fill="none" fill-rule="evenodd"><g transform="translate(0 .55)"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><path d="M35.953 16.593c.006-.19-.036-.386-.133-.562-1.02-1.884-2.052-3.65-3.17-5.243.773-.62 1.448-1.394 1.975-2.312 1.497-2.61 1.413-5.72.042-8.175-.063-.114-.176-.2-.294-.242.002.01-.006.016-.006.024-.008-.012-.018-.024-.03-.024-2.825-.03-5.558 1.46-7.112 4.09-.16.27-.3.572-.418.896-2.394-1.464-5.24-2.31-8.822-2.31-3.57 0-6.416.832-8.806 2.283v.007l-.02.014c-.12-.322-.257-.623-.416-.89C7.19 1.52 4.457.03 1.632.06c-.014 0-.028.014-.035.028 0-.007.007-.007.007-.014-.14.036-.267.12-.337.256-1.37 2.455-1.462 5.557.042 8.175.526.926 1.208 1.7 1.988 2.327-1.118 1.586-2.15 3.344-3.163 5.23-.092.166-.13.352-.13.533H.002c0 .007.002.013 0 .02v.016h.002c-.006.17.032.344.117.504 3.685 6.71 7.6 9.377 15 9.88.807.58 1.79.928 2.857.928 1.065 0 2.05-.347 2.857-.93 7.4-.5 11.323-3.168 15-9.878.09-.162.13-.35.12-.534v-.003-.004" fill="#F70" mask="url(#b)"/></g><g transform="translate(0 10.268)"><mask id="d" fill="#fff"><use xlink:href="#c"/></mask><path d="M17.978 17.39c9.31 0 13.732-2.447 17.857-9.975.09-.163.133-.356.12-.54h-4.238V5.23h-.014c.007-.113.014-.234.014-.348 0-2.462-1.975-4.46-4.407-4.46-2.43 0-4.406 1.998-4.406 4.46 0 .12.008.235.014.35h-9.88c.007-.1.014-.208.014-.314 0-2.462-1.974-4.462-4.406-4.462-2.43 0-4.406 2-4.406 4.462 0 .106.007.206.014.313h-.007v1.644H.002c-.013.185.03.37.12.54 4.132 7.53 8.545 9.976 17.856 9.976" fill="#FFC899" mask="url(#d)"/></g><path d="M35.954 17.15c.007-.192-.035-.39-.134-.57-1.018-1.885-2.05-3.65-3.17-5.243.774-.62 1.45-1.395 1.976-2.312 1.497-2.61 1.413-5.72.042-8.175-.063-.114-.175-.2-.295-.242.007.02.007.043-.014.057-.746.37-3.943 2.15-5.756 6.19-2.74-2.242-6.1-3.572-10.62-3.572-3.568 0-6.414.832-8.804 2.284v.007c-.632.384-1.23.81-1.8 1.28-1.474-3.28-3.85-5.065-5.1-5.82-.008 0-.008-.006-.015-.006C2.175.97 2.09.92 2.013.878c-.008 0-.015-.007-.015-.007C1.963.85 1.935.836 1.9.82c-.007 0-.007-.006-.014-.006-.035-.02-.064-.035-.098-.05-.008 0-.008-.006-.015-.006-.02-.015-.05-.03-.07-.036-.007 0-.014-.008-.02-.008C1.66.7 1.632.694 1.612.68 1.604.68 1.604.67 1.597.67L1.59.665V.65c0-.007 0-.007.008-.014 0-.007.007-.007.007-.014-.14.036-.267.12-.338.256-1.37 2.455-1.46 5.557.042 8.175.527.925 1.208 1.7 1.988 2.327-1.117 1.587-2.15 3.344-3.162 5.23-.098.177-.14.377-.133.57H4.24v-1.645h.007c-.007-.1-.014-.207-.014-.313 0-2.462 1.975-4.46 4.406-4.46 2.43 0 4.405 1.998 4.405 4.46 0 .106-.007.206-.014.313h.015v7.96c0 2.755 2.207 4.996 4.933 4.996 2.72 0 4.933-2.233 4.933-4.994v-7.99h.01c0-.042-.01-.085-.01-.128v-.47c.128-2.347 2.047-4.21 4.4-4.21 2.432 0 4.407 1.998 4.407 4.46 0 .12-.007.235-.015.348h.015v1.644h4.237z" fill="#F70"/><path d="M16.453 19.832s.05 0 .134.008c.084.006.204.014.345.014.14.007.31.007.49.014.177 0 .374.007.563.007.19 0 .38 0 .563-.007.175 0 .344-.007.492-.014.14-.008.26-.014.344-.014.084-.008.133-.008.133-.008.598-.057 1.132.39 1.18.996.03.3-.07.584-.245.804l-.942 1.146-.035.035c-.02.022-.056.058-.098.093-.043.036-.092.078-.148.114-.057.043-.127.078-.197.12-.07.036-.148.072-.233.107-.084.03-.168.057-.26.08-.175.042-.372.056-.56.048-.1-.006-.19-.014-.29-.028-.05-.007-.09-.014-.14-.02-.05-.008-.092-.023-.134-.03-.042-.007-.09-.028-.133-.035-.042-.015-.084-.03-.127-.043-.042-.015-.084-.03-.12-.05-.034-.015-.077-.036-.112-.05-.035-.022-.07-.036-.105-.057-.036-.022-.064-.036-.092-.058-.057-.035-.106-.07-.148-.106-.042-.03-.077-.065-.098-.08l-.036-.035-.906-.946c-.45-.47-.436-1.21.02-1.666.254-.25.577-.356.893-.342" fill="#994C00"/><path d="M8.407 19.398c-.618 0-1.243-.135-1.82-.412-.28-.136-.4-.477-.267-.762.134-.284.47-.405.752-.27.87.42 1.884.406 2.77-.043.28-.14.617-.027.75.258.14.284.03.625-.252.76-.612.314-1.272.47-1.933.47M26.938 19.398c-.618 0-1.244-.135-1.82-.412-.28-.136-.4-.477-.267-.762.135-.284.472-.405.753-.27.87.42 1.883.406 2.77-.043.28-.14.617-.027.75.258.134.284.03.625-.252.76-.61.314-1.27.47-1.932.47" fill="#F70"/><path d="M10.91 15.926c-.008-.064-.03-.12-.057-.178-.45-1.024-1.363-1.657-2.39-1.657-1.025 0-1.94.634-2.39 1.658-.027.057-.04.12-.055.178-.14.3-.077.67.183.904.31.277.788.25 1.07-.064.3-.342.736-.54 1.194-.54.456 0 .9.198 1.194.54.148.17.358.256.57.256.175 0 .358-.064.498-.192.26-.235.323-.605.183-.904M29.44 15.926c-.007-.064-.028-.12-.057-.178-.45-1.024-1.363-1.657-2.39-1.657-1.024 0-1.938.634-2.388 1.658-.028.057-.042.12-.056.178-.142.3-.078.67.182.904.14.128.323.192.5.192.21 0 .413-.086.568-.256.302-.342.737-.54 1.194-.54.457 0 .9.198 1.195.54.273.313.75.348 1.067.064.26-.235.323-.605.183-.904" fill="#363959"/><path d="M17.978 27.438c-1.405 0-2.122-.718-2.473-1.323-.373-.648-.415-1.288-.415-1.31-.007-.092.042-.184.12-.234.077-.05.175-.056.26-.014.007.008.934.456 2.48.456 1.553 0 2.53-.456 2.544-.456.085-.042.183-.028.26.022.078.05.12.142.112.235 0 .028-.042.67-.414 1.31-.352.597-1.068 1.315-2.474 1.315" fill="#994C00"/><path d="M28.597 6.855c1.468-3.28 3.843-5.066 5.094-5.82.008 0 .008-.007.016-.007.09-.057.175-.107.252-.15.007 0 .015-.007.015-.007.034-.02.063-.034.098-.05.008 0 .008-.006.015-.006.035-.02.063-.035.098-.05.007 0 .007-.007.015-.007.02-.014.05-.028.07-.035.006 0 .014-.008.02-.008.022-.014.05-.02.07-.035.008 0 .008-.008.015-.008l.007-.008V.65c0-.007 0-.007-.007-.013-.007-.015-.02-.03-.035-.03C31.513.58 28.78 2.068 27.226 4.7c-.16.27-.302.575-.42.902.617.356 1.215.783 1.79 1.253M7.374 6.855C5.906 3.575 3.53 1.79 2.28 1.035c-.008 0-.008-.007-.015-.007C2.175.97 2.09.92 2.013.878c-.008 0-.015-.007-.015-.007C1.963.85 1.935.837 1.9.82c-.007 0-.007-.006-.014-.006-.035-.02-.064-.035-.098-.05-.008 0-.008-.007-.015-.007-.02-.014-.05-.028-.07-.035-.007 0-.014-.008-.02-.008C1.66.7 1.632.694 1.612.68 1.604.68 1.604.67 1.597.67L1.59.664V.65c0-.007 0-.007.008-.013.007-.015.02-.03.035-.03C4.458.58 7.19 2.068 8.745 4.7c.16.27.302.575.42.902-.624.356-1.22.783-1.79 1.253" fill="#FF9F4D"/></g></svg>
--- a/browser/extensions/onboarding/content/onboarding.css +++ b/browser/extensions/onboarding/content/onboarding.css @@ -27,48 +27,51 @@ padding: 0; position: absolute; cursor: pointer; top: 34px; offset-inline-start: 34px; border: none; /* Set to none so no grey contrast background in the high-contrast mode */ background: none; + /* make sure the icon stay above the activity-stream searchbar */ + z-index: 10; } /* Keyboard focus styling */ #onboarding-overlay-button:-moz-focusring { outline: solid 2px rgba(0, 0, 0, 0.1); -moz-outline-radius: 5px; outline-offset: 5px; transition: outline-offset 150ms; } #onboarding-overlay-button-icon { - width: 36px; + width: 32px; vertical-align: top; } #onboarding-overlay-button::after { - background: #5ce6e6; - font-size: 12px; - border: 1px solid #fff; + background: #0060df; + font-size: 13px; text-align: center; - color: #10404a; + color: #fff; box-sizing: content-box; + font-weight: 400; content: attr(aria-label); display: inline-block; - offset-inline-start: 39px; - border-radius: 22px; - padding: 5px 12px; + border: 1px solid transparent; + border-radius: 2px; + padding: 10px 16px; min-width: 100px; max-width: 140px; white-space: pre-line; - margin-inline-start: 3px; - margin-top: -5px; + margin-inline-start: 4px; + margin-top: -10px; + box-shadow: -2px 0 5px 0 rgba(74, 74, 79, 0.25); } #onboarding-overlay-dialog, .onboarding-hidden, #onboarding-tour-sync-page[data-login-state=logged-in] .show-on-logged-out, #onboarding-tour-sync-page[data-login-state=logged-out] .show-on-logged-in { display: none; } @@ -81,17 +84,17 @@ width: 16px; height: 16px; border: none; background: none; padding: 0; } .onboarding-close-btn::before { - content: url(chrome://global/skin/icons/close.svg); + content: url("chrome://global/skin/icons/close.svg"); -moz-context-properties: fill, fill-opacity; fill-opacity: 0; } .onboarding-close-btn:-moz-any(:hover, :active, :focus, :-moz-focusring)::before { fill-opacity: 0.1; } @@ -302,24 +305,24 @@ .onboarding-tour-page.onboarding-no-button > .onboarding-tour-button-container { display: none; grid-row: tour-page-end; grid-column: tour-page-end; } .onboarding-tour-action-button { - padding: 10px 20px; - font-size: 15px; - font-weight: 600; - line-height: 21px; - background: #0a84ff; + background: #0060df; /* With 1px transparent border, could see a border in the high-constrast mode */ border: 1px solid transparent; - border-radius: 0; + border-radius: 2px; + padding: 10px 20px; + font-size: 14px; + font-weight: 600; + line-height: 16px; color: #fff; float: inline-end; margin-inline-end: 26px; margin-top: -32px; } /* Remove default dotted outline around buttons' text */ .onboarding-tour-action-button::-moz-focus-inner, @@ -332,22 +335,22 @@ #onboarding-notification-action-btn:-moz-focusring, #onboarding-tour-list .onboarding-tour-item:focus { outline: 2px solid rgba(0,149,221,0.5); outline-offset: 1px; -moz-outline-radius: 2px; } .onboarding-tour-action-button:hover:not([disabled]) { - background: #0060df; + background: #003eaa; cursor: pointer; } .onboarding-tour-action-button:active:not([disabled]) { - background: #003EAA; + background: #002275; } .onboarding-tour-action-button:disabled { opacity: 0.5; } /* Tour Icons */ #onboarding-tour-singlesearch, @@ -527,8 +530,14 @@ #onboarding-notification-action-btn:hover { background-color: #ebebeb; cursor: pointer; } #onboarding-notification-action-btn:active { background-color: #dadada; } + +@media (min-resolution: 2dppx) { + #onboarding-notification-tour-icon { + background-image: url("chrome://branding/content/icon128.png"); + } +}
--- a/browser/extensions/onboarding/content/onboarding.js +++ b/browser/extensions/onboarding/content/onboarding.js @@ -452,31 +452,33 @@ class Onboarding { // Only containers receive pointer events in onboarding tour tab list, // actual semantic tab is their first child. if (classList.contains("onboarding-tour-item-container")) { ({ id, classList } = target.firstChild); } switch (id) { case "onboarding-overlay-button": + this.showOverlay(); + this.gotoPage(this.selectedTour.id); + break; case "onboarding-overlay-close-btn": // If the clicking target is directly on the outer-most overlay, // that means clicking outside the tour content area. // Let's toggle the overlay. case "onboarding-overlay": - this.toggleOverlay(); - this.gotoPage(this.selectedTour.id); + this.hideOverlay(); break; case "onboarding-notification-close-btn": this.hideNotification(); this._removeTourFromNotificationQueue(this._notificationBar.dataset.targetTourId); break; case "onboarding-notification-action-btn": let tourId = this._notificationBar.dataset.targetTourId; - this.toggleOverlay(); + this.showOverlay(); this.gotoPage(tourId); this._removeTourFromNotificationQueue(tourId); break; } if (classList.contains("onboarding-tour-item")) { this.gotoPage(id); // Keep focus (not visible) on current item for potential keyboard // navigation. @@ -559,17 +561,17 @@ class Onboarding { if (targetIndex > -1 && targetIndex < this._tourItems.length - 1) { let next = this._tourItems[targetIndex + 1]; this.handleClick(next); next.focus(); } event.preventDefault(); break; case "Escape": - this.toggleOverlay(); + this.hideOverlay(); break; case "Tab": let next = this.wrapMoveFocus(target, shiftKey); // If focus was wrapped, prevent Tab key default action. if (next) { event.preventDefault(); } break; @@ -610,30 +612,32 @@ class Onboarding { this._overlay.remove(); if (this._notificationBar) { this._notificationBar.remove(); } this._tourItems = this._tourPages = this._overlayIcon = this._overlay = this._notificationBar = null; } - toggleOverlay() { + showOverlay() { if (this._tourItems.length == 0) { // Lazy loading until first toggle. this._loadTours(this._tours); } this.hideNotification(); - this._overlay.classList.toggle("onboarding-opened"); - this.toggleModal(this._overlay.classList.contains("onboarding-opened")); + this.toggleModal(this._overlay.classList.toggle("onboarding-opened")); + } + hideOverlay() { let hiddenCheckbox = this._window.document.getElementById("onboarding-tour-hidden-checkbox"); if (hiddenCheckbox.checked) { this.hide(); } + this.toggleModal(this._overlay.classList.toggle("onboarding-opened")); } /** * Set modal dialog state and properties for accessibility purposes. * @param {Boolean} opened whether the dialog is opened or closed. */ toggleModal(opened) { let { document: doc } = this._window; @@ -986,17 +990,17 @@ class Onboarding { let tooltip = this._bundle.formatStringFromName(tooltipStringId, [BRAND_SHORT_NAME], 1); button.setAttribute("aria-label", tooltip); button.id = "onboarding-overlay-button"; button.setAttribute("aria-haspopup", true); button.setAttribute("aria-controls", `${ONBOARDING_DIALOG_ID}`); let img = this._window.document.createElement("img"); img.id = "onboarding-overlay-button-icon"; img.setAttribute("role", "presentation"); - img.src = "resource://onboarding/img/overlay-icon.svg"; + img.src = "chrome://branding/content/icon64.png"; button.appendChild(img); return button; } _loadTours(tours) { let itemsFrag = this._window.document.createDocumentFragment(); let pagesFrag = this._window.document.createDocumentFragment(); for (let tour of tours) {
--- a/browser/extensions/onboarding/test/browser/browser.ini +++ b/browser/extensions/onboarding/test/browser/browser.ini @@ -5,11 +5,12 @@ support-files = [browser_onboarding_accessibility.js] [browser_onboarding_hide_all.js] [browser_onboarding_keyboard.js] skip-if = debug || os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences [browser_onboarding_notification.js] [browser_onboarding_notification_2.js] [browser_onboarding_notification_3.js] [browser_onboarding_notification_4.js] +[browser_onboarding_notification_click_auto_complete_tour.js] [browser_onboarding_select_default_tour.js] [browser_onboarding_tours.js] [browser_onboarding_tourset.js]
new file mode 100644 --- /dev/null +++ b/browser/extensions/onboarding/test/browser/browser_onboarding_notification_click_auto_complete_tour.js @@ -0,0 +1,33 @@ +add_task(async function test_show_click_auto_complete_tour_in_notification() { + resetOnboardingDefaultState(); + skipMuteNotificationOnFirstSession(); + // the second tour is an click-auto-complete tour + await SpecialPowers.pushPrefEnv({set: [ + ["browser.onboarding.newtour", "customize,library"], + ]}); + + let tab = await openTab(ABOUT_NEWTAB_URL); + await promiseOnboardingOverlayLoaded(tab.linkedBrowser); + await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser); + await promiseOnboardingOverlayOpened(tab.linkedBrowser); + + // Trigger CTA button to mark the tour as complete + let expectedPrefUpdates = [ + promisePrefUpdated(`browser.onboarding.tour.onboarding-tour-customize.completed`, true), + ] + BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize", {}, tab.linkedBrowser); + BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize-button", {}, tab.linkedBrowser); + await Promise.all(expectedPrefUpdates); + + await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-close-btn", {}, gBrowser.selectedBrowser); + let { activeNavItemId } = await getCurrentActiveTour(tab.linkedBrowser); + is("onboarding-tour-customize", activeNavItemId, "the active tour should be the previous shown tour"); + + await reloadTab(tab); + await promiseOnboardingOverlayLoaded(tab.linkedBrowser); + await promiseTourNotificationOpened(tab.linkedBrowser); + let targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser); + is("onboarding-tour-library", targetTourId, "correctly show the click-autocomplete-tour in notification"); + + await BrowserTestUtils.removeTab(tab); +});
--- a/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm +++ b/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm @@ -23,17 +23,17 @@ let WebCompatReporter = { get endpoint() { return Services.urlFormatter.formatURLPref( "extensions.webcompat-reporter.newIssueEndpoint"); }, init() { PageActions.addAction(new PageActions.Action({ id: "webcompat-reporter-button", - title: wcStrings.GetStringFromName("wc-reporter.label"), + title: wcStrings.GetStringFromName("wc-reporter.label2"), iconURL: "chrome://webcompat-reporter/skin/lightbulb.svg", onCommand: (e) => this.reportIssue(e.target.ownerGlobal), onShowingInPanel: (buttonNode) => this.onShowingInPanel(buttonNode) })); }, uninit() { let action = PageActions.actionForID("webcompat-reporter-button");
--- a/browser/extensions/webcompat-reporter/locales/en-US/webcompat.properties +++ b/browser/extensions/webcompat-reporter/locales/en-US/webcompat.properties @@ -1,11 +1,10 @@ # 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/. -# LOCALIZATION NOTE(wc-reporter.label): This string will be used in the -# Firefox menu panel below its button. Localized length should be considered. -# \u00ad is included at the beginning of the string to disable auto-hyphens. -wc-reporter.label=\u00adReport Site Issue +# LOCALIZATION NOTE(wc-reporter.label2): This string will be used in the +# Firefox page actions menu. Localized length should be considered. +wc-reporter.label2=Report Site Issue… # LOCALIZATION NOTE(wc-reporter.tooltip): A site compatibility issue is # a website bug that exists in one browser (Firefox), but not another. wc-reporter.tooltip=Report a site compatibility issue
--- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -464,27 +464,16 @@ notification[value="translation"] menuli %include ../shared/autocomplete.inc.css %include ../shared/urlbar-autocomplete.inc.css #PopupAutoComplete > richlistbox > richlistitem[originaltype~="datalist-first"] { border-top: 1px solid ThreeDShadow; } -.autocomplete-richlistitem:hover, -treechildren.searchbar-treebody::-moz-tree-row(hover) { - background-color: var(--arrowpanel-dimmed); - border-color: var(--panel-separator-color); -} - -.autocomplete-richlistitem[selected], -treechildren.searchbar-treebody::-moz-tree-row(selected) { - background-color: Highlight; -} - .ac-title { font-size: 1.05em; } .ac-separator, .ac-url, .ac-action, .ac-tags {
--- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -420,28 +420,16 @@ %include ../shared/autocomplete.inc.css %include ../shared/urlbar-autocomplete.inc.css #PopupAutoComplete > richlistbox > richlistitem[originaltype~="datalist-first"] { border-top: 1px solid #C7C7C7; } -.autocomplete-richlistitem:hover, -treechildren.searchbar-treebody::-moz-tree-row(hover) { - background-color: var(--arrowpanel-dimmed); - border-color: var(--panel-separator-color); -} - -.autocomplete-richlistitem[selected], -treechildren.searchbar-treebody::-moz-tree-row(selected) { - background-color: Highlight; - color: HighlightText; -} - .ac-title { font-size: 14px; } .ac-separator, .ac-url, .ac-action, .ac-tags {
--- a/browser/themes/shared/incontentprefs/preferences.inc.css +++ b/browser/themes/shared/incontentprefs/preferences.inc.css @@ -149,70 +149,31 @@ separator.thin:not([orient="vertical"]) justify-content: space-between; } .header[hidden=true] { display: none; } /* General Pane */ - -#startupGroup { - margin-top: 0px !important; -} - -#startupTable { - margin-top: 32px; - margin-inline-end: -4px; - border-collapse: collapse; -} - -#startupTable > tr > td { - padding: 0; /* remove the padding from html.css */ -} - -#startupTable > .tableGroup > td { +#startupPageBox { padding-top: 32px; } -#startupTable > .tableSubGroup > td { - padding-top: 8px; -} - -#startupTable > tr > .label-cell { - text-align: end; - width: 0; /* make the column as small as possible */ -} - -#startupTable > tr > .content-cell:not(:first-child) { - padding-inline-start: 8px; -} - -#startupTable > tr > .label-cell > label { - white-space: nowrap; +#browserHomePage { + margin-inline-start: 0; + margin-inline-end: 0; } -#startupTable > tr > .content-cell > menulist, -#startupTable > tr > .content-cell > textbox { - width: calc(100% - 8px); - margin-left: 4px; - margin-right: 4px; +.homepage-button:first-of-type { + margin-inline-start: 0; } -#startupTable > tr > .homepage-buttons { - display: flex; - flex-wrap: wrap; -} - -#startupTable > tr > .homepage-buttons > .content-cell-item { - flex-grow: 1; -} - -.content-cell-item { - margin: 2px 4px; +.homepage-button:last-of-type { + margin-inline-end: 0; } #getStarted { font-size: 90%; } #isNotDefaultLabel, #signedOutAccountBoxTitle {
--- a/browser/themes/shared/urlbar-autocomplete.inc.css +++ b/browser/themes/shared/urlbar-autocomplete.inc.css @@ -4,21 +4,32 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ %endif #treecolAutoCompleteImage { max-width: 36px; } .autocomplete-richlistbox { - padding: 4px; + padding: 4px 3px; } .autocomplete-richlistitem { min-height: 30px; font: message-box; border-radius: 2px; - border: 1px solid transparent; } :root[uidensity=touch] .autocomplete-richlistitem { min-height: 40px; } + +.autocomplete-richlistitem:hover, +treechildren.searchbar-treebody::-moz-tree-row(hover) { + background-color: var(--arrowpanel-dimmed); +} + +.autocomplete-richlistitem[selected], +treechildren.searchbar-treebody::-moz-tree-row(selected) { + background-color: Highlight; + color: HighlightText; +} +
--- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -673,28 +673,16 @@ html|span.ac-emphasize-text-url { } .ac-tags-text[selected] > html|span.ac-tag { background-color: HighlightText; color: Highlight; } } -.autocomplete-richlistitem:hover, -treechildren.searchbar-treebody::-moz-tree-row(hover) { - background-color: var(--arrowpanel-dimmed); - border-color: var(--panel-separator-color); -} - -.autocomplete-richlistitem[selected], -treechildren.searchbar-treebody::-moz-tree-row(selected) { - background-color: Highlight; - color: HighlightText; -} - .ac-type-icon[type=bookmark] { list-style-image: url("chrome://browser/skin/bookmark.svg"); -moz-context-properties: fill; fill: #b2b2b2; } .ac-type-icon[type=bookmark][selected][current] { fill: white;
--- a/devtools/client/responsive.html/browser/tunnel.js +++ b/devtools/client/responsive.html/browser/tunnel.js @@ -236,28 +236,24 @@ function tunnelToInnerBrowser(outer, inn // * Specific target names (everything treated as _blank) // * Window features // * window.opener // These things are deferred for now, since content which does depend on them seems // outside the main focus of RDM. let { detail } = event; event.preventDefault(); let uri = Services.io.newURI(detail.url); - let sourceNode = event.dataTransfer.mozSourceNode; - let triggeringPrincipal = sourceNode - ? sourceNode.nodePrincipal - : Services.scriptSecurityManager.getSystemPrincipal(); // This API is used mainly because it's near the path used for <a target/> with // regular browser tabs (which calls `openURIInFrame`). The more elaborate APIs // that support openers, window features, etc. didn't seem callable from JS and / or // this event doesn't give enough info to use them. browserWindow.browserDOMWindow .openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, Ci.nsIBrowserDOMWindow.OPEN_NEW, - triggeringPrincipal); + outer.contentPrincipal); }, stop() { let tab = gBrowser.getTabForBrowser(outer); let filteredProgressListener = gBrowser._tabFilters.get(tab); // The browser's state has changed over time while the tunnel was active. Push the // the current state down to the inner browser, so that it follows the content in
--- a/devtools/client/responsive.html/test/browser/browser.ini +++ b/devtools/client/responsive.html/test/browser/browser.ini @@ -35,15 +35,16 @@ support-files = [browser_network_throttling.js] [browser_page_state.js] [browser_permission_doorhanger.js] tags = geolocation [browser_resize_cmd.js] [browser_screenshot_button.js] [browser_tab_close.js] [browser_tab_remoteness_change.js] +[browser_target_blank.js] [browser_toolbox_computed_view.js] [browser_toolbox_rule_view.js] [browser_toolbox_swap_browsers.js] [browser_touch_device.js] [browser_touch_simulation.js] [browser_viewport_basics.js] [browser_window_close.js]
new file mode 100644 --- /dev/null +++ b/devtools/client/responsive.html/test/browser/browser_target_blank.js @@ -0,0 +1,26 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Ensure target="_blank" link opens a new tab + +const TAB_URL = "http://example.com/"; +const TEST_URL = + `data:text/html,<a href="${TAB_URL}" target="_blank">Click me</a>` + .replace(/ /g, "%20"); + +addRDMTask(TEST_URL, function* ({ ui }) { + let store = ui.toolWindow.store; + + // Wait until the viewport has been added + yield waitUntilState(store, state => state.viewports.length == 1); + + // Click the target="_blank" link and wait for a new tab + yield waitForFrameLoad(ui, TEST_URL); + let newTab = BrowserTestUtils.waitForNewTab(gBrowser, TAB_URL); + spawnViewportTask(ui, {}, function* () { + content.document.querySelector("a").click(); // eslint-disable-line + }); + ok(yield newTab, "New tab opened from link"); +});
--- a/dom/animation/test/mozilla/test_distance_of_transform.html +++ b/dom/animation/test/mozilla/test_distance_of_transform.html @@ -65,16 +65,22 @@ function rotate3dToMatrix(x, y, z, radia 0, 0, 1 ]; } test(function(t) { var target = addDiv(t); + var dist = getDistance(target, 'transform', 'none', 'none'); + assert_equals(dist, 0, 'distance of translate'); +}, 'Test distance of none and none'); + +test(function(t) { + var target = addDiv(t); var dist = getDistance(target, 'transform', 'translate(100px)', 'none'); assert_equals(dist, 100, 'distance of translate'); }, 'Test distance of translate function and none'); test(function(t) { var target = addDiv(t); var dist = getDistance(target, 'transform', 'translate(100px)', 'translate(200px)'); @@ -299,10 +305,44 @@ test(function(t) { var dist = getDistance(target, 'transform', 'rotate(180deg) translate(1000px)', 'rotate(360deg) translate(0px)'); assert_equals(dist, Math.sqrt(1000 * 1000 + Math.PI * Math.PI), 'distance of transform lists'); }, 'Test distance of transform lists'); +test(function(t) { + var target = addDiv(t); + var dist = getDistance(target, 'transform', + 'translate(100px) rotate(180deg)', + 'translate(50px) rotate(90deg) scale(5) skew(1rad)'); + assert_approx_equals(dist, + Math.sqrt(50 * 50 + + Math.PI / 2 * Math.PI / 2 + + 4 * 4 * 2 + + 1 * 1), + epsilon, + 'distance of transform lists'); +}, 'Test distance of transform lists where one has extra items'); + +test(function(t) { + var target = addDiv(t); + var dist = getDistance(target, 'transform', + 'translate(1000px) rotate3d(1, 0, 0, 180deg)', + 'translate(1000px) scale3d(2.5, 0.5, 1)'); + assert_equals(dist, Math.sqrt(Math.PI * Math.PI + 1.5 * 1.5 + 0.5 * 0.5), + 'distance of transform lists'); +}, 'Test distance of mismatched transform lists'); + +test(function(t) { + var target = addDiv(t); + var dist = getDistance(target, 'transform', + 'translate(100px) skew(1rad)', + 'translate(1000px) rotate3d(0, 1, 0, -2rad)'); + assert_approx_equals(dist, + Math.sqrt(900 * 900 + 1 * 1 + 2 * 2), + epsilon, + 'distance of transform lists'); +}, 'Test distance of mismatched transform lists with skew function'); + </script> </html>
--- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -444,47 +444,54 @@ private: }; /** * There is a reference cycle involving this class: MediaLoadListener * holds a reference to the HTMLMediaElement, which holds a reference * to an nsIChannel, which holds a reference to this listener. * We break the reference cycle in OnStartRequest by clearing mElement. */ -class HTMLMediaElement::MediaLoadListener final : public nsIStreamListener, - public nsIChannelEventSink, - public nsIInterfaceRequestor, - public nsIObserver +class HTMLMediaElement::MediaLoadListener final + : public nsIStreamListener + , public nsIChannelEventSink + , public nsIInterfaceRequestor + , public nsIObserver + , public nsIThreadRetargetableStreamListener { ~MediaLoadListener() {} NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIOBSERVER NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER public: explicit MediaLoadListener(HTMLMediaElement* aElement) : mElement(aElement), mLoadID(aElement->GetCurrentLoadID()) { MOZ_ASSERT(mElement, "Must pass an element to call back"); } private: RefPtr<HTMLMediaElement> mElement; nsCOMPtr<nsIStreamListener> mNextListener; const uint32_t mLoadID; }; -NS_IMPL_ISUPPORTS(HTMLMediaElement::MediaLoadListener, nsIRequestObserver, - nsIStreamListener, nsIChannelEventSink, - nsIInterfaceRequestor, nsIObserver) +NS_IMPL_ISUPPORTS(HTMLMediaElement::MediaLoadListener, + nsIRequestObserver, + nsIStreamListener, + nsIChannelEventSink, + nsIInterfaceRequestor, + nsIObserver, + nsIThreadRetargetableStreamListener) NS_IMETHODIMP HTMLMediaElement::MediaLoadListener::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { nsContentUtils::UnregisterShutdownObserver(this); @@ -617,16 +624,28 @@ HTMLMediaElement::MediaLoadListener::Asy if (sink) { return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, cb); } cb->OnRedirectVerifyCallback(NS_OK); return NS_OK; } NS_IMETHODIMP +HTMLMediaElement::MediaLoadListener::CheckListenerChain() +{ + MOZ_ASSERT(mNextListener); + nsCOMPtr<nsIThreadRetargetableStreamListener> retargetable = + do_QueryInterface(mNextListener); + if (retargetable) { + return retargetable->CheckListenerChain(); + } + return NS_ERROR_NO_INTERFACE; +} + +NS_IMETHODIMP HTMLMediaElement::MediaLoadListener::GetInterface(const nsIID& aIID, void** aResult) { return QueryInterface(aIID, aResult); } void HTMLMediaElement::ReportLoadError(const char* aMsg, const char16_t** aParams,
--- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -20,16 +20,17 @@ #include "mozilla/Attributes.h" #include "mozilla/dom/TextTrackManager.h" #include "mozilla/WeakPtr.h" #include "mozilla/dom/MediaKeys.h" #include "mozilla/StateWatching.h" #include "nsGkAtoms.h" #include "PrincipalChangeObserver.h" #include "nsStubMutationObserver.h" +#include "MediaSegment.h" // for PrincipalHandle // X.h on Linux #defines CurrentTime as 0L, so we have to #undef it here. #ifdef CurrentTime #undef CurrentTime #endif #include "mozilla/dom/HTMLMediaElementBinding.h"
--- a/dom/media/AudioNotificationReceiver.cpp +++ b/dom/media/AudioNotificationReceiver.cpp @@ -1,16 +1,15 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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 "AudioNotificationReceiver.h" -#include "AudioStream.h" // for AudioStream #include "mozilla/Logging.h" // for LazyLogModule #include "mozilla/StaticMutex.h" // for StaticMutex #include "mozilla/StaticPtr.h" // for StaticAutoPtr #include "nsAppRunner.h" // for XRE_IsContentProcess #include "nsTArray.h" // for nsTArray static mozilla::LazyLogModule sLogger("AudioNotificationReceiver"); @@ -20,65 +19,68 @@ static mozilla::LazyLogModule sLogger("A #define ANR_LOGW(...) MOZ_LOG(sLogger, mozilla::LogLevel::Warning, (__VA_ARGS__)) namespace mozilla { namespace audio { /* * A list containing all clients subscribering the device-changed notifications. */ -static StaticAutoPtr<nsTArray<AudioStream*>> sSubscribers; +static StaticAutoPtr<nsTArray<DeviceChangeListener*>> sSubscribers; static StaticMutex sMutex; /* * AudioNotificationReceiver Implementation */ /* static */ void -AudioNotificationReceiver::Register(AudioStream* aAudioStream) +AudioNotificationReceiver::Register(DeviceChangeListener* aDeviceChangeListener) { MOZ_ASSERT(XRE_IsContentProcess()); StaticMutexAutoLock lock(sMutex); if (!sSubscribers) { - sSubscribers = new nsTArray<AudioStream*>(); + sSubscribers = new nsTArray<DeviceChangeListener*>(); } - sSubscribers->AppendElement(aAudioStream); + sSubscribers->AppendElement(aDeviceChangeListener); - ANR_LOG("The AudioStream: %p is registered successfully.", aAudioStream); + ANR_LOG("The DeviceChangeListener: %p is registered successfully.", + aDeviceChangeListener); } /* static */ void -AudioNotificationReceiver::Unregister(AudioStream* aAudioStream) +AudioNotificationReceiver::Unregister(DeviceChangeListener* aDeviceChangeListener) { MOZ_ASSERT(XRE_IsContentProcess()); StaticMutexAutoLock lock(sMutex); MOZ_ASSERT(!sSubscribers->IsEmpty(), "No subscriber."); - sSubscribers->RemoveElement(aAudioStream); + sSubscribers->RemoveElement(aDeviceChangeListener); if (sSubscribers->IsEmpty()) { // Clear the static pointer here to prevent memory leak. sSubscribers = nullptr; } - ANR_LOG("The AudioStream: %p is unregistered successfully.", aAudioStream); + ANR_LOG("The DeviceChangeListener: %p is unregistered successfully.", + aDeviceChangeListener); } /* static */ void AudioNotificationReceiver::NotifyDefaultDeviceChanged() { MOZ_ASSERT(XRE_IsContentProcess()); StaticMutexAutoLock lock(sMutex); - // Do nothing when there is no AudioStream. + // Do nothing when there is no DeviceChangeListener. if (!sSubscribers) { return; } - for (AudioStream* stream : *sSubscribers) { - ANR_LOG("Notify the AudioStream: %p that the default device has been changed.", stream); + for (DeviceChangeListener* stream : *sSubscribers) { + ANR_LOG("Notify the DeviceChangeListener: %p " + "that the default device has been changed.", stream); stream->ResetDefaultDevice(); } } } // namespace audio } // namespace mozilla
--- a/dom/media/AudioNotificationReceiver.h +++ b/dom/media/AudioNotificationReceiver.h @@ -8,78 +8,88 @@ #define MOZILLA_AUDIONOTIFICATIONRECEIVER_H_ /* * Architecture to send/receive default device-changed notification: * * Chrome Process ContentProcess 1 * ------------------ ------------------ * - * AudioNotification AudioStream 1 AudioStream N + * AudioNotification DeviceChangeListener 1 DeviceChangeListener N * | ^ | ^ ^ * (4)| |(2) |(3) |(8) . * v | v | v * AudioNotificationSender AudioNotificationReceiver * ^ | ^ ^ * . (5)| |(1) |(7) * . v | | * . (P)ContentParent 1 (P)ContentChild 1 * . | ^ * . (6)| | * . | | * . | | * . +------------------------------------+ - * . + * . PContent IPC * . * . Content Process M * . ------------------ * . . * v . * (P)ContentParent M < . . . . . . . . . > (P)ContentChild M * PContent IPC * * Steps * -------- * 1) Initailize the AudioNotificationSender when ContentParent is created. * 2) Create an AudioNotification to get the device-changed signal * from the system. - * 3) Register the AudioStream to AudioNotificationReceiver when it's created. + * 3) Register the DeviceChangeListener to AudioNotificationReceiver when it's created. * 4) When the default device is changed, AudioNotification get the signal and * 5) Pass this message by AudioNotificationSender. * 6) The AudioNotificationSender sends the device-changed notification via * the PContent. * 7) The ContentChild will call AudioNotificationReceiver to * 8) Notify all the registered audio streams to reconfigure the output devices. * * Notes * -------- * a) There is only one AudioNotificationSender and AudioNotification * in a chrome process. * b) There is only one AudioNotificationReceiver and might be many - * AudioStreams in a content process. + * DeviceChangeListeners in a content process. * c) There might be many ContentParent in a chrome process. * d) There is only one ContentChild in a content process. - * e) All the Audiostreams are registered in the AudioNotificationReceiver. + * e) All the DeviceChangeListeners are registered in the AudioNotificationReceiver. * f) All the ContentParents are registered in the AudioNotificationSender. */ namespace mozilla { +namespace audio { -class AudioStream; - -namespace audio { +// The base class that provides a ResetDefaultDevice interface that +// will be called in AudioNotificationReceiver::NotifyDefaultDeviceChanged +// when it receives device-changed notification from the chrome process. +class DeviceChangeListener +{ +protected: + virtual ~DeviceChangeListener() {}; +public: + // The subclass shoule provide its own implementation switching the + // audio stream to the new default output device. + virtual void ResetDefaultDevice() = 0; +}; class AudioNotificationReceiver final { public: - // Add the AudioStream into the subscribers list. - static void Register(AudioStream* aAudioStream); + // Add the DeviceChangeListener into the subscribers list. + static void Register(DeviceChangeListener* aDeviceChangeListener); - // Remove the AudioStream from the subscribers list. - static void Unregister(AudioStream* aAudioStream); + // Remove the DeviceChangeListener from the subscribers list. + static void Unregister(DeviceChangeListener* aDeviceChangeListener); // Notify all the streams that the default device has been changed. static void NotifyDefaultDeviceChanged(); }; // AudioNotificationReceiver } // namespace audio } // namespace mozilla
--- a/dom/media/AudioSegment.h +++ b/dom/media/AudioSegment.h @@ -228,16 +228,28 @@ struct AudioChunk { template<typename T> const nsTArray<const T*>& ChannelData() const { MOZ_ASSERT(AudioSampleTypeToFormat<T>::Format == mBufferFormat); return *reinterpret_cast<const AutoTArray<const T*,GUESS_AUDIO_CHANNELS>*> (&mChannelData); } + /** + * ChannelFloatsForWrite() should be used only when mBuffer is owned solely + * by the calling thread. + */ + template<typename T> + T* ChannelDataForWrite(size_t aChannel) + { + MOZ_ASSERT(AudioSampleTypeToFormat<T>::Format == mBufferFormat); + MOZ_ASSERT(!mBuffer->IsShared()); + return static_cast<T*>(const_cast<void*>(mChannelData[aChannel])); + } + PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; } StreamTime mDuration; // in frames within the buffer RefPtr<ThreadSharedObject> mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes // one pointer per channel; empty if and only if mBuffer is null AutoTArray<const void*,GUESS_AUDIO_CHANNELS> mChannelData; float mVolume; // volume multiplier to apply (1.0f if mBuffer is nonnull) SampleFormat mBufferFormat; // format of frames in mBuffer (only meaningful if mBuffer is nonnull)
--- a/dom/media/AudioStream.cpp +++ b/dom/media/AudioStream.cpp @@ -14,19 +14,16 @@ #include "mozilla/Mutex.h" #include "mozilla/Sprintf.h" #include <algorithm> #include "mozilla/Telemetry.h" #include "CubebUtils.h" #include "nsPrintfCString.h" #include "gfxPrefs.h" #include "AudioConverter.h" -#if defined(XP_WIN) -#include "mozilla/audio/AudioNotificationReceiver.h" -#endif namespace mozilla { #undef LOG #undef LOGW LazyLogModule gAudioStreamLog("AudioStream"); // For simple logs @@ -470,30 +467,32 @@ AudioStream::Shutdown() // Must not try to shut down cubeb from within the lock! wasapi may still // call our callback after Pause()/stop()!?! Bug 996162 mCubebStream.reset(); } mState = SHUTDOWN; } +#if defined(XP_WIN) void AudioStream::ResetDefaultDevice() { MonitorAutoLock mon(mMonitor); if (mState != STARTED && mState != STOPPED) { return; } MOZ_ASSERT(mCubebStream); auto r = InvokeCubeb(cubeb_stream_reset_default_device); if (!(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED)) { mState = ERRORED; } } +#endif int64_t AudioStream::GetPosition() { MonitorAutoLock mon(mMonitor); int64_t frames = GetPositionInFramesUnlocked(); return frames >= 0 ? mAudioClock.GetPosition(frames) : -1; }
--- a/dom/media/AudioStream.h +++ b/dom/media/AudioStream.h @@ -12,16 +12,20 @@ #include "nsThreadUtils.h" #include "mozilla/Monitor.h" #include "mozilla/RefPtr.h" #include "mozilla/TimeStamp.h" #include "mozilla/UniquePtr.h" #include "CubebUtils.h" #include "soundtouch/SoundTouchFactory.h" +#if defined(XP_WIN) +#include "mozilla/audio/AudioNotificationReceiver.h" +#endif + namespace mozilla { struct CubebDestroyPolicy { void operator()(cubeb_stream* aStream) const { cubeb_stream_destroy(aStream); } }; @@ -145,16 +149,19 @@ public: using AudioBufferCursor::Available; }; // Access to a single instance of this class must be synchronized by // callers, or made from a single thread. One exception is that access to // GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels}, // SetMicrophoneActive is thread-safe without external synchronization. class AudioStream final +#if defined(XP_WIN) + : public audio::DeviceChangeListener +#endif { virtual ~AudioStream(); public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioStream) class Chunk { public: @@ -205,18 +212,20 @@ public: void Start(); // Pause audio playback. void Pause(); // Resume audio playback. void Resume(); - // Reset stream to default device. - void ResetDefaultDevice(); +#if defined(XP_WIN) + // Reset stream to the default device. + void ResetDefaultDevice() override; +#endif // Return the position in microseconds of the audio frame being played by // the audio hardware, compensated for playback rate change. Thread-safe. int64_t GetPosition(); // Return the position, measured in audio frames played since the stream // was opened, of the audio hardware. Thread-safe. int64_t GetPositionInFrames();
--- a/dom/media/Benchmark.cpp +++ b/dom/media/Benchmark.cpp @@ -11,16 +11,17 @@ #include "MediaPrefs.h" #include "PDMFactory.h" #include "VideoUtils.h" #include "WebMDemuxer.h" #include "gfxPrefs.h" #include "mozilla/AbstractThread.h" #include "mozilla/Preferences.h" #include "mozilla/SharedThreadPool.h" +#include "mozilla/SystemGroup.h" #include "mozilla/TaskQueue.h" #include "mozilla/Telemetry.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/gfx/gfxVars.h" #ifndef MOZ_WIDGET_ANDROID #include "WebMSample.h" #endif
--- a/dom/media/CubebUtils.cpp +++ b/dom/media/CubebUtils.cpp @@ -107,16 +107,19 @@ enum class CubebState { } sCubebState = CubebState::Uninitialized; cubeb* sCubebContext; double sVolumeScale; uint32_t sCubebPlaybackLatencyInMilliseconds; uint32_t sCubebMSGLatencyInFrames; bool sCubebPlaybackLatencyPrefSet; bool sCubebMSGLatencyPrefSet; bool sAudioStreamInitEverSucceeded = false; +#ifdef MOZ_CUBEB_REMOTING +bool sCubebSandbox; +#endif StaticAutoPtr<char> sBrandName; StaticAutoPtr<char> sCubebBackendName; const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties"; const char* AUDIOSTREAM_BACKEND_ID_STR[] = { "jack", "pulse", @@ -244,21 +247,21 @@ void PrefChanged(const char* aPref, void } else { sCubebBackendName = new char[value.Length() + 1]; PodCopy(sCubebBackendName.get(), value.get(), value.Length()); sCubebBackendName[value.Length()] = 0; } } #ifdef MOZ_CUBEB_REMOTING else if (strcmp(aPref, PREF_CUBEB_SANDBOX) == 0) { - bool cubebSandbox = false; - Preferences::GetBool(aPref, cubebSandbox); - MOZ_LOG(gCubebLog, LogLevel::Verbose, ("%s: %s", PREF_CUBEB_SANDBOX, cubebSandbox ? "true" : "false")); + StaticMutexAutoLock lock(sMutex); + sCubebSandbox = Preferences::GetBool(aPref); + MOZ_LOG(gCubebLog, LogLevel::Verbose, ("%s: %s", PREF_CUBEB_SANDBOX, sCubebSandbox ? "true" : "false")); - if (cubebSandbox && !sServerHandle && XRE_IsParentProcess()) { + if (sCubebSandbox && !sServerHandle && XRE_IsParentProcess()) { MOZ_LOG(gCubebLog, LogLevel::Debug, ("Starting cubeb server...")); StartSoundServer(); } } #endif } bool GetFirstStream() @@ -385,21 +388,19 @@ cubeb* GetCubebContextUnlocked() if (!sBrandName && NS_IsMainThread()) { InitBrandName(); } else { NS_WARNING_ASSERTION( sBrandName, "Did not initialize sbrandName, and not on the main thread?"); } #ifdef MOZ_CUBEB_REMOTING - bool cubebSandbox = false; - Preferences::GetBool(PREF_CUBEB_SANDBOX, cubebSandbox); - MOZ_LOG(gCubebLog, LogLevel::Info, ("%s: %s", PREF_CUBEB_SANDBOX, cubebSandbox ? "true" : "false")); + MOZ_LOG(gCubebLog, LogLevel::Info, ("%s: %s", PREF_CUBEB_SANDBOX, sCubebSandbox ? "true" : "false")); - int rv = cubebSandbox + int rv = sCubebSandbox ? audioipc_client_init(&sCubebContext, sBrandName) : cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get()); #else // !MOZ_CUBEB_REMOTING int rv = cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get()); #endif // MOZ_CUBEB_REMOTING NS_WARNING_ASSERTION(rv == CUBEB_OK, "Could not get a cubeb context."); sCubebState = (rv == CUBEB_OK) ? CubebState::Initialized : CubebState::Uninitialized;
--- a/dom/media/FileBlockCache.cpp +++ b/dom/media/FileBlockCache.cpp @@ -8,16 +8,17 @@ #include "MediaCache.h" #include "MediaPrefs.h" #include "mozilla/SharedThreadPool.h" #include "VideoUtils.h" #include "prio.h" #include <algorithm> #include "nsAnonymousTemporaryFile.h" #include "mozilla/dom/ContentChild.h" +#include "mozilla/SystemGroup.h" #include "nsXULAppAPI.h" namespace mozilla { #undef LOG LazyLogModule gFileBlockCacheLog("FileBlockCache"); #define LOG(x, ...) MOZ_LOG(gFileBlockCacheLog, LogLevel::Debug, \ ("%p " x, this, ##__VA_ARGS__))
--- a/dom/media/GraphDriver.cpp +++ b/dom/media/GraphDriver.cpp @@ -569,21 +569,31 @@ AudioCallbackDriver::AudioCallbackDriver , mStarted(false) , mAudioInput(nullptr) , mAddedMixer(false) , mInCallback(false) , mMicrophoneActive(false) , mFromFallback(false) { LOG(LogLevel::Debug, ("AudioCallbackDriver ctor for graph %p", aGraphImpl)); +#if defined(XP_WIN) + if (XRE_IsContentProcess()) { + audio::AudioNotificationReceiver::Register(this); + } +#endif } AudioCallbackDriver::~AudioCallbackDriver() { MOZ_ASSERT(mPromisesForOperation.IsEmpty()); +#if defined(XP_WIN) + if (XRE_IsContentProcess()) { + audio::AudioNotificationReceiver::Unregister(this); + } +#endif } bool IsMacbookOrMacbookAir() { #ifdef XP_MACOSX size_t len = 0; sysctlbyname("hw.model", NULL, &len, NULL, 0); if (len) { @@ -852,16 +862,26 @@ AudioCallbackDriver::WaitForNextIteratio void AudioCallbackDriver::WakeUp() { mGraphImpl->GetMonitor().AssertCurrentThreadOwns(); mGraphImpl->GetMonitor().Notify(); } +#if defined(XP_WIN) +void +AudioCallbackDriver::ResetDefaultDevice() +{ + if (cubeb_stream_reset_default_device(mAudioStream) != CUBEB_OK) { + NS_WARNING("Could not reset cubeb stream to default output device."); + } +} +#endif + /* static */ long AudioCallbackDriver::DataCallback_s(cubeb_stream* aStream, void* aUser, const void* aInputBuffer, void* aOutputBuffer, long aFrames) { AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser);
--- a/dom/media/GraphDriver.h +++ b/dom/media/GraphDriver.h @@ -10,16 +10,20 @@ #include "AudioBufferUtils.h" #include "AudioMixer.h" #include "AudioSegment.h" #include "SelfRef.h" #include "mozilla/Atomics.h" #include "mozilla/SharedThreadPool.h" #include "mozilla/StaticPtr.h" +#if defined(XP_WIN) +#include "mozilla/audio/AudioNotificationReceiver.h" +#endif + struct cubeb_stream; template <> class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream> { public: static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); } }; @@ -371,29 +375,35 @@ enum AsyncCubebOperation { * sometimes hardware components are involved and need to be warmed up) * - We have no control on how much audio we generate, we have to return exactly * the number of frames asked for by the callback. Since for the Web Audio * API, we have to do block processing at 128 frames per block, we need to * keep a little spill buffer to store the extra frames. */ class AudioCallbackDriver : public GraphDriver, public MixerCallbackReceiver +#if defined(XP_WIN) + , public audio::DeviceChangeListener +#endif { public: explicit AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl); virtual ~AudioCallbackDriver(); void Destroy() override; void Start() override; void Stop() override; void Resume() override; void Revive() override; void RemoveCallback() override; void WaitForNextIteration() override; void WakeUp() override; +#if defined(XP_WIN) + void ResetDefaultDevice() override; +#endif /* Static wrapper function cubeb calls back. */ static long DataCallback_s(cubeb_stream * aStream, void * aUser, const void * aInputBuffer, void * aOutputBuffer, long aFrames); static void StateCallback_s(cubeb_stream* aStream, void * aUser,
--- a/dom/media/MediaInfo.h +++ b/dom/media/MediaInfo.h @@ -8,17 +8,17 @@ #include "mozilla/UniquePtr.h" #include "mozilla/RefPtr.h" #include "nsDataHashtable.h" #include "nsString.h" #include "nsTArray.h" #include "ImageTypes.h" #include "MediaData.h" -#include "StreamTracks.h" // for TrackID +#include "TrackID.h" // for TrackID #include "TimeUnits.h" #include "mozilla/gfx/Point.h" // for gfx::IntSize #include "mozilla/gfx/Rect.h" // for gfx::IntRect namespace mozilla { class AudioInfo; class VideoInfo;
--- a/dom/media/MediaResource.cpp +++ b/dom/media/MediaResource.cpp @@ -117,18 +117,21 @@ ChannelMediaResource::~ChannelMediaResou // ChannelMediaResource::Listener just observes the channel and // forwards notifications to the ChannelMediaResource. We use multiple // listener objects so that when we open a new stream for a seek we can // disconnect the old listener from the ChannelMediaResource and hook up // a new listener, so notifications from the old channel are discarded // and don't confuse us. NS_IMPL_ISUPPORTS(ChannelMediaResource::Listener, - nsIRequestObserver, nsIStreamListener, nsIChannelEventSink, - nsIInterfaceRequestor) + nsIRequestObserver, + nsIStreamListener, + nsIChannelEventSink, + nsIInterfaceRequestor, + nsIThreadRetargetableStreamListener) nsresult ChannelMediaResource::Listener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) { if (!mResource) return NS_OK; return mResource->OnStartRequest(aRequest); @@ -169,17 +172,23 @@ ChannelMediaResource::Listener::AsyncOnC if (NS_FAILED(rv)) return rv; cb->OnRedirectVerifyCallback(NS_OK); return NS_OK; } nsresult -ChannelMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult) +ChannelMediaResource::Listener::CheckListenerChain() +{ + return NS_OK; +} + +nsresult +ChannelMediaResource::Listener::GetInterface(const nsIID& aIID, void** aResult) { return QueryInterface(aIID, aResult); } static bool IsPayloadCompressed(nsIHttpChannel* aChannel) { nsAutoCString encoding;
--- a/dom/media/MediaResource.h +++ b/dom/media/MediaResource.h @@ -8,16 +8,17 @@ #include "mozilla/Mutex.h" #include "nsIChannel.h" #include "nsIURI.h" #include "nsISeekableStream.h" #include "nsIStreamListener.h" #include "nsIChannelEventSink.h" #include "nsIInterfaceRequestor.h" +#include "nsIThreadRetargetableStreamListener.h" #include "Intervals.h" #include "MediaCache.h" #include "MediaContainerType.h" #include "MediaData.h" #include "MediaPrefs.h" #include "MediaResourceCallback.h" #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" @@ -498,29 +499,32 @@ public: return size; } size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); } - class Listener final : public nsIStreamListener, - public nsIInterfaceRequestor, - public nsIChannelEventSink + class Listener final + : public nsIStreamListener + , public nsIInterfaceRequestor + , public nsIChannelEventSink + , public nsIThreadRetargetableStreamListener { ~Listener() {} public: explicit Listener(ChannelMediaResource* aResource) : mResource(aResource) {} NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER void Revoke() { mResource = nullptr; } private: RefPtr<ChannelMediaResource> mResource; }; friend class Listener;
--- a/dom/media/SharedBuffer.h +++ b/dom/media/SharedBuffer.h @@ -9,28 +9,33 @@ #include "mozilla/CheckedInt.h" #include "mozilla/mozalloc.h" #include "mozilla/MemoryReporting.h" #include "nsISupportsImpl.h" namespace mozilla { class AudioBlockBuffer; +class ThreadSharedFloatArrayBufferList; /** * Base class for objects with a thread-safe refcount and a virtual * destructor. */ class ThreadSharedObject { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadSharedObject) bool IsShared() { return mRefCnt.get() > 1; } virtual AudioBlockBuffer* AsAudioBlockBuffer() { return nullptr; }; + virtual ThreadSharedFloatArrayBufferList* AsThreadSharedFloatArrayBufferList() + { + return nullptr; + }; virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { return 0; } virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { @@ -48,34 +53,52 @@ protected: * This only guarantees 4-byte alignment of the data. For alignment we simply * assume that the memory from malloc is at least 4-byte aligned and the * refcount's size is large enough that SharedBuffer's size is divisible by 4. */ class SharedBuffer : public ThreadSharedObject { public: void* Data() { return this + 1; } + static already_AddRefed<SharedBuffer> Create(size_t aSize, const fallible_t&) + { + void* m = operator new(AllocSize(aSize), fallible); + if (!m) { + return nullptr; + } + RefPtr<SharedBuffer> p = new (m) SharedBuffer(); + return p.forget(); + } + static already_AddRefed<SharedBuffer> Create(size_t aSize) { - CheckedInt<size_t> size = sizeof(SharedBuffer); - size += aSize; - if (!size.isValid()) { - MOZ_CRASH(); - } - void* m = moz_xmalloc(size.value()); + void* m = operator new(AllocSize(aSize)); RefPtr<SharedBuffer> p = new (m) SharedBuffer(); - NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) - reinterpret_cast<char*>(p.get())) % 4 == 0, - "SharedBuffers should be at least 4-byte aligned"); return p.forget(); } size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); } private: - SharedBuffer() {} + static size_t + AllocSize(size_t aDataSize) + { + CheckedInt<size_t> size = sizeof(SharedBuffer); + size += aDataSize; + if (!size.isValid()) { + MOZ_CRASH(); + } + return size.value(); + } + + SharedBuffer() + { + NS_ASSERTION((reinterpret_cast<char*>(this + 1) - reinterpret_cast<char*>(this)) % 4 == 0, + "SharedBuffers should be at least 4-byte aligned"); + } }; } // namespace mozilla #endif /* MOZILLA_SHAREDBUFFER_H_ */
--- a/dom/media/StreamTracks.h +++ b/dom/media/StreamTracks.h @@ -3,33 +3,20 @@ * 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/. */ #ifndef MOZILLA_STREAMTRACKS_H_ #define MOZILLA_STREAMTRACKS_H_ #include "MediaSegment.h" #include "nsAutoPtr.h" +#include "TrackID.h" namespace mozilla { -/** - * Unique ID for track within a StreamTracks. Tracks from different - * StreamTrackss may have the same ID; this matters when appending StreamTrackss, - * since tracks with the same ID are matched. Only IDs greater than 0 are allowed. - */ -typedef int32_t TrackID; -const TrackID TRACK_NONE = 0; -const TrackID TRACK_INVALID = -1; -const TrackID TRACK_ANY = -2; - -inline bool IsTrackIDExplicit(const TrackID& aId) { - return aId > TRACK_NONE; -} - inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate, TrackRate aInRate, TrackTicks aTicks) { NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate"); NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate"); NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); return (aTicks * aOutRate) / aInRate;
new file mode 100644 --- /dev/null +++ b/dom/media/TrackID.h @@ -0,0 +1,27 @@ +/* -*- 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/. */ + + #ifndef MOZILLA_TRACK_ID_H_ + #define MOZILLA_TRACK_ID_H_ + + namespace mozilla { + + /** + * Unique ID for track within a StreamTracks. Tracks from different + * StreamTrackss may have the same ID; this matters when appending StreamTrackss, + * since tracks with the same ID are matched. Only IDs greater than 0 are allowed. + */ + typedef int32_t TrackID; + const TrackID TRACK_NONE = 0; + const TrackID TRACK_INVALID = -1; + const TrackID TRACK_ANY = -2; + + inline bool IsTrackIDExplicit(const TrackID& aId) { + return aId > TRACK_NONE; + } + +} // namespace mozilla + +#endif // MOZILLA_TRACK_ID_H_
--- a/dom/media/gmp/GMPService.cpp +++ b/dom/media/gmp/GMPService.cpp @@ -35,16 +35,17 @@ #include "nsIFile.h" #include "nsISimpleEnumerator.h" #include "nsThreadUtils.h" #include "GMPCrashHelper.h" #include "mozilla/dom/PluginCrashedEvent.h" #include "mozilla/EventDispatcher.h" #include "mozilla/Attributes.h" +#include "mozilla/SystemGroup.h" namespace mozilla { #ifdef LOG #undef LOG #endif LogModule*
--- a/dom/media/gmp/GMPServiceChild.cpp +++ b/dom/media/gmp/GMPServiceChild.cpp @@ -14,16 +14,17 @@ #include "GMPContentParent.h" #include "nsXPCOMPrivate.h" #include "mozilla/SyncRunnable.h" #include "mozilla/StaticMutex.h" #include "runnable_utils.h" #include "base/task.h" #include "nsIObserverService.h" #include "nsComponentManagerUtils.h" +#include "mozilla/SystemGroup.h" namespace mozilla { #ifdef LOG #undef LOG #endif #define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
--- a/dom/media/gmp/GMPServiceParent.cpp +++ b/dom/media/gmp/GMPServiceParent.cpp @@ -35,16 +35,17 @@ #include "nsDirectoryServiceDefs.h" #include "nsHashKeys.h" #include "nsIFile.h" #include "nsISimpleEnumerator.h" #include "nsIXULRuntime.h" #include "GMPDecoderModule.h" #include <limits> #include "MediaPrefs.h" +#include "mozilla/SystemGroup.h" using mozilla::ipc::Transport; namespace mozilla { #ifdef LOG #undef LOG #endif
--- a/dom/media/hls/HLSDecoder.cpp +++ b/dom/media/hls/HLSDecoder.cpp @@ -1,54 +1,105 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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 "HLSDecoder.h" #include "AndroidBridge.h" #include "DecoderTraits.h" +#include "GeneratedJNINatives.h" +#include "GeneratedJNIWrappers.h" #include "HLSDemuxer.h" -#include "HLSResource.h" #include "HLSUtils.h" #include "MediaContainerType.h" #include "MediaDecoderStateMachine.h" #include "MediaFormatReader.h" #include "MediaPrefs.h" #include "MediaShutdownManager.h" +#include "nsContentUtils.h" #include "nsNetUtil.h" +using namespace mozilla::java; + namespace mozilla { +class HLSResourceCallbacksSupport + : public GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport> +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSResourceCallbacksSupport) +public: + typedef GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport> NativeCallbacks; + using NativeCallbacks::DisposeNative; + using NativeCallbacks::AttachNative; + + HLSResourceCallbacksSupport(HLSDecoder* aResource); + void Detach(); + void OnDataArrived(); + void OnError(int aErrorCode); + +private: + ~HLSResourceCallbacksSupport() {} + HLSDecoder* mDecoder; +}; + +HLSResourceCallbacksSupport::HLSResourceCallbacksSupport(HLSDecoder* aDecoder) +{ + MOZ_ASSERT(aDecoder); + mDecoder = aDecoder; +} + void -HLSDecoder::Shutdown() +HLSResourceCallbacksSupport::Detach() { MOZ_ASSERT(NS_IsMainThread()); - if (mResource) { - mResource->Detach(); + mDecoder = nullptr; +} + +void +HLSResourceCallbacksSupport::OnDataArrived() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (mDecoder) { + HLS_DEBUG("HLSResourceCallbacksSupport", "OnDataArrived"); + mDecoder->NotifyDataArrived(); } - MediaDecoder::Shutdown(); +} + +void +HLSResourceCallbacksSupport::OnError(int aErrorCode) +{ + MOZ_ASSERT(NS_IsMainThread()); + if (mDecoder) { + HLS_DEBUG("HLSResourceCallbacksSupport", "onError(%d)", aErrorCode); + // Since HLS source should be from the Internet, we treat all resource errors + // from GeckoHlsPlayer as network errors. + mDecoder->NetworkError(); + } +} + +HLSDecoder::HLSDecoder(MediaDecoderInit& aInit) + : MediaDecoder(aInit) +{ } MediaDecoderStateMachine* HLSDecoder::CreateStateMachine() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mResource); - auto resourceWrapper = mResource->GetResourceWrapper(); - MOZ_ASSERT(resourceWrapper); MediaFormatReaderInit init; init.mVideoFrameContainer = GetVideoFrameContainer(); init.mKnowsCompositor = GetCompositor(); init.mCrashHelper = GetOwner()->CreateGMPCrashHelper(); init.mFrameStats = mFrameStats; mReader = - new MediaFormatReader(init, new HLSDemuxer(resourceWrapper->GetPlayerId())); + new MediaFormatReader(init, new HLSDemuxer(mHLSResourceWrapper->GetPlayerId())); return new MediaDecoderStateMachine(this, mReader); } bool HLSDecoder::IsEnabled() { return MediaPrefs::HLSEnabled() && (jni::GetAPIVersion() >= 16); @@ -60,84 +111,110 @@ HLSDecoder::IsSupportedType(const MediaC return IsEnabled() && DecoderTraits::IsHttpLiveStreamingType(aContainerType); } nsresult HLSDecoder::Load(nsIChannel* aChannel) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mResource); - nsCOMPtr<nsIURI> uri; - nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); + nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(mURI)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - mResource = MakeUnique<HLSResource>(this, aChannel, uri); + mChannel = aChannel; + nsCString spec; + Unused << mURI->GetSpec(spec);; + HLSResourceCallbacksSupport::Init(); + mJavaCallbacks = GeckoHLSResourceWrapper::Callbacks::New(); + mCallbackSupport = new HLSResourceCallbacksSupport(this); + HLSResourceCallbacksSupport::AttachNative(mJavaCallbacks, mCallbackSupport); + mHLSResourceWrapper = java::GeckoHLSResourceWrapper::Create(NS_ConvertUTF8toUTF16(spec), + mJavaCallbacks); + MOZ_ASSERT(mHLSResourceWrapper); rv = MediaShutdownManager::Instance().Register(this); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } SetStateMachine(CreateStateMachine()); NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE); return InitializeStateMachine(); } void HLSDecoder::AddSizeOfResources(ResourceSizes* aSizes) { MOZ_ASSERT(NS_IsMainThread()); - if (mResource) { - aSizes->mByteSize += mResource->SizeOfIncludingThis(aSizes->mMallocSizeOf); - } + // TODO: track JAVA wrappers. } already_AddRefed<nsIPrincipal> HLSDecoder::GetCurrentPrincipal() { MOZ_ASSERT(NS_IsMainThread()); - return mResource ? mResource->GetCurrentPrincipal() : nullptr; + nsCOMPtr<nsIPrincipal> principal; + nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); + if (!secMan || !mChannel) { + return nullptr; + } + secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal)); + return principal.forget(); } nsresult HLSDecoder::Play() { MOZ_ASSERT(NS_IsMainThread()); HLS_DEBUG("HLSDecoder", "MediaElement called Play"); - auto resourceWrapper = mResource->GetResourceWrapper(); - resourceWrapper->Play(); + mHLSResourceWrapper->Play(); return MediaDecoder::Play(); } void HLSDecoder::Pause() { MOZ_ASSERT(NS_IsMainThread()); HLS_DEBUG("HLSDecoder", "MediaElement called Pause"); - auto resourceWrapper = mResource->GetResourceWrapper(); - resourceWrapper->Pause(); + mHLSResourceWrapper->Pause(); return MediaDecoder::Pause(); } void HLSDecoder::Suspend() { MOZ_ASSERT(NS_IsMainThread()); - if (mResource) { - mResource->Suspend(); - } + HLS_DEBUG("HLSDecoder", "Should suspend the resource fetching."); + mHLSResourceWrapper->Suspend(); } void HLSDecoder::Resume() { MOZ_ASSERT(NS_IsMainThread()); - if (mResource) { - mResource->Resume(); + HLS_DEBUG("HLSDecoder", "Should resume the resource fetching."); + mHLSResourceWrapper->Resume(); +} + +void +HLSDecoder::Shutdown() +{ + HLS_DEBUG("HLSDecoder", "Shutdown"); + if (mCallbackSupport) { + mCallbackSupport->Detach(); + mCallbackSupport = nullptr; } + if (mHLSResourceWrapper) { + mHLSResourceWrapper->Destroy(); + mHLSResourceWrapper = nullptr; + } + if (mJavaCallbacks) { + HLSResourceCallbacksSupport::DisposeNative(mJavaCallbacks); + mJavaCallbacks = nullptr; + } + MediaDecoder::Shutdown(); } } // namespace mozilla
--- a/dom/media/hls/HLSDecoder.h +++ b/dom/media/hls/HLSDecoder.h @@ -2,31 +2,27 @@ /* vim: set ts=8 sts=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/. */ #ifndef HLSDecoder_h_ #define HLSDecoder_h_ -#include "HLSResource.h" #include "MediaDecoder.h" namespace mozilla { +class HLSResourceCallbacksSupport; + class HLSDecoder final : public MediaDecoder { public: // MediaDecoder interface. - explicit HLSDecoder(MediaDecoderInit& aInit) - : MediaDecoder(aInit) - { - } - - void Shutdown() override; + explicit HLSDecoder(MediaDecoderInit& aInit); // Returns true if the HLS backend is pref'ed on. static bool IsEnabled(); // Returns true if aContainerType is an HLS type that we think we can render // with the a platform decoder backend. // If provided, codecs are checked for support. static bool IsSupportedType(const MediaContainerType& aContainerType); @@ -37,30 +33,37 @@ public: void Pause() override; void AddSizeOfResources(ResourceSizes* aSizes) override; already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override; bool IsTransportSeekable() override { return true; } void Suspend() override; void Resume() override; + void Shutdown() override; private: + friend class HLSResourceCallbacksSupport; + void PinForSeek() override {} void UnpinForSeek() override {} MediaDecoderStateMachine* CreateStateMachine(); bool CanPlayThroughImpl() override final { // TODO: We don't know how to estimate 'canplaythrough' for this decoder. // For now we just return true for 'autoplay' can work. return true; } bool IsLiveStream() override final { return false; } - UniquePtr<HLSResource> mResource; + nsCOMPtr<nsIChannel> mChannel; + nsCOMPtr<nsIURI> mURI; + java::GeckoHLSResourceWrapper::GlobalRef mHLSResourceWrapper; + java::GeckoHLSResourceWrapper::Callbacks::GlobalRef mJavaCallbacks; + RefPtr<HLSResourceCallbacksSupport> mCallbackSupport; }; } // namespace mozilla #endif /* HLSDecoder_h_ */
deleted file mode 100644 --- a/dom/media/hls/HLSResource.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "HLSDecoder.h" -#include "HLSResource.h" -#include "HLSUtils.h" - -using namespace mozilla::java; - -namespace mozilla { - -HLSResourceCallbacksSupport::HLSResourceCallbacksSupport(HLSResource* aResource) -{ - MOZ_ASSERT(aResource); - mResource = aResource; -} - -void -HLSResourceCallbacksSupport::Detach() -{ - MOZ_ASSERT(NS_IsMainThread()); - mResource = nullptr; -} - -void -HLSResourceCallbacksSupport::OnDataArrived() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (mResource) { - mResource->onDataAvailable(); - } -} - -void -HLSResourceCallbacksSupport::OnError(int aErrorCode) -{ - MOZ_ASSERT(NS_IsMainThread()); - if (mResource) { - mResource->onError(aErrorCode); - } -} - -HLSResource::HLSResource(HLSDecoder* aDecoder, - nsIChannel* aChannel, - nsIURI* aURI) - : mDecoder(aDecoder) - , mChannel(aChannel) - , mURI(aURI) -{ - nsCString spec; - nsresult rv = aURI->GetSpec(spec); - (void)rv; - HLSResourceCallbacksSupport::Init(); - mJavaCallbacks = GeckoHLSResourceWrapper::Callbacks::New(); - mCallbackSupport = new HLSResourceCallbacksSupport(this); - HLSResourceCallbacksSupport::AttachNative(mJavaCallbacks, mCallbackSupport); - mHLSResourceWrapper = java::GeckoHLSResourceWrapper::Create(NS_ConvertUTF8toUTF16(spec), - mJavaCallbacks); - MOZ_ASSERT(mHLSResourceWrapper); -} - -void -HLSResource::onDataAvailable() -{ - HLS_DEBUG("HLSResource", "onDataAvailable"); - if (mDecoder) { - mDecoder->NotifyDataArrived(); - } -} - -void -HLSResource::onError(int aErrorCode) -{ - HLS_DEBUG("HLSResource", "onError(%d)", aErrorCode); - // Since HLS source should be from the Internet, we treat all resource errors - // from GeckoHlsPlayer as network errors. - if (mDecoder) { - mDecoder->NetworkError(); - } -} - -void -HLSResource::Suspend() -{ - MOZ_ASSERT(NS_IsMainThread(), "Don't call on non-main thread"); - HLS_DEBUG("HLSResource", "Should suspend the resource fetching."); - mHLSResourceWrapper->Suspend(); -} - -void HLSResource::Resume() -{ - MOZ_ASSERT(NS_IsMainThread(), "Don't call on non-main thread"); - HLS_DEBUG("HLSResource", "Should resume the resource fetching."); - mHLSResourceWrapper->Resume(); -} - -HLSResource::~HLSResource() -{ - HLS_DEBUG("HLSResource", "~HLSResource()"); - if (mCallbackSupport) { - mCallbackSupport->Detach(); - mCallbackSupport = nullptr; - } - if (mHLSResourceWrapper) { - mHLSResourceWrapper->Destroy(); - mHLSResourceWrapper = nullptr; - } - if (mJavaCallbacks) { - HLSResourceCallbacksSupport::DisposeNative(mJavaCallbacks); - mJavaCallbacks = nullptr; - } -} - -} // namespace mozilla
deleted file mode 100644 --- a/dom/media/hls/HLSResource.h +++ /dev/null @@ -1,93 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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/. */ - -#ifndef HLSResource_h_ -#define HLSResource_h_ - -#include "GeneratedJNINatives.h" -#include "GeneratedJNIWrappers.h" -#include "HLSUtils.h" -#include "nsContentUtils.h" - -using namespace mozilla::java; - -namespace mozilla { - -class HLSDecoder; -class HLSResource; - -class HLSResourceCallbacksSupport - : public GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport> -{ - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSResourceCallbacksSupport) -public: - typedef GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport> NativeCallbacks; - using NativeCallbacks::DisposeNative; - using NativeCallbacks::AttachNative; - - HLSResourceCallbacksSupport(HLSResource* aResource); - void Detach(); - void OnDataArrived(); - void OnError(int aErrorCode); - -private: - ~HLSResourceCallbacksSupport() {} - HLSResource* mResource; -}; - -class HLSResource final -{ -public: - HLSResource(HLSDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI); - ~HLSResource(); - void Suspend(); - void Resume(); - - already_AddRefed<nsIPrincipal> GetCurrentPrincipal() - { - NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); - - nsCOMPtr<nsIPrincipal> principal; - nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); - if (!secMan || !mChannel) - return nullptr; - secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal)); - return principal.forget(); - } - - java::GeckoHLSResourceWrapper::GlobalRef GetResourceWrapper() { - return mHLSResourceWrapper; - } - - void Detach() { mDecoder = nullptr; } - - size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const - { - // TODO: track JAVA wrappers. - return 0; - } - - size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const - { - return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); - } - -private: - friend class HLSResourceCallbacksSupport; - - void onDataAvailable(); - void onError(int aErrorCode); - - HLSDecoder* mDecoder; - nsCOMPtr<nsIChannel> mChannel; - nsCOMPtr<nsIURI> mURI; - java::GeckoHLSResourceWrapper::GlobalRef mHLSResourceWrapper; - java::GeckoHLSResourceWrapper::Callbacks::GlobalRef mJavaCallbacks; - RefPtr<HLSResourceCallbacksSupport> mCallbackSupport; -}; - -} // namespace mozilla -#endif /* HLSResource_h_ */
--- a/dom/media/hls/moz.build +++ b/dom/media/hls/moz.build @@ -2,24 +2,22 @@ # 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/. EXPORTS += [ 'HLSDecoder.h', 'HLSDemuxer.h', - 'HLSResource.h', 'HLSUtils.h', ] UNIFIED_SOURCES += [ 'HLSDecoder.cpp', 'HLSDemuxer.cpp', - 'HLSResource.cpp', 'HLSUtils.cpp', ] include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' if CONFIG['GNU_CXX']:
--- a/dom/media/ipc/PVideoDecoderManager.ipdl +++ b/dom/media/ipc/PVideoDecoderManager.ipdl @@ -12,17 +12,25 @@ using struct mozilla::layers::TextureFac namespace mozilla { namespace dom { sync protocol PVideoDecoderManager { manages PVideoDecoder; parent: - sync PVideoDecoder(VideoInfo info, TextureFactoryIdentifier identifier) returns (bool success); + // aBlacklistedD3D11Driver and aBlacklistedD3D9Driver are used to read back the blacklisted driver information + // from GPU process to content process. + // We should have added a new sync method to read back this information but, in that way, we also introduce one + // more sync IPC call. + // Considering that this information is only used for telemetry usage in bug 1393392 and should be removed once + // we have collected enough data, we add these two return values here for convenience. + sync PVideoDecoder(VideoInfo info, TextureFactoryIdentifier identifier) returns (bool success, + nsCString aBlacklistedD3D11Driver, + nsCString aBlacklistedD3D9Driver); sync Readback(SurfaceDescriptorGPUVideo sd) returns (SurfaceDescriptor aResult); async DeallocateSurfaceDescriptorGPUVideo(SurfaceDescriptorGPUVideo sd); }; } // namespace dom } // namespace mozilla
--- a/dom/media/ipc/VideoDecoderChild.cpp +++ b/dom/media/ipc/VideoDecoderChild.cpp @@ -1,29 +1,48 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=99: */ /* 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 "VideoDecoderChild.h" #include "VideoDecoderManagerChild.h" #include "mozilla/layers/TextureClient.h" +#include "mozilla/Telemetry.h" #include "base/thread.h" #include "MediaInfo.h" #include "ImageContainer.h" #include "GPUVideoImage.h" namespace mozilla { namespace dom { using base::Thread; using namespace ipc; using namespace layers; using namespace gfx; +#ifdef XP_WIN +static void +ReportUnblacklistingTelemetry(bool isGPUProcessCrashed, + const nsCString& aD3D11BlacklistedDriver, + const nsCString& aD3D9BlacklistedDriver) +{ + const nsCString& blacklistedDLL = !aD3D11BlacklistedDriver.IsEmpty() + ? aD3D11BlacklistedDriver + : aD3D9BlacklistedDriver; + + if (!blacklistedDLL.IsEmpty()) { + Telemetry::Accumulate(Telemetry::VIDEO_UNBLACKINGLISTING_DXVA_DRIVER_RUNTIME_STATUS, + blacklistedDLL, + isGPUProcessCrashed ? 1 : 0); + } +} +#endif // XP_WIN + VideoDecoderChild::VideoDecoderChild() : mThread(VideoDecoderManagerChild::GetManagerThread()) , mCanSend(false) , mInitialized(false) , mIsHardwareAccelerated(false) , mConversion(MediaDataDecoder::ConversionRequired::kNeedNone) , mNeedNewDecoder(false) { @@ -139,16 +158,22 @@ VideoDecoderChild::ActorDestroy(ActorDes mNeedNewDecoder = true; } else { ref->mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, __func__); } })); } mCanSend = false; + +#ifdef XP_WIN + ReportUnblacklistingTelemetry(aWhy == AbnormalShutdown, + mBlacklistedD3D11Driver, + mBlacklistedD3D9Driver); +#endif // XP_WIN } bool VideoDecoderChild::InitIPDL(const VideoInfo& aVideoInfo, const layers::TextureFactoryIdentifier& aIdentifier) { RefPtr<VideoDecoderManagerChild> manager = VideoDecoderManagerChild::GetSingleton(); @@ -168,17 +193,19 @@ VideoDecoderChild::InitIPDL(const VideoI // available. If not, then the cycle repeats until we're ready. if (!manager->CanSend()) { return true; } mIPDLSelfRef = this; bool success = false; if (manager->SendPVideoDecoderConstructor(this, aVideoInfo, aIdentifier, - &success)) { + &success, + &mBlacklistedD3D11Driver, + &mBlacklistedD3D9Driver)) { mCanSend = true; } return success; } void VideoDecoderChild::DestroyIPDL() {
--- a/dom/media/ipc/VideoDecoderChild.h +++ b/dom/media/ipc/VideoDecoderChild.h @@ -74,14 +74,17 @@ private: bool mInitialized; Atomic<bool> mIsHardwareAccelerated; Atomic<MediaDataDecoder::ConversionRequired> mConversion; // Set to true if the actor got destroyed and we haven't yet notified the // caller. bool mNeedNewDecoder; MediaDataDecoder::DecodedData mDecodedData; + + nsCString mBlacklistedD3D11Driver; + nsCString mBlacklistedD3D9Driver; }; } // namespace dom } // namespace mozilla #endif // include_dom_ipc_VideoDecoderChild_h
--- a/dom/media/ipc/VideoDecoderManagerChild.cpp +++ b/dom/media/ipc/VideoDecoderManagerChild.cpp @@ -112,17 +112,19 @@ VideoDecoderManagerChild::GetManagerThre VideoDecoderManagerChild::GetManagerAbstractThread() { return sVideoDecoderChildAbstractThread; } PVideoDecoderChild* VideoDecoderManagerChild::AllocPVideoDecoderChild(const VideoInfo& aVideoInfo, const layers::TextureFactoryIdentifier& aIdentifier, - bool* aSuccess) + bool* aSuccess, + nsCString* /* not used */, + nsCString* /* not used */) { return new VideoDecoderChild(); } bool VideoDecoderManagerChild::DeallocPVideoDecoderChild(PVideoDecoderChild* actor) { VideoDecoderChild* child = static_cast<VideoDecoderChild*>(actor);
--- a/dom/media/ipc/VideoDecoderManagerChild.h +++ b/dom/media/ipc/VideoDecoderManagerChild.h @@ -67,17 +67,19 @@ protected: void ActorDestroy(ActorDestroyReason aWhy) override; void DeallocPVideoDecoderManagerChild() override; void HandleFatalError(const char* aName, const char* aMsg) const override; PVideoDecoderChild* AllocPVideoDecoderChild(const VideoInfo& aVideoInfo, const layers::TextureFactoryIdentifier& aIdentifier, - bool* aSuccess) override; + bool* aSuccess, + nsCString* aBlacklistedD3D11Driver, + nsCString* aBlacklistedD3D9Driver) override; bool DeallocPVideoDecoderChild(PVideoDecoderChild* actor) override; private: // Main thread only static void InitializeThread(); VideoDecoderManagerChild() : mCanSend(false)
--- a/dom/media/ipc/VideoDecoderManagerParent.cpp +++ b/dom/media/ipc/VideoDecoderManagerParent.cpp @@ -19,16 +19,22 @@ #include "mozilla/layers/ImageDataSerializer.h" #include "mozilla/SyncRunnable.h" #if XP_WIN #include <objbase.h> #endif namespace mozilla { + +#ifdef XP_WIN +extern const nsCString GetFoundD3D11BlacklistedDLL(); +extern const nsCString GetFoundD3D9BlacklistedDLL(); +#endif // XP_WIN + namespace dom { using namespace ipc; using namespace layers; using namespace gfx; SurfaceDescriptorGPUVideo VideoDecoderManagerParent::StoreImage(Image* aImage, TextureClient* aTexture) @@ -188,25 +194,34 @@ void VideoDecoderManagerParent::ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) { mThreadHolder = nullptr; } PVideoDecoderParent* VideoDecoderManagerParent::AllocPVideoDecoderParent(const VideoInfo& aVideoInfo, const layers::TextureFactoryIdentifier& aIdentifier, - bool* aSuccess) + bool* aSuccess, + nsCString* aBlacklistedD3D11Driver, + nsCString* aBlacklistedD3D9Driver) { RefPtr<TaskQueue> decodeTaskQueue = new TaskQueue( SharedThreadPool::Get(NS_LITERAL_CSTRING("VideoDecoderParent"), 4), "VideoDecoderParent::mDecodeTaskQueue"); - return new VideoDecoderParent( + auto* parent = new VideoDecoderParent( this, aVideoInfo, aIdentifier, sManagerTaskQueue, decodeTaskQueue, aSuccess); + +#ifdef XP_WIN + *aBlacklistedD3D11Driver = GetFoundD3D11BlacklistedDLL(); + *aBlacklistedD3D9Driver = GetFoundD3D9BlacklistedDLL(); +#endif // XP_WIN + + return parent; } bool VideoDecoderManagerParent::DeallocPVideoDecoderParent(PVideoDecoderParent* actor) { VideoDecoderParent* parent = static_cast<VideoDecoderParent*>(actor); parent->Destroy(); return true;
--- a/dom/media/ipc/VideoDecoderManagerParent.h +++ b/dom/media/ipc/VideoDecoderManagerParent.h @@ -26,17 +26,21 @@ public: static void StartupThreads(); static void ShutdownThreads(); static void ShutdownVideoBridge(); bool OnManagerThread(); protected: - PVideoDecoderParent* AllocPVideoDecoderParent(const VideoInfo& aVideoInfo, const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess) override; + PVideoDecoderParent* AllocPVideoDecoderParent(const VideoInfo& aVideoInfo, + const layers::TextureFactoryIdentifier& aIdentifier, + bool* aSuccess, + nsCString* aBlacklistedD3D11Driver, + nsCString* aBlacklistedD3D9Driver) override; bool DeallocPVideoDecoderParent(PVideoDecoderParent* actor) override; mozilla::ipc::IPCResult RecvReadback(const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) override; mozilla::ipc::IPCResult RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) override; void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override; void DeallocPVideoDecoderManagerParent() override;
--- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -142,16 +142,17 @@ EXPORTS += [ 'QueueObject.h', 'SeekJob.h', 'SeekTarget.h', 'SelfRef.h', 'SharedBuffer.h', 'StreamTracks.h', 'ThreadPoolCOMListener.h', 'TimeUnits.h', + 'TrackID.h', 'TrackUnionStream.h', 'VideoFrameContainer.h', 'VideoLimits.h', 'VideoSegment.h', 'VideoUtils.h', 'VorbisUtils.h', 'XiphExtradata.h', ]
--- a/dom/media/platforms/apple/AppleVTLinker.cpp +++ b/dom/media/platforms/apple/AppleVTLinker.cpp @@ -4,16 +4,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/. */ #include <dlfcn.h> #include "AppleVTLinker.h" #include "mozilla/ArrayUtils.h" #include "nsDebug.h" +#include "PlatformDecoderModule.h" // for sPDMLog #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) namespace mozilla { AppleVTLinker::LinkStatus AppleVTLinker::sLinkStatus = LinkStatus_INIT;
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp @@ -405,53 +405,75 @@ FindD3D11BlacklistedDLL() static const nsCString& FindD3D9BlacklistedDLL() { return FindDXVABlacklistedDLL(sD3D9BlacklistingCache, gfx::gfxVars::PDMWMFDisableD3D9Dlls(), "media.wmf.disable-d3d9-for-dlls"); } +const nsCString +GetFoundD3D11BlacklistedDLL() +{ + if (sD3D11BlacklistingCache) { + return sD3D11BlacklistingCache->mBlacklistedDLL; + } + + return nsCString(); +} + +const nsCString +GetFoundD3D9BlacklistedDLL() +{ + if (sD3D9BlacklistingCache) { + return sD3D9BlacklistingCache->mBlacklistedDLL; + } + + return nsCString(); +} + class CreateDXVAManagerEvent : public Runnable { public: CreateDXVAManagerEvent(layers::KnowsCompositor* aKnowsCompositor, nsCString& aFailureReason) : Runnable("CreateDXVAManagerEvent") , mBackend(LayersBackend::LAYERS_D3D11) , mKnowsCompositor(aKnowsCompositor) , mFailureReason(aFailureReason) { } NS_IMETHOD Run() override { NS_ASSERTION(NS_IsMainThread(), "Must be on main thread."); + const bool deblacklistingForTelemetry = + XRE_IsGPUProcess() && gfxPrefs::PDMWMFDeblacklistingForTelemetryInGPUProcess(); nsACString* failureReason = &mFailureReason; nsCString secondFailureReason; if (mBackend == LayersBackend::LAYERS_D3D11 && gfxPrefs::PDMWMFAllowD3D11() && IsWin8OrLater()) { const nsCString& blacklistedDLL = FindD3D11BlacklistedDLL(); - if (!blacklistedDLL.IsEmpty()) { + if (!deblacklistingForTelemetry && !blacklistedDLL.IsEmpty()) { failureReason->AppendPrintf("D3D11 blacklisted with DLL %s", blacklistedDLL.get()); } else { mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA(mKnowsCompositor, *failureReason); if (mDXVA2Manager) { return NS_OK; } } // Try again with d3d9, but record the failure reason // into a new var to avoid overwriting the d3d11 failure. failureReason = &secondFailureReason; mFailureReason.Append(NS_LITERAL_CSTRING("; ")); } const nsCString& blacklistedDLL = FindD3D9BlacklistedDLL(); - if (!blacklistedDLL.IsEmpty()) { + if (!deblacklistingForTelemetry && !blacklistedDLL.IsEmpty()) { mFailureReason.AppendPrintf("D3D9 blacklisted with DLL %s", blacklistedDLL.get()); } else { mDXVA2Manager = DXVA2Manager::CreateD3D9DXVA(mKnowsCompositor, *failureReason); // Make sure we include the messages from both attempts (if applicable). mFailureReason.Append(secondFailureReason); }
--- a/dom/media/webaudio/AudioBuffer.cpp +++ b/dom/media/webaudio/AudioBuffer.cpp @@ -155,25 +155,32 @@ AudioBufferMemoryTracker::CollectReports return NS_OK; } AudioBuffer::AudioBuffer(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels, uint32_t aLength, float aSampleRate, - already_AddRefed<ThreadSharedFloatArrayBufferList> - aInitialContents) + ErrorResult& aRv) : mOwnerWindow(do_GetWeakReference(aWindow)), - mSharedChannels(aInitialContents), - mLength(aLength), mSampleRate(aSampleRate) { - MOZ_ASSERT(!mSharedChannels || - mSharedChannels->GetChannels() == aNumberOfChannels); + // Note that a buffer with zero channels is permitted here for the sake of + // AudioProcessingEvent, where channel counts must match parameters passed + // to createScriptProcessor(), one of which may be zero. + if (aSampleRate < WebAudioUtils::MinSampleRate || + aSampleRate > WebAudioUtils::MaxSampleRate || + aNumberOfChannels > WebAudioUtils::MaxChannelCount || + !aLength || aLength > INT32_MAX) { + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return; + } + + mSharedChannels.mDuration = aLength; mJSChannels.SetLength(aNumberOfChannels); mozilla::HoldJSObjects(this); AudioBufferMemoryTracker::RegisterAudioBuffer(this); } AudioBuffer::~AudioBuffer() { AudioBufferMemoryTracker::UnregisterAudioBuffer(this); @@ -199,146 +206,194 @@ AudioBuffer::Constructor(const GlobalObj } void AudioBuffer::ClearJSChannels() { mJSChannels.Clear(); } +void +AudioBuffer::SetSharedChannels( + already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) +{ + RefPtr<ThreadSharedFloatArrayBufferList> buffer = aBuffer; + uint32_t channelCount = buffer->GetChannels(); + mSharedChannels.mChannelData.SetLength(channelCount); + for (uint32_t i = 0; i < channelCount; ++i) { + mSharedChannels.mChannelData[i] = buffer->GetData(i); + } + mSharedChannels.mBuffer = buffer.forget(); + mSharedChannels.mVolume = 1.0f; + mSharedChannels.mBufferFormat = AUDIO_FORMAT_FLOAT32; +} + /* static */ already_AddRefed<AudioBuffer> AudioBuffer::Create(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels, uint32_t aLength, float aSampleRate, already_AddRefed<ThreadSharedFloatArrayBufferList> aInitialContents, ErrorResult& aRv) { - // Note that a buffer with zero channels is permitted here for the sake of - // AudioProcessingEvent, where channel counts must match parameters passed - // to createScriptProcessor(), one of which may be zero. - if (aSampleRate < WebAudioUtils::MinSampleRate || - aSampleRate > WebAudioUtils::MaxSampleRate || - aNumberOfChannels > WebAudioUtils::MaxChannelCount || - !aLength || aLength > INT32_MAX) { - aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + RefPtr<ThreadSharedFloatArrayBufferList> initialContents = aInitialContents; + RefPtr<AudioBuffer> buffer = + new AudioBuffer(aWindow, aNumberOfChannels, aLength, aSampleRate, aRv); + if (aRv.Failed()) { return nullptr; } + if (initialContents) { + MOZ_ASSERT(initialContents->GetChannels() == aNumberOfChannels); + buffer->SetSharedChannels(initialContents.forget()); + } + + return buffer.forget(); +} + +/* static */ already_AddRefed<AudioBuffer> +AudioBuffer::Create(nsPIDOMWindowInner* aWindow, float aSampleRate, + AudioChunk&& aInitialContents) +{ + AudioChunk initialContents = aInitialContents; + ErrorResult rv; RefPtr<AudioBuffer> buffer = - new AudioBuffer(aWindow, aNumberOfChannels, aLength, aSampleRate, - Move(aInitialContents)); + new AudioBuffer(aWindow, initialContents.ChannelCount(), + initialContents.mDuration, aSampleRate, rv); + if (rv.Failed()) { + return nullptr; + } + buffer->mSharedChannels = Move(aInitialContents); return buffer.forget(); } JSObject* AudioBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return AudioBufferBinding::Wrap(aCx, this, aGivenProto); } +static void +CopyChannelDataToFloat(const AudioChunk& aChunk, uint32_t aChannel, + uint32_t aSrcOffset, float* aOutput, uint32_t aLength) +{ + MOZ_ASSERT(aChunk.mVolume == 1.0f); + if (aChunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) { + mozilla::PodCopy(aOutput, + aChunk.ChannelData<float>()[aChannel] + aSrcOffset, + aLength); + } else { + MOZ_ASSERT(aChunk.mBufferFormat == AUDIO_FORMAT_S16); + ConvertAudioSamples(aChunk.ChannelData<int16_t>()[aChannel] + aSrcOffset, + aOutput, aLength); + } +} + bool AudioBuffer::RestoreJSChannelData(JSContext* aJSContext) { for (uint32_t i = 0; i < mJSChannels.Length(); ++i) { if (mJSChannels[i]) { // Already have data in JS array. continue; } // The following code first zeroes the array and then copies our data // into it. We could avoid this with additional JS APIs to construct // an array (or ArrayBuffer) containing initial data. JS::Rooted<JSObject*> array(aJSContext, - JS_NewFloat32Array(aJSContext, mLength)); + JS_NewFloat32Array(aJSContext, Length())); if (!array) { return false; } - if (mSharedChannels) { + if (!mSharedChannels.IsNull()) { // "4. Attach ArrayBuffers containing copies of the data to the // AudioBuffer, to be returned by the next call to getChannelData." - const float* data = mSharedChannels->GetData(i); JS::AutoCheckCannotGC nogc; bool isShared; - mozilla::PodCopy(JS_GetFloat32ArrayData(array, &isShared, nogc), data, mLength); + float* jsData = JS_GetFloat32ArrayData(array, &isShared, nogc); MOZ_ASSERT(!isShared); // Was created as unshared above + CopyChannelDataToFloat(mSharedChannels, i, 0, jsData, Length()); } mJSChannels[i] = array; } - mSharedChannels = nullptr; + mSharedChannels.mBuffer = nullptr; + mSharedChannels.mChannelData.Clear(); return true; } void AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber, uint32_t aStartInChannel, ErrorResult& aRv) { aDestination.ComputeLengthAndData(); uint32_t length = aDestination.Length(); CheckedInt<uint32_t> end = aStartInChannel; end += length; if (aChannelNumber >= NumberOfChannels() || - !end.isValid() || end.value() > mLength) { + !end.isValid() || end.value() > Length()) { aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return; } JS::AutoCheckCannotGC nogc; JSObject* channelArray = mJSChannels[aChannelNumber]; - const float* sourceData = nullptr; if (channelArray) { - if (JS_GetTypedArrayLength(channelArray) != mLength) { + if (JS_GetTypedArrayLength(channelArray) != Length()) { // The array's buffer was detached. aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return; } bool isShared = false; - sourceData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc); + const float* sourceData = + JS_GetFloat32ArrayData(channelArray, &isShared, nogc); // The sourceData arrays should all have originated in // RestoreJSChannelData, where they are created unshared. MOZ_ASSERT(!isShared); - } else if (mSharedChannels) { - sourceData = mSharedChannels->GetData(aChannelNumber); + PodMove(aDestination.Data(), sourceData + aStartInChannel, length); + return; } - if (sourceData) { - PodMove(aDestination.Data(), sourceData + aStartInChannel, length); - } else { - PodZero(aDestination.Data(), length); + if (!mSharedChannels.IsNull()) { + CopyChannelDataToFloat(mSharedChannels, aChannelNumber, aStartInChannel, + aDestination.Data(), length); + return; } + + PodZero(aDestination.Data(), length); } void AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource, uint32_t aChannelNumber, uint32_t aStartInChannel, ErrorResult& aRv) { aSource.ComputeLengthAndData(); uint32_t length = aSource.Length(); CheckedInt<uint32_t> end = aStartInChannel; end += length; if (aChannelNumber >= NumberOfChannels() || - !end.isValid() || end.value() > mLength) { + !end.isValid() || end.value() > Length()) { aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return; } if (!RestoreJSChannelData(aJSContext)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } JS::AutoCheckCannotGC nogc; JSObject* channelArray = mJSChannels[aChannelNumber]; - if (JS_GetTypedArrayLength(channelArray) != mLength) { + if (JS_GetTypedArrayLength(channelArray) != Length()) { // The array's buffer was detached. aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return; } bool isShared = false; float* channelData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc); // The channelData arrays should all have originated in @@ -368,17 +423,17 @@ AudioBuffer::GetChannelData(JSContext* a already_AddRefed<ThreadSharedFloatArrayBufferList> AudioBuffer::StealJSArrayDataIntoSharedChannels(JSContext* aJSContext) { // "1. If any of the AudioBuffer's ArrayBuffer have been detached, abort // these steps, and return a zero-length channel data buffers to the // invoker." for (uint32_t i = 0; i < mJSChannels.Length(); ++i) { JSObject* channelArray = mJSChannels[i]; - if (!channelArray || mLength != JS_GetTypedArrayLength(channelArray)) { + if (!channelArray || Length() != JS_GetTypedArrayLength(channelArray)) { // Either empty buffer or one of the arrays' buffers was detached. return nullptr; } } // "2. Detach all ArrayBuffers for arrays previously returned by // getChannelData on this AudioBuffer." // "3. Retain the underlying data buffers from those ArrayBuffers and return @@ -409,31 +464,35 @@ AudioBuffer::StealJSArrayDataIntoSharedC for (uint32_t i = 0; i < mJSChannels.Length(); ++i) { mJSChannels[i] = nullptr; } return result.forget(); } -ThreadSharedFloatArrayBufferList* +const AudioChunk& AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext) { - if (!mSharedChannels) { - mSharedChannels = StealJSArrayDataIntoSharedChannels(aJSContext); + if (mSharedChannels.IsNull()) { + // mDuration is set in constructor + RefPtr<ThreadSharedFloatArrayBufferList> buffer = + StealJSArrayDataIntoSharedChannels(aJSContext); + + if (buffer) { + SetSharedChannels(buffer.forget()); + } } return mSharedChannels; } size_t AudioBuffer::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { size_t amount = aMallocSizeOf(this); amount += mJSChannels.ShallowSizeOfExcludingThis(aMallocSizeOf); - if (mSharedChannels) { - amount += mSharedChannels->SizeOfIncludingThis(aMallocSizeOf); - } + amount += mSharedChannels.SizeOfExcludingThis(aMallocSizeOf, false); return amount; } } // namespace dom } // namespace mozilla
--- a/dom/media/webaudio/AudioBuffer.h +++ b/dom/media/webaudio/AudioBuffer.h @@ -2,16 +2,17 @@ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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/. */ #ifndef AudioBuffer_h_ #define AudioBuffer_h_ +#include "AudioSegment.h" #include "nsWrapperCache.h" #include "nsCycleCollectionParticipant.h" #include "mozilla/Attributes.h" #include "mozilla/StaticPtr.h" #include "mozilla/StaticMutex.h" #include "nsTArray.h" #include "js/TypeDecls.h" #include "mozilla/MemoryReporting.h" @@ -48,16 +49,21 @@ public: Create(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels, uint32_t aLength, float aSampleRate, ErrorResult& aRv) { return Create(aWindow, aNumberOfChannels, aLength, aSampleRate, nullptr, aRv); } + // Non-unit AudioChunk::mVolume is not supported + static already_AddRefed<AudioBuffer> + Create(nsPIDOMWindowInner* aWindow, float aSampleRate, + AudioChunk&& aInitialContents); + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AudioBuffer) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioBuffer) static already_AddRefed<AudioBuffer> Constructor(const GlobalObject& aGlobal, const AudioBufferOptions& aOptions, ErrorResult& aRv); @@ -72,22 +78,22 @@ public: float SampleRate() const { return mSampleRate; } uint32_t Length() const { - return mLength; + return mSharedChannels.mDuration; } double Duration() const { - return mLength / static_cast<double> (mSampleRate); + return Length() / static_cast<double> (mSampleRate); } uint32_t NumberOfChannels() const { return mJSChannels.Length(); } /** @@ -100,43 +106,44 @@ public: void CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber, uint32_t aStartInChannel, ErrorResult& aRv); void CopyToChannel(JSContext* aJSContext, const Float32Array& aSource, uint32_t aChannelNumber, uint32_t aStartInChannel, ErrorResult& aRv); /** - * Returns a ThreadSharedFloatArrayBufferList containing the sample data. - * Can return null if there is no data. + * Returns a reference to an AudioChunk containing the sample data. + * The AudioChunk can have a null buffer if there is no data. */ - ThreadSharedFloatArrayBufferList* GetThreadSharedChannelsForRate(JSContext* aContext); + const AudioChunk& GetThreadSharedChannelsForRate(JSContext* aContext); protected: AudioBuffer(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels, - uint32_t aLength, float aSampleRate, - already_AddRefed<ThreadSharedFloatArrayBufferList> - aInitialContents); + uint32_t aLength, float aSampleRate, ErrorResult& aRv); ~AudioBuffer(); + void + SetSharedChannels(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer); + bool RestoreJSChannelData(JSContext* aJSContext); already_AddRefed<ThreadSharedFloatArrayBufferList> StealJSArrayDataIntoSharedChannels(JSContext* aJSContext); void ClearJSChannels(); - nsWeakPtr mOwnerWindow; // Float32Arrays AutoTArray<JS::Heap<JSObject*>, 2> mJSChannels; + // mSharedChannels aggregates the data from mJSChannels. This is non-null + // if and only if the mJSChannels' buffers are detached, but its mDuration + // member keeps the buffer length regardless of whether the buffer is + // provided by mJSChannels or mSharedChannels. + AudioChunk mSharedChannels; - // mSharedChannels aggregates the data from mJSChannels. This is non-null - // if and only if the mJSChannels' buffers are detached. - RefPtr<ThreadSharedFloatArrayBufferList> mSharedChannels; - - uint32_t mLength; + nsWeakPtr mOwnerWindow; float mSampleRate; }; } // namespace dom } // namespace mozilla #endif
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp +++ b/dom/media/webaudio/AudioBufferSourceNode.cpp @@ -139,17 +139,17 @@ public: case AudioBufferSourceNode::LOOPEND: MOZ_ASSERT(aParam >= 0); mLoopEnd = aParam; break; default: NS_ERROR("Bad AudioBufferSourceNodeEngine Int32Parameter"); } } - void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) override + void SetBuffer(AudioChunk&& aBuffer) override { mBuffer = aBuffer; } bool BegunResampling() { return mBeginProcessing == -STREAM_TIME_MAX; } @@ -210,36 +210,40 @@ public: } } // Borrow a full buffer of size WEBAUDIO_BLOCK_SIZE from the source buffer // at offset aSourceOffset. This avoids copying memory. void BorrowFromInputBuffer(AudioBlock* aOutput, uint32_t aChannels) { - aOutput->SetBuffer(mBuffer); + aOutput->SetBuffer(mBuffer.mBuffer); aOutput->mChannelData.SetLength(aChannels); for (uint32_t i = 0; i < aChannels; ++i) { - aOutput->mChannelData[i] = mBuffer->GetData(i) + mBufferPosition; + aOutput->mChannelData[i] = + mBuffer.ChannelData<float>()[i] + mBufferPosition; } - aOutput->mVolume = 1.0f; + aOutput->mVolume = mBuffer.mVolume; aOutput->mBufferFormat = AUDIO_FORMAT_FLOAT32; } // Copy aNumberOfFrames frames from the source buffer at offset aSourceOffset // and put it at offset aBufferOffset in the destination buffer. - void CopyFromInputBuffer(AudioBlock* aOutput, - uint32_t aChannels, - uintptr_t aOffsetWithinBlock, - uint32_t aNumberOfFrames) { + template <typename T> void + CopyFromInputBuffer(AudioBlock* aOutput, + uint32_t aChannels, + uintptr_t aOffsetWithinBlock, + uint32_t aNumberOfFrames) + { + MOZ_ASSERT(mBuffer.mVolume == 1.0f); for (uint32_t i = 0; i < aChannels; ++i) { float* baseChannelData = aOutput->ChannelFloatsForWrite(i); - memcpy(baseChannelData + aOffsetWithinBlock, - mBuffer->GetData(i) + mBufferPosition, - aNumberOfFrames * sizeof(float)); + ConvertAudioSamples(mBuffer.ChannelData<T>()[i] + mBufferPosition, + baseChannelData + aOffsetWithinBlock, + aNumberOfFrames); } } // Resamples input data to an output buffer, according to |mBufferSampleRate| and // the playbackRate/detune. // The number of frames consumed/produced depends on the amount of space // remaining in both the input and output buffer, and the playback rate (that // is, the ratio between the output samplerate and the input samplerate). @@ -285,27 +289,38 @@ public: } speex_resampler_set_skip_frac_num(resampler, std::min<int64_t>(skipFracNum, UINT32_MAX)); mBeginProcessing = -STREAM_TIME_MAX; } inputLimit = std::min(inputLimit, availableInInputBuffer); + MOZ_ASSERT(mBuffer.mVolume == 1.0f); for (uint32_t i = 0; true; ) { uint32_t inSamples = inputLimit; - const float* inputData = mBuffer->GetData(i) + mBufferPosition; uint32_t outSamples = aAvailableInOutput; float* outputData = aOutput->ChannelFloatsForWrite(i) + *aOffsetWithinBlock; - WebAudioUtils::SpeexResamplerProcess(resampler, i, - inputData, &inSamples, - outputData, &outSamples); + if (mBuffer.mBufferFormat == AUDIO_FORMAT_FLOAT32) { + const float* inputData = + mBuffer.ChannelData<float>()[i] + mBufferPosition; + WebAudioUtils::SpeexResamplerProcess(resampler, i, + inputData, &inSamples, + outputData, &outSamples); + } else { + MOZ_ASSERT(mBuffer.mBufferFormat == AUDIO_FORMAT_S16); + const int16_t* inputData = + mBuffer.ChannelData<int16_t>()[i] + mBufferPosition; + WebAudioUtils::SpeexResamplerProcess(resampler, i, + inputData, &inSamples, + outputData, &outSamples); + } if (++i == aChannels) { mBufferPosition += inSamples; MOZ_ASSERT(mBufferPosition <= mBufferEnd || mLoop); *aOffsetWithinBlock += outSamples; *aCurrentPosition += outSamples; if (inSamples == availableInInputBuffer && !mLoop) { // We'll feed in enough zeros to empty out the resampler's memory. // This handles the output latency as well as capturing the low @@ -413,32 +428,42 @@ public: mBufferSampleRate / mResamplerOutRate; mBufferPosition += end - start; return; } uint32_t numFrames = std::min(aBufferMax - mBufferPosition, availableInOutput); - bool inputBufferAligned = true; - for (uint32_t i = 0; i < aChannels; ++i) { - if (!IS_ALIGNED16(mBuffer->GetData(i) + mBufferPosition)) { - inputBufferAligned = false; + bool shouldBorrow = false; + if (numFrames == WEBAUDIO_BLOCK_SIZE && + mBuffer.mBufferFormat == AUDIO_FORMAT_FLOAT32) { + shouldBorrow = true; + for (uint32_t i = 0; i < aChannels; ++i) { + if (!IS_ALIGNED16(mBuffer.ChannelData<float>()[i] + mBufferPosition)) { + shouldBorrow = false; + break; + } } } - - if (numFrames == WEBAUDIO_BLOCK_SIZE && inputBufferAligned) { - MOZ_ASSERT(mBufferPosition < aBufferMax); + MOZ_ASSERT(mBufferPosition < aBufferMax); + if (shouldBorrow) { BorrowFromInputBuffer(aOutput, aChannels); } else { if (*aOffsetWithinBlock == 0) { aOutput->AllocateChannels(aChannels); } - MOZ_ASSERT(mBufferPosition < aBufferMax); - CopyFromInputBuffer(aOutput, aChannels, *aOffsetWithinBlock, numFrames); + if (mBuffer.mBufferFormat == AUDIO_FORMAT_FLOAT32) { + CopyFromInputBuffer<float>(aOutput, aChannels, + *aOffsetWithinBlock, numFrames); + } else { + MOZ_ASSERT(mBuffer.mBufferFormat == AUDIO_FORMAT_S16); + CopyFromInputBuffer<int16_t>(aOutput, aChannels, + *aOffsetWithinBlock, numFrames); + } } *aOffsetWithinBlock += numFrames; *aCurrentPosition += numFrames; mBufferPosition += numFrames; } int32_t ComputeFinalOutSampleRate(float aPlaybackRate, float aDetune) { @@ -484,17 +509,17 @@ public: { if (mBufferSampleRate == 0) { // start() has not yet been called or no buffer has yet been set aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); return; } StreamTime streamPosition = mDestination->GraphTimeToStreamTime(aFrom); - uint32_t channels = mBuffer ? mBuffer->GetChannels() : 0; + uint32_t channels = mBuffer.ChannelCount(); UpdateSampleRateIfNeeded(channels, streamPosition); uint32_t written = 0; while (written < WEBAUDIO_BLOCK_SIZE) { if (mStop != STREAM_TIME_MAX && streamPosition >= mStop) { FillWithZeroes(aOutput, channels, &written, &streamPosition, STREAM_TIME_MAX); @@ -564,17 +589,17 @@ public: double mStart; // including the fractional position between ticks // Low pass filter effects from the resampler mean that samples before the // start time are influenced by resampling the buffer. mBeginProcessing // includes the extent of this filter. The special value of -STREAM_TIME_MAX // indicates that the resampler has begun processing. StreamTime mBeginProcessing; StreamTime mStop; - RefPtr<ThreadSharedFloatArrayBufferList> mBuffer; + AudioChunk mBuffer; SpeexResamplerState* mResampler; // mRemainingResamplerTail, like mBufferPosition, and // mBufferEnd, is measured in input buffer samples. uint32_t mRemainingResamplerTail; uint32_t mBufferEnd; uint32_t mLoopStart; uint32_t mLoopEnd; uint32_t mBufferPosition; @@ -725,26 +750,25 @@ void AudioBufferSourceNode::SendBufferParameterToStream(JSContext* aCx) { AudioNodeStream* ns = mStream; if (!ns) { return; } if (mBuffer) { - RefPtr<ThreadSharedFloatArrayBufferList> data = - mBuffer->GetThreadSharedChannelsForRate(aCx); - ns->SetBuffer(data.forget()); + AudioChunk data = mBuffer->GetThreadSharedChannelsForRate(aCx); + ns->SetBuffer(Move(data)); if (mStartCalled) { SendOffsetAndDurationParametersToStream(ns); } } else { ns->SetInt32Parameter(BUFFEREND, 0); - ns->SetBuffer(nullptr); + ns->SetBuffer(AudioChunk()); MarkInactive(); } } void AudioBufferSourceNode::SendOffsetAndDurationParametersToStream(AudioNodeStream* aStream) {
--- a/dom/media/webaudio/AudioNodeEngine.h +++ b/dom/media/webaudio/AudioNodeEngine.h @@ -43,16 +43,22 @@ public: /** * Create with buffers suitable for transfer to * JS_NewArrayBufferWithContents(). The buffer contents are uninitialized * and so should be set using GetDataForWrite(). */ static already_AddRefed<ThreadSharedFloatArrayBufferList> Create(uint32_t aChannelCount, size_t aLength, const mozilla::fallible_t&); + ThreadSharedFloatArrayBufferList* + AsThreadSharedFloatArrayBufferList() override + { + return this; + }; + struct Storage final { Storage() : mDataToFree(nullptr), mFree(nullptr), mSampleData(nullptr) {} ~Storage() { @@ -283,17 +289,17 @@ public: { NS_ERROR("Invalid RecvTimelineEvent index"); } virtual void SetThreeDPointParameter(uint32_t aIndex, const dom::ThreeDPoint& aValue) { NS_ERROR("Invalid SetThreeDPointParameter index"); } - virtual void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) + virtual void SetBuffer(AudioChunk&& aBuffer) { NS_ERROR("SetBuffer called on engine that doesn't support it"); } // This consumes the contents of aData. aData will be emptied after this returns. virtual void SetRawArrayData(nsTArray<float>& aData) { NS_ERROR("SetRawArrayData called on an engine that doesn't support it"); }
--- a/dom/media/webaudio/AudioNodeStream.cpp +++ b/dom/media/webaudio/AudioNodeStream.cpp @@ -249,34 +249,33 @@ AudioNodeStream::SetThreeDPointParameter ThreeDPoint mValue; uint32_t mIndex; }; GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aValue)); } void -AudioNodeStream::SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&& aBuffer) +AudioNodeStream::SetBuffer(AudioChunk&& aBuffer) { class Message final : public ControlMessage { public: - Message(AudioNodeStream* aStream, - already_AddRefed<ThreadSharedFloatArrayBufferList>& aBuffer) + Message(AudioNodeStream* aStream, AudioChunk&& aBuffer) : ControlMessage(aStream), mBuffer(aBuffer) {} void Run() override { static_cast<AudioNodeStream*>(mStream)->Engine()-> - SetBuffer(mBuffer.forget()); + SetBuffer(Move(mBuffer)); } - RefPtr<ThreadSharedFloatArrayBufferList> mBuffer; + AudioChunk mBuffer; }; - GraphImpl()->AppendMessage(MakeUnique<Message>(this, aBuffer)); + GraphImpl()->AppendMessage(MakeUnique<Message>(this, Move(aBuffer))); } void AudioNodeStream::SetRawArrayData(nsTArray<float>& aData) { class Message final : public ControlMessage { public:
--- a/dom/media/webaudio/AudioNodeStream.h +++ b/dom/media/webaudio/AudioNodeStream.h @@ -86,17 +86,17 @@ public: * Sets a parameter that's a time relative to some stream's played time. * This time is converted to a time relative to this stream when it's set. */ void SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext, double aStreamTime); void SetDoubleParameter(uint32_t aIndex, double aValue); void SetInt32Parameter(uint32_t aIndex, int32_t aValue); void SetThreeDPointParameter(uint32_t aIndex, const dom::ThreeDPoint& aValue); - void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&& aBuffer); + void SetBuffer(AudioChunk&& aBuffer); // This sends a single event to the timeline on the MSG thread side. void SendTimelineEvent(uint32_t aIndex, const dom::AudioTimelineEvent& aEvent); // This consumes the contents of aData. aData will be emptied after this returns. void SetRawArrayData(nsTArray<float>& aData); void SetChannelMixingParameters(uint32_t aNumberOfChannels, ChannelCountMode aChannelCountMoe, ChannelInterpretation aChannelInterpretation); void SetPassThrough(bool aPassThrough);
--- a/dom/media/webaudio/ConvolverNode.cpp +++ b/dom/media/webaudio/ConvolverNode.cpp @@ -25,39 +25,30 @@ NS_IMPL_ADDREF_INHERITED(ConvolverNode, NS_IMPL_RELEASE_INHERITED(ConvolverNode, AudioNode) class ConvolverNodeEngine final : public AudioNodeEngine { typedef PlayingRefChangeHandler PlayingRefChanged; public: ConvolverNodeEngine(AudioNode* aNode, bool aNormalize) : AudioNodeEngine(aNode) - , mBufferLength(0) , mLeftOverData(INT32_MIN) , mSampleRate(0.0f) , mUseBackgroundThreads(!aNode->Context()->IsOffline()) , mNormalize(aNormalize) { } enum Parameters { - BUFFER_LENGTH, SAMPLE_RATE, NORMALIZE }; void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override { switch (aIndex) { - case BUFFER_LENGTH: - // BUFFER_LENGTH is the first parameter that we set when setting a new buffer, - // so we should be careful to invalidate the rest of our state here. - mSampleRate = 0.0f; - mBufferLength = aParam; - mLeftOverData = INT32_MIN; - break; case NORMALIZE: mNormalize = !!aParam; break; default: NS_ERROR("Bad ConvolverNodeEngine Int32Parameter"); } } void SetDoubleParameter(uint32_t aIndex, double aParam) override @@ -67,36 +58,34 @@ public: mSampleRate = aParam; // The buffer is passed after the sample rate. // mReverb will be set using this sample rate when the buffer is received. break; default: NS_ERROR("Bad ConvolverNodeEngine DoubleParameter"); } } - void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) override + void SetBuffer(AudioChunk&& aBuffer) override { - RefPtr<ThreadSharedFloatArrayBufferList> buffer = aBuffer; - // Note about empirical tuning (this is copied from Blink) // The maximum FFT size affects reverb performance and accuracy. // If the reverb is single-threaded and processes entirely in the real-time audio thread, // it's important not to make this too high. In this case 8192 is a good value. // But, the Reverb object is multi-threaded, so we want this as high as possible without losing too much accuracy. // Very large FFTs will have worse phase errors. Given these constraints 32768 is a good compromise. const size_t MaxFFTSize = 32768; - if (!buffer || !mBufferLength || !mSampleRate) { + mLeftOverData = INT32_MIN; // reset + + if (aBuffer.IsNull() || !mSampleRate) { mReverb = nullptr; - mLeftOverData = INT32_MIN; return; } - mReverb = new WebCore::Reverb(buffer, mBufferLength, - MaxFFTSize, mUseBackgroundThreads, + mReverb = new WebCore::Reverb(aBuffer, MaxFFTSize, mUseBackgroundThreads, mNormalize, mSampleRate); } void ProcessBlock(AudioNodeStream* aStream, GraphTime aFrom, const AudioBlock& aInput, AudioBlock* aOutput, bool* aFinished) override @@ -137,17 +126,17 @@ public: } if (mLeftOverData <= 0) { RefPtr<PlayingRefChanged> refchanged = new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF); aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate( refchanged.forget()); } - mLeftOverData = mBufferLength; + mLeftOverData = mReverb->impulseResponseLength(); MOZ_ASSERT(mLeftOverData > 0); } aOutput->AllocateChannels(2); mReverb->process(&input, aOutput); } bool IsActive() const override @@ -168,17 +157,16 @@ public: size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); } private: nsAutoPtr<WebCore::Reverb> mReverb; - int32_t mBufferLength; int32_t mLeftOverData; float mSampleRate; bool mUseBackgroundThreads; bool mNormalize; }; ConvolverNode::ConvolverNode(AudioContext* aContext) : AudioNode(aContext, @@ -258,32 +246,55 @@ ConvolverNode::SetBuffer(JSContext* aCx, // Supported number of channels break; default: aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return; } } - mBuffer = aBuffer; - // Send the buffer to the stream AudioNodeStream* ns = mStream; MOZ_ASSERT(ns, "Why don't we have a stream here?"); - if (mBuffer) { - uint32_t length = mBuffer->Length(); - RefPtr<ThreadSharedFloatArrayBufferList> data = - mBuffer->GetThreadSharedChannelsForRate(aCx); - SendInt32ParameterToStream(ConvolverNodeEngine::BUFFER_LENGTH, length); + if (aBuffer) { + AudioChunk data = aBuffer->GetThreadSharedChannelsForRate(aCx); + if (data.mBufferFormat == AUDIO_FORMAT_S16) { + // Reverb expects data in float format. + // Convert on the main thread so as to minimize allocations on the audio + // thread. + // Reverb will dispose of the buffer once initialized, so convert here + // and leave the smaller arrays in the AudioBuffer. + // There is currently no value in providing 16/32-byte aligned data + // because PadAndMakeScaledDFT() will copy the data (without SIMD + // instructions) to aligned arrays for the FFT. + RefPtr<SharedBuffer> floatBuffer = + SharedBuffer::Create(sizeof(float) * + data.mDuration * data.ChannelCount()); + if (!floatBuffer) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + auto floatData = static_cast<float*>(floatBuffer->Data()); + for (size_t i = 0; i < data.ChannelCount(); ++i) { + ConvertAudioSamples(data.ChannelData<int16_t>()[i], + floatData, data.mDuration); + data.mChannelData[i] = floatData; + floatData += data.mDuration; + } + data.mBuffer = Move(floatBuffer); + data.mBufferFormat = AUDIO_FORMAT_FLOAT32; + } SendDoubleParameterToStream(ConvolverNodeEngine::SAMPLE_RATE, - mBuffer->SampleRate()); - ns->SetBuffer(data.forget()); + aBuffer->SampleRate()); + ns->SetBuffer(Move(data)); } else { - ns->SetBuffer(nullptr); + ns->SetBuffer(AudioChunk()); } + + mBuffer = aBuffer; } void ConvolverNode::SetNormalize(bool aNormalize) { mNormalize = aNormalize; SendInt32ParameterToStream(ConvolverNodeEngine::NORMALIZE, aNormalize); }
--- a/dom/media/webaudio/MediaBufferDecoder.cpp +++ b/dom/media/webaudio/MediaBufferDecoder.cpp @@ -333,86 +333,114 @@ MediaDecodeTask::FinishDecode() resampler = speex_resampler_init(channelCount, sampleRate, destSampleRate, SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr); speex_resampler_skip_zeros(resampler); resampledFrames += speex_resampler_get_output_latency(resampler); } - // Allocate the channel buffers. Note that if we end up resampling, we may - // write fewer bytes than mResampledFrames to the output buffer, in which - // case mWriteIndex will tell us how many valid samples we have. - mDecodeJob.mBuffer = ThreadSharedFloatArrayBufferList:: + // Allocate contiguous channel buffers. Note that if we end up resampling, + // we may write fewer bytes than mResampledFrames to the output buffer, in + // which case writeIndex will tell us how many valid samples we have. + mDecodeJob.mBuffer.mChannelData.SetLength(channelCount); +#if AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_FLOAT32 + // This buffer has separate channel arrays that could be transferred to + // JS_NewArrayBufferWithContents(), but AudioBuffer::RestoreJSChannelData() + // does not yet take advantage of this. + RefPtr<ThreadSharedFloatArrayBufferList> buffer = + ThreadSharedFloatArrayBufferList:: Create(channelCount, resampledFrames, fallible); - if (!mDecodeJob.mBuffer) { + if (!buffer) { ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); return; } + for (uint32_t i = 0; i < channelCount; ++i) { + mDecodeJob.mBuffer.mChannelData[i] = buffer->GetData(i); + } +#else + RefPtr<SharedBuffer> buffer = + SharedBuffer::Create(sizeof(AudioDataValue) * + resampledFrames * channelCount); + if (!buffer) { + ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); + return; + } + auto data = static_cast<AudioDataValue*>(floatBuffer->Data()); + for (uint32_t i = 0; i < channelCount; ++i) { + mDecodeJob.mBuffer.mChannelData[i] = data; + data += resampledFrames; + } +#endif + mDecodeJob.mBuffer.mBuffer = buffer.forget(); + mDecodeJob.mBuffer.mVolume = 1.0f; + mDecodeJob.mBuffer.mBufferFormat = AUDIO_OUTPUT_FORMAT; + uint32_t writeIndex = 0; RefPtr<AudioData> audioData; while ((audioData = mAudioQueue.PopFront())) { audioData->EnsureAudioBuffer(); // could lead to a copy :( - AudioDataValue* bufferData = static_cast<AudioDataValue*> + const AudioDataValue* bufferData = static_cast<AudioDataValue*> (audioData->mAudioBuffer->Data()); if (sampleRate != destSampleRate) { - const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex; + const uint32_t maxOutSamples = resampledFrames - writeIndex; for (uint32_t i = 0; i < audioData->mChannels; ++i) { uint32_t inSamples = audioData->mFrames; uint32_t outSamples = maxOutSamples; - float* outData = - mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex; + AudioDataValue* outData = mDecodeJob.mBuffer. + ChannelDataForWrite<AudioDataValue>(i) + writeIndex; WebAudioUtils::SpeexResamplerProcess( resampler, i, &bufferData[i * audioData->mFrames], &inSamples, outData, &outSamples); if (i == audioData->mChannels - 1) { - mDecodeJob.mWriteIndex += outSamples; - MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); + writeIndex += outSamples; + MOZ_ASSERT(writeIndex <= resampledFrames); MOZ_ASSERT(inSamples == audioData->mFrames); } } } else { for (uint32_t i = 0; i < audioData->mChannels; ++i) { - float* outData = - mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex; - ConvertAudioSamples(&bufferData[i * audioData->mFrames], - outData, audioData->mFrames); + AudioDataValue* outData = mDecodeJob.mBuffer. + ChannelDataForWrite<AudioDataValue>(i) + writeIndex; + PodCopy(outData, &bufferData[i * audioData->mFrames], + audioData->mFrames); if (i == audioData->mChannels - 1) { - mDecodeJob.mWriteIndex += audioData->mFrames; + writeIndex += audioData->mFrames; } } } } if (sampleRate != destSampleRate) { uint32_t inputLatency = speex_resampler_get_input_latency(resampler); - const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex; + const uint32_t maxOutSamples = resampledFrames - writeIndex; for (uint32_t i = 0; i < channelCount; ++i) { uint32_t inSamples = inputLatency; uint32_t outSamples = maxOutSamples; - float* outData = - mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex; + AudioDataValue* outData = + mDecodeJob.mBuffer.ChannelDataForWrite<AudioDataValue>(i) + writeIndex; WebAudioUtils::SpeexResamplerProcess( resampler, i, (AudioDataValue*)nullptr, &inSamples, outData, &outSamples); if (i == channelCount - 1) { - mDecodeJob.mWriteIndex += outSamples; - MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); + writeIndex += outSamples; + MOZ_ASSERT(writeIndex <= resampledFrames); MOZ_ASSERT(inSamples == inputLatency); } } } + mDecodeJob.mBuffer.mDuration = writeIndex; mPhase = PhaseEnum::AllocateBuffer; mMainThread->Dispatch(do_AddRef(this)); } void MediaDecodeTask::AllocateBuffer() { MOZ_ASSERT(NS_IsMainThread()); @@ -439,22 +467,19 @@ MediaDecodeTask::CallbackTheResult() bool WebAudioDecodeJob::AllocateBuffer() { MOZ_ASSERT(!mOutput); MOZ_ASSERT(NS_IsMainThread()); // Now create the AudioBuffer - ErrorResult rv; - uint32_t channelCount = mBuffer->GetChannels(); - mOutput = AudioBuffer::Create(mContext->GetOwner(), channelCount, - mWriteIndex, mContext->SampleRate(), - mBuffer.forget(), rv); - return !rv.Failed(); + mOutput = AudioBuffer::Create(mContext->GetOwner(), + mContext->SampleRate(), Move(mBuffer)); + return mOutput != nullptr; } void AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer, uint32_t aLength, WebAudioDecodeJob& aDecodeJob) { Maybe<MediaContainerType> containerType = MakeMediaContainerType(aContentType); // Do not attempt to decode the media if we were not successful at sniffing @@ -490,17 +515,16 @@ AsyncDecodeWebAudio(const char* aContent } WebAudioDecodeJob::WebAudioDecodeJob(const nsACString& aContentType, AudioContext* aContext, Promise* aPromise, DecodeSuccessCallback* aSuccessCallback, DecodeErrorCallback* aFailureCallback) : mContentType(aContentType) - , mWriteIndex(0) , mContext(aContext) , mPromise(aPromise) , mSuccessCallback(aSuccessCallback) , mFailureCallback(aFailureCallback) { MOZ_ASSERT(aContext); MOZ_ASSERT(NS_IsMainThread()); MOZ_COUNT_CTOR(WebAudioDecodeJob); @@ -590,19 +614,17 @@ WebAudioDecodeJob::SizeOfExcludingThis(M amount += mSuccessCallback->SizeOfIncludingThis(aMallocSizeOf); } if (mFailureCallback) { amount += mFailureCallback->SizeOfIncludingThis(aMallocSizeOf); } if (mOutput) { amount += mOutput->SizeOfIncludingThis(aMallocSizeOf); } - if (mBuffer) { - amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf); - } + amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf, false); return amount; } size_t WebAudioDecodeJob::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); }
--- a/dom/media/webaudio/MediaBufferDecoder.h +++ b/dom/media/webaudio/MediaBufferDecoder.h @@ -2,16 +2,17 @@ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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/. */ #ifndef MediaBufferDecoder_h_ #define MediaBufferDecoder_h_ +#include "AudioSegment.h" #include "nsWrapperCache.h" #include "nsCOMPtr.h" #include "nsString.h" #include "nsTArray.h" #include "mozilla/dom/TypedArray.h" #include "mozilla/MemoryReporting.h" namespace mozilla { @@ -50,24 +51,23 @@ struct WebAudioDecodeJob final void OnSuccess(ErrorCode /* ignored */); void OnFailure(ErrorCode aErrorCode); bool AllocateBuffer(); size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + AudioChunk mBuffer; nsCString mContentType; - uint32_t mWriteIndex; RefPtr<dom::AudioContext> mContext; RefPtr<dom::Promise> mPromise; RefPtr<dom::DecodeSuccessCallback> mSuccessCallback; RefPtr<dom::DecodeErrorCallback> mFailureCallback; // can be null RefPtr<dom::AudioBuffer> mOutput; - RefPtr<ThreadSharedFloatArrayBufferList> mBuffer; }; void AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer, uint32_t aLength, WebAudioDecodeJob& aDecodeJob); } // namespace mozilla #endif
--- a/dom/media/webaudio/OscillatorNode.cpp +++ b/dom/media/webaudio/OscillatorNode.cpp @@ -36,33 +36,31 @@ public: // Keep the default values in sync with OscillatorNode::OscillatorNode. , mFrequency(440.f) , mDetune(0.f) , mType(OscillatorType::Sine) , mPhase(0.) , mFinalFrequency(0.) , mPhaseIncrement(0.) , mRecomputeParameters(true) - , mCustomLength(0) , mCustomDisableNormalization(false) { MOZ_ASSERT(NS_IsMainThread()); mBasicWaveFormCache = aDestination->Context()->GetBasicWaveFormCache(); } void SetSourceStream(AudioNodeStream* aSource) { mSource = aSource; } enum Parameters { FREQUENCY, DETUNE, TYPE, - PERIODICWAVE_LENGTH, DISABLE_NORMALIZATION, START, STOP, }; void RecvTimelineEvent(uint32_t aIndex, AudioTimelineEvent& aEvent) override { mRecomputeParameters = true; @@ -100,19 +98,17 @@ public: void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override { switch (aIndex) { case TYPE: // Set the new type. mType = static_cast<OscillatorType>(aParam); if (mType == OscillatorType::Sine) { // Forget any previous custom data. - mCustomLength = 0; mCustomDisableNormalization = false; - mCustom = nullptr; mPeriodicWave = nullptr; mRecomputeParameters = true; } switch (mType) { case OscillatorType::Sine: mPhase = 0.0; break; case OscillatorType::Square: @@ -122,41 +118,37 @@ public: break; case OscillatorType::Custom: break; default: NS_ERROR("Bad OscillatorNodeEngine type parameter."); } // End type switch. break; - case PERIODICWAVE_LENGTH: - MOZ_ASSERT(aParam >= 0, "negative custom array length"); - mCustomLength = static_cast<uint32_t>(aParam); - break; case DISABLE_NORMALIZATION: MOZ_ASSERT(aParam >= 0, "negative custom array length"); mCustomDisableNormalization = static_cast<uint32_t>(aParam); break; default: NS_ERROR("Bad OscillatorNodeEngine Int32Parameter."); } // End index switch. } - void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) override + void SetBuffer(AudioChunk&& aBuffer) override { - MOZ_ASSERT(mCustomLength, "Custom buffer sent before length"); - mCustom = aBuffer; - MOZ_ASSERT(mCustom->GetChannels() == 2, + MOZ_ASSERT(aBuffer.ChannelCount() == 2, "PeriodicWave should have sent two channels"); - mPeriodicWave = WebCore::PeriodicWave::create(mSource->SampleRate(), - mCustom->GetData(0), - mCustom->GetData(1), - mCustomLength, - mCustomDisableNormalization); + MOZ_ASSERT(aBuffer.mVolume == 1.0f); + mPeriodicWave = + WebCore::PeriodicWave::create(mSource->SampleRate(), + aBuffer.ChannelData<float>()[0], + aBuffer.ChannelData<float>()[1], + aBuffer.mDuration, + mCustomDisableNormalization); } void IncrementPhase() { const float twoPiFloat = float(2 * M_PI); mPhase += mPhaseIncrement; if (mPhase > twoPiFloat) { mPhase -= twoPiFloat; @@ -368,20 +360,16 @@ public: size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); // Not owned: // - mSource // - mDestination // - mFrequency (internal ref owned by node) // - mDetune (internal ref owned by node) - if (mCustom) { - amount += mCustom->SizeOfIncludingThis(aMallocSizeOf); - } - if (mPeriodicWave) { amount += mPeriodicWave->sizeOfIncludingThis(aMallocSizeOf); } return amount; } size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override @@ -396,19 +384,17 @@ public: StreamTime mStop; AudioParamTimeline mFrequency; AudioParamTimeline mDetune; OscillatorType mType; float mPhase; float mFinalFrequency; float mPhaseIncrement; bool mRecomputeParameters; - RefPtr<ThreadSharedFloatArrayBufferList> mCustom; RefPtr<BasicWaveFormCache> mBasicWaveFormCache; - uint32_t mCustomLength; bool mCustomDisableNormalization; RefPtr<WebCore::PeriodicWave> mPeriodicWave; }; OscillatorNode::OscillatorNode(AudioContext* aContext) : AudioScheduledSourceNode(aContext, 2, ChannelCountMode::Max, @@ -509,23 +495,20 @@ OscillatorNode::SendTypeToStream() } void OscillatorNode::SendPeriodicWaveToStream() { NS_ASSERTION(mType == OscillatorType::Custom, "Sending custom waveform to engine thread with non-custom type"); MOZ_ASSERT(mStream, "Missing node stream."); MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object."); - SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE_LENGTH, - mPeriodicWave->DataLength()); SendInt32ParameterToStream(OscillatorNodeEngine::DISABLE_NORMALIZATION, mPeriodicWave->DisableNormalization()); - RefPtr<ThreadSharedFloatArrayBufferList> data = - mPeriodicWave->GetThreadSharedBuffer(); - mStream->SetBuffer(data.forget()); + AudioChunk data = mPeriodicWave->GetThreadSharedBuffer(); + mStream->SetBuffer(Move(data)); } void OscillatorNode::Start(double aWhen, ErrorResult& aRv) { if (!WebAudioUtils::IsTimeValid(aWhen)) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return;
--- a/dom/media/webaudio/PeriodicWave.cpp +++ b/dom/media/webaudio/PeriodicWave.cpp @@ -25,41 +25,47 @@ PeriodicWave::PeriodicWave(AudioContext* : mContext(aContext) , mDisableNormalization(aDisableNormalization) { MOZ_ASSERT(aContext); MOZ_ASSERT(aRealData || aImagData); // Caller should have checked this and thrown. MOZ_ASSERT(aLength > 0); - mLength = aLength; + mCoefficients.mDuration = aLength; - // Copy coefficient data. The two arrays share an allocation. - mCoefficients = new ThreadSharedFloatArrayBufferList(2); - float* buffer = static_cast<float*>(malloc(aLength*sizeof(float)*2)); - if (buffer == nullptr) { + // Copy coefficient data. + // The SharedBuffer and two arrays share a single allocation. + RefPtr<SharedBuffer> buffer = + SharedBuffer::Create(sizeof(float) * aLength * 2, fallible); + if (!buffer) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } + auto data = static_cast<float*>(buffer->Data()); + mCoefficients.mBuffer = Move(buffer); + if (aRealData) { - PodCopy(buffer, aRealData, aLength); + PodCopy(data, aRealData, aLength); } else { - PodZero(buffer, aLength); + PodZero(data, aLength); } - - mCoefficients->SetData(0, buffer, free, buffer); + mCoefficients.mChannelData.AppendElement(data); + data += aLength; if (aImagData) { - PodCopy(buffer+aLength, aImagData, aLength); + PodCopy(data, aImagData, aLength); } else { - PodZero(buffer+aLength, aLength); + PodZero(data, aLength); } + mCoefficients.mChannelData.AppendElement(data); - mCoefficients->SetData(1, nullptr, free, buffer+aLength); + mCoefficients.mVolume = 1.0f; + mCoefficients.mBufferFormat = AUDIO_FORMAT_FLOAT32; } /* static */ already_AddRefed<PeriodicWave> PeriodicWave::Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext, const PeriodicWaveOptions& aOptions, ErrorResult& aRv) { @@ -97,19 +103,17 @@ PeriodicWave::Constructor(const GlobalOb } size_t PeriodicWave::SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const { // Not owned: // - mContext size_t amount = 0; - if (!mCoefficients->IsShared()) { - amount += mCoefficients->SizeOfIncludingThis(aMallocSizeOf); - } + amount += mCoefficients.SizeOfExcludingThisIfUnshared(aMallocSizeOf); return amount; } size_t PeriodicWave::SizeOfIncludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this) + SizeOfExcludingThisIfNotShared(aMallocSizeOf);
--- a/dom/media/webaudio/PeriodicWave.h +++ b/dom/media/webaudio/PeriodicWave.h @@ -41,37 +41,36 @@ public: { return mContext; } JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; uint32_t DataLength() const { - return mLength; + return mCoefficients.mDuration; } bool DisableNormalization() const { return mDisableNormalization; } - ThreadSharedFloatArrayBufferList* GetThreadSharedBuffer() const + const AudioChunk& GetThreadSharedBuffer() const { return mCoefficients; } size_t SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const; size_t SizeOfIncludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const; private: ~PeriodicWave() = default; + AudioChunk mCoefficients; RefPtr<AudioContext> mContext; - RefPtr<ThreadSharedFloatArrayBufferList> mCoefficients; - uint32_t mLength; bool mDisableNormalization; }; } // namespace dom } // namespace mozilla #endif
--- a/dom/media/webaudio/ScriptProcessorNode.cpp +++ b/dom/media/webaudio/ScriptProcessorNode.cpp @@ -116,18 +116,17 @@ public: MutexAutoLock lock(mOutputQueue.Lock()); amount += mOutputQueue.SizeOfExcludingThis(aMallocSizeOf); } return amount; } // main thread - void FinishProducingOutputBuffer(ThreadSharedFloatArrayBufferList* aBuffer, - uint32_t aBufferSize) + void FinishProducingOutputBuffer(const AudioChunk& aBuffer) { MOZ_ASSERT(NS_IsMainThread()); TimeStamp now = TimeStamp::Now(); if (mLastEventTime.IsNull()) { mLastEventTime = now; } else { @@ -137,47 +136,38 @@ public: // latency is also reset to 0. // It could happen that the output queue becomes empty before the input // node has fully caught up. In this case there will be events where // |(now - mLastEventTime)| is very short, making mLatency negative. // As this happens and the size of |mLatency| becomes greater than // MAX_LATENCY_S, frame dropping starts again to maintain an as short // output queue as possible. float latency = (now - mLastEventTime).ToSeconds(); - float bufferDuration = aBufferSize / mSampleRate; + float bufferDuration = aBuffer.mDuration / mSampleRate; mLatency += latency - bufferDuration; mLastEventTime = now; if (fabs(mLatency) > MAX_LATENCY_S) { mDroppingBuffers = true; } } MutexAutoLock lock(mOutputQueue.Lock()); if (mDroppingBuffers) { if (mOutputQueue.ReadyToConsume()) { return; } mDroppingBuffers = false; mLatency = 0; } - for (uint32_t offset = 0; offset < aBufferSize; offset += WEBAUDIO_BLOCK_SIZE) { + for (uint32_t offset = 0; offset < aBuffer.mDuration; + offset += WEBAUDIO_BLOCK_SIZE) { AudioChunk& chunk = mOutputQueue.Produce(); - if (aBuffer) { - chunk.mDuration = WEBAUDIO_BLOCK_SIZE; - chunk.mBuffer = aBuffer; - chunk.mChannelData.SetLength(aBuffer->GetChannels()); - for (uint32_t i = 0; i < aBuffer->GetChannels(); ++i) { - chunk.mChannelData[i] = aBuffer->GetData(i) + offset; - } - chunk.mVolume = 1.0f; - chunk.mBufferFormat = AUDIO_FORMAT_FLOAT32; - } else { - chunk.SetNull(WEBAUDIO_BLOCK_SIZE); - } + chunk = aBuffer; + chunk.SliceTo(offset, offset + WEBAUDIO_BLOCK_SIZE); } } // graph thread AudioChunk GetOutputBuffer() { MOZ_ASSERT(!NS_IsMainThread()); AudioChunk buffer; @@ -379,67 +369,67 @@ private: , mStream(aStream) , mInputBuffer(aInputBuffer) , mPlaybackTime(aPlaybackTime) { } NS_IMETHOD Run() override { - RefPtr<ThreadSharedFloatArrayBufferList> output; auto engine = static_cast<ScriptProcessorNodeEngine*>(mStream->Engine()); + AudioChunk output; + output.SetNull(engine->mBufferSize); { auto node = static_cast<ScriptProcessorNode*> (engine->NodeMainThread()); if (!node) { return NS_OK; } if (node->HasListenersFor(nsGkAtoms::onaudioprocess)) { - output = DispatchAudioProcessEvent(node); + DispatchAudioProcessEvent(node, &output); } // The node may have been destroyed during event dispatch. } // Append it to our output buffer queue - engine->GetSharedBuffers()-> - FinishProducingOutputBuffer(output, engine->mBufferSize); + engine->GetSharedBuffers()->FinishProducingOutputBuffer(output); return NS_OK; } - // Returns the output buffers if set in event handlers. - ThreadSharedFloatArrayBufferList* - DispatchAudioProcessEvent(ScriptProcessorNode* aNode) + // Sets up |output| iff buffers are set in event handlers. + void DispatchAudioProcessEvent(ScriptProcessorNode* aNode, + AudioChunk* aOutput) { AudioContext* context = aNode->Context(); if (!context) { - return nullptr; + return; } AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(aNode->GetOwner()))) { - return nullptr; + return; } JSContext* cx = jsapi.cx(); uint32_t inputChannelCount = aNode->ChannelCount(); // Create the input buffer RefPtr<AudioBuffer> inputBuffer; if (mInputBuffer) { ErrorResult rv; inputBuffer = AudioBuffer::Create(context->GetOwner(), inputChannelCount, aNode->BufferSize(), context->SampleRate(), mInputBuffer.forget(), rv); if (rv.Failed()) { rv.SuppressException(); - return nullptr; + return; } } // Ask content to produce data in the output buffer // Note that we always avoid creating the output buffer here, and we try to // avoid creating the input buffer as well. The AudioProcessingEvent class // knows how to lazily create them if needed once the script tries to access // them. Otherwise, we may be able to get away without creating them! @@ -453,20 +443,21 @@ private: // FinishProducingOutputBuffer() will optimize output = null. // GetThreadSharedChannelsForRate() may also return null after OOM. if (event->HasOutputBuffer()) { ErrorResult rv; AudioBuffer* buffer = event->GetOutputBuffer(rv); // HasOutputBuffer() returning true means that GetOutputBuffer() // will not fail. MOZ_ASSERT(!rv.Failed()); - return buffer->GetThreadSharedChannelsForRate(cx); + *aOutput = buffer->GetThreadSharedChannelsForRate(cx); + MOZ_ASSERT(aOutput->IsNull() || + aOutput->mBufferFormat == AUDIO_FORMAT_FLOAT32, + "AudioBuffers initialized from JS have float data"); } - - return nullptr; } private: RefPtr<AudioNodeStream> mStream; RefPtr<ThreadSharedFloatArrayBufferList> mInputBuffer; double mPlaybackTime; }; RefPtr<Command> command = new Command(aStream, mInputBuffer.forget(),
--- a/dom/media/webaudio/blink/Reverb.cpp +++ b/dom/media/webaudio/blink/Reverb.cpp @@ -39,25 +39,25 @@ namespace WebCore { // Empirical gain calibration tested across many impulse responses to ensure perceived volume is same as dry (unprocessed) signal const float GainCalibration = 0.00125f; const float GainCalibrationSampleRate = 44100; // A minimum power value to when normalizing a silent (or very quiet) impulse response const float MinPower = 0.000125f; -static float calculateNormalizationScale(ThreadSharedFloatArrayBufferList* response, size_t aLength, float sampleRate) +static float calculateNormalizationScale(const nsTArray<const float*>& response, size_t aLength, float sampleRate) { // Normalize by RMS power - size_t numberOfChannels = response->GetChannels(); + size_t numberOfChannels = response.Length(); float power = 0; for (size_t i = 0; i < numberOfChannels; ++i) { - float channelPower = AudioBufferSumOfSquares(static_cast<const float*>(response->GetData(i)), aLength); + float channelPower = AudioBufferSumOfSquares(response[i], aLength); power += channelPower; } power = sqrt(power / (numberOfChannels * aLength)); // Protect against accidental overload if (!IsFinite(power) || IsNaN(power) || power < MinPower) power = MinPower; @@ -66,43 +66,41 @@ static float calculateNormalizationScale scale *= GainCalibration; // calibrate to make perceived volume same as unprocessed // Scale depends on sample-rate. if (sampleRate) scale *= GainCalibrationSampleRate / sampleRate; // True-stereo compensation - if (response->GetChannels() == 4) + if (numberOfChannels == 4) scale *= 0.5f; return scale; } -Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t maxFFTSize, bool useBackgroundThreads, bool normalize, float sampleRate) +Reverb::Reverb(const AudioChunk& impulseResponse, size_t maxFFTSize, bool useBackgroundThreads, bool normalize, float sampleRate) { - float scale = 1; + size_t impulseResponseBufferLength = impulseResponse.mDuration; + float scale = impulseResponse.mVolume; - AutoTArray<const float*,4> irChannels; - for (size_t i = 0; i < impulseResponse->GetChannels(); ++i) { - irChannels.AppendElement(impulseResponse->GetData(i)); - } + AutoTArray<const float*,4> irChannels(impulseResponse.ChannelData<float>()); AutoTArray<float,1024> tempBuf; if (normalize) { - scale = calculateNormalizationScale(impulseResponse, impulseResponseBufferLength, sampleRate); + scale = calculateNormalizationScale(irChannels, impulseResponseBufferLength, sampleRate); + } - if (scale) { - tempBuf.SetLength(irChannels.Length()*impulseResponseBufferLength); - for (uint32_t i = 0; i < irChannels.Length(); ++i) { - float* buf = &tempBuf[i*impulseResponseBufferLength]; - AudioBufferCopyWithScale(irChannels[i], scale, buf, - impulseResponseBufferLength); - irChannels[i] = buf; - } + if (scale != 1.0f) { + tempBuf.SetLength(irChannels.Length()*impulseResponseBufferLength); + for (uint32_t i = 0; i < irChannels.Length(); ++i) { + float* buf = &tempBuf[i*impulseResponseBufferLength]; + AudioBufferCopyWithScale(irChannels[i], scale, buf, + impulseResponseBufferLength); + irChannels[i] = buf; } } initialize(irChannels, impulseResponseBufferLength, maxFFTSize, useBackgroundThreads); } size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
--- a/dom/media/webaudio/blink/Reverb.h +++ b/dom/media/webaudio/blink/Reverb.h @@ -30,31 +30,26 @@ #define Reverb_h #include "ReverbConvolver.h" #include "nsAutoPtr.h" #include "nsTArray.h" #include "AudioBlock.h" #include "mozilla/MemoryReporting.h" -namespace mozilla { -class ThreadSharedFloatArrayBufferList; -} // namespace mozilla - namespace WebCore { // Multi-channel convolution reverb with channel matrixing - one or more ReverbConvolver objects are used internally. class Reverb { public: enum { MaxFrameSize = 256 }; // renderSliceSize is a rendering hint, so the FFTs can be optimized to not all occur at the same time (very bad when rendering on a real-time thread). - Reverb(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer, - size_t impulseResponseBufferLength, size_t maxFFTSize, + Reverb(const mozilla::AudioChunk& impulseResponseBuffer, size_t maxFFTSize, bool useBackgroundThreads, bool normalize, float sampleRate); void process(const mozilla::AudioBlock* sourceBus, mozilla::AudioBlock* destinationBus); size_t impulseResponseLength() const { return m_impulseResponseLength; } size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
--- a/dom/tests/mochitest/pointerlock/file_targetOutOfFocus.html +++ b/dom/tests/mochitest/pointerlock/file_targetOutOfFocus.html @@ -38,17 +38,17 @@ function runTests () { ok(divPointerLock, "Pointer should be locked even if " + "the element being locked is not focused"); } input.addEventListener("focus", function() { div.requestPointerLock(); - }); + }, { once: true }); document.addEventListener("pointerlockchange", function (e) { if (document.pointerLockElement === div) { divPointerLock = true; addFullscreenChangeContinuation("exit", function() { runTests(); SimpleTest.finish(); });
--- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -662,16 +662,17 @@ private: HardwareVideoDecodingForceEnabled, bool, false); #ifdef XP_WIN DECL_GFX_PREF(Live, "media.windows-media-foundation.allow-d3d11-dxva", PDMWMFAllowD3D11, bool, true); DECL_GFX_PREF(Live, "media.windows-media-foundation.max-dxva-videos", PDMWMFMaxDXVAVideos, uint32_t, 8); DECL_GFX_PREF(Live, "media.windows-media-foundation.use-nv12-format", PDMWMFUseNV12Format, bool, true); DECL_GFX_PREF(Once, "media.windows-media-foundation.use-sync-texture", PDMWMFUseSyncTexture, bool, true); DECL_GFX_PREF(Live, "media.wmf.low-latency.enabled", PDMWMFLowLatencyEnabled, bool, false); DECL_GFX_PREF(Live, "media.wmf.skip-blacklist", PDMWMFSkipBlacklist, bool, false); + DECL_GFX_PREF(Live, "media.wmf.deblacklisting-for-telemetry-in-gpu-process", PDMWMFDeblacklistingForTelemetryInGPUProcess, bool, false); #endif // These affect how line scrolls from wheel events will be accelerated. DECL_GFX_PREF(Live, "mousewheel.acceleration.factor", MouseWheelAccelerationFactor, int32_t, -1); DECL_GFX_PREF(Live, "mousewheel.acceleration.start", MouseWheelAccelerationStart, int32_t, -1); // This affects whether events will be routed through APZ or not. DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.enabled",
--- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -3548,18 +3548,21 @@ nsDisplayBackgroundImage::CanBuildWebRen bool nsDisplayBackgroundImage::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc, nsTArray<WebRenderParentCommand>& aParentCommands, WebRenderLayerManager* aManager, nsDisplayListBuilder* aDisplayListBuilder) { - if (!CanBuildWebRenderDisplayItems(aManager)) { - return false; + if (aManager->IsLayersFreeTransaction()) { + ContainerLayerParameters parameter; + if (GetLayerState(aDisplayListBuilder, aManager, parameter) != LAYER_ACTIVE) { + return false; + } } if (aDisplayListBuilder) { mImageFlags = aDisplayListBuilder->GetBackgroundPaintFlags(); } CheckForBorderItem(this, mImageFlags); nsCSSRendering::PaintBGParams params = nsCSSRendering::PaintBGParams::ForSingleLayer(*StyleFrame()->PresContext(),
--- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -2767,19 +2767,17 @@ Gecko_DocumentRule_UseForPresentation(Ra return css::DocumentRule::UseForPresentation(doc, docURI, docURISpec, *aPattern, aURLMatchingFunction); } void Gecko_SetJemallocThreadLocalArena(bool enabled) { #if defined(MOZ_MEMORY) - // At this point we convert |enabled| from a plain C++ bool to a - // |jemalloc_bool|, so be on the safe side. - jemalloc_thread_local_arena(!!enabled); + jemalloc_thread_local_arena(enabled); #endif } #include "nsStyleStructList.h" #undef STYLE_STRUCT #ifndef MOZ_STYLO
deleted file mode 100644 --- a/memory/build/jemalloc_config.cpp +++ /dev/null @@ -1,10 +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 <mozilla/Assertions.h> - -/* Provide an abort function for use in jemalloc code */ -extern "C" void moz_abort() { - MOZ_CRASH(); -}
--- a/memory/build/malloc_decls.h +++ b/memory/build/malloc_decls.h @@ -56,16 +56,16 @@ MALLOC_DECL(memalign, void *, size_t, si MALLOC_DECL(valloc, void *, size_t) MALLOC_DECL(malloc_usable_size, size_t, usable_ptr_t) MALLOC_DECL(malloc_good_size, size_t, size_t) # endif # if MALLOC_FUNCS & MALLOC_FUNCS_JEMALLOC MALLOC_DECL_VOID(jemalloc_stats, jemalloc_stats_t *) MALLOC_DECL_VOID(jemalloc_purge_freed_pages) MALLOC_DECL_VOID(jemalloc_free_dirty_pages) -MALLOC_DECL_VOID(jemalloc_thread_local_arena, jemalloc_bool) +MALLOC_DECL_VOID(jemalloc_thread_local_arena, bool) # endif # undef MALLOC_DECL_VOID #endif /* MALLOC_DECL */ #undef MALLOC_DECL #undef MALLOC_FUNCS
--- a/memory/build/moz.build +++ b/memory/build/moz.build @@ -15,17 +15,16 @@ DEFINES['MOZ_MEMORY_IMPL'] = True if CONFIG['MOZ_REPLACE_MALLOC']: EXPORTS += [ 'malloc_decls.h', 'replace_malloc.h', 'replace_malloc_bridge.h', ] SOURCES += [ - 'jemalloc_config.cpp', 'mozmemory_wrap.c', ] if CONFIG['MOZ_REPLACE_MALLOC']: SOURCES += [ 'replace_malloc.c', ]
--- a/memory/build/mozmemory.h +++ b/memory/build/mozmemory.h @@ -80,11 +80,11 @@ MOZ_JEMALLOC_API void jemalloc_purge_fre /* * Free all unused dirty pages in all arenas. Calling this function will slow * down subsequent allocations so it is recommended to use it only when * memory needs to be reclaimed at all costs (see bug 805855). This function * provides functionality similar to mallctl("arenas.purge") in jemalloc 3. */ MOZ_JEMALLOC_API void jemalloc_free_dirty_pages(); -MOZ_JEMALLOC_API void jemalloc_thread_local_arena(jemalloc_bool enabled); +MOZ_JEMALLOC_API void jemalloc_thread_local_arena(bool enabled); #endif /* mozmemory_h */
--- a/memory/mozjemalloc/mozjemalloc.cpp +++ b/memory/mozjemalloc/mozjemalloc.cpp @@ -239,18 +239,16 @@ typedef long ssize_t; #include <malloc/malloc.h> #endif #endif #include "mozjemalloc_types.h" #include "linkedlist.h" -extern "C" void moz_abort(); - /* Some tools, such as /dev/dsp wrappers, LD_PRELOAD libraries that * happen to override mmap() and call dlsym() from their overridden * mmap(). The problem is that dlsym() calls malloc(), and this ends * up in a dead lock in jemalloc. * On these systems, we prefer to directly use the system call. * We do that for Linux systems and kfreebsd with GNU userland. * Note sanity checks are not done (alignment of offset, ...) because * the uses of mmap are pretty limited, in jemalloc. @@ -1267,25 +1265,25 @@ pages_decommit(void *addr, size_t size) * to VirtualAlloc and recycled, so decommitting the entire region in one * go may not be valid. However, since we allocate at least a chunk at a * time, we may touch any region in chunksized increments. */ size_t pages_size = std::min(size, chunksize - CHUNK_ADDR2OFFSET((uintptr_t)addr)); while (size > 0) { if (!VirtualFree(addr, pages_size, MEM_DECOMMIT)) - moz_abort(); + MOZ_CRASH(); addr = (void *)((uintptr_t)addr + pages_size); size -= pages_size; pages_size = std::min(size, chunksize); } #else if (mmap(addr, size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) == MAP_FAILED) - moz_abort(); + MOZ_CRASH(); MozTagAnonymousMemory(addr, size, "jemalloc-decommitted"); #endif } static inline void pages_commit(void *addr, size_t size) { @@ -1295,25 +1293,25 @@ pages_commit(void *addr, size_t size) * to VirtualAlloc and recycled, so committing the entire region in one * go may not be valid. However, since we allocate at least a chunk at a * time, we may touch any region in chunksized increments. */ size_t pages_size = std::min(size, chunksize - CHUNK_ADDR2OFFSET((uintptr_t)addr)); while (size > 0) { if (!VirtualAlloc(addr, pages_size, MEM_COMMIT, PAGE_READWRITE)) - moz_abort(); + MOZ_CRASH(); addr = (void *)((uintptr_t)addr + pages_size); size -= pages_size; pages_size = std::min(size, chunksize); } # else if (mmap(addr, size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) == MAP_FAILED) - moz_abort(); + MOZ_CRASH(); MozTagAnonymousMemory(addr, size, "jemalloc"); # endif } static bool base_pages_alloc(size_t minsize) { size_t csize; @@ -4274,17 +4272,17 @@ malloc_init_hard(void) #endif /* We assume that the page size is a power of 2. */ MOZ_ASSERT(((result - 1) & result) == 0); #ifdef MALLOC_STATIC_SIZES if (pagesize % (size_t) result) { _malloc_message(_getprogname(), "Compile-time page size does not divide the runtime one.\n"); - moz_abort(); + MOZ_CRASH(); } #else pagesize = (size_t) result; pagesize_mask = (size_t) result - 1; pagesize_2pow = ffs((int)result) - 1; #endif /* Get runtime configuration. */ @@ -5097,17 +5095,17 @@ void #if defined(MOZ_MEMORY_DARWIN) __attribute__((constructor)) void jemalloc_darwin_init(void) { if (malloc_init_hard()) - moz_abort(); + MOZ_CRASH(); } #endif /* * is_malloc(malloc_impl) is some macro magic to detect if malloc_impl is * defined as "malloc" in mozmemory_wrap.h */
--- a/memory/mozjemalloc/mozjemalloc_types.h +++ b/memory/mozjemalloc/mozjemalloc_types.h @@ -33,34 +33,33 @@ #define _JEMALLOC_TYPES_H_ /* grab size_t */ #ifdef _MSC_VER #include <crtdefs.h> #else #include <stddef.h> #endif +#include <stdbool.h> #ifdef __cplusplus extern "C" { #endif -typedef unsigned char jemalloc_bool; - /* * jemalloc_stats() is not a stable interface. When using jemalloc_stats_t, be * sure that the compiled results of jemalloc.c are in sync with this header * file. */ typedef struct { /* * Run-time configuration settings. */ - jemalloc_bool opt_junk; /* Fill allocated memory with kAllocJunk? */ - jemalloc_bool opt_zero; /* Fill allocated memory with 0x0? */ + bool opt_junk; /* Fill allocated memory with kAllocJunk? */ + bool opt_zero; /* Fill allocated memory with 0x0? */ size_t narenas; /* Number of arenas. */ size_t quantum; /* Allocation quantum. */ size_t small_max; /* Max quantum-spaced allocation size. */ size_t large_max; /* Max sub-chunksize allocation size. */ size_t chunksize; /* Size of each virtual memory mapping. */ size_t dirty_max; /* Max dirty pages per arena. */ /*
--- a/memory/replace/replace/ReplaceMalloc.cpp +++ b/memory/replace/replace/ReplaceMalloc.cpp @@ -248,16 +248,16 @@ replace_jemalloc_free_dirty_pages(void) gFuncs->jemalloc_free_dirty_pages(); const malloc_hook_table_t* hook_table = gHookTable; if (hook_table && hook_table->jemalloc_free_dirty_pages_hook) { hook_table->jemalloc_free_dirty_pages_hook(); } } void -replace_jemalloc_thread_local_arena(jemalloc_bool aEnabled) +replace_jemalloc_thread_local_arena(bool aEnabled) { gFuncs->jemalloc_thread_local_arena(aEnabled); const malloc_hook_table_t* hook_table = gHookTable; if (hook_table && hook_table->jemalloc_thread_local_arena_hook) { hook_table->jemalloc_thread_local_arena_hook(aEnabled); } }
--- a/mfbt/Attributes.h +++ b/mfbt/Attributes.h @@ -61,16 +61,17 @@ # define MOZ_HAVE_NEVER_INLINE __attribute__((noinline)) # endif # if __has_attribute(noreturn) # define MOZ_HAVE_NORETURN __attribute__((noreturn)) # endif #elif defined(__GNUC__) # define MOZ_HAVE_NEVER_INLINE __attribute__((noinline)) # define MOZ_HAVE_NORETURN __attribute__((noreturn)) +# define MOZ_HAVE_NORETURN_PTR __attribute__((noreturn)) #endif /* * When built with clang analyzer (a.k.a scan-build), define MOZ_HAVE_NORETURN * to mark some false positives */ #ifdef __clang_analyzer__ # if __has_extension(attribute_analyzer_noreturn) @@ -97,23 +98,31 @@ * * MOZ_NORETURN void abort(const char* msg); * * This modifier permits the compiler to optimize code assuming a call to such a * function will never return. It also enables the compiler to avoid spurious * warnings about not initializing variables, or about any other seemingly-dodgy * operations performed after the function returns. * + * There are two variants. The GCC version of NORETURN may be applied to a + * function pointer, while for MSVC it may not. + * * This modifier does not affect the corresponding function's linking behavior. */ #if defined(MOZ_HAVE_NORETURN) # define MOZ_NORETURN MOZ_HAVE_NORETURN #else # define MOZ_NORETURN /* no support */ #endif +#if defined(MOZ_HAVE_NORETURN_PTR) +# define MOZ_NORETURN_PTR MOZ_HAVE_NORETURN_PTR +#else +# define MOZ_NORETURN_PTR /* no support */ +#endif /** * MOZ_COLD tells the compiler that a function is "cold", meaning infrequently * executed. This may lead it to optimize for size more aggressively than speed, * or to allocate the body of the function in a distant part of the text segment * to help keep it from taking up unnecessary icache when it isn't in use. * * Place this attribute at the very beginning of a function definition. For
--- a/mobile/android/app/src/main/res/layout-large/tabs_layout_item_view.xml +++ b/mobile/android/app/src/main/res/layout-large/tabs_layout_item_view.xml @@ -25,17 +25,19 @@ android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1.0" style="@style/TabLayoutItemTextAppearance" android:textSize="14sp" android:textColor="@color/tab_item_title" android:singleLine="true" android:duplicateParentState="true" + android:layout_gravity="center_vertical" gecko:fadeWidth="15dp" + android:minHeight="24dp" android:paddingRight="5dp" android:paddingEnd="5dp" android:paddingLeft="0dp" android:paddingStart="0dp" android:drawablePadding="6dp"/> <!-- Use of baselineAlignBottom only supported from API 11+ - if this needs to work on lower API versions we'll need to override getBaseLine() and return image height, but we assume this won't happen -->
--- a/mobile/android/app/src/main/res/layout/activity_stream_topsites_page.xml +++ b/mobile/android/app/src/main/res/layout/activity_stream_topsites_page.xml @@ -1,6 +1,16 @@ <?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/. --> + +<!-- The paddingEnd visible to the user comes from the padding between items. As such, paddingEnd here should be 0dp. + We need to specify it explicitly to ensure paddingLeft (for devices that don't support paddingStart) doesn't take + effect in addition to paddingStart in RTL layouts. --> <org.mozilla.gecko.activitystream.homepanel.topsites.TopSitesPage xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingLeft="10dp"/> + android:paddingStart="10dp" + android:paddingLeft="10dp" + android:paddingEnd="0dp" + android:paddingRight="0dp"/>
new file mode 100644 --- /dev/null +++ b/mobile/android/app/src/main/res/menu-large-v26/browser_app_menu.xml @@ -0,0 +1,120 @@ +<?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/. --> + +<!-- We disable AlwaysShowAction because we interpret the menu + attributes ourselves and thus the warning isn't relevant to us. --> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="AlwaysShowAction"> + + <item android:id="@+id/reload" + android:icon="@drawable/ic_menu_reload" + android:title="@string/reload" + android:showAsAction="always"/> + + <item android:id="@+id/back" + android:icon="@drawable/ic_menu_back" + android:title="@string/back" + android:visible="false"/> + + <item android:id="@+id/forward" + android:icon="@drawable/ic_menu_forward" + android:title="@string/forward" + android:visible="false"/> + + <item android:id="@+id/bookmark" + android:icon="@drawable/ic_menu_bookmark_add" + android:title="@string/bookmark" + android:showAsAction="ifRoom"/> + + <item android:id="@+id/share" + android:icon="@drawable/ic_menu_share" + android:title="@string/share" + android:showAsAction="ifRoom"/> + + <item android:id="@+id/new_tab" + android:title="@string/new_tab"/> + + <item android:id="@+id/new_private_tab" + android:title="@string/new_private_tab"/> + + <item android:id="@+id/bookmarks_list" + android:title="@string/bookmarks_title"/> + + <item android:id="@+id/history_list" + android:title="@string/history_title"/> + + <item android:id="@+id/find_in_page" + android:title="@string/find_in_page" /> + + <item android:id="@+id/desktop_mode" + android:title="@string/desktop_mode" + android:checkable="true" /> + + <item android:id="@+id/addons_top_level" + android:title="@string/addons" + android:visible="false" /> + + <item android:id="@+id/page" + android:title="@string/page"> + + <menu> + + <item android:id="@+id/subscribe" + android:title="@string/contextmenu_subscribe"/> + + <item android:id="@+id/save_as_pdf" + android:title="@string/save_as_pdf"/> + + <item android:id="@+id/print" + android:title="@string/print"/> + + <item android:id="@+id/add_search_engine" + android:title="@string/contextmenu_add_search_engine"/> + + <item android:id="@+id/set_as_homepage" + android:title="@string/contextmenu_set_as_homepage"/> + + </menu> + + </item> + + <item android:id="@+id/tools" + android:title="@string/tools"> + + <menu> + + <item android:id="@+id/downloads" + android:title="@string/downloads"/> + + <item android:id="@+id/addons" + android:title="@string/addons"/> + + <item android:id="@+id/logins" + android:title="@string/logins"/> + + <item android:id="@+id/new_guest_session" + android:visible="false" + android:title="@string/new_guest_session"/> + + <item android:id="@+id/exit_guest_session" + android:visible="false" + android:title="@string/exit_guest_session"/> + + </menu> + + </item> + + <item android:id="@+id/char_encoding" + android:visible="false" + android:title="@string/char_encoding"/> + + <item android:id="@+id/settings" + android:title="@string/settings" /> + + <item android:id="@+id/help" + android:title="@string/help_menu" /> + +</menu>
new file mode 100644 --- /dev/null +++ b/mobile/android/app/src/main/res/menu-v26/activitystream_contextmenu.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Group ID's are required, otherwise NavigationView won't show any dividers. The ID's are unused, but still required. --> + <group android:id="@+id/group0"> + <item + android:id="@+id/open_new_tab" + android:icon="@drawable/as_tab" + android:title="@string/contextmenu_open_new_tab"/> + <item + android:id="@+id/open_new_private_tab" + android:icon="@drawable/as_private" + android:title="@string/contextmenu_open_private_tab"/> + </group> + + <group android:id="@+id/group1"> + <item + android:id="@+id/dismiss" + android:icon="@drawable/as_dismiss" + android:title="@string/activity_stream_remove"/> + + <item + android:id="@+id/delete" + android:icon="@drawable/as_bin" + android:visible="false" + android:title="@string/activity_stream_delete_history"/> + </group> + + <group android:id="@+id/group2"> + <item + android:id="@+id/bookmark" + android:icon="@drawable/as_bookmark" + android:title="@string/bookmark"/> + <item + android:id="@+id/share" + android:icon="@drawable/as_share" + android:title="@string/share"/> + <item + android:id="@+id/copy_url" + android:icon="@drawable/as_copy" + android:title="@string/contextmenu_copyurl"/> + <item + android:id="@+id/pin" + android:icon="@drawable/as_pin" + android:title="@string/contextmenu_top_sites_pin"/> + </group> +</menu> \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/mobile/android/app/src/main/res/menu-v26/browser_app_menu.xml @@ -0,0 +1,120 @@ +<?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/. --> + +<!-- We disable AlwaysShowAction because we interpret the menu + attributes ourselves and thus the warning isn't relevant to us. --> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="AlwaysShowAction"> + + <item android:id="@+id/back" + android:icon="@drawable/ic_menu_back" + android:title="@string/back" + android:showAsAction="always"/> + + <item android:id="@+id/forward" + android:icon="@drawable/ic_menu_forward" + android:title="@string/forward" + android:showAsAction="always"/> + + <item android:id="@+id/bookmark" + android:icon="@drawable/ic_menu_bookmark_add" + android:title="@string/bookmark" + android:showAsAction="always"/> + + <item android:id="@+id/reload" + android:icon="@drawable/ic_menu_reload" + android:title="@string/reload" + android:showAsAction="always"/> + + <item android:id="@+id/share" + android:icon="@drawable/ic_menu_share" + android:title="@string/share" + android:showAsAction="ifRoom"/> + + <item android:id="@+id/new_tab" + android:title="@string/new_tab"/> + + <item android:id="@+id/new_private_tab" + android:title="@string/new_private_tab"/> + + <item android:id="@+id/bookmarks_list" + android:title="@string/bookmarks_title"/> + + <item android:id="@+id/history_list" + android:title="@string/history_title"/> + + <item android:id="@+id/find_in_page" + android:title="@string/find_in_page" /> + + <item android:id="@+id/desktop_mode" + android:title="@string/desktop_mode" + android:checkable="true" /> + + <item android:id="@+id/addons_top_level" + android:title="@string/addons" + android:visible="false" /> + + <item android:id="@+id/page" + android:title="@string/page"> + + <menu> + + <item android:id="@+id/subscribe" + android:title="@string/contextmenu_subscribe"/> + + <item android:id="@+id/save_as_pdf" + android:title="@string/save_as_pdf"/> + + <item android:id="@+id/print" + android:title="@string/print"/> + + <item android:id="@+id/add_search_engine" + android:title="@string/contextmenu_add_search_engine"/> + + <item android:id="@+id/set_as_homepage" + android:title="@string/contextmenu_set_as_homepage"/> + + </menu> + + </item> + + <item android:id="@+id/tools" + android:title="@string/tools"> + + <menu> + + <item android:id="@+id/downloads" + android:title="@string/downloads"/> + + <item android:id="@+id/addons" + android:title="@string/addons"/> + + <item android:id="@+id/logins" + android:title="@string/logins"/> + + <item android:id="@+id/new_guest_session" + android:visible="false" + android:title="@string/new_guest_session"/> + + <item android:id="@+id/exit_guest_session" + android:visible="false" + android:title="@string/exit_guest_session"/> + + </menu> + + </item> + + <item android:id="@+id/char_encoding" + android:visible="false" + android:title="@string/char_encoding"/> + + <item android:id="@+id/settings" + android:title="@string/settings" /> + + <item android:id="@+id/help" + android:title="@string/help_menu" /> + +</menu>
new file mode 100644 --- /dev/null +++ b/mobile/android/app/src/main/res/menu-v26/home_contextmenu.xml @@ -0,0 +1,41 @@ +<?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/. --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:id="@+id/home_open_new_tab" + android:title="@string/contextmenu_open_new_tab"/> + + <item android:id="@+id/home_open_private_tab" + android:title="@string/contextmenu_open_private_tab"/> + + <item android:id="@+id/home_copyurl" + android:title="@string/contextmenu_copyurl"/> + + <item android:id="@+id/home_share" + android:title="@string/contextmenu_share"/> + + <item android:id="@+id/top_sites_edit" + android:title="@string/contextmenu_top_sites_edit"/> + + <item android:id="@+id/top_sites_pin" + android:title="@string/contextmenu_top_sites_pin"/> + + <item android:id="@+id/top_sites_unpin" + android:title="@string/contextmenu_top_sites_unpin"/> + + <item android:id="@+id/home_edit_bookmark" + android:title="@string/contextmenu_edit_bookmark"/> + + <item android:id="@+id/home_remove" + android:title="@string/contextmenu_remove"/> + + <item android:id="@+id/home_as_pin" + android:title="@string/contextmenu_top_sites_pin"/> + + <item android:id="@+id/home_set_as_homepage" + android:title="@string/contextmenu_set_as_homepage"/> + +</menu>
new file mode 100644 --- /dev/null +++ b/mobile/android/app/src/main/res/menu-v26/titlebar_contextmenu.xml @@ -0,0 +1,26 @@ +<?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/. --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:id="@+id/pasteandgo" + android:title="@string/contextmenu_pasteandgo"/> + + <item android:id="@+id/paste" + android:title="@string/contextmenu_paste"/> + + <item android:id="@+id/subscribe" + android:title="@string/contextmenu_subscribe"/> + + <item android:id="@+id/add_search_engine" + android:title="@string/contextmenu_add_search_engine"/> + + <item android:id="@+id/copyurl" + android:title="@string/contextmenu_copyurl"/> + + <item android:id="@+id/set_as_homepage" + android:title="@string/contextmenu_set_as_homepage"/> + +</menu>
new file mode 100644 --- /dev/null +++ b/mobile/android/app/src/main/res/menu-xlarge-v26/browser_app_menu.xml @@ -0,0 +1,121 @@ +<?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/. --> + +<!-- We disable AlwaysShowAction because we interpret the menu + attributes ourselves and thus the warning isn't relevant to us. --> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="AlwaysShowAction"> + + <item android:id="@+id/reload" + android:icon="@drawable/ic_menu_reload" + android:title="@string/reload" + android:showAsAction="always"/> + + <item android:id="@+id/back" + android:icon="@drawable/ic_menu_back" + android:title="@string/back" + android:visible="false"/> + + <item android:id="@+id/forward" + android:icon="@drawable/ic_menu_forward" + android:title="@string/forward" + android:visible="false"/> + + <item android:id="@+id/bookmark" + android:icon="@drawable/ic_menu_bookmark_add" + android:title="@string/bookmark" + android:showAsAction="always"/> + + <item android:id="@+id/share" + android:icon="@drawable/ic_menu_share" + android:title="@string/share" + android:showAsAction="ifRoom"/> + + <item android:id="@+id/new_tab" + android:title="@string/new_tab"/> + + <item android:id="@+id/new_private_tab" + android:title="@string/new_private_tab"/> + + <item android:id="@+id/bookmarks_list" + android:title="@string/bookmarks_title"/> + + <item android:id="@+id/history_list" + android:title="@string/history_title"/> + + <item android:id="@+id/find_in_page" + android:title="@string/find_in_page" /> + + <item android:id="@+id/addons_top_level" + android:title="@string/addons" + android:visible="false" /> + + <item android:id="@+id/desktop_mode" + android:title="@string/desktop_mode" + android:checkable="true" /> + + + <item android:id="@+id/page" + android:title="@string/page"> + + <menu> + + <item android:id="@+id/subscribe" + android:title="@string/contextmenu_subscribe"/> + + <item android:id="@+id/save_as_pdf" + android:title="@string/save_as_pdf"/> + + <item android:id="@+id/print" + android:title="@string/print"/> + + <item android:id="@+id/add_search_engine" + android:title="@string/contextmenu_add_search_engine"/> + + <item android:id="@+id/set_as_homepage" + android:title="@string/contextmenu_set_as_homepage"/> + + </menu> + + </item> + + <item android:id="@+id/tools" + android:title="@string/tools"> + + <menu> + + <item android:id="@+id/downloads" + android:title="@string/downloads"/> + + <item android:id="@+id/addons" + android:title="@string/addons"/> + + <item android:id="@+id/logins" + android:title="@string/logins"/> + + <item android:id="@+id/new_guest_session" + android:visible="false" + android:title="@string/new_guest_session"/> + + <item android:id="@+id/exit_guest_session" + android:visible="false" + android:title="@string/exit_guest_session"/> + + </menu> + + </item> + + <item android:id="@+id/char_encoding" + android:visible="false" + android:title="@string/char_encoding"/> + + <item android:id="@+id/settings" + android:title="@string/settings" /> + + <item android:id="@+id/help" + android:title="@string/help_menu" /> + +</menu>
--- a/mobile/android/base/java/org/mozilla/gecko/delegates/BookmarkStateChangeDelegate.java +++ b/mobile/android/base/java/org/mozilla/gecko/delegates/BookmarkStateChangeDelegate.java @@ -11,16 +11,17 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.support.design.widget.Snackbar; import android.support.v4.content.ContextCompat; import android.view.View; import android.widget.ListView; import org.mozilla.gecko.AboutPages; +import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.BrowserApp; import org.mozilla.gecko.GeckoApplication; import org.mozilla.gecko.GeckoSharedPrefs; import org.mozilla.gecko.R; import org.mozilla.gecko.SnackbarBuilder; import org.mozilla.gecko.Tab; import org.mozilla.gecko.Tabs; import org.mozilla.gecko.Telemetry; @@ -187,21 +188,28 @@ public class BookmarkStateChangeDelegate GeckoApplication.createShortcut(title, url); } }); } } } }); - final PromptListItem[] items = new PromptListItem[2]; - items[0] = new PromptListItem(res.getString(R.string.contextmenu_edit_bookmark)); - items[1] = new PromptListItem(res.getString(R.string.contextmenu_add_to_launcher)); + if (AppConstants.Versions.feature26Plus) { + final PromptListItem[] items = new PromptListItem[1]; + items[0] = new PromptListItem(res.getString(R.string.contextmenu_edit_bookmark)); + ps.show("", "", items, ListView.CHOICE_MODE_NONE); + } else { + final PromptListItem[] items = new PromptListItem[2]; + items[0] = new PromptListItem(res.getString(R.string.contextmenu_edit_bookmark)); + items[1] = new PromptListItem(res.getString(R.string.contextmenu_add_to_launcher)); + ps.show("", "", items, ListView.CHOICE_MODE_NONE); + } - ps.show("", "", items, ListView.CHOICE_MODE_NONE); + } private void showReaderModeBookmarkAddedSnackbar() { final BrowserApp browserApp = getBrowserApp(); if (browserApp == null) { return; }
--- a/mobile/android/base/java/org/mozilla/gecko/promotion/AddToHomeScreenPromotion.java +++ b/mobile/android/base/java/org/mozilla/gecko/promotion/AddToHomeScreenPromotion.java @@ -8,16 +8,17 @@ package org.mozilla.gecko.promotion; import android.app.Activity; import android.content.Context; import android.database.Cursor; import android.os.Bundle; import android.support.annotation.CallSuper; import android.support.annotation.Nullable; import android.util.Log; +import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.switchboard.SwitchBoard; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.AboutPages; import org.mozilla.gecko.BrowserApp; import org.mozilla.gecko.GeckoProfile; import org.mozilla.gecko.Tab; @@ -131,16 +132,21 @@ public class AddToHomeScreenPromotion ex } if (isTabsTrayVisible()) { // We only want to show this prompt if this tab is in the foreground and not on top // of the tabs tray. return; } + // Temporary remove add to home screen + if (AppConstants.Versions.feature26Plus) { + return; + } + ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { maybeShowPromotionForUrl(tab.getURL(), tab.getTitle()); } }); }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java @@ -364,21 +364,23 @@ public class GeckoScreenOrientation { * * @param aScreenOrientation * Gecko screen orientation. * @return Android ActivityInfo orientation. */ public static int screenOrientationToActivityInfoOrientation(ScreenOrientation aScreenOrientation) { switch (aScreenOrientation) { case PORTRAIT: + return ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; case PORTRAIT_PRIMARY: return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; case PORTRAIT_SECONDARY: return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; case LANDSCAPE: + return ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; case LANDSCAPE_PRIMARY: return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; case LANDSCAPE_SECONDARY: return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; case DEFAULT: case NONE: return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; default:
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/RepositorySession.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/RepositorySession.java @@ -298,31 +298,59 @@ public abstract class RepositorySession this.status = to; return; } Logger.warn(LOG_TAG, "Wanted to transition from " + from + " but in state " + this.status); throw new InvalidSessionTransitionException(null); } /** + * Indicate if the specified records should be reconciled. + * + * @param remoteRecord + * The record retrieved from upstream, already adjusted for clock skew. + * @param localRecord + * The record retrieved from local storage. + * + * @return + * true if the records should be passed to shouldReconcileRecords for + * the actual resolution, false otherwise. + */ + public boolean shouldReconcileRecords(final Record remoteRecord, + final Record localRecord) { + Logger.debug(LOG_TAG, "Checking if we should reconcile remote " + remoteRecord.guid + " against local " + localRecord.guid); + if (localRecord.equalPayloads(remoteRecord)) { + if (remoteRecord.lastModified > localRecord.lastModified) { + Logger.debug(LOG_TAG, "Records are equal (remote is newer). No record application needed."); + return false; + } + // Local wins. + Logger.debug(LOG_TAG, "Records are equal (local is newer). No record application needed."); + return false; + } + return true; + } + + /** * Produce a record that is some combination of the remote and local records * provided. * * The returned record must be produced without mutating either remoteRecord * or localRecord. It is acceptable to return either remoteRecord or localRecord * if no modifications are to be propagated. * * The returned record *should* have the local androidID and the remote GUID, * and some optional merge of data from the two records. * - * This method can be called with records that are identical, or differ in - * any regard. + * This method should only be called when shouldReconcileRecords returns true + * for the specified records. * * This method will not be called if: * + * * shouldReconcileRecords() returns false * * either record is marked as deleted, or * * there is no local mapping for a new remote record. * * Otherwise, it will be called precisely once. * * Side-effects (e.g., for transactional storage) can be hooked in here. * * @param remoteRecord @@ -330,34 +358,24 @@ public abstract class RepositorySession * @param localRecord * The record retrieved from local storage. * @param lastRemoteRetrieval * The timestamp of the last retrieved set of remote records, adjusted for * clock skew. * @param lastLocalRetrieval * The timestamp of the last retrieved set of local records. * @return - * A Record instance to apply, or null to apply nothing. + * A Record instance to apply. */ public Record reconcileRecords(final Record remoteRecord, - final Record localRecord, - final long lastRemoteRetrieval, - final long lastLocalRetrieval) { + final Record localRecord, + final long lastRemoteRetrieval, + final long lastLocalRetrieval) { Logger.debug(LOG_TAG, "Reconciling remote " + remoteRecord.guid + " against local " + localRecord.guid); - if (localRecord.equalPayloads(remoteRecord)) { - if (remoteRecord.lastModified > localRecord.lastModified) { - Logger.debug(LOG_TAG, "Records are equal. No record application needed."); - return null; - } - - // Local wins. - return null; - } - // TODO: Decide what to do based on: // * Which of the two records is modified; // * Whether they are equal or congruent; // * The modified times of each record (interpreted through the lens of clock skew); // * Whether localVersion==syncVersion or localVersion>syncVersion for localRecord // * ... boolean localIsMoreRecent = localRecord.lastModified > remoteRecord.lastModified; Logger.debug(LOG_TAG, "Local record is more recent? " + localIsMoreRecent);
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/PasswordsRepositorySession.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/PasswordsRepositorySession.java @@ -314,22 +314,23 @@ public class PasswordsRepositorySession trace("Incoming record " + remoteRecord.guid + " dupes to local record " + existingRecord.guid); Logger.debug(LOG_TAG, "remote " + remoteRecord.guid + " dupes to " + existingRecord.guid); if (existingRecord.deleted && existingRecord.lastModified > remoteRecord.lastModified) { Logger.debug(LOG_TAG, "Local deletion is newer, not storing remote record."); return; } - Record toStore = reconcileRecords(remoteRecord, existingRecord, lastRemoteRetrieval, lastLocalRetrieval); - if (toStore == null) { - Logger.debug(LOG_TAG, "Reconciling returned null. Not inserting a record."); + if (!shouldReconcileRecords(remoteRecord, existingRecord)) { + Logger.debug(LOG_TAG, "shouldReconcileRecords returned false. Not inserting a record."); return; } + Record toStore = reconcileRecords(remoteRecord, existingRecord, lastRemoteRetrieval, lastLocalRetrieval); + // TODO: pass in timestamps? Logger.debug(LOG_TAG, "Replacing " + existingRecord.guid + " with record " + toStore.guid); Record replaced = null; try { replaced = replace(existingRecord, toStore); } catch (RemoteException e) { Logger.debug(LOG_TAG, "Record replace caused a RemoteException."); storeDelegate.onRecordStoreFailed(e, record.guid);
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/SessionHelper.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/SessionHelper.java @@ -173,16 +173,21 @@ import org.mozilla.gecko.sync.repositori } } void finish() { // TODO are we paranoid that a reference might persist for too long once we're done with it? recordToGuid = null; } + private boolean shouldReconcileRecords(final Record remoteRecord, + final Record localRecord) { + return session.shouldReconcileRecords(remoteRecord, localRecord); + } + private void putRecordToGuidMap(String recordString, String guid) throws NoGuidForIdException, NullCursorException, ParentNotFoundException { if (recordString == null) { return; } if (recordToGuid == null) { createRecordToGuidMap(); @@ -292,24 +297,22 @@ import org.mozilla.gecko.sync.repositori } // We found a local dupe. trace("Incoming record " + record.guid + " dupes to local record " + existingRecord.guid); // Populate more expensive fields prior to reconciling. existingRecord = transformRecord(existingRecord); + if (!shouldReconcileRecords(record, existingRecord)) { + Logger.debug(LOG_TAG, "shouldReconcileRecords returned false. Not processing a record."); + break; + } // Implementation is expected to decide if it's necessary to "track" the record. Record toStore = reconcileRecords(record, existingRecord, lastRemoteRetrieval, lastLocalRetrieval); - - if (toStore == null) { - Logger.debug(LOG_TAG, "Reconciling returned null. Not inserting a record."); - break; - } - Logger.debug(LOG_TAG, "Reconcile attempt #" + reconcileAttempt); // This section of code will only run if the incoming record is not // marked as deleted, so we never want to just drop ours from the database: // we need to upload it later. // Implementations are expected to ensure this happens. boolean replaceSuccessful = doReplaceRecord(toStore, existingRecord, storeDelegate);
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -368,16 +368,17 @@ pref("media.wmf.enabled", true); pref("media.wmf.decoder.thread-count", -1); pref("media.wmf.low-latency.enabled", false); pref("media.wmf.skip-blacklist", false); pref("media.wmf.vp9.enabled", true); pref("media.wmf.allow-unsupported-resolutions", false); pref("media.windows-media-foundation.allow-d3d11-dxva", true); pref("media.wmf.disable-d3d11-for-dlls", "igd11dxva64.dll: 20.19.15.4463, 20.19.15.4454, 20.19.15.4444, 20.19.15.4416, 20.19.15.4404, 20.19.15.4390, 20.19.15.4380, 20.19.15.4377, 20.19.15.4364, 20.19.15.4360, 20.19.15.4352, 20.19.15.4331, 20.19.15.4326, 20.19.15.4300; igd10iumd32.dll: 20.19.15.4444, 20.19.15.4424, 20.19.15.4409, 20.19.15.4390, 20.19.15.4380, 20.19.15.4360, 10.18.10.4358, 20.19.15.4331, 20.19.15.4312, 20.19.15.4300, 10.18.15.4281, 10.18.15.4279, 10.18.10.4276, 10.18.15.4268, 10.18.15.4256, 10.18.10.4252, 10.18.15.4248, 10.18.14.4112, 10.18.10.3958, 10.18.10.3496, 10.18.10.3431, 10.18.10.3412, 10.18.10.3355, 9.18.10.3234, 9.18.10.3071, 9.18.10.3055, 9.18.10.3006; igd10umd32.dll: 9.17.10.4229, 9.17.10.3040, 9.17.10.2857, 8.15.10.2274, 8.15.10.2272, 8.15.10.2246, 8.15.10.1840, 8.15.10.1808; igd10umd64.dll: 9.17.10.4229, 9.17.10.2857, 10.18.10.3496; isonyvideoprocessor.dll: 4.1.2247.8090, 4.1.2153.6200; tosqep.dll: 1.2.15.526, 1.1.12.201, 1.0.11.318, 1.0.11.215, 1.0.10.1224; tosqep64.dll: 1.1.12.201, 1.0.11.215; nvwgf2um.dll: 22.21.13.8253, 22.21.13.8233, 22.21.13.8205, 22.21.13.8189, 22.21.13.8178, 22.21.13.8165, 21.21.13.7892, 21.21.13.7878, 21.21.13.7866, 21.21.13.7849, 21.21.13.7654, 21.21.13.7653, 21.21.13.7633, 21.21.13.7619, 21.21.13.7563, 21.21.13.7306, 21.21.13.7290, 21.21.13.7270, 21.21.13.7254, 21.21.13.6939, 21.21.13.6926, 21.21.13.6909, 21.21.13.4201, 21.21.13.4200, 10.18.13.6881, 10.18.13.6839, 10.18.13.6510, 10.18.13.6472, 10.18.13.6143, 10.18.13.5946, 10.18.13.5923, 10.18.13.5921, 10.18.13.5891, 10.18.13.5887, 10.18.13.5582, 10.18.13.5445, 10.18.13.5382, 10.18.13.5362, 9.18.13.4788, 9.18.13.4752, 9.18.13.4725, 9.18.13.4709, 9.18.13.4195, 9.18.13.4192, 9.18.13.4144, 9.18.13.4052, 9.18.13.3788, 9.18.13.3523, 9.18.13.3235, 9.18.13.3165, 9.18.13.2723, 9.18.13.2702, 9.18.13.1422, 9.18.13.1407, 9.18.13.1106, 9.18.13.546; atidxx32.dll: 21.19.151.3, 21.19.142.257, 21.19.137.514, 21.19.137.1, 21.19.134.1, 21.19.128.7, 21.19.128.4, 20.19.0.32837, 20.19.0.32832, 8.17.10.682, 8.17.10.671, 8.17.10.661, 8.17.10.648, 8.17.10.644, 8.17.10.625, 8.17.10.605, 8.17.10.581, 8.17.10.569, 8.17.10.560, 8.17.10.545, 8.17.10.539, 8.17.10.531, 8.17.10.525, 8.17.10.520, 8.17.10.519, 8.17.10.514, 8.17.10.511, 8.17.10.494, 8.17.10.489, 8.17.10.483, 8.17.10.453, 8.17.10.451, 8.17.10.441, 8.17.10.436, 8.17.10.432, 8.17.10.425, 8.17.10.418, 8.17.10.414, 8.17.10.401, 8.17.10.395, 8.17.10.385, 8.17.10.378, 8.17.10.362, 8.17.10.355, 8.17.10.342, 8.17.10.331, 8.17.10.318, 8.17.10.310, 8.17.10.286, 8.17.10.269, 8.17.10.261, 8.17.10.247, 8.17.10.240, 8.15.10.212; atidxx64.dll: 21.19.151.3, 21.19.142.257, 21.19.137.514, 21.19.137.1, 21.19.134.1, 21.19.128.7, 21.19.128.4, 20.19.0.32832, 8.17.10.682, 8.17.10.661, 8.17.10.644, 8.17.10.625; nvumdshim.dll: 10.18.13.6822"); pref("media.wmf.disable-d3d9-for-dlls", "igdumd64.dll: 8.15.10.2189, 8.15.10.2119, 8.15.10.2104, 8.15.10.2102, 8.771.1.0; atiumd64.dll: 7.14.10.833, 7.14.10.867, 7.14.10.885, 7.14.10.903, 7.14.10.911, 8.14.10.768, 9.14.10.1001, 9.14.10.1017, 9.14.10.1080, 9.14.10.1128, 9.14.10.1162, 9.14.10.1171, 9.14.10.1183, 9.14.10.1197, 9.14.10.945, 9.14.10.972, 9.14.10.984, 9.14.10.996"); +pref("media.wmf.deblacklisting-for-telemetry-in-gpu-process", true); #endif #if defined(MOZ_FFMPEG) #if defined(XP_MACOSX) pref("media.ffmpeg.enabled", false); #else pref("media.ffmpeg.enabled", true); #endif pref("media.libavcodec.allow-obsolete", false);
--- a/mozglue/build/WindowsDllBlocklist.cpp +++ b/mozglue/build/WindowsDllBlocklist.cpp @@ -295,17 +295,17 @@ printf_stderr(const char *fmt, ...) vfprintf(fp, fmt, args); va_end(args); fclose(fp); } #ifdef _M_IX86 -typedef void (__fastcall* BaseThreadInitThunk_func)(BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam); +typedef MOZ_NORETURN_PTR void (__fastcall* BaseThreadInitThunk_func)(BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam); static BaseThreadInitThunk_func stub_BaseThreadInitThunk = nullptr; #endif typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle); static LdrLoadDll_func stub_LdrLoadDll; #ifdef _M_AMD64 typedef decltype(RtlInstallFunctionTableCallback)* RtlInstallFunctionTableCallback_func; @@ -515,17 +515,17 @@ DllBlockSet::Write(HANDLE file) // Because this method is called after a crash occurs, and uses heap memory, // protect this entire block with a structured exception handler. MOZ_SEH_TRY { DWORD nBytes; for (DllBlockSet* b = gFirst; b; b = b->mNext) { // write name[,v.v.v.v]; WriteFile(file, b->mName, strlen(b->mName), &nBytes, nullptr); - if (b->mVersion != -1) { + if (b->mVersion != ALL_VERSIONS) { WriteFile(file, ",", 1, &nBytes, nullptr); uint16_t parts[4]; parts[0] = b->mVersion >> 48; parts[1] = (b->mVersion >> 32) & 0xFFFF; parts[2] = (b->mVersion >> 16) & 0xFFFF; parts[3] = b->mVersion & 0xFFFF; for (int p = 0; p < 4; ++p) { char buf[32];
new file mode 100644 --- /dev/null +++ b/netwerk/test/unit/test_tls_flags.js @@ -0,0 +1,233 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ + +"use strict"; + +// a fork of test_be_conservative + +// Tests that nsIHttpChannelInternal.tlsFlags can be used to set the +// client max version level. Flags can also be used to set the +// level of intolerance rollback and to test out an experimental 1.3 +// hello, though they are not tested here. + +const { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); +const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); + +// Get a profile directory and ensure PSM initializes NSS. +do_get_profile(); +Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports); + +function getCert() { + return new Promise((resolve, reject) => { + let certService = Cc["@mozilla.org/security/local-cert-service;1"] + .getService(Ci.nsILocalCertService); + certService.getOrCreateCert("tlsflags-test", { + handleCert: function(c, rv) { + if (rv) { + reject(rv); + return; + } + resolve(c); + } + }); + }); +} + +class InputStreamCallback { + constructor(output) { + this.output = output; + this.stopped = false; + } + + onInputStreamReady(stream) { + do_print("input stream ready"); + if (this.stopped) { + do_print("input stream callback stopped - bailing"); + return; + } + let available = 0; + try { + available = stream.available(); + } catch (e) { + // onInputStreamReady may fire when the stream has been closed. + equal(e.result, Cr.NS_BASE_STREAM_CLOSED, + "error should be NS_BASE_STREAM_CLOSED"); + } + if (available > 0) { + let request = NetUtil.readInputStreamToString(stream, available, + { charset: "utf8"}); + ok(request.startsWith("GET / HTTP/1.1\r\n"), + "Should get a simple GET / HTTP/1.1 request"); + let response = "HTTP/1.1 200 OK\r\n" + + "Content-Length: 2\r\n" + + "Content-Type: text/plain\r\n" + + "\r\nOK"; + let written = this.output.write(response, response.length); + equal(written, response.length, + "should have been able to write entire response"); + } + this.output.close(); + do_print("done with input stream ready"); + } + + stop() { + this.stopped = true; + this.output.close(); + } +} + +class TLSServerSecurityObserver { + constructor(input, output, expectedVersion) { + this.input = input; + this.output = output; + this.expectedVersion = expectedVersion; + this.callbacks = []; + this.stopped = false; + } + + onHandshakeDone(socket, status) { + do_print("TLS handshake done"); + do_print(`TLS version used: ${status.tlsVersionUsed}`); + do_print(this.expectedVersion); + equal(status.tlsVersionUsed, this.expectedVersion, "expected version check"); + if (this.stopped) { + do_print("handshake done callback stopped - bailing"); + return; + } + + let callback = new InputStreamCallback(this.output); + this.callbacks.push(callback); + this.input.asyncWait(callback, 0, 0, Services.tm.currentThread); + } + + stop() { + this.stopped = true; + this.input.close(); + this.output.close(); + this.callbacks.forEach((callback) => { + callback.stop(); + }); + } +} + + +function startServer(cert, minServerVersion, maxServerVersion, expectedVersion) { + let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"] + .createInstance(Ci.nsITLSServerSocket); + tlsServer.init(-1, true, -1); + tlsServer.serverCert = cert; + tlsServer.setVersionRange(minServerVersion, maxServerVersion); + tlsServer.setSessionCache(false); + tlsServer.setSessionTickets(false); + + let listener = { + securityObservers : [], + + onSocketAccepted: function(socket, transport) { + do_print("accepted TLS client connection"); + let connectionInfo = transport.securityInfo + .QueryInterface(Ci.nsITLSServerConnectionInfo); + let input = transport.openInputStream(0, 0, 0); + let output = transport.openOutputStream(0, 0, 0); + let securityObserver = new TLSServerSecurityObserver(input, output, expectedVersion); + this.securityObservers.push(securityObserver); + connectionInfo.setSecurityObserver(securityObserver); + }, + + // For some reason we get input stream callback events after we've stopped + // listening, so this ensures we just drop those events. + onStopListening: function() { + do_print("onStopListening"); + this.securityObservers.forEach((observer) => { + observer.stop(); + }); + } + } + tlsServer.asyncListen(listener); + return tlsServer; +} + +const hostname = "example.com" + +function storeCertOverride(port, cert) { + let certOverrideService = Cc["@mozilla.org/security/certoverride;1"] + .getService(Ci.nsICertOverrideService); + let overrideBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED | + Ci.nsICertOverrideService.ERROR_MISMATCH; + certOverrideService.rememberValidityOverride(hostname, port, cert, + overrideBits, true); +} + +function startClient(port, tlsFlags, expectSuccess) { + let req = new XMLHttpRequest(); + req.open("GET", `https://${hostname}:${port}`); + let internalChannel = req.channel.QueryInterface(Ci.nsIHttpChannelInternal); + internalChannel.tlsFlags = tlsFlags; + return new Promise((resolve, reject) => { + req.onload = () => { + ok(expectSuccess, + `should ${expectSuccess ? "" : "not "}have gotten load event`); + equal(req.responseText, "OK", "response text should be 'OK'"); + resolve(); + }; + req.onerror = () => { + ok(!expectSuccess, + `should ${!expectSuccess ? "" : "not "}have gotten an error`); + resolve(); + }; + + req.send(); + }); +} + +add_task(async function() { + Services.prefs.setIntPref("security.tls.version.max", 4); + Services.prefs.setCharPref("network.dns.localDomains", hostname); + let cert = await getCert(); + + // server that accepts 1.1->1.3 and a client max 1.3. expect 1.3 + do_print("TEST 1"); + let server = startServer(cert, + Ci.nsITLSClientStatus.TLS_VERSION_1_1, + Ci.nsITLSClientStatus.TLS_VERSION_1_3, + Ci.nsITLSClientStatus.TLS_VERSION_1_3); + storeCertOverride(server.port, cert); + await startClient(server.port, 4, true /*should succeed*/); + server.close(); + + // server that accepts 1.1->1.3 and a client max 1.1. expect 1.1 + do_print("TEST 2"); + server = startServer(cert, + Ci.nsITLSClientStatus.TLS_VERSION_1_1, + Ci.nsITLSClientStatus.TLS_VERSION_1_3, + Ci.nsITLSClientStatus.TLS_VERSION_1_1); + storeCertOverride(server.port, cert); + await startClient(server.port, 2, true); + server.close(); + + // server that accepts 1.2->1.2 and a client max 1.3. expect 1.2 + do_print("TEST 3"); + server = startServer(cert, + Ci.nsITLSClientStatus.TLS_VERSION_1_2, + Ci.nsITLSClientStatus.TLS_VERSION_1_2, + Ci.nsITLSClientStatus.TLS_VERSION_1_2); + storeCertOverride(server.port, cert); + await startClient(server.port, 4, true); + server.close(); + + // server that accepts 1.2->1.2 and a client max 1.1. expect fail + do_print("TEST 4"); + server = startServer(cert, + Ci.nsITLSClientStatus.TLS_VERSION_1_2, + Ci.nsITLSClientStatus.TLS_VERSION_1_2, 0); + storeCertOverride(server.port, cert); + await startClient(server.port, 2, false); + + server.close(); +}); + +do_register_cleanup(function() { + Services.prefs.clearUserPref("security.tls.version.max"); + Services.prefs.clearUserPref("network.dns.localDomains"); +});
--- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -392,8 +392,9 @@ skip-if = os == "android" [test_race_cache_with_network.js] [test_channel_priority.js] [test_bug1312774_http1.js] [test_1351443-missing-NewChannel2.js] [test_bug1312782_http1.js] [test_bug1355539_http1.js] [test_bug1378385_http1.js] [test_tls_flags_separate_connections.js] +[test_tls_flags.js]
--- a/python/mozbuild/mozbuild/action/langpack_manifest.py +++ b/python/mozbuild/mozbuild/action/langpack_manifest.py @@ -13,18 +13,16 @@ from __future__ import absolute_import import argparse import sys import os import json import io from mozpack.chrome.manifest import ( Manifest, ManifestLocale, - ManifestOverride, - ManifestResource, parse_manifest, ) from mozbuild.preprocessor import Preprocessor def write_file(path, content): with io.open(path, 'w', encoding='utf-8') as out: out.write(content + '\n') @@ -76,20 +74,94 @@ def convert_contributors(str): str = str.replace('<em:contributor>', '') tokens = str.split('</em:contributor>') tokens = map(lambda t: t.strip(), tokens) tokens = filter(lambda t: t != '', tokens) return ', '.join(tokens) ### +# Build the manifest author string based on the author string +# and optionally adding the list of contributors, if provided. +# +# Args: +# author (str) - a string with the name of the author +# contributors (str) - RDF based list of contributors from a chrome manifest +# +# Returns: +# (str) - a string to be placed in the author field of the manifest.json +# +# Example: +# s = build_author_string( +# 'Aviary.pl', +# ' +# <em:contributor>Marek Wawoczny</em:contributor> +# <em:contributor>Marek Stepien</em:contributor> +# ') +# s == 'Aviary.pl (contributors: Marek Wawoczny, Marek Stepien)' +### +def build_author_string(author, contributors): + contrib = convert_contributors(contributors) + if len(contrib) == 0: + return author + return '{0} (contributors: {1})'.format(author, contrib) + + +## +# Converts the list of chrome manifest entry flags to the list of platforms +# for the langpack manifest. +# +# The list of result platforms is taken from AppConstants.platform. +# +# Args: +# flags (FlagList) - a list of Chrome Manifest entry flags +# +# Returns: +# (list) - a list of platform the entry applies to +# +# Example: +# str(flags) == "os==MacOS os==Windows" +# platforms = convert_entry_flags_to_platform_codes(flags) +# platforms == ['mac', 'win'] +# +# The method supports only `os` flag name and equality operator. +# It will throw if tried with other flags or operators. +### +def convert_entry_flags_to_platform_codes(flags): + if not flags: + return None + + ret = [] + for key in flags: + if key != 'os': + raise Exception('Unknown flag name') + + for value in flags[key].values: + if value[0] != '==': + raise Exception('Inequality flag cannot be converted') + + if value[1] == 'Android': + ret.append('android') + elif value[1] == 'LikeUnix': + ret.append('linux') + elif value[1] == 'Darwin': + ret.append('macosx') + elif value[1] == 'WINNT': + ret.append('win') + else: + raise Exception('Unknown flag value {0}'.format(value[1])) + + return ret + + +### # Recursively parse a chrome manifest file appending new entries # to the result list # -# The function can handle three entry types: 'locale', 'override' and 'resource' +# The function can handle two entry types: 'locale' and 'manifest' # # Args: # path (str) - a path to a chrome manifest # base_path (str) - a path to the base directory all chrome registry # entries will be relative to # chrome_entries (list) - a list to which entries will be appended to # # Example: @@ -97,22 +169,24 @@ def convert_contributors(str): # chrome_entries = {} # parse_manifest('./chrome.manifest', './', chrome_entries) # # chrome_entries == [ # { # 'type': 'locale', # 'alias': 'devtools', # 'locale': 'pl', +# 'platforms': null, # 'path': 'chrome/pl/locale/pl/devtools/' # }, # { # 'type': 'locale', # 'alias': 'autoconfig', # 'locale': 'pl', +# 'platforms': ['win', 'mac'], # 'path': 'chrome/pl/locale/pl/autoconfig/' # }, # ] ### def parse_chrome_manifest(path, base_path, chrome_entries): for entry in parse_manifest(None, path): if isinstance(entry, Manifest): parse_chrome_manifest( @@ -120,38 +194,27 @@ def parse_chrome_manifest(path, base_pat base_path, chrome_entries ) elif isinstance(entry, ManifestLocale): chrome_entries.append({ 'type': 'locale', 'alias': entry.name, 'locale': entry.id, + 'platforms': convert_entry_flags_to_platform_codes(entry.flags), 'path': os.path.join( os.path.relpath( os.path.dirname(path), base_path ), entry.relpath ) }) - elif isinstance(entry, ManifestOverride): - chrome_entries.append({ - 'type': 'override', - 'real-path': entry.overloaded, - 'overlay-path': entry.overload - }) - elif isinstance(entry, ManifestResource): - chrome_entries.append({ - 'type': 'resource', - 'alias': entry.name, - 'path': entry.target - }) else: - raise Exception('Unknown type %s' % entry[0]) + raise Exception('Unknown type {0}'.format(entry.name)) ### # Generates a new web manifest dict with values specific for a language pack. # # Args: # locstr (str) - A string with a comma separated list of locales # for which resources are embedded in the @@ -163,81 +226,99 @@ def parse_chrome_manifest(path, base_pat # # Returns: # (dict) - a web manifest # # Example: # manifest = create_webmanifest( # ['pl'], # '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}', -# '55.0a1', +# '57.0', # {'MOZ_LANG_TITLE': 'Polski'}, # chrome_entries # ) # manifest == { -# 'languages': ['pl'], +# 'languages': { +# 'pl': { +# 'version': '201709121481', +# 'resources': None, +# 'chrome_resources': { +# 'alert': 'chrome/pl/locale/pl/alert/', +# 'branding': 'browser/chrome/pl/locale/global/', +# 'global-platform': { +# 'macosx': 'chrome/pl/locale/pl/global-platform/mac/', +# 'win': 'chrome/pl/locale/pl/global-platform/win/', +# 'linux': 'chrome/pl/locale/pl/global-platform/unix/', +# 'android': 'chrome/pl/locale/pl/global-platform/unix/', +# }, +# 'forms': 'browser/chrome/pl/locale/forms/', +# ... +# } +# } +# }, # 'applications': { # 'gecko': { -# 'strict_min_version': '55.0a1', -# 'id': '', +# 'strict_min_version': '57.0', +# 'strict_max_version': '57.0.*', +# 'id': 'langpack-pl@mozilla.org', # } # }, -# 'version': '55.0a1', +# 'version': '57.0', # 'name': 'Polski Language Pack', # ... # } ### def create_webmanifest(locstr, appver, defines, chrome_entries): locales = map(lambda loc: loc.strip(), locstr.split(',')) main_locale = locales[0] - contributors = convert_contributors(defines['MOZ_LANGPACK_CONTRIBUTORS']) + author = build_author_string( + defines['MOZ_LANGPACK_CREATOR'], + defines['MOZ_LANGPACK_CONTRIBUTORS'] + ) manifest = { - 'langpack-id': main_locale, + 'langpack_id': main_locale, 'manifest_version': 2, 'applications': { 'gecko': { - 'id': "langpack-" + main_locale + "@mozilla.org", - 'strict_min_version': appver + 'id': 'langpack-{0}@firefox.mozilla.org'.format(main_locale), + 'strict_min_version': appver, + 'strict_max_version': '{0}.*'.format(appver) } }, - 'name': defines['MOZ_LANG_TITLE'] + ' Language Pack', - 'description': 'Language pack for Firefox for ' + main_locale, + 'name': '{0} Language Pack'.format(defines['MOZ_LANG_TITLE']), + 'description': 'Language pack for Firefox for {0}'.format(main_locale), 'version': appver, - 'languages': locales, - 'author': '%s (contributors: %s)' % (defines['MOZ_LANGPACK_CREATOR'], contributors), - 'chrome_entries': [ - ] + 'languages': {}, + 'author': author } + cr = {} for entry in chrome_entries: - line = '' if entry['type'] == 'locale': - line = '%s %s %s %s' % ( - entry['type'], - entry['alias'], - entry['locale'], - entry['path'] - ) - elif entry['type'] == 'override': - line = '%s %s %s' % ( - entry['type'], - entry['real-path'], - entry['overlay-path'] - ) - elif entry['type'] == 'resource': - line = '%s %s %s' % ( - entry['type'], - entry['alias'], - entry['path'] - ) + platforms = entry['platforms'] + if platforms: + if entry['alias'] not in cr: + cr[entry['alias']] = {} + for platform in platforms: + cr[entry['alias']][platform] = entry['path'] + else: + assert entry['alias'] not in cr + cr[entry['alias']] = entry['path'] else: - raise Exception('Unknown type %s' % entry['type']) - manifest['chrome_entries'].append(line) + raise Exception('Unknown type {0}'.format(entry['type'])) + + for loc in locales: + manifest['languages'][loc] = { + 'version': appver, + 'resources': None, + 'chrome_resources': cr + } + return json.dumps(manifest, indent=2, ensure_ascii=False, encoding='utf8') def main(args): parser = argparse.ArgumentParser() parser.add_argument('--locales', help='List of language codes provided by the langpack') parser.add_argument('--appver',
--- a/security/manager/ssl/SharedSSLState.cpp +++ b/security/manager/ssl/SharedSSLState.cpp @@ -112,25 +112,28 @@ PrivateBrowsingObserver::Observe(nsISupp const char16_t *aData) { if (!nsCRT::strcmp(aTopic, "last-pb-context-exited")) { mOwner->ResetStoredData(); } return NS_OK; } -SharedSSLState::SharedSSLState() -: mClientAuthRemember(new nsClientAuthRememberService) +SharedSSLState::SharedSSLState(uint32_t aTlsFlags) +: mIOLayerHelpers(aTlsFlags) , mMutex("SharedSSLState::mMutex") , mSocketCreated(false) , mOCSPStaplingEnabled(false) , mOCSPMustStapleEnabled(false) { mIOLayerHelpers.Init(); - mClientAuthRemember->Init(); + if (!aTlsFlags) { // the per socket flags don't need memory + mClientAuthRemember = new nsClientAuthRememberService(); + mClientAuthRemember->Init(); + } } SharedSSLState::~SharedSSLState() { } void SharedSSLState::NotePrivateBrowsingStatus() @@ -139,16 +142,19 @@ SharedSSLState::NotePrivateBrowsingStatu mObserver = new PrivateBrowsingObserver(this); nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); obsSvc->AddObserver(mObserver, "last-pb-context-exited", false); } void SharedSSLState::ResetStoredData() { + if (!mClientAuthRemember) { + return; + } MOZ_ASSERT(NS_IsMainThread(), "Not on main thread"); mClientAuthRemember->ClearRememberedDecisions(); mIOLayerHelpers.clearStoredData(); } void SharedSSLState::NoteSocketCreated() {
--- a/security/manager/ssl/SharedSSLState.h +++ b/security/manager/ssl/SharedSSLState.h @@ -14,17 +14,17 @@ class nsClientAuthRememberService; class nsIObserver; namespace mozilla { namespace psm { class SharedSSLState { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedSSLState) - SharedSSLState(); + explicit SharedSSLState(uint32_t aTlsFlags = 0); static void GlobalInit(); static void GlobalCleanup(); nsClientAuthRememberService* GetClientAuthRememberService() { return mClientAuthRemember; }
--- a/security/manager/ssl/nsClientAuthRemember.cpp +++ b/security/manager/ssl/nsClientAuthRemember.cpp @@ -80,20 +80,26 @@ void nsClientAuthRememberService::ClearR ReentrantMonitorAutoEnter lock(monitor); RemoveAllFromMemory(); } void nsClientAuthRememberService::ClearAllRememberedDecisions() { RefPtr<nsClientAuthRememberService> svc = PublicSSLState()->GetClientAuthRememberService(); - svc->ClearRememberedDecisions(); + MOZ_ASSERT(svc); + if (svc) { + svc->ClearRememberedDecisions(); + } svc = PrivateSSLState()->GetClientAuthRememberService(); - svc->ClearRememberedDecisions(); + MOZ_ASSERT(svc); + if (svc) { + svc->ClearRememberedDecisions(); + } } void nsClientAuthRememberService::RemoveAllFromMemory() { mSettingsTable.Clear(); }
--- a/security/manager/ssl/nsNSSIOLayer.cpp +++ b/security/manager/ssl/nsNSSIOLayer.cpp @@ -39,32 +39,71 @@ #include "pkix/pkixtypes.h" #include "prmem.h" #include "prnetdb.h" #include "secder.h" #include "secerr.h" #include "ssl.h" #include "sslerr.h" #include "sslproto.h" +#include "sslexp.h" using namespace mozilla; using namespace mozilla::psm; //#define DEBUG_SSL_VERBOSE //Enable this define to get minimal //reports when doing SSL read/write //#define DUMP_BUFFER //Enable this define along with //DEBUG_SSL_VERBOSE to dump SSL //read/write buffer to a log. //Uses PR_LOG except on Mac where //we always write out to our own //file. namespace { +// The NSSSocketInfo tls flags are meant to be opaque to most calling applications +// but provide a mechanism for direct TLS manipulation when experimenting with new +// features in the scope of a single socket. They do not create a persistent ABI. +// +// Use of these flags creates a new 'sharedSSLState' so existing states for intolerance +// are not carried to sockets that use these flags (and intolerance they discover +// does not impact other normal sockets not using the flags.) +// +// Their current definitions are: +// +// bits 0-2 (mask 0x07) specify the max tls version +// 0 means no override 1->4 are 1.0, 1.1, 1.2, 1.3, 4->7 unused +// bits 3-5 (mask 0x38) specify the tls fallback limit +// 0 means no override, values 1->4 match prefs +// bit 6 (mask 0x40) specifies use of SSL_AltServerHelloType on handshake + +enum { + kTLSProviderFlagMaxVersion10 = 0x01, + kTLSProviderFlagMaxVersion11 = 0x02, + kTLSProviderFlagMaxVersion12 = 0x03, + kTLSProviderFlagMaxVersion13 = 0x04, +}; + +static uint32_t getTLSProviderFlagMaxVersion(uint32_t flags) +{ + return (flags & 0x07); +} + +static uint32_t getTLSProviderFlagFallbackLimit(uint32_t flags) +{ + return (flags & 0x38) >> 3; +} + +static bool getTLSProviderFlagAltServerHello(uint32_t flags) +{ + return (flags & 0x40); +} + #define MAX_ALPN_LENGTH 255 void getSiteKey(const nsACString& hostName, uint16_t port, /*out*/ nsACString& key) { key = hostName; key.AppendASCII(":"); @@ -658,16 +697,22 @@ nsNSSSocketInfo::SetCertVerificationResu } SharedSSLState& nsNSSSocketInfo::SharedState() { return mSharedState; } +void +nsNSSSocketInfo::SetSharedOwningReference(SharedSSLState* aRef) +{ + mOwningSharedRef = aRef; +} + void nsSSLIOLayerHelpers::Cleanup() { MutexAutoLock lock(mutex); mTLSIntoleranceInfo.Clear(); mInsecureFallbackSites.Clear(); } static void @@ -1352,21 +1397,22 @@ nsSSLIOLayerPoll(PRFileDesc* fd, int16_t // it reaches any point that would be unsafe to send/receive something before // cert validation is complete. int16_t result = fd->lower->methods->poll(fd->lower, in_flags, out_flags); MOZ_LOG(gPIPNSSLog, LogLevel::Verbose, ("[%p] poll SSL socket returned %d\n", (void*) fd, (int) result)); return result; } -nsSSLIOLayerHelpers::nsSSLIOLayerHelpers() +nsSSLIOLayerHelpers::nsSSLIOLayerHelpers(uint32_t aTlsFlags) : mTreatUnsafeNegotiationAsBroken(false) , mTLSIntoleranceInfo() , mVersionFallbackLimit(SSL_LIBRARY_VERSION_TLS_1_0) , mutex("nsSSLIOLayerHelpers.mutex") + , mTlsFlags(aTlsFlags) { } static int _PSM_InvalidInt(void) { MOZ_ASSERT_UNREACHABLE("I/O method is invalid"); PR_SetError(PR_INVALID_METHOD_ERROR, 0); @@ -1673,16 +1719,17 @@ nsSSLIOLayerHelpers::~nsSSLIOLayerHelper "security.tls.insecure_fallback_hosts"); } } nsresult nsSSLIOLayerHelpers::Init() { if (!nsSSLIOLayerInitialized) { + MOZ_ASSERT(NS_IsMainThread()); nsSSLIOLayerInitialized = true; nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer"); nsSSLIOLayerMethods = *PR_GetDefaultIOMethods(); nsSSLIOLayerMethods.available = (PRAvailableFN) PSMAvailable; nsSSLIOLayerMethods.available64 = (PRAvailable64FN) PSMAvailable64; nsSSLIOLayerMethods.fsync = (PRFsyncFN) _PSM_InvalidStatus; nsSSLIOLayerMethods.seek = (PRSeekFN) _PSM_InvalidInt; @@ -1714,39 +1761,60 @@ nsSSLIOLayerHelpers::Init() nsSSLIOLayerMethods.read = nsSSLIOLayerRead; nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll; nsSSLPlaintextLayerIdentity = PR_GetUniqueIdentity("Plaintxext PSM layer"); nsSSLPlaintextLayerMethods = *PR_GetDefaultIOMethods(); nsSSLPlaintextLayerMethods.recv = PlaintextRecv; } - bool enabled = false; - Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled); - setTreatUnsafeNegotiationAsBroken(enabled); - loadVersionFallbackLimit(); - initInsecureFallbackSites(); - - mPrefObserver = new PrefObserver(this); - Preferences::AddStrongObserver(mPrefObserver, - "security.ssl.treat_unsafe_negotiation_as_broken"); - Preferences::AddStrongObserver(mPrefObserver, - "security.tls.version.fallback-limit"); - Preferences::AddStrongObserver(mPrefObserver, - "security.tls.insecure_fallback_hosts"); + + // non main thread helpers will need to use defaults + if (NS_IsMainThread()) { + bool enabled = false; + Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled); + setTreatUnsafeNegotiationAsBroken(enabled); + + initInsecureFallbackSites(); + + mPrefObserver = new PrefObserver(this); + Preferences::AddStrongObserver(mPrefObserver, + "security.ssl.treat_unsafe_negotiation_as_broken"); + Preferences::AddStrongObserver(mPrefObserver, + "security.tls.version.fallback-limit"); + Preferences::AddStrongObserver(mPrefObserver, + "security.tls.insecure_fallback_hosts"); + } else { + MOZ_ASSERT(mTlsFlags, "Only per socket version can ignore prefs"); + } + return NS_OK; } void nsSSLIOLayerHelpers::loadVersionFallbackLimit() { // see nsNSSComponent::setEnabledTLSVersions for pref handling rules - uint32_t limit = Preferences::GetUint("security.tls.version.fallback-limit", - 3); // 3 = TLS 1.2 + uint32_t limit = 3; // TLS 1.2 + + if (NS_IsMainThread()) { + limit = Preferences::GetUint("security.tls.version.fallback-limit", + 3); // 3 = TLS 1.2 + } + + // set fallback limit if it is set in the tls flags + uint32_t tlsFlagsFallbackLimit = getTLSProviderFlagFallbackLimit(mTlsFlags); + + if (tlsFlagsFallbackLimit) { + limit = tlsFlagsFallbackLimit; + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("loadVersionFallbackLimit overriden by tlsFlags %d\n", limit)); + } + SSLVersionRange defaults = { SSL_LIBRARY_VERSION_TLS_1_2, SSL_LIBRARY_VERSION_TLS_1_2 }; SSLVersionRange filledInRange; nsNSSComponent::FillTLSVersionRange(filledInRange, limit, limit, defaults); if (filledInRange.max < SSL_LIBRARY_VERSION_TLS_1_2) { filledInRange.max = SSL_LIBRARY_VERSION_TLS_1_2; } @@ -2493,17 +2561,47 @@ nsSSLIOLayerSetOptions(PRFileDesc* fd, b } } SSLVersionRange range; if (SSL_VersionRangeGet(fd, &range) != SECSuccess) { return NS_ERROR_FAILURE; } - // Use infoObject->GetProviderTlsFlags() to get the TLS flags + // setting TLS max version + uint32_t versionFlags = + getTLSProviderFlagMaxVersion(infoObject->GetProviderTlsFlags()); + if (versionFlags) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("[%p] nsSSLIOLayerSetOptions: version flags %d\n", fd, versionFlags)); + if (versionFlags == kTLSProviderFlagMaxVersion10) { + range.max = SSL_LIBRARY_VERSION_TLS_1_0; + } else if (versionFlags == kTLSProviderFlagMaxVersion11) { + range.max = SSL_LIBRARY_VERSION_TLS_1_1; + } else if (versionFlags == kTLSProviderFlagMaxVersion12) { + range.max = SSL_LIBRARY_VERSION_TLS_1_2; + } else if (versionFlags == kTLSProviderFlagMaxVersion13) { + range.max = SSL_LIBRARY_VERSION_TLS_1_3; + } else { + MOZ_LOG(gPIPNSSLog, LogLevel::Error, + ("[%p] nsSSLIOLayerSetOptions: unknown version flags %d\n", + fd, versionFlags)); + } + } + + // enabling alternative server hello + if (getTLSProviderFlagAltServerHello(infoObject->GetProviderTlsFlags())) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("[%p] nsSSLIOLayerSetOptions: Use AltServerHello\n", fd)); + if (SECSuccess != SSL_UseAltServerHelloType(fd, PR_TRUE)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Error, + ("[%p] nsSSLIOLayerSetOptions: Use AltServerHello failed\n", fd)); + // continue on default path + } + } if ((infoObject->GetProviderFlags() & nsISocketProvider::BE_CONSERVATIVE) && (range.max > SSL_LIBRARY_VERSION_TLS_1_2)) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] nsSSLIOLayerSetOptions: range.max limited to 1.2 due to BE_CONSERVATIVE flag\n", fd)); range.max = SSL_LIBRARY_VERSION_TLS_1_2; } @@ -2626,26 +2724,36 @@ nsSSLIOLayerAddToSocket(int32_t family, uint32_t providerTlsFlags) { nsNSSShutDownPreventionLock locker; PRFileDesc* layer = nullptr; PRFileDesc* plaintextLayer = nullptr; nsresult rv; PRStatus stat; - SharedSSLState* sharedState = - providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE ? PrivateSSLState() : PublicSSLState(); + SharedSSLState* sharedState = nullptr; + RefPtr<SharedSSLState> allocatedState; + if (providerTlsFlags) { + allocatedState = new SharedSSLState(providerTlsFlags); + sharedState = allocatedState.get(); + } else { + sharedState = (providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE) ? PrivateSSLState() : PublicSSLState(); + } + nsNSSSocketInfo* infoObject = new nsNSSSocketInfo(*sharedState, providerFlags, providerTlsFlags); if (!infoObject) return NS_ERROR_FAILURE; NS_ADDREF(infoObject); infoObject->SetForSTARTTLS(forSTARTTLS); infoObject->SetHostName(host); infoObject->SetPort(port); infoObject->SetOriginAttributes(originAttributes); + if (allocatedState) { + infoObject->SetSharedOwningReference(allocatedState); + } bool haveProxy = false; if (proxy) { nsCString proxyHost; proxy->GetHost(proxyHost); haveProxy = !proxyHost.IsEmpty(); }
--- a/security/manager/ssl/nsNSSIOLayer.h +++ b/security/manager/ssl/nsNSSIOLayer.h @@ -159,16 +159,18 @@ public: MOZ_ASSERT(amount >= mShortWriteOriginalAmount, "unexpected amount length after short write"); MOZ_ASSERT(!memcmp(mShortWriteBufferCheck.get(), data, mShortWriteOriginalAmount), "unexpected buffer content after short write"); mShortWriteBufferCheck = nullptr; } #endif + void SetSharedOwningReference(mozilla::psm::SharedSSLState* ref); + protected: virtual ~nsNSSSocketInfo(); private: PRFileDesc* mFd; CertVerificationState mCertVerificationState; @@ -220,22 +222,29 @@ private: bool mBypassAuthentication; uint32_t mProviderFlags; uint32_t mProviderTlsFlags; mozilla::TimeStamp mSocketCreationTimestamp; uint64_t mPlaintextBytesRead; nsCOMPtr<nsIX509Cert> mClientCert; + + // if non-null this is a reference to the mSharedState (which is + // not an owning reference). If this is used, the info has a private + // state that does not share things like intolerance lists with the + // rest of the session. This is normally used when you have per + // socket tls flags overriding session wide defaults. + RefPtr<mozilla::psm::SharedSSLState> mOwningSharedRef; }; class nsSSLIOLayerHelpers { public: - nsSSLIOLayerHelpers(); + explicit nsSSLIOLayerHelpers(uint32_t aTlsFlags = 0); ~nsSSLIOLayerHelpers(); nsresult Init(); void Cleanup(); static bool nsSSLIOLayerInitialized; static PRDescIdentity nsSSLIOLayerIdentity; static PRDescIdentity nsSSLPlaintextLayerIdentity; @@ -283,16 +292,17 @@ public: bool isPublic() const; void removeInsecureFallbackSite(const nsACString& hostname, uint16_t port); bool isInsecureFallbackSite(const nsACString& hostname); uint16_t mVersionFallbackLimit; private: mozilla::Mutex mutex; nsCOMPtr<nsIObserver> mPrefObserver; + uint32_t mTlsFlags; }; nsresult nsSSLIOLayerNewSocket(int32_t family, const char* host, int32_t port, nsIProxyInfo *proxy, const OriginAttributes& originAttributes, PRFileDesc** fd,
--- a/security/nss.symbols +++ b/security/nss.symbols @@ -659,16 +659,17 @@ SSL_ClearSessionCache SSL_ConfigSecureServer SSL_ConfigSecureServerWithCertChain SSL_ConfigServerSessionIDCache SSL_ExportKeyingMaterial SSL_ForceHandshake SSL_GetChannelInfo SSL_GetCipherSuiteInfo SSL_GetClientAuthDataHook +SSL_GetExperimentalAPI SSL_GetImplementedCiphers SSL_GetNextProto SSL_GetNumImplementedCiphers SSL_GetPreliminaryChannelInfo SSL_GetSRTPCipher SSL_GetStatistics SSL_HandshakeCallback SSL_HandshakeNegotiatedExtension
--- a/servo/Cargo.lock +++ b/servo/Cargo.lock @@ -3527,17 +3527,17 @@ dependencies = [ "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "webdriver 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "webrender" version = "0.49.0" -source = "git+https://github.com/servo/webrender#95a4ba0fb673091f7258d929bd2091e629f1c475" +source = "git+https://github.com/servo/webrender#108405dd65004b13c41cd3a032d2750663d3f378" dependencies = [ "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-text 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3555,17 +3555,17 @@ dependencies = [ "thread_profiler 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "webrender_api 0.49.0 (git+https://github.com/servo/webrender)", ] [[package]] name = "webrender_api" version = "0.49.0" -source = "git+https://github.com/servo/webrender#95a4ba0fb673091f7258d929bd2091e629f1c475" +source = "git+https://github.com/servo/webrender#108405dd65004b13c41cd3a032d2750663d3f378" dependencies = [ "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/servo/components/layout/block.rs +++ b/servo/components/layout/block.rs @@ -1424,16 +1424,19 @@ impl BlockFlow { .to_used_value(containing_block_size); } } } /// Determines the type of formatting context this is. See the definition of /// `FormattingContextType`. pub fn formatting_context_type(&self) -> FormattingContextType { + if self.is_inline_flex_item() || self.is_block_flex_item() { + return FormattingContextType::Other + } let style = self.fragment.style(); if style.get_box().float != float::T::none { return FormattingContextType::Other } match style.get_box().display { display::T::table_cell | display::T::table_caption | display::T::table_row_group |
--- a/servo/components/layout/flex.rs +++ b/servo/components/layout/flex.rs @@ -2,36 +2,38 @@ * 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/. */ //! Layout for elements with a CSS `display` property of `flex`. #![deny(unsafe_code)] use app_units::{Au, MAX_AU}; -use block::{BlockFlow, MarginsMayCollapseFlag}; +use block::{AbsoluteAssignBSizesTraversal, BlockFlow, MarginsMayCollapseFlag}; use context::LayoutContext; use display_list_builder::{DisplayListBuildState, FlexFlowDisplayListBuilding}; use euclid::Point2D; use floats::FloatKind; use flow; use flow::{Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow}; use flow::{INLINE_POSITION_IS_STATIC, IS_ABSOLUTELY_POSITIONED}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; use layout_debug; +use model::{AdjoiningMargins, CollapsibleMargins}; use model::{IntrinsicISizes, MaybeAuto, SizeConstraint}; use std::cmp::{max, min}; use std::ops::Range; use style::computed_values::{align_content, align_self, flex_direction, flex_wrap, justify_content}; use style::logical_geometry::{Direction, LogicalSize}; use style::properties::ComputedValues; use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW}; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; use style::values::computed::flex::FlexBasis; use style::values::generics::flex::FlexBasis as GenericFlexBasis; +use traversal::PreorderFlowTraversal; /// The size of an axis. May be a specified size, a min/max /// constraint, or an unlimited size #[derive(Debug, Serialize)] enum AxisSize { Definite(Au), MinMax(SizeConstraint), Infinite, @@ -799,17 +801,16 @@ impl FlexFlow { }; } } cur_b += line_interval + line.cross_size; } let total_block_size = total_cross_size + self.block_flow.fragment.border_padding.block_start_end(); self.block_flow.fragment.border_box.size.block = total_block_size; self.block_flow.base.position.size.block = total_block_size; - } } impl Flow for FlexFlow { fn class(&self) -> FlowClass { FlowClass::Flex } @@ -936,23 +937,39 @@ impl Flow for FlexFlow { inline_start_content_edge, inline_end_content_edge, content_inline_size) } } } fn assign_block_size(&mut self, layout_context: &LayoutContext) { - self.block_flow - .assign_block_size_block_base(layout_context, - None, - MarginsMayCollapseFlag::MarginsMayNotCollapse); match self.main_mode { - Direction::Inline => self.inline_mode_assign_block_size(layout_context), - Direction::Block => self.block_mode_assign_block_size(), + Direction::Inline => { + self.inline_mode_assign_block_size(layout_context); + let block_start = AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_start); + let block_end = AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_end); + self.block_flow.base.collapsible_margins = CollapsibleMargins::Collapse(block_start, block_end); + + // TODO(stshine): assign proper static position for absolute descendants. + if (&*self as &Flow).contains_roots_of_absolute_flow_tree() { + // Assign block-sizes for all flows in this absolute flow tree. + // This is preorder because the block-size of an absolute flow may depend on + // the block-size of its containing block, which may also be an absolute flow. + let assign_abs_b_sizes = AbsoluteAssignBSizesTraversal(layout_context.shared_context()); + assign_abs_b_sizes.traverse_absolute_flows(&mut *self); + } + } + Direction::Block =>{ + self.block_flow + .assign_block_size_block_base(layout_context, + None, + MarginsMayCollapseFlag::MarginsMayNotCollapse); + self.block_mode_assign_block_size(); + } } } fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) { self.block_flow.compute_stacking_relative_position(layout_context) } fn place_float_if_applicable<'a>(&mut self) {
--- a/servo/components/layout/flow.rs +++ b/servo/components/layout/flow.rs @@ -1274,19 +1274,17 @@ impl<'a> ImmutableFlowUtils for &'a Flow fn baseline_offset_of_last_line_box_in_flow(self) -> Option<Au> { for kid in base(self).children.iter().rev() { if kid.is_inline_flow() { if let Some(baseline_offset) = kid.as_inline().baseline_offset_of_last_line() { return Some(base(kid).position.start.b + baseline_offset) } } - if kid.is_block_like() && - kid.as_block().formatting_context_type() == FormattingContextType::None && - !base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) { + if kid.is_block_like() && !base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) { if let Some(baseline_offset) = kid.baseline_offset_of_last_line_box_in_flow() { return Some(base(kid).position.start.b + baseline_offset) } } } None } }
--- a/servo/components/layout/fragment.rs +++ b/servo/components/layout/fragment.rs @@ -5,52 +5,53 @@ //! The `Fragment` type, which represents the leaves of the layout tree. #![deny(unsafe_code)] use ServoArc; use app_units::Au; use canvas_traits::canvas::CanvasMsg; use context::{LayoutContext, with_thread_local_font_context}; -use euclid::{Transform3D, Point2D, Vector2D, Radians, Rect, Size2D}; +use euclid::{Transform3D, Point2D, Vector2D, Rect, Size2D}; use floats::ClearType; use flow::{self, ImmutableFlowUtils}; use flow_ref::FlowRef; use gfx; use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode}; use gfx::text::glyph::ByteIndex; use gfx::text::text_run::{TextRun, TextRunSlice}; use gfx_traits::StackingContextId; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFragmentContext, InlineFragmentNodeInfo}; use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT, LineMetrics}; use ipc_channel::ipc::IpcSender; #[cfg(debug_assertions)] use layout_debug; use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint}; -use model::{style_length, ToGfxMatrix}; +use model::style_length; use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder}; use range::*; use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource}; use script_layout_interface::SVGSVGData; use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use serde::ser::{Serialize, SerializeStruct, Serializer}; use servo_url::ServoUrl; use std::{f32, fmt}; use std::borrow::ToOwned; use std::cmp::{Ordering, max, min}; use std::collections::LinkedList; use std::sync::{Arc, Mutex}; use style::computed_values::{border_collapse, box_sizing, clear, color, display, mix_blend_mode}; -use style::computed_values::{overflow_wrap, overflow_x, position, text_decoration_line, transform}; +use style::computed_values::{overflow_wrap, overflow_x, position, text_decoration_line}; use style::computed_values::{transform_style, vertical_align, white_space, word_break}; use style::computed_values::content::ContentItem; use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode}; use style::properties::ComputedValues; +use style::properties::longhands::transform::computed_value::T as TransformList; use style::selector_parser::RestyleDamage; use style::servo::restyle_damage::RECONSTRUCT_FLOW; use style::str::char_is_whitespace; use style::values::{self, Either, Auto}; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; use text; use text::TextRunScanner; use webrender_api; @@ -2858,22 +2859,22 @@ impl Fragment { SpecificFragmentInfo::TruncatedFragment(_) | SpecificFragmentInfo::Svg(_) | SpecificFragmentInfo::UnscannedText(_) => true } } /// Returns the 4D matrix representing this fragment's transform. pub fn transform_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<Transform3D<f32>> { - let operations = match self.style.get_box().transform.0 { + let list = &self.style.get_box().transform; + let transform = match list.to_transform_3d_matrix(Some(stacking_relative_border_box)) { + Some(transform) => transform, None => return None, - Some(ref operations) => operations, }; - let mut transform = Transform3D::identity(); let transform_origin = &self.style.get_box().transform_origin; let transform_origin_x = transform_origin.horizontal .to_used_value(stacking_relative_border_box.size.width) .to_f32_px(); let transform_origin_y = transform_origin.vertical .to_used_value(stacking_relative_border_box.size.height) @@ -2882,65 +2883,16 @@ impl Fragment { let pre_transform = Transform3D::create_translation(transform_origin_x, transform_origin_y, transform_origin_z); let post_transform = Transform3D::create_translation(-transform_origin_x, -transform_origin_y, -transform_origin_z); - for operation in operations { - let matrix = match *operation { - transform::ComputedOperation::Rotate(ax, ay, az, theta) => { - // https://www.w3.org/TR/css-transforms-1/#funcdef-rotate3d - // A direction vector that cannot be normalized, such as [0, 0, 0], will cause - // the rotation to not be applied, so we use identity matrix in this case. - let len = (ax * ax + ay * ay + az * az).sqrt(); - if len > 0. { - let theta = 2.0f32 * f32::consts::PI - theta.radians(); - Transform3D::create_rotation(ax / len, ay / len, az / len, - Radians::new(theta)) - } else { - Transform3D::identity() - } - } - transform::ComputedOperation::Perspective(d) => { - create_perspective_matrix(d) - } - transform::ComputedOperation::Scale(sx, sy, sz) => { - Transform3D::create_scale(sx, sy, sz) - } - transform::ComputedOperation::Translate(tx, ty, tz) => { - let tx = tx.to_used_value(stacking_relative_border_box.size.width).to_f32_px(); - let ty = ty.to_used_value(stacking_relative_border_box.size.height).to_f32_px(); - let tz = tz.to_f32_px(); - Transform3D::create_translation(tx, ty, tz) - } - transform::ComputedOperation::Matrix(m) => { - m.to_gfx_matrix() - } - transform::ComputedOperation::MatrixWithPercents(_) => { - // `-moz-transform` is not implemented in Servo yet. - unreachable!() - } - transform::ComputedOperation::Skew(theta_x, theta_y) => { - Transform3D::create_skew(Radians::new(theta_x.radians()), - Radians::new(theta_y.radians())) - } - transform::ComputedOperation::InterpolateMatrix { .. } | - transform::ComputedOperation::AccumulateMatrix { .. } => { - // TODO: Convert InterpolateMatrix/AccmulateMatrix into a valid Transform3D by - // the reference box. - Transform3D::identity() - } - }; - - transform = transform.pre_mul(&matrix); - } - Some(pre_transform.pre_mul(&transform).pre_mul(&post_transform)) } /// Returns the 4D matrix representing this fragment's perspective. pub fn perspective_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<Transform3D<f32>> { match self.style().get_box().perspective { Either::First(length) => { let perspective_origin = self.style().get_box().perspective_origin; @@ -2955,17 +2907,17 @@ impl Fragment { let pre_transform = Transform3D::create_translation(perspective_origin.x, perspective_origin.y, 0.0); let post_transform = Transform3D::create_translation(-perspective_origin.x, -perspective_origin.y, 0.0); - let perspective_matrix = create_perspective_matrix(length); + let perspective_matrix = TransformList::create_perspective_matrix(length); Some(pre_transform.pre_mul(&perspective_matrix).pre_mul(&post_transform)) } Either::Second(values::None_) => { None } } } @@ -3199,25 +3151,8 @@ impl Serialize for DebugId { } #[cfg(debug_assertions)] impl Serialize for DebugId { fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { serializer.serialize_u16(self.0) } } - -// TODO(gw): The transforms spec says that perspective length must -// be positive. However, there is some confusion between the spec -// and browser implementations as to handling the case of 0 for the -// perspective value. Until the spec bug is resolved, at least ensure -// that a provided perspective value of <= 0.0 doesn't cause panics -// and behaves as it does in other browsers. -// See https://lists.w3.org/Archives/Public/www-style/2016Jan/0020.html for more details. -#[inline] -fn create_perspective_matrix(d: Au) -> Transform3D<f32> { - let d = d.to_f32_px(); - if d <= 0.0 { - Transform3D::identity() - } else { - Transform3D::create_perspective(d) - } -}
--- a/servo/components/layout/model.rs +++ b/servo/components/layout/model.rs @@ -2,21 +2,20 @@ * 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/. */ //! Borders, padding, and margins. #![deny(unsafe_code)] use app_units::Au; -use euclid::{Transform3D, SideOffsets2D, Size2D}; +use euclid::{SideOffsets2D, Size2D}; use fragment::Fragment; use std::cmp::{max, min}; use std::fmt; -use style::computed_values::transform::ComputedMatrix; use style::logical_geometry::{LogicalMargin, WritingMode}; use style::properties::ComputedValues; use style::values::computed::{BorderCornerRadius, LengthOrPercentageOrAuto}; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrNone}; /// A collapsible margin. See CSS 2.1 § 8.3.1. #[derive(Clone, Copy, Debug)] pub struct AdjoiningMargins { @@ -503,30 +502,16 @@ pub fn specified_margin_from_style(style let margin_style = style.get_margin(); LogicalMargin::from_physical(writing_mode, SideOffsets2D::new( MaybeAuto::from_style(margin_style.margin_top, Au(0)).specified_or_zero(), MaybeAuto::from_style(margin_style.margin_right, Au(0)).specified_or_zero(), MaybeAuto::from_style(margin_style.margin_bottom, Au(0)).specified_or_zero(), MaybeAuto::from_style(margin_style.margin_left, Au(0)).specified_or_zero())) } -pub trait ToGfxMatrix { - fn to_gfx_matrix(&self) -> Transform3D<f32>; -} - -impl ToGfxMatrix for ComputedMatrix { - fn to_gfx_matrix(&self) -> Transform3D<f32> { - Transform3D::row_major( - self.m11 as f32, self.m12 as f32, self.m13 as f32, self.m14 as f32, - self.m21 as f32, self.m22 as f32, self.m23 as f32, self.m24 as f32, - self.m31 as f32, self.m32 as f32, self.m33 as f32, self.m34 as f32, - self.m41 as f32, self.m42 as f32, self.m43 as f32, self.m44 as f32) - } -} - /// A min-size and max-size constraint. The constructor has a optional `border` /// parameter, and when it is present the constraint will be subtracted. This is /// used to adjust the constraint for `box-sizing: border-box`, and when you do so /// make sure the size you want to clamp is intended to be used for content box. #[derive(Clone, Copy, Debug, Serialize)] pub struct SizeConstraint { min_size: Au, max_size: Option<Au>,
--- a/servo/components/style/custom_properties.rs +++ b/servo/components/style/custom_properties.rs @@ -12,17 +12,17 @@ use parser::ParserContext; use properties::{CSSWideKeyword, DeclaredValue}; use selectors::parser::SelectorParseError; use servo_arc::Arc; use std::ascii::AsciiExt; use std::borrow::{Borrow, Cow}; use std::collections::{HashMap, HashSet}; use std::fmt; use std::hash::Hash; -use style_traits::{HasViewportPercentage, ToCss, StyleParseError, ParseError}; +use style_traits::{ToCss, StyleParseError, ParseError}; /// A custom property name is just an `Atom`. /// /// Note that this does not include the `--` prefix pub type Name = Atom; /// Parse a custom property name. /// @@ -46,22 +46,16 @@ pub struct SpecifiedValue { first_token_type: TokenSerializationType, last_token_type: TokenSerializationType, /// Custom property names in var() functions. references: HashSet<Name>, } -impl HasViewportPercentage for SpecifiedValue { - fn has_viewport_percentage(&self) -> bool { - panic!("has_viewport_percentage called before resolving!"); - } -} - /// This struct is a cheap borrowed version of a `SpecifiedValue`. pub struct BorrowedSpecifiedValue<'a> { css: &'a str, first_token_type: TokenSerializationType, last_token_type: TokenSerializationType, references: Option<&'a HashSet<Name>>, }
--- a/servo/components/style/gecko_string_cache/mod.rs +++ b/servo/components/style/gecko_string_cache/mod.rs @@ -260,17 +260,17 @@ impl Atom { "Called from_static for a non-static atom!"); atom } /// Creates an atom from a dynamic atom pointer that has already had AddRef /// called on it. #[inline] pub unsafe fn from_addrefed(ptr: *mut nsIAtom) -> Self { - debug_assert!(!ptr.is_null()); + assert!(!ptr.is_null()); unsafe { Atom(WeakAtom::new(ptr)) } } /// Convert this atom into an addrefed nsIAtom pointer. #[inline] pub fn into_addrefed(self) -> *mut nsIAtom { @@ -373,17 +373,17 @@ impl From<String> for Atom { fn from(string: String) -> Atom { Atom::from(&*string) } } impl From<*mut nsIAtom> for Atom { #[inline] fn from(ptr: *mut nsIAtom) -> Atom { - debug_assert!(!ptr.is_null()); + assert!(!ptr.is_null()); unsafe { let ret = Atom(WeakAtom::new(ptr)); if !ret.is_static() { Gecko_AddRefAtom(ptr); } ret } }
--- a/servo/components/style/macros.rs +++ b/servo/components/style/macros.rs @@ -52,36 +52,34 @@ macro_rules! define_numbered_css_keyword match *self { $( $name::$variant => dest.write_str($css) ),+ } } } } } -/// A macro for implementing `ComputedValueAsSpecified`, `Parse` -/// and `HasViewportPercentage` traits for the enums defined -/// using `define_css_keyword_enum` macro. +/// A macro for implementing `ComputedValueAsSpecified`, and `Parse` traits for +/// the enums defined using `define_css_keyword_enum` macro. /// /// NOTE: We should either move `Parse` trait to `style_traits` /// or `define_css_keyword_enum` macro to this crate, but that /// may involve significant cleanup in both the crates. macro_rules! add_impls_for_keyword_enum { ($name:ident) => { impl $crate::parser::Parse for $name { #[inline] fn parse<'i, 't>(_context: &$crate::parser::ParserContext, input: &mut ::cssparser::Parser<'i, 't>) -> Result<Self, ::style_traits::ParseError<'i>> { $name::parse(input) } } impl $crate::values::computed::ComputedValueAsSpecified for $name {} - no_viewport_percentage!($name); }; } macro_rules! define_keyword_type { ($name: ident, $css: expr) => { #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Animate, Clone, ComputeSquaredDistance, Copy, PartialEq)] @@ -99,11 +97,10 @@ macro_rules! define_keyword_type { input: &mut ::cssparser::Parser<'i, 't>) -> Result<$name, ::style_traits::ParseError<'i>> { input.expect_ident_matching($css).map(|_| $name).map_err(|e| e.into()) } } impl $crate::values::computed::ComputedValueAsSpecified for $name {} impl $crate::values::animated::AnimatedValueAsComputed for $name {} - no_viewport_percentage!($name); }; }
--- a/servo/components/style/properties/helpers.mako.rs +++ b/servo/components/style/properties/helpers.mako.rs @@ -78,18 +78,16 @@ </%doc> <%def name="vector_longhand(name, animation_value_type=None, allow_empty=False, separator='Comma', need_animatable=False, **kwargs)"> <%call expr="longhand(name, animation_value_type=animation_value_type, vector=True, need_animatable=need_animatable, **kwargs)"> #[allow(unused_imports)] use smallvec::SmallVec; use std::fmt; - #[allow(unused_imports)] - use style_traits::HasViewportPercentage; use style_traits::{Separator, ToCss}; pub mod single_value { #[allow(unused_imports)] use cssparser::{Parser, BasicParseError}; #[allow(unused_imports)] use parser::{Parse, ParserContext}; #[allow(unused_imports)] @@ -174,17 +172,17 @@ dest.write_str(::style_traits::${separator}::separator())?; i.to_css(dest)?; } Ok(()) } } /// The specified value of ${name}. - #[derive(Clone, Debug, HasViewportPercentage, PartialEq)] + #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct SpecifiedValue(pub Vec<single_value::SpecifiedValue>); impl ToCss for SpecifiedValue { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write, { let mut iter = self.0.iter(); @@ -415,17 +413,16 @@ 'gecko_constant_prefix', 'gecko_enum_prefix', 'extra_gecko_values', 'extra_servo_values', 'custom_consts', 'gecko_inexhaustive', ]} keyword = keyword=Keyword(name, values, **keyword_kwargs) %> <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> use properties::longhands::system_font::SystemFont; - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { use cssparser::Parser; use parser::{Parse, ParserContext}; use style_traits::{ToCss, ParseError}; define_css_keyword_enum! { T: % for value in keyword.values_for(product): @@ -524,17 +521,16 @@ } } } % else: use values::computed::ComputedValueAsSpecified; impl ComputedValueAsSpecified for SpecifiedValue {} % endif - no_viewport_percentage!(SpecifiedValue); </%call> </%def> <%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue', cast_to=None)"> <% if not values: values = keyword.values_for(product) maybe_cast = "as %s" % cast_to if cast_to else "" @@ -943,17 +939,17 @@ % endif use values::specified::${length_type}; pub mod computed_value { pub type T = ::values::computed::${length_type}; } #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)] + #[derive(Clone, Debug, PartialEq, ToCss)] pub struct SpecifiedValue(pub ${length_type}); % if length_type == "MozLength": impl SpecifiedValue { /// Returns the `auto` value. pub fn auto() -> Self { use values::specified::length::LengthOrPercentageOrAuto; SpecifiedValue(MozLength::LengthOrPercentageOrAuto(LengthOrPercentageOrAuto::Auto))
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs +++ b/servo/components/style/properties/helpers/animated_properties.mako.rs @@ -3,17 +3,16 @@ * 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::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; @@ -49,16 +48,17 @@ use values::computed::{Angle, BorderCorn use values::computed::{ClipRect, Context, ComputedUrl, ComputedValueAsSpecified}; use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; use values::computed::{LengthOrPercentageOrNone, MaxLength, NonNegativeAu}; use values::computed::{NonNegativeNumber, Number, NumberOrPercentage, Percentage}; use values::computed::{PositiveIntegerOrAuto, ToComputedValue}; #[cfg(feature = "gecko")] use values::computed::MozLength; use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal}; use values::computed::length::NonNegativeLengthOrPercentage; +use values::computed::transform::DirectionVector; use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::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}; /// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list @@ -209,17 +209,16 @@ pub enum TransitionProperty { ${prop.camel_case}, % endif % endfor /// Unrecognized property which could be any non-transitionable, custom property, or /// unknown property. Unsupported(CustomIdent) } -no_viewport_percentage!(TransitionProperty); impl ComputedValueAsSpecified for TransitionProperty {} impl TransitionProperty { /// Iterates over each longhand property. pub fn each<F: FnMut(&TransitionProperty) -> ()>(mut cb: F) { % for prop in data.longhands: % if prop.transitionable: @@ -955,17 +954,17 @@ impl ToAnimatedZero for TransformOperati ty.to_animated_zero()?, tz.to_animated_zero()?, )) }, TransformOperation::Scale(..) => { 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); + let (x, y, z, _) = TransformList::get_normalized_vector_and_angle(x, y, z, a); 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 @@ -1032,18 +1031,20 @@ impl Animate for TransformOperation { 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); + let (fx, fy, fz, fa) = + TransformList::get_normalized_vector_and_angle(fx, fy, fz, fa); + let (tx, ty, tz, ta) = + TransformList::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)?, @@ -1446,39 +1447,34 @@ pub struct MatrixDecomposed3D { /// The skew component of the transformation. pub skew: Skew, /// The perspective component of the transformation. pub perspective: Perspective, /// The quaternion used to represent the rotation. pub quaternion: Quaternion, } -/// A wrapper of Point3D to represent the direction vector (rotate axis) for Rotate3D. -#[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct DirectionVector(Point3D<f64>); - impl Quaternion { /// Return a quaternion from a unit direction vector and angle (unit: radian). #[inline] fn from_direction_and_angle(vector: &DirectionVector, angle: f64) -> Self { - debug_assert!((vector.length() - 1.).abs() < 0.0001f64, - "Only accept an unit direction vector to create a quaternion"); + debug_assert!((vector.length() - 1.).abs() < 0.0001, + "Only accept an unit direction vector to create a quaternion"); // Reference: // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation // // if the direction axis is (x, y, z) = xi + yj + zk, // and the angle is |theta|, this formula can be done using // an extension of Euler's formula: // q = cos(theta/2) + (xi + yj + zk)(sin(theta/2)) // = cos(theta/2) + // x*sin(theta/2)i + y*sin(theta/2)j + z*sin(theta/2)k - Quaternion(vector.0.x * (angle / 2.).sin(), - vector.0.y * (angle / 2.).sin(), - vector.0.z * (angle / 2.).sin(), + Quaternion(vector.x as f64 * (angle / 2.).sin(), + vector.y as f64 * (angle / 2.).sin(), + vector.z as f64 * (angle / 2.).sin(), (angle / 2.).cos()) } /// Calculate the dot product. #[inline] fn dot(&self, other: &Self) -> f64 { self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3 } @@ -1490,57 +1486,16 @@ impl ComputeSquaredDistance for Quaterni // Use quaternion vectors to get the angle difference. Both q1 and q2 are unit vectors, // so we can get their angle difference by: // cos(theta/2) = (q1 dot q2) / (|q1| * |q2|) = q1 dot q2. let distance = self.dot(other).max(-1.0).min(1.0).acos() * 2.0; Ok(SquaredDistance::Value(distance * distance)) } } -impl DirectionVector { - /// Create a DirectionVector. - #[inline] - fn new(x: f32, y: f32, z: f32) -> Self { - DirectionVector(Point3D::new(x as f64, y as f64, z as f64)) - } - - /// Return the normalized direction vector. - #[inline] - fn normalize(&mut self) -> bool { - let len = self.length(); - if len > 0. { - self.0.x = self.0.x / len; - self.0.y = self.0.y / len; - self.0.z = self.0.z / len; - true - } else { - false - } - } - - /// Get the length of this vector. - #[inline] - fn length(&self) -> f64 { - self.0.to_array().iter().fold(0f64, |sum, v| sum + v * v).sqrt() - } -} - -/// Return the normalized direction vector and its angle. -// A direction vector that cannot be normalized, such as [0,0,0], will cause the -// rotation to not be applied. i.e. Use an identity matrix or rotate3d(0, 0, 1, 0). -fn get_normalized_vector_and_angle(x: f32, y: f32, z: f32, angle: Angle) - -> (f32, f32, f32, Angle) { - let mut vector = DirectionVector::new(x, y, z); - if vector.normalize() { - (vector.0.x as f32, vector.0.y as f32, vector.0.z as f32, angle) - } else { - (0., 0., 1., Angle::zero()) - } -} - /// Decompose a 3D matrix. /// https://drafts.csswg.org/css-transforms/#decomposing-a-3d-matrix fn decompose_3d_matrix(mut matrix: ComputedMatrix) -> Result<MatrixDecomposed3D, ()> { // Normalize the matrix. if matrix.m44 == 0.0 { return Err(()); } @@ -1687,18 +1642,18 @@ fn decompose_3d_matrix(mut matrix: Compu fn decompose_2d_matrix(matrix: &ComputedMatrix) -> Result<MatrixDecomposed3D, ()> { // The index is column-major, so the equivalent transform matrix is: // | m11 m21 0 m41 | => | m11 m21 | and translate(m41, m42) // | m12 m22 0 m42 | | m12 m22 | // | 0 0 1 0 | // | 0 0 0 1 | let (mut m11, mut m12) = (matrix.m11, matrix.m12); let (mut m21, mut m22) = (matrix.m21, matrix.m22); + // Check if this is a singular matrix. if m11 * m22 == m12 * m21 { - // singular matrix return Err(()); } let mut scale_x = (m11 * m11 + m12 * m12).sqrt(); m11 /= scale_x; m12 /= scale_x; let mut shear_xy = m11 * m21 + m12 * m22; @@ -1706,18 +1661,20 @@ fn decompose_2d_matrix(matrix: &Computed m22 -= m12 * shear_xy; let scale_y = (m21 * m21 + m22 * m22).sqrt(); m21 /= scale_y; m22 /= scale_y; shear_xy /= scale_y; let determinant = m11 * m22 - m12 * m21; - debug_assert!(0.99 < determinant.abs() && determinant.abs() < 1.01, - "determinant should now be 1 or -1"); + // Determinant should now be 1 or -1. + if 0.99 > determinant.abs() || determinant.abs() > 1.01 { + return Err(()); + } if determinant < 0. { m11 = -m11; m12 = -m12; shear_xy = -shear_xy; scale_x = -scale_x; } @@ -2195,18 +2152,20 @@ impl ComputeSquaredDistance for Transfor 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_and_angle(fx, fy, fz, fa); - let (tx, ty, tz, angle2) = get_normalized_vector_and_angle(tx, ty, tz, ta); + let (fx, fy, fz, angle1) = + TransformList::get_normalized_vector_and_angle(fx, fy, fz, fa); + let (tx, ty, tz, angle2) = + TransformList::get_normalized_vector_and_angle(tx, ty, tz, ta); if (fx, fy, fz) == (tx, ty, tz) { angle1.compute_squared_distance(&angle2) } else { let v1 = DirectionVector::new(fx, fy, fz); let v2 = DirectionVector::new(tx, ty, tz); let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64()); let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64()); q1.compute_squared_distance(&q2) @@ -2243,29 +2202,38 @@ impl ComputeSquaredDistance for Transfor _ => Err(()), } } } impl ComputeSquaredDistance for TransformList { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { - let this = self.0.as_ref().map_or(&[][..], |l| l); - let other = other.0.as_ref().map_or(&[][..], |l| l); + let list1 = self.0.as_ref().map_or(&[][..], |l| l); + let list2 = other.0.as_ref().map_or(&[][..], |l| l); - this.iter().zip_longest(other).map(|it| { + let squared_dist: Result<SquaredDistance, _> = list1.iter().zip_longest(list2).map(|it| { match it { EitherOrBoth::Both(this, other) => { this.compute_squared_distance(other) }, EitherOrBoth::Left(list) | EitherOrBoth::Right(list) => { list.to_animated_zero()?.compute_squared_distance(list) }, } - }).sum() + }).sum(); + + // Roll back to matrix interpolation if there is any Err(()) in the transform lists, such + // as mismatched transform functions. + if let Err(_) = squared_dist { + let matrix1: ComputedMatrix = self.to_transform_3d_matrix(None).ok_or(())?.into(); + let matrix2: ComputedMatrix = other.to_transform_3d_matrix(None).ok_or(())?.into(); + return matrix1.compute_squared_distance(&matrix2); + } + squared_dist } } impl ToAnimatedZero for TransformList { #[inline] fn to_animated_zero(&self) -> Result<Self, ()> { match self.0 { None => Ok(TransformList(None)),
--- a/servo/components/style/properties/longhand/background.mako.rs +++ b/servo/components/style/properties/longhand/background.mako.rs @@ -64,17 +64,16 @@ pub mod computed_value { pub use super::RepeatKeyword; #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct T(pub RepeatKeyword, pub RepeatKeyword); } - no_viewport_percentage!(SpecifiedValue); impl ToCss for computed_value::T { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match (self.0, self.1) { (RepeatKeyword::Repeat, RepeatKeyword::NoRepeat) => dest.write_str("repeat-x"), (RepeatKeyword::NoRepeat, RepeatKeyword::Repeat) => dest.write_str("repeat-y"), (horizontal, vertical) => { horizontal.to_css(dest)?;
--- a/servo/components/style/properties/longhand/border.mako.rs +++ b/servo/components/style/properties/longhand/border.mako.rs @@ -73,17 +73,16 @@ <%helpers:longhand name="-moz-border-${side}-colors" animation_value_type="discrete" spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-border-*-colors)" products="gecko" flags="APPLIES_TO_FIRST_LETTER" ignored_when_colors_disabled="True"> use std::fmt; use style_traits::ToCss; use values::specified::RGBAColor; - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { use cssparser::RGBA; #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct T(pub Option<Vec<RGBA>>); } @@ -225,17 +224,16 @@ flags="APPLIES_TO_FIRST_LETTER", boxed=True)} <%helpers:longhand name="border-image-repeat" animation_value_type="discrete" flags="APPLIES_TO_FIRST_LETTER" spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat"> use style_traits::ToCss; - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { pub use super::RepeatKeyword; #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Clone, Debug, PartialEq, ToCss)] pub struct T(pub RepeatKeyword, pub RepeatKeyword); }
--- a/servo/components/style/properties/longhand/box.mako.rs +++ b/servo/components/style/properties/longhand/box.mako.rs @@ -31,17 +31,16 @@ values += """grid inline-grid ruby ruby-base ruby-base-container ruby-text ruby-text-container contents flow-root -webkit-box -webkit-inline-box -moz-box -moz-inline-box -moz-grid -moz-inline-grid -moz-grid-group -moz-grid-line -moz-stack -moz-inline-stack -moz-deck -moz-popup -moz-groupbox""".split() %> use values::computed::ComputedValueAsSpecified; use style_traits::ToCss; - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { pub use super::SpecifiedValue as T; impl T { /// Returns whether this "display" value is the display of a flex or /// grid container. /// @@ -217,17 +216,16 @@ needs_conversion="True" animation_value_type="discrete" gecko_enum_prefix="StyleFloat" gecko_inexhaustive="True" gecko_ffi_name="mFloat" gecko_pref_ident="float_" flags="APPLIES_TO_FIRST_LETTER" spec="https://drafts.csswg.org/css-box/#propdef-float"> - no_viewport_percentage!(SpecifiedValue); impl ToComputedValue for SpecifiedValue { type ComputedValue = computed_value::T; #[inline] fn to_computed_value(&self, context: &Context) -> computed_value::T { let ltr = context.style().writing_mode.is_bidi_ltr(); // https://drafts.csswg.org/css-logical-props/#float-clear match *self { @@ -256,17 +254,16 @@ // https://drafts.csswg.org/css-logical-props/#float-clear extra_specified="inline-start inline-end" needs_conversion="True" gecko_inexhaustive="True" animation_value_type="discrete" gecko_enum_prefix="StyleClear" gecko_ffi_name="mBreakType" spec="https://www.w3.org/TR/CSS2/visuren.html#flow-control"> - no_viewport_percentage!(SpecifiedValue); impl ToComputedValue for SpecifiedValue { type ComputedValue = computed_value::T; #[inline] fn to_computed_value(&self, context: &Context) -> computed_value::T { let ltr = context.style().writing_mode.is_bidi_ltr(); // https://drafts.csswg.org/css-logical-props/#float-clear match *self { @@ -322,17 +319,17 @@ "baseline sub super top text-top middle bottom text-bottom", extra_gecko_values="-moz-middle-with-baseline") %> <% vertical_align_keywords = vertical_align.keyword.values_for(product) %> ${helpers.gecko_keyword_conversion(vertical_align.keyword)} /// The `vertical-align` value. #[allow(non_camel_case_types)] - #[derive(Clone, Debug, HasViewportPercentage, PartialEq)] + #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum SpecifiedValue { % for keyword in vertical_align_keywords: ${to_rust_ident(keyword)}, % endfor LengthOrPercentage(specified::LengthOrPercentage), } @@ -566,17 +563,16 @@ -> Result<Self, ParseError<'i>> { if let Ok(name) = input.try(|input| KeyframesName::parse(context, input)) { Ok(SpecifiedValue(Some(name))) } else { input.expect_ident_matching("none").map(|_| SpecifiedValue(None)).map_err(|e| e.into()) } } } - no_viewport_percentage!(SpecifiedValue); pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<SpecifiedValue,ParseError<'i>> { SpecifiedValue::parse(context, input) } impl ComputedValueAsSpecified for SpecifiedValue {} </%helpers:vector_longhand> @@ -636,17 +632,16 @@ if number < 0.0 { return Err(StyleParseError::UnspecifiedError.into()); } Ok(SpecifiedValue::Number(number)) } } - no_viewport_percentage!(SpecifiedValue); #[inline] pub fn get_initial_value() -> computed_value::T { get_initial_specified_value() } #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { @@ -837,17 +832,17 @@ } /// Describes a single parsed /// [Transform Function](https://drafts.csswg.org/css-transforms/#typedef-transform-function). /// /// Multiple transform functions compose a transformation. /// /// Some transformations can be expressed by other more general functions. - #[derive(Clone, Debug, HasViewportPercentage, PartialEq)] + #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum SpecifiedOperation { /// Represents a 2D 2x3 matrix. Matrix(Matrix<Number>), /// Represents a 3D 4x4 matrix with percentage and length values. /// For `moz-transform`. PrefixedMatrix(Matrix<Number, LoPoNumber>), /// Represents a 3D 4x4 matrix. @@ -986,17 +981,17 @@ SpecifiedOperation::AccumulateMatrix { ref from_list, ref to_list, count } => { write!(dest, "accumulatematrix({}, {}, {})", Css(from_list), Css(to_list), Css(count)) } } } } - #[derive(Clone, Debug, HasViewportPercentage, PartialEq)] + #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct SpecifiedValue(Vec<SpecifiedOperation>); impl ToCss for SpecifiedValue { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { if self.0.is_empty() { return dest.write_str("none") @@ -1700,17 +1695,16 @@ <%helpers:longhand name="contain" animation_value_type="discrete" products="gecko" need_clone="True" flags="FIXPOS_CB" spec="https://drafts.csswg.org/css-contain/#contain-property"> use std::fmt; use style_traits::ToCss; use values::computed::ComputedValueAsSpecified; impl ComputedValueAsSpecified for SpecifiedValue {} - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { pub type T = super::SpecifiedValue; } bitflags! { #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub flags SpecifiedValue: u8 { @@ -1845,17 +1839,16 @@ <%helpers:longhand name="will-change" products="gecko" animation_value_type="discrete" spec="https://drafts.csswg.org/css-will-change/#will-change"> use std::fmt; use style_traits::ToCss; use values::CustomIdent; use values::computed::ComputedValueAsSpecified; impl ComputedValueAsSpecified for SpecifiedValue {} - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { pub use super::SpecifiedValue as T; } #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum SpecifiedValue { @@ -1919,17 +1912,16 @@ animation_value_type="discrete" spec="https://compat.spec.whatwg.org/#touch-action"> use gecko_bindings::structs; use std::fmt; use style_traits::ToCss; use values::computed::ComputedValueAsSpecified; impl ComputedValueAsSpecified for SpecifiedValue {} - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { pub use super::SpecifiedValue as T; } bitflags! { /// These constants match Gecko's `NS_STYLE_TOUCH_ACTION_*` constants. pub flags SpecifiedValue: u8 {
--- a/servo/components/style/properties/longhand/color.mako.rs +++ b/servo/components/style/properties/longhand/color.mako.rs @@ -29,17 +29,16 @@ fn from_computed_value(computed: &computed_value::T) -> Self { SpecifiedValue(Color::rgba(*computed).into()) } } #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Clone, Debug, PartialEq, ToCss)] pub struct SpecifiedValue(pub Color); - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { use cssparser; pub type T = cssparser::RGBA; } #[inline] pub fn get_initial_value() -> computed_value::T { RGBA::new(0, 0, 0, 255) // black
--- a/servo/components/style/properties/longhand/counters.mako.rs +++ b/servo/components/style/properties/longhand/counters.mako.rs @@ -18,17 +18,16 @@ #[cfg(feature = "servo")] use super::list_style_type; pub use self::computed_value::T as SpecifiedValue; pub use self::computed_value::ContentItem; impl ComputedValueAsSpecified for SpecifiedValue {} - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { use cssparser; use std::fmt; use style_traits::ToCss; #[cfg(feature = "gecko")] use values::specified::url::SpecifiedUrl; @@ -291,17 +290,16 @@ } } #[inline] pub fn get_initial_value() -> computed_value::T { computed_value::T(Vec::new()) } - no_viewport_percentage!(SpecifiedValue); impl ToCss for SpecifiedValue { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write, { if self.0.is_empty() { return dest.write_str("none"); }
--- a/servo/components/style/properties/longhand/font.mako.rs +++ b/servo/components/style/properties/longhand/font.mako.rs @@ -71,17 +71,16 @@ macro_rules! impl_gecko_keyword_conversi <%helpers:longhand name="font-family" animation_value_type="discrete" need_index="True" boxed="${product == 'gecko'}" flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" spec="https://drafts.csswg.org/css-fonts/#propdef-font-family"> use properties::longhands::system_font::SystemFont; use self::computed_value::{FontFamily, FamilyName}; use std::fmt; use style_traits::ToCss; - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { use cssparser::{CssStringWriter, Parser, serialize_identifier}; use std::fmt::{self, Write}; use Atom; use style_traits::{ToCss, ParseError}; pub use self::FontFamily as SingleComputedValue; @@ -425,17 +424,16 @@ macro_rules! impl_gecko_keyword_conversi flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", animation_value_type="discrete")} <%helpers:longhand name="font-weight" need_clone="True" animation_value_type="ComputedValue" flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight"> use properties::longhands::system_font::SystemFont; - no_viewport_percentage!(SpecifiedValue); #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss)] pub enum SpecifiedValue { Normal, Bold, Bolder, Lighter, @@ -592,17 +590,17 @@ macro_rules! impl_gecko_keyword_conversi </%helpers:longhand> <%helpers:longhand name="font-size" need_clone="True" animation_value_type="NonNegativeAu" flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" allow_quirks="True" spec="https://drafts.csswg.org/css-fonts/#propdef-font-size"> use app_units::Au; use properties::longhands::system_font::SystemFont; use std::fmt; - use style_traits::{HasViewportPercentage, ToCss}; + use style_traits::ToCss; use values::FONT_MEDIUM_PX; use values::computed::NonNegativeAu; use values::specified::{AllowQuirks, FontRelativeLength, LengthOrPercentage, NoCalcLength}; use values::specified::length::FontBaseSize; impl ToCss for SpecifiedValue { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self { @@ -610,25 +608,16 @@ macro_rules! impl_gecko_keyword_conversi SpecifiedValue::Keyword(kw, _) => kw.to_css(dest), SpecifiedValue::Smaller => dest.write_str("smaller"), SpecifiedValue::Larger => dest.write_str("larger"), SpecifiedValue::System(sys) => sys.to_css(dest), } } } - impl HasViewportPercentage for SpecifiedValue { - fn has_viewport_percentage(&self) -> bool { - match *self { - SpecifiedValue::Length(ref lop) => lop.has_viewport_percentage(), - _ => false - } - } - } - #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum SpecifiedValue { Length(specified::LengthOrPercentage), /// A keyword value, along with a ratio. /// The ratio in any specified keyword value /// will be 1, but we cascade keywordness even /// after font-relative (percent and em) values @@ -1063,17 +1052,16 @@ macro_rules! impl_gecko_keyword_conversi </%helpers:longhand> <%helpers:longhand products="gecko" name="font-size-adjust" animation_value_type="longhands::font_size_adjust::computed_value::T" flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust"> use properties::longhands::system_font::SystemFont; - no_viewport_percentage!(SpecifiedValue); #[derive(Clone, Copy, Debug, PartialEq, ToCss)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum SpecifiedValue { None, Number(specified::Number), System(SystemFont), } @@ -1185,17 +1173,16 @@ macro_rules! impl_gecko_keyword_conversi <%helpers:longhand products="gecko" name="font-synthesis" animation_value_type="discrete" flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis"> use std::fmt; use style_traits::ToCss; use values::computed::ComputedValueAsSpecified; impl ComputedValueAsSpecified for SpecifiedValue {} - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { pub use super::SpecifiedValue as T; } #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct SpecifiedValue { @@ -1297,17 +1284,16 @@ macro_rules! impl_gecko_keyword_conversi <%helpers:longhand name="font-variant-alternates" products="gecko" animation_value_type="discrete" flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates"> use properties::longhands::system_font::SystemFont; use std::fmt; use style_traits::ToCss; use values::CustomIdent; - no_viewport_percentage!(SpecifiedValue); #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum VariantAlternates { Stylistic(CustomIdent), Styleset(Box<[CustomIdent]>), CharacterVariant(Box<[CustomIdent]>), Swash(CustomIdent), @@ -1507,17 +1493,16 @@ macro_rules! exclusive_value { <%helpers:longhand name="font-variant-east-asian" products="gecko" animation_value_type="discrete" flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian"> use properties::longhands::system_font::SystemFont; use std::fmt; use style_traits::ToCss; - no_viewport_percentage!(SpecifiedValue); bitflags! { #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub flags VariantEastAsian: u16 { const NORMAL = 0, const JIS78 = 0x01, const JIS83 = 0x02, const JIS90 = 0x04, @@ -1653,17 +1638,16 @@ macro_rules! exclusive_value { <%helpers:longhand name="font-variant-ligatures" products="gecko" animation_value_type="discrete" flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures"> use properties::longhands::system_font::SystemFont; use std::fmt; use style_traits::ToCss; - no_viewport_percentage!(SpecifiedValue); bitflags! { #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub flags VariantLigatures: u16 { const NORMAL = 0, const NONE = 0x01, const COMMON_LIGATURES = 0x02, const NO_COMMON_LIGATURES = 0x04, @@ -1813,17 +1797,16 @@ macro_rules! exclusive_value { <%helpers:longhand name="font-variant-numeric" products="gecko" animation_value_type="discrete" flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-numeric"> use properties::longhands::system_font::SystemFont; use std::fmt; use style_traits::ToCss; - no_viewport_percentage!(SpecifiedValue); bitflags! { #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub flags VariantNumeric: u8 { const NORMAL = 0, const LINING_NUMS = 0x01, const OLDSTYLE_NUMS = 0x02, const PROPORTIONAL_NUMS = 0x04, @@ -1976,17 +1959,16 @@ macro_rules! exclusive_value { use properties::longhands::system_font::SystemFont; use values::generics::FontSettings; #[derive(Clone, Debug, PartialEq, ToCss)] pub enum SpecifiedValue { Value(computed_value::T), System(SystemFont) } - no_viewport_percentage!(SpecifiedValue); <%self:simple_system_boilerplate name="font_feature_settings"></%self:simple_system_boilerplate> pub mod computed_value { use values::generics::{FontSettings, FontSettingTagInt}; pub type T = FontSettings<FontSettingTagInt>; } @@ -2018,17 +2000,16 @@ https://drafts.csswg.org/css-fonts-4/#lo spec="${variation_spec}"> use values::computed::ComputedValueAsSpecified; use values::generics::FontSettings; impl ComputedValueAsSpecified for SpecifiedValue {} pub type SpecifiedValue = computed_value::T; - no_viewport_percentage!(SpecifiedValue); pub mod computed_value { use values::generics::{FontSettings, FontSettingTagFloat}; pub type T = FontSettings<FontSettingTagFloat>; } #[inline] @@ -2046,17 +2027,16 @@ https://drafts.csswg.org/css-fonts-4/#lo <%helpers:longhand name="font-language-override" products="gecko" animation_value_type="discrete"