author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Fri, 07 Aug 2015 13:13:06 +0200 | |
changeset 256773 | 3e51753a099f8014b3dc37ed3fa27b887842735b |
parent 256673 | 91de9c6708006c45824bc972b576fb5532a8b9ff (current diff) |
parent 256772 | 51ebb22f97a07958624c0ed6c52d9181f08e199f (diff) |
child 256785 | d098f59caf971c3f5429353674ffce6bf8c85cfe |
child 256807 | 90601e5616c31b85973c68a99d4800f98fc1f9c5 |
child 256828 | a2cfbfac8ae898ff1e16ec893dfbbc247c8f8ee2 |
push id | 29187 |
push user | cbook@mozilla.com |
push date | Fri, 07 Aug 2015 11:13:32 +0000 |
treeherder | mozilla-central@3e51753a099f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 42.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
browser/app/profile/firefox.js | file | annotate | diff | comparison | revisions | |
browser/locales/en-US/chrome/browser/browser.dtd | file | annotate | diff | comparison | revisions | |
browser/themes/linux/browser.css | file | annotate | diff | comparison | revisions | |
build/autoconf/gcc-pr49911.m4 | file | annotate | diff | comparison | revisions | |
build/package/mac_osx/pkg-dmg | file | annotate | diff | comparison | revisions | |
dom/workers/test/serviceworkers/client_focus_worker.js | file | annotate | diff | comparison | revisions | |
dom/workers/test/serviceworkers/sw_clients/focus_stealing_client.html | file | annotate | diff | comparison | revisions | |
memory/jemalloc/Makefile.in | file | annotate | diff | comparison | revisions | |
mobile/android/chrome/content/browser.js | file | annotate | diff | comparison | revisions |
--- a/accessible/atk/AccessibleWrap.cpp +++ b/accessible/atk/AccessibleWrap.cpp @@ -1127,16 +1127,20 @@ a11y::ProxyDestroyed(ProxyAccessible* aP } nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { nsresult rv = Accessible::HandleAccEvent(aEvent); NS_ENSURE_SUCCESS(rv, rv); + if (IPCAccessibilityActive()) { + return NS_OK; + } + Accessible* accessible = aEvent->GetAccessible(); NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE); // The accessible can become defunct if we have an xpcom event listener // which decides it would be fun to change the DOM and flush layout. if (accessible->IsDefunct()) return NS_OK;
--- a/accessible/mac/AccessibleWrap.mm +++ b/accessible/mac/AccessibleWrap.mm @@ -89,16 +89,20 @@ AccessibleWrap::Shutdown () nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; nsresult rv = Accessible::HandleAccEvent(aEvent); NS_ENSURE_SUCCESS(rv, rv); + if (IPCAccessibilityActive()) { + return NS_OK; + } + uint32_t eventType = aEvent->GetEventType(); // ignore everything but focus-changed, value-changed, caret and selection // events for now. if (eventType != nsIAccessibleEvent::EVENT_FOCUS && eventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE && eventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED && eventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED)
--- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -1203,16 +1203,20 @@ AccessibleWrap::GetNativeInterface(void* // Accessible nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { nsresult rv = Accessible::HandleAccEvent(aEvent); NS_ENSURE_SUCCESS(rv, rv); + if (IPCAccessibilityActive()) { + return NS_OK; + } + uint32_t eventType = aEvent->GetEventType(); static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY, "MSAA event map skewed"); NS_ENSURE_TRUE(eventType > 0 && eventType < ArrayLength(gWinEventMap), NS_ERROR_FAILURE); uint32_t winEvent = gWinEventMap[eventType];
--- a/aclocal.m4 +++ b/aclocal.m4 @@ -14,17 +14,16 @@ builtin(include, build/autoconf/nspr-bui builtin(include, build/autoconf/nss.m4)dnl builtin(include, build/autoconf/pkg.m4)dnl builtin(include, build/autoconf/codeset.m4)dnl builtin(include, build/autoconf/altoptions.m4)dnl builtin(include, build/autoconf/mozprog.m4)dnl builtin(include, build/autoconf/mozheader.m4)dnl builtin(include, build/autoconf/mozcommonheader.m4)dnl builtin(include, build/autoconf/lto.m4)dnl -builtin(include, build/autoconf/gcc-pr49911.m4)dnl builtin(include, build/autoconf/llvm-pr8927.m4)dnl builtin(include, build/autoconf/frameptr.m4)dnl builtin(include, build/autoconf/compiler-opts.m4)dnl builtin(include, build/autoconf/expandlibs.m4)dnl builtin(include, build/autoconf/arch.m4)dnl builtin(include, build/autoconf/android.m4)dnl builtin(include, build/autoconf/zlib.m4)dnl builtin(include, build/autoconf/linux.m4)dnl
--- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -1148,18 +1148,19 @@ pref("dom.requestSync.enabled", true); // Resample touch events on b2g pref("gfx.touch.resample", true); // Comma separated list of activity names that can only be provided by // the system app in dev mode. pref("dom.activities.developer_mode_only", "import-app"); // mulet apparently loads firefox.js as well as b2g.js, so we have to explicitly -// disable serviceworkers here to get them disabled in mulet. +// disable serviceworkers and push here to get them disabled in mulet. pref("dom.serviceWorkers.enabled", false); +pref("dom.push.enabled", false); // Retain at most 10 processes' layers buffers pref("layers.compositor-lru-size", 10); // Enable Cardboard VR on mobile, assuming VR at all is enabled pref("dom.vr.cardboard.enabled", true); // In B2G by deafult any AudioChannelAgent is muted when created.
--- a/b2g/components/MailtoProtocolHandler.js +++ b/b2g/components/MailtoProtocolHandler.js @@ -35,16 +35,16 @@ MailtoProtocolHandler.prototype = { cpmm.sendAsyncMessage("mail-handler", { URI: aURI.spec, type: "mail" }); throw Components.results.NS_ERROR_ILLEGAL_VALUE; }, newChannel: function Proto_newChannel(aURI) { - return newChannel2(aURI, null); + return this.newChannel2(aURI, null); }, classID: Components.ID("{50777e53-0331-4366-a191-900999be386c}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]) }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MailtoProtocolHandler]);
--- a/b2g/components/SmsProtocolHandler.js +++ b/b2g/components/SmsProtocolHandler.js @@ -61,16 +61,16 @@ SmsProtocolHandler.prototype = { type: "websms/sms", body: body }); } throw Components.results.NS_ERROR_ILLEGAL_VALUE; }, newChannel: function Proto_newChannel(aURI) { - return newChannel2(aURI, null); + return this.newChannel2(aURI, null); }, classID: Components.ID("{81ca20cb-0dad-4e32-8566-979c8998bd73}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]) }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SmsProtocolHandler]);
--- a/b2g/components/TelProtocolHandler.js +++ b/b2g/components/TelProtocolHandler.js @@ -47,16 +47,16 @@ TelProtocolHandler.prototype = { number: number, type: "webtelephony/number" }); } throw Components.results.NS_ERROR_ILLEGAL_VALUE; }, newChannel: function Proto_newChannel(aURI) { - return newChannel2(aURI, null); + return this.newChannel2(aURI, null); }, classID: Components.ID("{782775dd-7351-45ea-aff1-0ffa872cfdd2}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]) }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelProtocolHandler]);
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1939,13 +1939,14 @@ pref("browser.pocket.enabled", true); pref("browser.pocket.api", "api.getpocket.com"); pref("browser.pocket.site", "getpocket.com"); pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4"); pref("browser.pocket.useLocaleList", true); pref("browser.pocket.enabledLocales", "cs de en-GB en-US en-ZA es-ES es-MX fr hu it ja ja-JP-mac ko nl pl pt-BR pt-PT ru zh-CN zh-TW"); pref("view_source.tab", true); -// Enable Service Workers for desktop on non-release builds -#ifndef RELEASE_BUILD +// Enable ServiceWorkers for Push API consumers. +// Interception is still disabled. pref("dom.serviceWorkers.enabled", true); -pref("dom.serviceWorkers.interception.enabled", true); -#endif + +// Enable Push API. +pref("dom.push.enabled", true);
--- a/browser/base/content/browser-sets.inc +++ b/browser/base/content/browser-sets.inc @@ -27,16 +27,17 @@ <command id="Browser:SendLink" oncommand="MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);"/> <command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/> <command id="cmd_print" oncommand="PrintUtils.printWindow(window.gBrowser.selectedBrowser.outerWindowID, window.gBrowser.selectedBrowser);"/> <command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/> <command id="cmd_close" oncommand="BrowserCloseTabOrWindow()" reserved="true"/> <command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()" reserved="true"/> + <command id="cmd_toggleMute" oncommand="gBrowser.selectedTab.toggleMuteAudio()"/> <command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/> <command id="cmd_quitApplication" oncommand="goQuitApplication()" reserved="true"/> <commandset id="editMenuCommands"/> <command id="View:PageSource" oncommand="BrowserViewSource(window.gBrowser.selectedBrowser);" observes="isImage"/> <command id="View:PageInfo" oncommand="BrowserPageInfo();"/> @@ -311,16 +312,17 @@ <key id="key_scratchpad" keycode="&scratchpad.keycode;" modifiers="shift" keytext="&scratchpad.keytext;" command="Tools:Scratchpad"/> <key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile" modifiers="accel"/> <key id="key_savePage" key="&savePageCmd.commandkey;" command="Browser:SavePage" modifiers="accel"/> <key id="printKb" key="&printCmd.commandkey;" command="cmd_print" modifiers="accel"/> <key id="key_close" key="&closeCmd.key;" command="cmd_close" modifiers="accel"/> <key id="key_closeWindow" key="&closeCmd.key;" command="cmd_closeWindow" modifiers="accel,shift"/> + <key id="key_toggleMute" key="&toggleMuteCmd.key;" command="cmd_toggleMute" modifiers="alt,shift"/> <key id="key_undo" key="&undoCmd.key;" modifiers="accel"/> #ifdef XP_UNIX <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift"/> #else <key id="key_redo" key="&redoCmd.key;" modifiers="accel"/> #endif
--- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -515,16 +515,20 @@ ContentLinkHandler.init(this); // TODO: Load this lazily so the JSM is run only if a relevant event/message fires. let pluginContent = new PluginContent(global); addEventListener("DOMWebNotificationClicked", function(event) { sendAsyncMessage("DOMWebNotificationClicked", {}); }, false); +addEventListener("DOMServiceWorkerFocusClient", function(event) { + sendAsyncMessage("DOMServiceWorkerFocusClient", {}); +}, false); + ContentWebRTC.init(); addMessageListener("webrtc:Allow", ContentWebRTC); addMessageListener("webrtc:Deny", ContentWebRTC); addMessageListener("webrtc:StopSharing", ContentWebRTC); addMessageListener("webrtc:StartBrowserSharing", () => { let windowID = content.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils).outerWindowID; sendAsyncMessage("webrtc:response:StartBrowserSharing", {
--- a/browser/base/content/tab-content.js +++ b/browser/base/content/tab-content.js @@ -641,16 +641,22 @@ let DOMFullscreenHandler = { } case "MozDOMFullscreen:Exit": { sendAsyncMessage("DOMFullscreen:Exit"); break; } case "MozDOMFullscreen:Entered": case "MozDOMFullscreen:Exited": { addEventListener("MozAfterPaint", this); + if (!content || !content.document.mozFullScreen) { + // If we receive any fullscreen change event, and find we are + // actually not in fullscreen, also ask the parent to exit to + // ensure that the parent always exits fullscreen when we do. + sendAsyncMessage("DOMFullscreen:Exit"); + } break; } case "MozAfterPaint": { removeEventListener("MozAfterPaint", this); sendAsyncMessage("DOMFullscreen:Painted"); break; } }
--- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3897,30 +3897,30 @@ <parameter name="event"/> <body><![CDATA[ event.stopPropagation(); var tab = document.tooltipNode; if (tab.localName != "tab") { event.preventDefault(); return; } - var stringID, label; + var label; if (tab.mOverCloseButton) { - stringID = "tabs.closeTab.tooltip"; + label = this.mStringBundle.getString("tabs.closeTab.tooltip"); } else if (tab._overPlayingIcon) { - stringID = tab.linkedBrowser.audioMuted ? - "tabs.mutedAudio.tooltip" : - "tabs.playingAudio.tooltip"; + var stringID = tab.linkedBrowser.audioMuted ? + "tabs.unmuteAudio.tooltip" : + "tabs.muteAudio.tooltip"; + var key = document.getElementById("key_toggleMute"); + var shortcut = ShortcutUtils.prettifyShortcut(key); + label = this.mStringBundle.getFormattedString(stringID, [shortcut]); } else { label = tab.getAttribute("label") + (this.AppConstants.E10S_TESTING_ONLY && tab.linkedBrowser && tab.linkedBrowser.isRemoteBrowser ? " - e10s" : ""); } - if (stringID && !label) { - label = this.mStringBundle.getString(stringID); - } event.target.setAttribute("label", label); ]]></body> </method> <method name="handleEvent"> <parameter name="aEvent"/> <body><![CDATA[ switch (aEvent.type) { @@ -3995,16 +3995,17 @@ selectionInfo: aMessage.data.selectionInfo, disableSetDesktopBackground: aMessage.data.disableSetDesktopBg, }; let popup = browser.ownerDocument.getElementById("contentAreaContextMenu"); let event = gContextMenuContentData.event; popup.openPopupAtScreen(event.screenX, event.screenY, true); break; } + case "DOMServiceWorkerFocusClient": case "DOMWebNotificationClicked": { let tab = this.getTabForBrowser(browser); if (!tab) return; this.selectedTab = tab; window.focus(); break; } @@ -4091,16 +4092,17 @@ // If this window has remote tabs, switch to our tabpanels fork // which does asynchronous tab switching. this.mPanelContainer.classList.add("tabbrowser-tabpanels"); } else { this._outerWindowIDBrowserMap.set(this.mCurrentBrowser.outerWindowID, this.mCurrentBrowser); } messageManager.addMessageListener("DOMWebNotificationClicked", this); + messageManager.addMessageListener("DOMServiceWorkerFocusClient", this); messageManager.addMessageListener("Findbar:Keypress", this); ]]> </constructor> <method name="_generateUniquePanelID"> <body><![CDATA[ if (!this._uniquePanelIDCounter) { this._uniquePanelIDCounter = 0; @@ -5896,17 +5898,17 @@ tabContainer._afterHoveredTab.removeAttribute("afterhovered"); tabContainer._afterHoveredTab = null; } tabContainer._hoveredTab = null; ]]></body> </method> - <method name="_toggleMuteAudio"> + <method name="toggleMuteAudio"> <body> <![CDATA[ let tabContainer = this.parentNode; let browser = this.linkedBrowser; if (browser.audioMuted) { browser.unmute(); this.removeAttribute("muted"); } else { @@ -5968,17 +5970,18 @@ return; } let anonid = event.originalTarget.getAttribute("anonid"); let iconVisible = this.hasAttribute("soundplaying") || this.hasAttribute("muted"); if ((anonid == "soundplaying-icon") || ((anonid == "overlay-icon") && iconVisible)) { - this._toggleMuteAudio(); + this.toggleMuteAudio(); + this._overPlayingIcon = false; } ]]> </handler> </handlers> </binding> <binding id="tabbrowser-alltabs-popup" extends="chrome://global/content/bindings/popup.xml#popup"> @@ -6077,16 +6080,23 @@ aMenuitem.setAttribute("pending", aTab.getAttribute("pending")); else aMenuitem.removeAttribute("pending"); if (aTab.selected) aMenuitem.setAttribute("selected", "true"); else aMenuitem.removeAttribute("selected"); + + if (aTab.hasAttribute("muted")) + aMenuitem.setAttribute("endimage", "chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted"); + else if (aTab.hasAttribute("soundplaying")) + aMenuitem.setAttribute("endimage", "chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio"); + else + aMenuitem.removeAttribute("endimage"); ]]></body> </method> </implementation> <handlers> <handler event="popupshowing"> <![CDATA[ document.getElementById("alltabs_undoCloseTab").disabled =
--- a/browser/base/content/test/general/browser_audioTabIcon.js +++ b/browser/base/content/test/general/browser_audioTabIcon.js @@ -35,17 +35,17 @@ function leave_icon(icon) { disable_non_test_mouse(false); } function* test_tooltip(icon, expectedTooltip) { let tooltip = document.getElementById("tabbrowser-tab-tooltip"); yield hover_icon(icon, tooltip); - is(tooltip.getAttribute("label"), expectedTooltip, "Correct tooltip expected"); + is(tooltip.getAttribute("label").indexOf(expectedTooltip), 0, "Correct tooltip expected"); leave_icon(icon); } function* test_mute_tab(tab, icon, expectMuted) { let mutedPromise = BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => { if (event.detail.changed.indexOf("muted") >= 0) { is(tab.hasAttribute("muted"), expectMuted, "The tab should " + (expectMuted ? "" : "not ") + "be muted"); return true; @@ -72,38 +72,38 @@ function* test_playing_icon_on_tab(tab, yield ContentTask.spawn(browser, {}, function* () { let audio = content.document.querySelector("audio"); audio.play(); }); yield wait_for_tab_playing_event(tab, true); - yield test_tooltip(icon, "This tab is playing audio"); + yield test_tooltip(icon, "Mute tab"); yield test_mute_tab(tab, icon, true); - yield test_tooltip(icon, "This tab has been muted"); + yield test_tooltip(icon, "Unmute tab"); yield test_mute_tab(tab, icon, false); - yield test_tooltip(icon, "This tab is playing audio"); + yield test_tooltip(icon, "Mute tab"); yield test_mute_tab(tab, icon, true); yield ContentTask.spawn(browser, {}, function* () { let audio = content.document.querySelector("audio"); audio.pause(); }); yield wait_for_tab_playing_event(tab, false); ok(tab.hasAttribute("muted") && !tab.hasAttribute("soundplaying"), "Tab should still be muted but not playing"); - yield test_tooltip(icon, "This tab has been muted"); + yield test_tooltip(icon, "Unmute tab"); yield test_mute_tab(tab, icon, false); ok(!tab.hasAttribute("muted") && !tab.hasAttribute("soundplaying"), "Tab should not be be muted or playing"); } function* test_swapped_browser(oldTab, newBrowser, isPlaying) { @@ -154,16 +154,68 @@ function* test_browser_swapping(tab, bro yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank", }, newBrowser => test_swapped_browser(tab, newBrowser, false)); }); } +function* test_click_on_pinned_tab_after_mute() { + function* test_on_browser(browser) { + let tab = gBrowser.getTabForBrowser(browser); + + gBrowser.selectedTab = originallySelectedTab; + isnot(tab, gBrowser.selectedTab, "Sanity check, the tab should not be selected!"); + + // Steps to reproduce the bug: + // Pin the tab. + gBrowser.pinTab(tab); + + // Start playbak. + yield ContentTask.spawn(browser, {}, function* () { + let audio = content.document.querySelector("audio"); + audio.play(); + }); + + // Wait for playback to start. + yield wait_for_tab_playing_event(tab, true); + + // Mute the tab. + let icon = document.getAnonymousElementByAttribute(tab, "anonid", "overlay-icon"); + yield test_mute_tab(tab, icon, true); + + // Stop playback + yield ContentTask.spawn(browser, {}, function* () { + let audio = content.document.querySelector("audio"); + audio.pause(); + }); + + // Unmute tab. + yield test_mute_tab(tab, icon, false); + + // Now click on the tab. + let image = document.getAnonymousElementByAttribute(tab, "anonid", "tab-icon-image"); + EventUtils.synthesizeMouseAtCenter(image, {button: 0}); + + is(tab, gBrowser.selectedTab, "Tab switch should be successful"); + + // Cleanup. + gBrowser.unpinTab(tab); + gBrowser.selectedTab = originallySelectedTab; + } + + let originallySelectedTab = gBrowser.selectedTab; + + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: PAGE + }, test_on_browser); +} + function* test_on_browser(browser) { let tab = gBrowser.getTabForBrowser(browser); // Test the icon in a normal tab. yield test_playing_icon_on_tab(tab, browser, false); gBrowser.pinTab(tab); @@ -175,16 +227,18 @@ function* test_on_browser(browser) { // Retest with another browser in the foreground tab if (gBrowser.selectedBrowser.currentURI.spec == PAGE) { yield BrowserTestUtils.withNewTab({ gBrowser, url: "data:text/html,test" }, () => test_on_browser(browser)); } else { yield test_browser_swapping(tab, browser); + + yield test_click_on_pinned_tab_after_mute(); } } add_task(function*() { yield new Promise((resolve) => { SpecialPowers.pushPrefEnv({"set": [ ["media.useAudioChannelService", true], ["browser.tabs.showAudioPlayingIcon", true],
--- a/browser/base/content/test/general/browser_parsable_css.js +++ b/browser/base/content/test/general/browser_parsable_css.js @@ -50,30 +50,48 @@ function ignoredError(aErrorObject) { } if (matches) { return true; } } return false; } +function once(target, name) { + return new Promise((resolve, reject) => { + let cb = () => { + target.removeEventListener(name, cb); + resolve(); + }; + target.addEventListener(name, cb); + }); +} + add_task(function checkAllTheCSS() { let appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile); // This asynchronously produces a list of URLs (sadly, mostly sync on our // test infrastructure because it runs against jarfiles there, and // our zipreader APIs are all sync) let uris = yield generateURIsFromDirTree(appDir, ".css"); - // Create a clean iframe to load all the files into: - let hiddenWin = Services.appShell.hiddenDOMWindow; - let iframe = hiddenWin.document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe"); - hiddenWin.document.documentElement.appendChild(iframe); + // Create a clean iframe to load all the files into. This needs to live at a + // file or jar URI (depending on whether we're using a packaged build or not) + // so that it's allowed to load other same-scheme URIs (i.e. the browser css). + let resHandler = Services.io.getProtocolHandler("resource") + .QueryInterface(Ci.nsISubstitutingProtocolHandler); + let resURI = Services.io.newURI('resource://testing-common/resource_test_file.html', null, null); + let testFile = resHandler.resolveURI(resURI); + let windowless = Services.appShell.createWindowlessBrowser(); + let iframe = windowless.document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe"); + windowless.document.documentElement.appendChild(iframe); + let iframeLoaded = once(iframe, 'load'); + iframe.contentWindow.location = testFile; + yield iframeLoaded; let doc = iframe.contentWindow.document; - // Listen for errors caused by the CSS: let errorListener = { observe: function(aMessage) { if (!aMessage || !(aMessage instanceof Ci.nsIScriptError)) { return; } // Only care about CSS errors generated by our iframe: if (aMessage.category.includes("CSS") && aMessage.innerWindowID === 0 && aMessage.outerWindowID === 0) {
--- a/browser/components/preferences/handlers.xml +++ b/browser/components/preferences/handlers.xml @@ -67,15 +67,15 @@ </implementation> </binding> <binding id="offlineapp" extends="chrome://global/content/bindings/listbox.xml#listitem"> <content> <children> - <xul:listcell xbl:inherits="label=host"/> + <xul:listcell xbl:inherits="label=origin"/> <xul:listcell xbl:inherits="label=usage"/> </children> </content> </binding> </bindings>
--- a/browser/components/translation/test/browser_translation_exceptions.js +++ b/browser/components/translation/test/browser_translation_exceptions.js @@ -53,17 +53,29 @@ function getDomainExceptions() { perm.capability == Services.perms.DENY_ACTION) results.push(perm.principal); } return results; } function getInfoBar() { - return gBrowser.getNotificationBox().getNotificationWithValue("translation"); + let deferred = Promise.defer(); + let infobar = + gBrowser.getNotificationBox().getNotificationWithValue("translation"); + + if (!infobar) { + deferred.resolve(); + } else { + // Wait for all animations to finish + Promise.all(infobar.getAnimations().map(animation => animation.finished)) + .then(() => deferred.resolve(infobar)); + } + + return deferred.promise; } function openPopup(aPopup) { let deferred = Promise.defer(); aPopup.addEventListener("popupshown", function popupShown() { aPopup.removeEventListener("popupshown", popupShown); deferred.resolve(); @@ -104,17 +116,17 @@ let gTests = [ { desc: "never for language", run: function* checkNeverForLanguage() { // Show the infobar for example.com and fr. Translation.documentStateReceived(gBrowser.selectedBrowser, {state: Translation.STATE_OFFER, originalShown: true, detectedLanguage: "fr"}); - let notif = getInfoBar(); + let notif = yield getInfoBar(); ok(notif, "the infobar is visible"); let ui = gBrowser.selectedBrowser.translationUI; let uri = gBrowser.selectedBrowser.currentURI; ok(ui.shouldShowInfoBar(uri, "fr"), "check shouldShowInfoBar initially returns true"); // Open the "options" drop down. yield openPopup(notif._getAnonElt("options")); @@ -122,28 +134,29 @@ let gTests = [ "the options menu is open"); // Check that the item is not disabled. ok(!notif._getAnonElt("neverForLanguage").disabled, "The 'Never translate <language>' item isn't disabled"); // Click the 'Never for French' item. notif._getAnonElt("neverForLanguage").click(); - ok(!getInfoBar(), "infobar hidden"); + notif = yield getInfoBar(); + ok(!notif, "infobar hidden"); // Check this has been saved to the exceptions list. let langs = getLanguageExceptions(); is(langs.length, 1, "one language in the exception list"); is(langs[0], "fr", "correct language in the exception list"); ok(!ui.shouldShowInfoBar(uri, "fr"), "the infobar wouldn't be shown anymore"); // Reopen the infobar. PopupNotifications.getNotification("translate").anchorElement.click(); - notif = getInfoBar(); + notif = yield getInfoBar(); // Open the "options" drop down. yield openPopup(notif._getAnonElt("options")); ok(notif._getAnonElt("neverForLanguage").disabled, "The 'Never translate French' item is disabled"); // Cleanup. Services.prefs.setCharPref(kLanguagesPref, ""); notif.close(); @@ -153,17 +166,17 @@ let gTests = [ { desc: "never for site", run: function* checkNeverForSite() { // Show the infobar for example.com and fr. Translation.documentStateReceived(gBrowser.selectedBrowser, {state: Translation.STATE_OFFER, originalShown: true, detectedLanguage: "fr"}); - let notif = getInfoBar(); + let notif = yield getInfoBar(); ok(notif, "the infobar is visible"); let ui = gBrowser.selectedBrowser.translationUI; let uri = gBrowser.selectedBrowser.currentURI; ok(ui.shouldShowInfoBar(uri, "fr"), "check shouldShowInfoBar initially returns true"); // Open the "options" drop down. yield openPopup(notif._getAnonElt("options")); @@ -171,28 +184,29 @@ let gTests = [ "the options menu is open"); // Check that the item is not disabled. ok(!notif._getAnonElt("neverForSite").disabled, "The 'Never translate site' item isn't disabled"); // Click the 'Never for French' item. notif._getAnonElt("neverForSite").click(); - ok(!getInfoBar(), "infobar hidden"); + notif = yield getInfoBar(); + ok(!notif, "infobar hidden"); // Check this has been saved to the exceptions list. let sites = getDomainExceptions(); is(sites.length, 1, "one site in the exception list"); is(sites[0].origin, "http://example.com", "correct site in the exception list"); ok(!ui.shouldShowInfoBar(uri, "fr"), "the infobar wouldn't be shown anymore"); // Reopen the infobar. PopupNotifications.getNotification("translate").anchorElement.click(); - notif = getInfoBar(); + notif = yield getInfoBar(); // Open the "options" drop down. yield openPopup(notif._getAnonElt("options")); ok(notif._getAnonElt("neverForSite").disabled, "The 'Never translate French' item is disabled"); // Cleanup. Services.perms.remove(makeURI("http://example.com"), "translate"); notif.close();
--- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -638,16 +638,18 @@ you can use these alternative items. Oth <!ENTITY quitApplicationCmdMac2.label "Quit &brandShorterName;"> <!-- LOCALIZATION NOTE(quitApplicationCmdUnix.key): This keyboard shortcut is used by both Linux and OSX builds. --> <!ENTITY quitApplicationCmdUnix.key "Q"> <!ENTITY closeCmd.label "Close"> <!ENTITY closeCmd.key "W"> <!ENTITY closeCmd.accesskey "C"> +<!ENTITY toggleMuteCmd.key "M"> + <!ENTITY pageStyleMenu.label "Page Style"> <!ENTITY pageStyleMenu.accesskey "y"> <!ENTITY pageStyleNoStyle.label "No Style"> <!ENTITY pageStyleNoStyle.accesskey "n"> <!ENTITY pageStylePersistentOnly.label "Basic Page Style"> <!ENTITY pageStylePersistentOnly.accesskey "b"> <!ENTITY pageReportIcon.tooltip "Change pop-up blocking settings for this website">
--- a/browser/locales/en-US/chrome/browser/tabbrowser.properties +++ b/browser/locales/en-US/chrome/browser/tabbrowser.properties @@ -26,10 +26,14 @@ tabs.closeWarningTitle=Confirm close # http://developer.mozilla.org/en/docs/Localization_and_Plurals # The singular form is not considered since this string is used only for # multiple tabs. tabs.closeWarningMultiple=;You are about to close #1 tabs. Are you sure you want to continue? tabs.closeButtonMultiple=Close tabs tabs.closeWarningPromptMe=Warn me when I attempt to close multiple tabs tabs.closeTab.tooltip=Close tab -tabs.playingAudio.tooltip=This tab is playing audio -tabs.mutedAudio.tooltip=This tab has been muted +# LOCALIZATION NOTE (tabs.muteAudio.tooltip): +# %S is the keyboard shortcut for "Mute tab" +tabs.muteAudio.tooltip=Mute tab (%S) +# LOCALIZATION NOTE (tabs.unmuteAudio.tooltip): +# %S is the keyboard shortcut for "Unmute tab" +tabs.unmuteAudio.tooltip=Unmute tab (%S)
--- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -322,17 +322,17 @@ menuitem.bookmark-item { } .bookmark-item[cutting] > .toolbarbutton-text, .bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text { opacity: 0.7; } /* Stock icons for the menu bar items */ -menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) { +menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip):not([endimage]) { -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic"); } #placesContext_open\:newwindow, #menu_newNavigator, #context-openlink, #context-openframe { list-style-image: url("chrome://browser/skin/Toolbar-small.png");
--- a/browser/themes/shared/tabs.inc.css +++ b/browser/themes/shared/tabs.inc.css @@ -89,79 +89,84 @@ } .tab-icon-overlay { width: 16px; height: 16px; margin-top: -12px; -moz-margin-start: -16px; display: none; + position: relative; } .tab-icon-overlay[crashed] { - display: -moz-box; list-style-image: url("chrome://browser/skin/tabbrowser/crashed.svg"); } +.tab-icon-overlay[crashed], .tab-icon-overlay[soundplaying][pinned], -.tab-icon-overlay[muted][pinned] { +.tab-icon-overlay[muted][pinned]:not([crashed]) { display: -moz-box; +} + +.tab-icon-overlay[soundplaying][pinned], +.tab-icon-overlay[muted][pinned]:not([crashed]) { border-radius: 8px; } .tab-icon-overlay[soundplaying][pinned]:hover, -.tab-icon-overlay[muted][pinned]:hover { +.tab-icon-overlay[muted][pinned]:not([crashed]):hover { background-color: white; } .tab-icon-overlay[soundplaying][pinned] { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio"); } .tab-icon-overlay[soundplaying][pinned]:hover { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-hover"); } .tab-icon-overlay[soundplaying][pinned]:hover:active { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-pressed"); } -.tab-icon-overlay[muted][pinned] { +.tab-icon-overlay[muted][pinned]:not([crashed]) { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted"); } -.tab-icon-overlay[muted][pinned]:hover { +.tab-icon-overlay[muted][pinned]:not([crashed]):hover { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-hover"); } -.tab-icon-overlay[muted][pinned]:hover:active { +.tab-icon-overlay[muted][pinned]:not([crashed]):hover:active { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-pressed"); } #TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned] { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-dark"); } #TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:hover { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-hover"); } #TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:hover:active { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-pressed"); } -#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned] { +#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:not([crashed]) { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted"); } -#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:hover { +#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:not([crashed]):hover { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-hover"); } -#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:hover:active { +#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:not([crashed]):hover:active { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-pressed"); } .tab-throbber[busy] { list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png"); } .tab-throbber[progress] { @@ -416,17 +421,17 @@ /* Tab pointer-events */ .tabbrowser-tab { pointer-events: none; } .tab-background-middle, .tabs-newtab-button, .tab-icon-overlay[soundplaying], -.tab-icon-overlay[muted], +.tab-icon-overlay[muted]:not([crashed]), .tab-icon-sound, .tab-close-button { pointer-events: auto; } /* Pinned tabs */ /* Pinned tab separators need position: absolute when positioned (during overflow). */
deleted file mode 100644 --- a/build/autoconf/gcc-pr49911.m4 +++ /dev/null @@ -1,71 +0,0 @@ -dnl This Source Code Form is subject to the terms of the Mozilla Public -dnl License, v. 2.0. If a copy of the MPL was not distributed with this -dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. - -dnl Check if the compiler is gcc and has PR49911. If so -dnl disable vrp. - -AC_DEFUN([MOZ_GCC_PR49911], -[ -if test "$GNU_CC"; then - -AC_MSG_CHECKING(for gcc PR49911) -ac_have_gcc_pr49911="no" -AC_LANG_SAVE -AC_LANG_CPLUSPLUS - -_SAVE_CXXFLAGS=$CXXFLAGS -CXXFLAGS="-O2" -AC_TRY_RUN([ -extern "C" void abort(void); -typedef enum { -eax, ecx, edx, ebx, esp, ebp, -esi, edi } -RegisterID; -union StateRemat { - RegisterID reg_; - int offset_; -}; -static StateRemat FromRegister(RegisterID reg) { - StateRemat sr; - sr.reg_ = reg; - return sr; -} -static StateRemat FromAddress3(int address) { - StateRemat sr; - sr.offset_ = address; - if (address < 46 && address >= 0) { - abort(); - } - return sr; -} -struct FrameState { - StateRemat dataRematInfo2(bool y, int z) { - if (y) return FromRegister(RegisterID(1)); - return FromAddress3(z); - } -}; -FrameState frame; -StateRemat x; -__attribute__((noinline)) void jsop_setelem(bool y, int z) { - x = frame.dataRematInfo2(y, z); -} -int main(void) { - jsop_setelem(0, 47); -} -], true, - ac_have_gcc_pr49911="yes", - true) -CXXFLAGS="$_SAVE_CXXFLAGS" - -AC_LANG_RESTORE - -if test "$ac_have_gcc_pr49911" = "yes"; then - AC_MSG_RESULT(yes) - CFLAGS="$CFLAGS -fno-tree-vrp" - CXXFLAGS="$CXXFLAGS -fno-tree-vrp" -else - AC_MSG_RESULT(no) -fi -fi -])
--- a/build/mozconfig.cache +++ b/build/mozconfig.cache @@ -7,17 +7,17 @@ # Avoid duplication if the file happens to be included twice. if test -z "$bucket"; then read branch platform master <<EOF $(python2.7 -c 'import json; p = json.loads(open("'"$topsrcdir"'/../buildprops.json").read())["properties"]; print p["branch"], p["platform"], p["master"]' 2> /dev/null) EOF bucket= -if test -z "$SCCACHE_DISABLE" -a -z "$no_sccache"; then +if test -z "$SCCACHE_DISABLE" -a -z "$no_sccache" -a -z "$MOZ_PGO_IS_SET"; then case "${branch}" in try) case "${master}" in *scl1.mozilla.com*|*.scl3.mozilla.com*) bucket=mozilla-releng-s3-cache-us-west-1-try ;; *use1.mozilla.com*) bucket=mozilla-releng-s3-cache-us-east-1-try @@ -25,24 +25,20 @@ if test -z "$SCCACHE_DISABLE" -a -z "$no *usw2.mozilla.com*) bucket=mozilla-releng-s3-cache-us-west-2-try ;; esac ;; b2g-inbound|mozilla-inbound|fx-team) case "${master}" in *use1.mozilla.com*) - if test -z "$MOZ_PGO"; then - bucket=mozilla-releng-s3-cache-us-east-1-prod - fi + bucket=mozilla-releng-s3-cache-us-east-1-prod ;; *usw2.mozilla.com*) - if test -z "$MOZ_PGO"; then - bucket=mozilla-releng-s3-cache-us-west-2-prod - fi + bucket=mozilla-releng-s3-cache-us-west-2-prod ;; esac ;; esac fi if test -z "$bucket"; then case "$platform" in
--- a/build/unix/mozconfig.gtk +++ b/build/unix/mozconfig.gtk @@ -3,26 +3,28 @@ TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} # $TOOLTOOL_DIR/gtk3 comes from tooltool, when the tooltool manifest contains it. if [ -d "$TOOLTOOL_DIR/gtk3" ]; then if [ -z "$PKG_CONFIG_LIBDIR" ]; then echo PKG_CONFIG_LIBDIR must be set >&2 exit 1 fi export PKG_CONFIG_SYSROOT_DIR="$TOOLTOOL_DIR/gtk3" export PKG_CONFIG_PATH="$TOOLTOOL_DIR/gtk3/usr/local/lib/pkgconfig" + PKG_CONFIG="$TOOLTOOL_DIR/gtk3/usr/local/bin/pkg-config" export PATH="$TOOLTOOL_DIR/gtk3/usr/local/bin:${PATH}" # Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages. LDFLAGS="-L$TOOLTOOL_DIR/gtk3/usr/local/lib ${LDFLAGS}" ac_add_options --enable-default-toolkit=cairo-gtk3 # Set things up to use Gtk+3 from the tooltool package mk_add_options "export FONTCONFIG_PATH=$TOOLTOOL_DIR/gtk3/usr/local/etc/fonts" mk_add_options "export PANGO_SYSCONFDIR=$TOOLTOOL_DIR/gtk3/usr/local/etc" mk_add_options "export PANGO_LIBDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib" mk_add_options "export GDK_PIXBUF_MODULE_FILE=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" mk_add_options "export GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders" mk_add_options "export LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib" # Until a tooltool with bug 1188571 landed is available everywhere $TOOLTOOL_DIR/gtk3/setup.sh else + PKG_CONFIG=pkg-config ac_add_options --enable-default-toolkit=cairo-gtk2 fi
--- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -838,28 +838,22 @@ nsScriptSecurityManager::CheckLoadURIWit // Allow domains that were whitelisted in the prefs. In 99.9% of cases, // this array is empty. for (size_t i = 0; i < mFileURIWhitelist.Length(); ++i) { if (EqualOrSubdomain(sourceURI, mFileURIWhitelist[i])) { return NS_OK; } } - // resource: and chrome: are equivalent, securitywise - // That's bogus!! Fix this. But watch out for - // the view-source stylesheet? - bool sourceIsChrome; - rv = NS_URIChainHasFlags(sourceURI, - nsIProtocolHandler::URI_IS_UI_RESOURCE, - &sourceIsChrome); - NS_ENSURE_SUCCESS(rv, rv); - if (sourceIsChrome) { + // Allow chrome:// + if (sourceScheme.EqualsLiteral("chrome")) { return NS_OK; } + // Nothing else. if (reportErrors) { ReportError(nullptr, errorTag, sourceURI, aTargetURI); } return NS_ERROR_DOM_BAD_URI; } // OK, everyone is allowed to load this, since unflagged handlers are // deprecated but treated as URI_LOADABLE_BY_ANYONE. But check whether we
--- a/config/makefiles/xpidl/Makefile.in +++ b/config/makefiles/xpidl/Makefile.in @@ -37,16 +37,24 @@ endif # TODO we should use py_action, but that would require extra directories to be # in the virtualenv. %.xpt: @echo "$(@F)" $(PYTHON_PATH) $(PLY_INCLUDE) -I$(IDL_PARSER_DIR) -I$(IDL_PARSER_CACHE_DIR) \ $(process_py) --cache-dir $(IDL_PARSER_CACHE_DIR) $(dist_idl_dir) \ $(dist_include_dir) $(@D) $(idl_deps_dir) $(libxul_sdk_includes) \ $(basename $(notdir $@)) $($(basename $(notdir $@))_deps) +# When some IDL is added or removed, if the actual IDL file was already, or +# still is, in the tree, simple dependencies can't detect that the XPT needs +# to be rebuilt. +# Add the current value of $($(xpidl_module)_deps) in the depend file, such that +# we can later check if the value has changed since last build, which will +# indicate whether IDLs were added or removed. +# Note that removing previously built files is not covered. + @echo $(basename $(notdir $@))_deps_built = $($(basename $(notdir $@))_deps) >> $(idl_deps_dir)/$(basename $(notdir $@)).pp # Chrome manifests may be written from several Makefiles at various times during # the build. The 'buildlist' action adds to the file if it already exists, but # if it does exist, make considers it to be up-to-date (as we have no inputs to # depend on). We use FORCE to ensure that we always add the interface manifest, # whether or not the chrome manifest already exists. %/chrome.manifest: FORCE $(call py_action,buildlist,$@ 'manifest components/interfaces.manifest') @@ -61,18 +69,21 @@ xpt_files := @xpt_files@ depends_files := $(foreach root,$(xpidl_modules),$(idl_deps_dir)/$(root).pp) GARBAGE += $(xpt_files) $(depends_files) xpidl:: $(xpt_files) $(chrome_manifests) $(xpt_files): $(process_py) $(call mkdir_deps,$(idl_deps_dir) $(dist_include_dir)) +-include $(depends_files) + define xpt_deps $(1): $(call mkdir_deps,$(dir $(1))) $(1): $(addsuffix .idl,$(addprefix $(dist_idl_dir)/,$($(basename $(notdir $(1)))_deps))) +ifneq ($($(basename $(notdir $(1)))_deps),$($(basename $(notdir $(1)))_deps_built)) +$(1): FORCE +endif endef $(foreach xpt,$(xpt_files),$(eval $(call xpt_deps,$(xpt)))) -$(call include_deps,$(depends_files)) - .PHONY: xpidl
--- a/configure.in +++ b/configure.in @@ -2638,39 +2638,18 @@ WINNT|Darwin|Android) STL_FLAGS='-I$(DIST)/stl_wrappers' WRAP_STL_INCLUDES=1 ;; esac AC_SUBST(WRAP_SYSTEM_INCLUDES) AC_SUBST(VISIBILITY_FLAGS) -MOZ_GCC_PR49911 MOZ_LLVM_PR8927 -dnl Check for __force_align_arg_pointer__ for SSE2 on gcc -dnl ======================================================== -if test "$GNU_CC"; then - CFLAGS_save="${CFLAGS}" - CFLAGS="${CFLAGS} -Werror" - AC_CACHE_CHECK(for __force_align_arg_pointer__ attribute, - ac_cv_force_align_arg_pointer, - [AC_TRY_COMPILE([__attribute__ ((__force_align_arg_pointer__)) void test() {}], - [], - ac_cv_force_align_arg_pointer="yes", - ac_cv_force_align_arg_pointer="no")]) - CFLAGS="${CFLAGS_save}" - if test "$ac_cv_force_align_arg_pointer" = "yes"; then - HAVE_GCC_ALIGN_ARG_POINTER=1 - else - HAVE_GCC_ALIGN_ARG_POINTER= - fi -fi -AC_SUBST(HAVE_GCC_ALIGN_ARG_POINTER) - dnl Checks for header files. dnl ======================================================== AC_HEADER_DIRENT case "$target_os" in freebsd*|openbsd*) # for stuff like -lXshm CPPFLAGS="${CPPFLAGS} ${X_CFLAGS}" ;; @@ -5868,19 +5847,19 @@ if test -n "$MOZ_ANGLE_RENDERER"; then fi ###################################### # Find _46+ for use by Vista+. # Find a D3D compiler DLL in a Windows SDK. MOZ_D3DCOMPILER_VISTA_DLL= case "$MOZ_WINSDK_MAXVER" in - 0x0603*) + 0x0603*|0x0A00*) MOZ_D3DCOMPILER_VISTA_DLL=d3dcompiler_47.dll - AC_MSG_RESULT([Found D3D compiler in Windows SDK 8.1.]) + AC_MSG_RESULT([Found D3D compiler in Windows SDK.]) ;; esac if test -n "$MOZ_D3DCOMPILER_VISTA_DLL"; then # We have a name, now track down the path. if test -n "$WINDOWSSDKDIR"; then MOZ_D3DCOMPILER_VISTA_DLL_PATH="$WINDOWSSDKDIR/Redist/D3D/$MOZ_D3D_CPU_SUFFIX/$MOZ_D3DCOMPILER_VISTA_DLL" if test -f "$MOZ_D3DCOMPILER_VISTA_DLL_PATH"; then @@ -5889,16 +5868,18 @@ if test -n "$MOZ_ANGLE_RENDERER"; then else AC_MSG_RESULT([MOZ_D3DCOMPILER_VISTA_DLL_PATH doesn't exist: $MOZ_D3DCOMPILER_VISTA_DLL_PATH]) AC_MSG_ERROR([Windows SDK at "$WINDOWSSDKDIR" appears broken. Try updating to MozillaBuild 1.9 final or higher.]) MOZ_D3DCOMPILER_VISTA_DLL_PATH= fi else AC_MSG_RESULT([Windows SDK not found.]) fi + else + AC_MSG_ERROR([Couldn't find Windows SDK 8.1 or higher needed for ANGLE.]) fi if test -z "$MOZ_D3DCOMPILER_VISTA_DLL_PATH"; then MOZ_D3DCOMPILER_VISTA_DLL= fi # On mingw, check if headers are provided by toolchain. if test -n "$GNU_CC"; then @@ -8194,17 +8175,17 @@ else MOZ_PIXMAN_CFLAGS="$PIXMAN_CFLAGS" MOZ_PIXMAN_LIBS="$PIXMAN_LIBS" fi AC_SUBST(MOZ_PIXMAN_CFLAGS) AC_SUBST_LIST(MOZ_PIXMAN_LIBS) # Check for headers defining standard int types. if test -n "$COMPILE_ENVIRONMENT"; then - MOZ_CHECK_HEADERS(stdint.h inttypes.h sys/int_types.h) + MOZ_CHECK_HEADERS(stdint.h inttypes.h) if test "${ac_cv_header_inttypes_h}" = "yes"; then HAVE_INTTYPES_H=1 fi fi AC_SUBST(HAVE_INTTYPES_H) @@ -8859,17 +8840,17 @@ HOST_CXXFLAGS=`echo \ $_DEPEND_CFLAGS` AC_SUBST(MOZ_NATIVE_JPEG) AC_SUBST(MOZ_NATIVE_PNG) AC_SUBST(MOZ_NATIVE_BZ2) AC_SUBST(MOZ_JPEG_CFLAGS) AC_SUBST_LIST(MOZ_JPEG_LIBS) -AC_SUBST(MOZ_BZ2_CFLAGS) +AC_SUBST_LIST(MOZ_BZ2_CFLAGS) AC_SUBST_LIST(MOZ_BZ2_LIBS) AC_SUBST(MOZ_PNG_CFLAGS) AC_SUBST_LIST(MOZ_PNG_LIBS) if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then export MOZ_NUWA_PROCESS AC_DEFINE(MOZ_NUWA_PROCESS) fi
--- a/dom/animation/Animation.cpp +++ b/dom/animation/Animation.cpp @@ -512,53 +512,67 @@ Animation::HasLowerCompositeOrderThan(co "Sequence numbers should be unique"); return mSequenceNum < aOther.mSequenceNum; } bool Animation::CanThrottle() const { - if (!mEffect || - mEffect->IsFinishedTransition() || - mEffect->Properties().IsEmpty()) { + // This method answers the question, "Can we get away with NOT updating + // style on the main thread for this animation on this tick?" + + // Ignore animations that were never going to have any effect anyway. + if (!mEffect || mEffect->Properties().IsEmpty()) { return true; } - if (!mIsRunningOnCompositor) { - return false; + // Finished animations can be throttled unless this is the first + // sample since finishing. In that case we need an unthrottled sample + // so we can apply the correct end-of-animation behavior on the main + // thread (either removing the animation style or applying the fill mode). + if (PlayState() == AnimationPlayState::Finished) { + return mFinishedAtLastComposeStyle; } - if (PlayState() != AnimationPlayState::Finished) { - // Unfinished animations can be throttled. + // We should also ignore animations which are not "in effect"--i.e. not + // producing an output. This includes animations that are idle or in their + // delay phase but with no backwards fill. + // + // Note that unlike newly-finished animations, we don't need to worry about + // special handling for newly-idle animations or animations that are newly + // yet-to-start since any operation that would cause that change (e.g. a call + // to cancel() on the animation, or seeking its current time) will trigger an + // unthrottled sample. + if (!IsInEffect()) { return true; } - // The animation has finished but, if this is the first sample since - // finishing, we need an unthrottled sample so we can apply the correct - // end-of-animation behavior on the main thread (either removing the - // animation style or applying the fill mode). - return mFinishedAtLastComposeStyle; + return mIsRunningOnCompositor; } void -Animation::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule, +Animation::ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule, nsCSSPropertySet& aSetProperties, bool& aNeedsRefreshes) { - if (!mEffect || mEffect->IsFinishedTransition()) { + if (!mEffect) { return; } AnimationPlayState playState = PlayState(); if (playState == AnimationPlayState::Running || playState == AnimationPlayState::Pending) { aNeedsRefreshes = true; } + if (!IsInEffect()) { + return; + } + // In order to prevent flicker, there are a few cases where we want to use // a different time for rendering that would otherwise be returned by // GetCurrentTime. These are: // // (a) For animations that are pausing but which are still running on the // compositor. In this case we send a layer transaction that removes the // animation but which also contains the animation values calculated on // the main thread. To prevent flicker when this occurs we want to ensure @@ -893,19 +907,16 @@ Animation::UpdateFinishedState(SeekFlag } } bool currentFinishedState = PlayState() == AnimationPlayState::Finished; if (currentFinishedState && !mFinishedIsResolved) { DoFinishNotification(aSyncNotifyFlag); } else if (!currentFinishedState && mFinishedIsResolved) { ResetFinishedPromise(); - if (mEffect->AsTransition()) { - mEffect->SetIsFinishedTransition(false); - } } // We must recalculate the current time to take account of any mHoldTime // changes the code above made. mPreviousCurrentTime = GetCurrentTime(); } void Animation::UpdateEffect() @@ -1051,17 +1062,17 @@ Animation::GetPresContext() const return nullptr; } return shell->GetPresContext(); } AnimationCollection* Animation::GetCollection() const { - css::CommonAnimationManager* manager = GetAnimationManager(); + CommonAnimationManager* manager = GetAnimationManager(); if (!manager) { return nullptr; } MOZ_ASSERT(mEffect, "An animation with an animation manager must have an effect"); Element* targetElement; nsCSSPseudoElements::Type targetPseudoType;
--- a/dom/animation/Animation.h +++ b/dom/animation/Animation.h @@ -31,21 +31,20 @@ #endif struct JSContext; class nsCSSPropertySet; class nsIDocument; class nsPresContext; namespace mozilla { + struct AnimationCollection; -namespace css { class AnimValuesStyleRule; class CommonAnimationManager; -} // namespace css namespace dom { class CSSAnimation; class CSSTransition; class Animation : public DOMEventTargetHelper @@ -136,17 +135,17 @@ public: * CSSAnimation::PauseFromJS so we leave it for now. */ void PauseFromJS(ErrorResult& aRv) { Pause(aRv); } // Wrapper functions for Animation DOM methods when called from style. virtual void CancelFromStyle() { DoCancel(); } - void Tick(); + virtual void Tick(); /** * Set the time to use for starting or pausing a pending animation. * * Typically, when an animation is played, it does not start immediately but * is added to a table of pending animations on the document of its effect. * In the meantime it sets its hold time to the time from which playback * should begin. @@ -290,17 +289,17 @@ public: * Updates |aStyleRule| with the animation values of this animation's effect, * if any. * Any properties already contained in |aSetProperties| are not changed. Any * properties that are changed are added to |aSetProperties|. * |aNeedsRefreshes| will be set to true if this animation expects to update * the style rule on the next refresh driver tick as well (because it * is running and has an effect to sample). */ - void ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule, + void ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule, nsCSSPropertySet& aSetProperties, bool& aNeedsRefreshes); protected: void SilentlySetCurrentTime(const TimeDuration& aNewCurrentTime); void SilentlySetPlaybackRate(double aPlaybackRate); void DoCancel(); void DoPlay(ErrorResult& aRv, LimitBehavior aLimitBehavior); void DoPause(ErrorResult& aRv); @@ -351,17 +350,17 @@ protected: */ void CancelPendingTasks(); bool IsPossiblyOrphanedPendingAnimation() const; StickyTimeDuration EffectEnd() const; nsIDocument* GetRenderedDocument() const; nsPresContext* GetPresContext() const; - virtual css::CommonAnimationManager* GetAnimationManager() const = 0; + virtual CommonAnimationManager* GetAnimationManager() const = 0; AnimationCollection* GetCollection() const; nsRefPtr<AnimationTimeline> mTimeline; nsRefPtr<KeyframeEffectReadOnly> mEffect; // The beginning of the delay period. Nullable<TimeDuration> mStartTime; // Timeline timescale Nullable<TimeDuration> mHoldTime; // Animation timescale Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale
--- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -231,45 +231,39 @@ KeyframeEffectReadOnly::ActiveDuration(c return StickyTimeDuration( aTiming.mIterationDuration.MultDouble(aTiming.mIterationCount)); } // http://w3c.github.io/web-animations/#in-play bool KeyframeEffectReadOnly::IsInPlay(const Animation& aAnimation) const { - if (IsFinishedTransition() || - aAnimation.PlayState() == AnimationPlayState::Finished) { + if (aAnimation.PlayState() == AnimationPlayState::Finished) { return false; } return GetComputedTiming().mPhase == ComputedTiming::AnimationPhase_Active; } // http://w3c.github.io/web-animations/#current bool KeyframeEffectReadOnly::IsCurrent(const Animation& aAnimation) const { - if (IsFinishedTransition() || - aAnimation.PlayState() == AnimationPlayState::Finished) { + if (aAnimation.PlayState() == AnimationPlayState::Finished) { return false; } ComputedTiming computedTiming = GetComputedTiming(); return computedTiming.mPhase == ComputedTiming::AnimationPhase_Before || computedTiming.mPhase == ComputedTiming::AnimationPhase_Active; } bool KeyframeEffectReadOnly::IsInEffect() const { - if (IsFinishedTransition()) { - return false; - } - ComputedTiming computedTiming = GetComputedTiming(); return computedTiming.mProgress != ComputedTiming::kNullProgress; } const AnimationProperty* KeyframeEffectReadOnly::GetAnimationOfProperty(nsCSSProperty aProperty) const { for (size_t propIdx = 0, propEnd = mProperties.Length(); @@ -294,19 +288,18 @@ KeyframeEffectReadOnly::HasAnimationOfPr if (HasAnimationOfProperty(aProperties[i])) { return true; } } return false; } void -KeyframeEffectReadOnly::ComposeStyle( - nsRefPtr<css::AnimValuesStyleRule>& aStyleRule, - nsCSSPropertySet& aSetProperties) +KeyframeEffectReadOnly::ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule, + nsCSSPropertySet& aSetProperties) { ComputedTiming computedTiming = GetComputedTiming(); // If the progress is null, we don't have fill data for the current // time so we shouldn't animate. if (computedTiming.mProgress == ComputedTiming::kNullProgress) { return; } @@ -365,17 +358,17 @@ KeyframeEffectReadOnly::ComposeStyle( MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys"); MOZ_ASSERT(segment >= prop.mSegments.Elements() && size_t(segment - prop.mSegments.Elements()) < prop.mSegments.Length(), "out of array bounds"); if (!aStyleRule) { // Allocate the style rule now that we know we have animation data. - aStyleRule = new css::AnimValuesStyleRule(); + aStyleRule = new AnimValuesStyleRule(); } double positionInSegment = (computedTiming.mProgress - segment->mFromKey) / (segment->mToKey - segment->mFromKey); double valuePosition = segment->mTimingFunction.GetValue(positionInSegment);
--- a/dom/animation/KeyframeEffect.h +++ b/dom/animation/KeyframeEffect.h @@ -21,19 +21,18 @@ #include "mozilla/dom/Nullable.h" #include "nsSMILKeySpline.h" #include "nsStyleStruct.h" // for nsTimingFunction struct JSContext; class nsCSSPropertySet; namespace mozilla { -namespace css { + class AnimValuesStyleRule; -} // namespace css /** * Input timing parameters. * * Eventually this will represent all the input timing parameters specified * by content but for now it encapsulates just the subset of those * parameters passed to GetPositionInIteration. */ @@ -192,17 +191,16 @@ class KeyframeEffectReadOnly : public An public: KeyframeEffectReadOnly(nsIDocument* aDocument, Element* aTarget, nsCSSPseudoElements::Type aPseudoType, const AnimationTiming &aTiming) : AnimationEffectReadOnly(aDocument) , mTarget(aTarget) , mTiming(aTiming) - , mIsFinishedTransition(false) , mPseudoType(aPseudoType) { MOZ_ASSERT(aTarget, "null animation target is not yet supported"); } NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly) @@ -278,30 +276,16 @@ public: = nullptr) const { return GetComputedTimingAt(GetLocalTime(), aTiming ? *aTiming : mTiming); } // Return the duration of the active interval for the given timing parameters. static StickyTimeDuration ActiveDuration(const AnimationTiming& aTiming); - // After transitions finish they need to be retained in order to - // address the issue described in - // https://lists.w3.org/Archives/Public/www-style/2015Jan/0444.html . - // However, finished transitions are ignored for many purposes. - bool IsFinishedTransition() const { - return mIsFinishedTransition; - } - - void SetIsFinishedTransition(bool aIsFinished) { - MOZ_ASSERT(AsTransition(), - "Calling SetIsFinishedTransition but it's not a transition"); - mIsFinishedTransition = aIsFinished; - } - bool IsInPlay(const Animation& aAnimation) const; bool IsCurrent(const Animation& aAnimation) const; bool IsInEffect() const; const AnimationProperty* GetAnimationOfProperty(nsCSSProperty aProperty) const; bool HasAnimationOfProperty(nsCSSProperty aProperty) const { return GetAnimationOfProperty(aProperty) != nullptr; @@ -314,29 +298,26 @@ public: InfallibleTArray<AnimationProperty>& Properties() { return mProperties; } // Updates |aStyleRule| with the animation values produced by this // Animation for the current time except any properties already contained // in |aSetProperties|. // Any updated properties are added to |aSetProperties|. - void ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule, + void ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule, nsCSSPropertySet& aSetProperties); protected: virtual ~KeyframeEffectReadOnly() { } nsCOMPtr<Element> mTarget; Nullable<TimeDuration> mParentTime; AnimationTiming mTiming; - // A flag to mark transitions that have finished and are due to - // be removed on the next throttle-able cycle. - bool mIsFinishedTransition; nsCSSPseudoElements::Type mPseudoType; InfallibleTArray<AnimationProperty> mProperties; }; } // namespace dom } // namespace mozilla
--- a/dom/base/EventSource.cpp +++ b/dom/base/EventSource.cpp @@ -776,27 +776,27 @@ EventSource::InitChannelAndRequestEventS nsCOMPtr<nsIChannel> channel; // If we have the document, use it if (doc) { rv = NS_NewChannel(getter_AddRefs(channel), mSrc, doc, securityFlags, - nsIContentPolicy::TYPE_DATAREQUEST, + nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE, mLoadGroup, // loadGroup nullptr, // aCallbacks loadFlags); // aLoadFlags } else { // otherwise use the principal rv = NS_NewChannel(getter_AddRefs(channel), mSrc, mPrincipal, securityFlags, - nsIContentPolicy::TYPE_DATAREQUEST, + nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE, mLoadGroup, // loadGroup nullptr, // aCallbacks loadFlags); // aLoadFlags } NS_ENSURE_SUCCESS(rv, rv); mHttpChannel = do_QueryInterface(channel);
--- a/dom/base/nsContentPolicyUtils.h +++ b/dom/base/nsContentPolicyUtils.h @@ -87,48 +87,50 @@ NS_CP_ResponseName(int16_t response) * * @param contentType the content type code * @return the name of the given content type code */ inline const char * NS_CP_ContentTypeName(uint32_t contentType) { switch (contentType) { - CASE_RETURN( TYPE_OTHER ); - CASE_RETURN( TYPE_SCRIPT ); - CASE_RETURN( TYPE_IMAGE ); - CASE_RETURN( TYPE_STYLESHEET ); - CASE_RETURN( TYPE_OBJECT ); - CASE_RETURN( TYPE_DOCUMENT ); - CASE_RETURN( TYPE_SUBDOCUMENT ); - CASE_RETURN( TYPE_REFRESH ); - CASE_RETURN( TYPE_XBL ); - CASE_RETURN( TYPE_PING ); - CASE_RETURN( TYPE_XMLHTTPREQUEST ); - CASE_RETURN( TYPE_OBJECT_SUBREQUEST ); - CASE_RETURN( TYPE_DTD ); - CASE_RETURN( TYPE_FONT ); - CASE_RETURN( TYPE_MEDIA ); - CASE_RETURN( TYPE_WEBSOCKET ); - CASE_RETURN( TYPE_CSP_REPORT ); - CASE_RETURN( TYPE_XSLT ); - CASE_RETURN( TYPE_BEACON ); - CASE_RETURN( TYPE_FETCH ); - CASE_RETURN( TYPE_IMAGESET ); - CASE_RETURN( TYPE_WEB_MANIFEST ); - CASE_RETURN( TYPE_INTERNAL_SCRIPT ); - CASE_RETURN( TYPE_INTERNAL_WORKER ); - CASE_RETURN( TYPE_INTERNAL_SHARED_WORKER ); - CASE_RETURN( TYPE_INTERNAL_EMBED ); - CASE_RETURN( TYPE_INTERNAL_OBJECT ); - CASE_RETURN( TYPE_INTERNAL_FRAME ); - CASE_RETURN( TYPE_INTERNAL_IFRAME ); - CASE_RETURN( TYPE_INTERNAL_AUDIO ); - CASE_RETURN( TYPE_INTERNAL_VIDEO ); - CASE_RETURN( TYPE_INTERNAL_TRACK ); + CASE_RETURN( TYPE_OTHER ); + CASE_RETURN( TYPE_SCRIPT ); + CASE_RETURN( TYPE_IMAGE ); + CASE_RETURN( TYPE_STYLESHEET ); + CASE_RETURN( TYPE_OBJECT ); + CASE_RETURN( TYPE_DOCUMENT ); + CASE_RETURN( TYPE_SUBDOCUMENT ); + CASE_RETURN( TYPE_REFRESH ); + CASE_RETURN( TYPE_XBL ); + CASE_RETURN( TYPE_PING ); + CASE_RETURN( TYPE_XMLHTTPREQUEST ); + CASE_RETURN( TYPE_OBJECT_SUBREQUEST ); + CASE_RETURN( TYPE_DTD ); + CASE_RETURN( TYPE_FONT ); + CASE_RETURN( TYPE_MEDIA ); + CASE_RETURN( TYPE_WEBSOCKET ); + CASE_RETURN( TYPE_CSP_REPORT ); + CASE_RETURN( TYPE_XSLT ); + CASE_RETURN( TYPE_BEACON ); + CASE_RETURN( TYPE_FETCH ); + CASE_RETURN( TYPE_IMAGESET ); + CASE_RETURN( TYPE_WEB_MANIFEST ); + CASE_RETURN( TYPE_INTERNAL_SCRIPT ); + CASE_RETURN( TYPE_INTERNAL_WORKER ); + CASE_RETURN( TYPE_INTERNAL_SHARED_WORKER ); + CASE_RETURN( TYPE_INTERNAL_EMBED ); + CASE_RETURN( TYPE_INTERNAL_OBJECT ); + CASE_RETURN( TYPE_INTERNAL_FRAME ); + CASE_RETURN( TYPE_INTERNAL_IFRAME ); + CASE_RETURN( TYPE_INTERNAL_AUDIO ); + CASE_RETURN( TYPE_INTERNAL_VIDEO ); + CASE_RETURN( TYPE_INTERNAL_TRACK ); + CASE_RETURN( TYPE_INTERNAL_XMLHTTPREQUEST ); + CASE_RETURN( TYPE_INTERNAL_EVENTSOURCE ); default: return "<Unknown Type>"; } } #undef CASE_RETURN /* Passes on parameters from its "caller"'s context. */
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7918,16 +7918,20 @@ nsContentUtils::InternalContentPolicyTyp case nsIContentPolicy::TYPE_INTERNAL_IFRAME: return nsIContentPolicy::TYPE_SUBDOCUMENT; case nsIContentPolicy::TYPE_INTERNAL_AUDIO: case nsIContentPolicy::TYPE_INTERNAL_VIDEO: case nsIContentPolicy::TYPE_INTERNAL_TRACK: return nsIContentPolicy::TYPE_MEDIA; + case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST: + case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE: + return nsIContentPolicy::TYPE_XMLHTTPREQUEST; + default: return aType; } } nsresult nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
--- a/dom/base/nsIContentPolicy.idl +++ b/dom/base/nsIContentPolicy.idl @@ -15,17 +15,17 @@ interface nsIPrincipal; * Interface for content policy mechanism. Implementations of this * interface can be used to control loading of various types of out-of-line * content, or processing of certain types of in-line content. * * WARNING: do not block the caller from shouldLoad or shouldProcess (e.g., * by launching a dialog to prompt the user for something). */ -[scriptable,uuid(b545899e-42bd-434c-8fec-a0af3448ea15)] +[scriptable,uuid(3663021e-5670-496f-887b-b408d6526b5b)] interface nsIContentPolicy : nsIContentPolicyBase { /** * Should the resource at this location be loaded? * ShouldLoad will be called before loading the resource at aContentLocation * to determine whether to start the load at all. * * @param aContentType the type of content being tested. This will be one
--- a/dom/base/nsIContentPolicyBase.idl +++ b/dom/base/nsIContentPolicyBase.idl @@ -19,17 +19,17 @@ typedef unsigned long nsContentPolicyTyp * Interface for content policy mechanism. Implementations of this * interface can be used to control loading of various types of out-of-line * content, or processing of certain types of in-line content. * * WARNING: do not block the caller from shouldLoad or shouldProcess (e.g., * by launching a dialog to prompt the user for something). */ -[scriptable,uuid(11b8d725-7c2b-429e-b51f-8b5b542d5009)] +[scriptable,uuid(20f7b9bf-d7d5-4987-ade8-b7dc0398d44a)] interface nsIContentPolicyBase : nsISupports { /** * Indicates a unset or bogus policy type. */ const nsContentPolicyType TYPE_INVALID = 0; /** @@ -252,16 +252,30 @@ interface nsIContentPolicyBase : nsISupp /** * Indicates an internal constant for content loaded from track elements. * * This will be mapped to TYPE_MEDIA. */ const nsContentPolicyType TYPE_INTERNAL_TRACK = 32; + /** + * Indicates an internal constant for an XMLHttpRequest. + * + * This will be mapped to TYPE_XMLHTTPREQUEST. + */ + const nsContentPolicyType TYPE_INTERNAL_XMLHTTPREQUEST = 33; + + /** + * Indicates an internal constant for EventSource. + * + * This will be mapped to TYPE_DATAREQUEST. + */ + const nsContentPolicyType TYPE_INTERNAL_EVENTSOURCE = 34; + /* When adding new content types, please update nsContentBlocker, * NS_CP_ContentTypeName, nsCSPContext, all nsIContentPolicy * implementations, the static_assert in dom/cache/DBSchema.cpp, * and other things that are not listed here that are related to * nsIContentPolicy. */ //////////////////////////////////////////////////////////////////////
--- a/dom/base/nsXMLHttpRequest.cpp +++ b/dom/base/nsXMLHttpRequest.cpp @@ -1706,17 +1706,17 @@ nsXMLHttpRequest::Open(const nsACString& } rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, baseURI); if (NS_FAILED(rv)) return rv; rv = CheckInnerWindowCorrectness(); NS_ENSURE_SUCCESS(rv, rv); int16_t shouldLoad = nsIContentPolicy::ACCEPT; - rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_XMLHTTPREQUEST, + rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST, uri, mPrincipal, doc, EmptyCString(), //mime guess nullptr, //extra &shouldLoad, nsContentUtils::GetContentPolicy(), nsContentUtils::GetSecurityManager()); @@ -1760,27 +1760,27 @@ nsXMLHttpRequest::Open(const nsACString& } // If we have the document, use it if (doc) { rv = NS_NewChannel(getter_AddRefs(mChannel), uri, doc, secFlags, - nsIContentPolicy::TYPE_XMLHTTPREQUEST, + nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST, loadGroup, nullptr, // aCallbacks nsIRequest::LOAD_BACKGROUND); } else { //otherwise use the principal rv = NS_NewChannel(getter_AddRefs(mChannel), uri, mPrincipal, secFlags, - nsIContentPolicy::TYPE_XMLHTTPREQUEST, + nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST, loadGroup, nullptr, // aCallbacks nsIRequest::LOAD_BACKGROUND); } if (NS_FAILED(rv)) return rv; mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
--- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -136,17 +136,19 @@ static_assert(nsIContentPolicy::TYPE_INV nsIContentPolicy::TYPE_INTERNAL_WORKER == 24 && nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER == 25 && nsIContentPolicy::TYPE_INTERNAL_EMBED == 26 && nsIContentPolicy::TYPE_INTERNAL_OBJECT == 27 && nsIContentPolicy::TYPE_INTERNAL_FRAME == 28 && nsIContentPolicy::TYPE_INTERNAL_IFRAME == 29 && nsIContentPolicy::TYPE_INTERNAL_AUDIO == 30 && nsIContentPolicy::TYPE_INTERNAL_VIDEO == 31 && - nsIContentPolicy::TYPE_INTERNAL_TRACK == 32, + nsIContentPolicy::TYPE_INTERNAL_TRACK == 32 && + nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST == 33 && + nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE == 34, "nsContentPolicytType values are as expected"); namespace { typedef int32_t EntryId; struct IdCount {
--- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -4263,17 +4263,17 @@ CanvasRenderingContext2D::DrawImage(cons mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::DrawImage); } MOZ_ASSERT(optional_argc == 0 || optional_argc == 2 || optional_argc == 6); RefPtr<SourceSurface> srcSurf; gfx::IntSize imgSize; - Element* element; + Element* element = nullptr; EnsureTarget(); if (image.IsHTMLCanvasElement()) { HTMLCanvasElement* canvas = &image.GetAsHTMLCanvasElement(); element = canvas; nsIntSize size = canvas->GetSize(); if (size.width == 0 || size.height == 0) { error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
--- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -1363,16 +1363,17 @@ WebGLContext::ForceClearFramebufferWithD { MakeContextCurrent(); bool initializeColorBuffer = 0 != (mask & LOCAL_GL_COLOR_BUFFER_BIT); bool initializeDepthBuffer = 0 != (mask & LOCAL_GL_DEPTH_BUFFER_BIT); bool initializeStencilBuffer = 0 != (mask & LOCAL_GL_STENCIL_BUFFER_BIT); bool drawBuffersIsEnabled = IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers); bool shouldOverrideDrawBuffers = false; + bool usingDefaultFrameBuffer = !mBoundDrawFramebuffer; GLenum currentDrawBuffers[WebGLContext::kMaxColorAttachments]; // Fun GL fact: No need to worry about the viewport here, glViewport is just // setting up a coordinates transformation, it doesn't affect glClear at all. AssertCachedState(); // Can't check cached bindings, as we could // have a different FB bound temporarily. @@ -1380,27 +1381,37 @@ WebGLContext::ForceClearFramebufferWithD gl->fDisable(LOCAL_GL_SCISSOR_TEST); if (initializeColorBuffer) { if (drawBuffersIsEnabled) { GLenum drawBuffersCommand[WebGLContext::kMaxColorAttachments] = { LOCAL_GL_NONE }; - for(int32_t i = 0; i < mGLMaxDrawBuffers; i++) { + for (int32_t i = 0; i < mGLMaxDrawBuffers; i++) { GLint temp; gl->fGetIntegerv(LOCAL_GL_DRAW_BUFFER0 + i, &temp); currentDrawBuffers[i] = temp; if (colorAttachmentsMask[i]) { drawBuffersCommand[i] = LOCAL_GL_COLOR_ATTACHMENT0 + i; } if (currentDrawBuffers[i] != drawBuffersCommand[i]) shouldOverrideDrawBuffers = true; } + + // When clearing the default framebuffer, we must be clearing only + // GL_BACK, and nothing else, or else gl may return an error. We will + // only use the first element of currentDrawBuffers in this case. + if (usingDefaultFrameBuffer) { + gl->Screen()->SetDrawBuffer(LOCAL_GL_BACK); + if (currentDrawBuffers[0] == LOCAL_GL_COLOR_ATTACHMENT0) + currentDrawBuffers[0] = LOCAL_GL_BACK; + shouldOverrideDrawBuffers = false; + } // calling draw buffers can cause resolves on adreno drivers so // we try to avoid calling it if (shouldOverrideDrawBuffers) gl->fDrawBuffers(mGLMaxDrawBuffers, drawBuffersCommand); } gl->fColorMask(1, 1, 1, 1); @@ -1436,18 +1447,23 @@ WebGLContext::ForceClearFramebufferWithD gl->fEnable(LOCAL_GL_SCISSOR_TEST); if (mRasterizerDiscardEnabled) { gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD); } // Restore GL state after clearing. if (initializeColorBuffer) { - if (shouldOverrideDrawBuffers) { - gl->fDrawBuffers(mGLMaxDrawBuffers, currentDrawBuffers); + + if (drawBuffersIsEnabled) { + if (usingDefaultFrameBuffer) { + gl->Screen()->SetDrawBuffer(currentDrawBuffers[0]); + } else if (shouldOverrideDrawBuffers) { + gl->fDrawBuffers(mGLMaxDrawBuffers, currentDrawBuffers); + } } gl->fColorMask(mColorWriteMask[0], mColorWriteMask[1], mColorWriteMask[2], mColorWriteMask[3]); gl->fClearColor(mColorClearValue[0], mColorClearValue[1],
--- a/dom/canvas/WebGLContextFramebufferOperations.cpp +++ b/dom/canvas/WebGLContextFramebufferOperations.cpp @@ -3,16 +3,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 "WebGLContext.h" #include "WebGLTexture.h" #include "WebGLRenderbuffer.h" #include "WebGLFramebuffer.h" #include "GLContext.h" +#include "GLScreenBuffer.h" namespace mozilla { void WebGLContext::Clear(GLbitfield mask) { if (IsContextLost()) return; @@ -155,26 +156,18 @@ WebGLContext::DrawBuffers(const dom::Seq buffered contexts, or into the back buffer for double-buffered contexts. If DrawBuffersEXT is supplied with a constant other than BACK and NONE, the error INVALID_OPERATION is generated. */ if (buffersLength != 1) { return ErrorInvalidValue("drawBuffers: invalid <buffers> (main framebuffer: buffers.length must be 1)"); } - MakeContextCurrent(); - - if (buffers[0] == LOCAL_GL_NONE) { - const GLenum drawBuffersCommand = LOCAL_GL_NONE; - gl->fDrawBuffers(1, &drawBuffersCommand); - return; - } - else if (buffers[0] == LOCAL_GL_BACK) { - const GLenum drawBuffersCommand = LOCAL_GL_COLOR_ATTACHMENT0; - gl->fDrawBuffers(1, &drawBuffersCommand); + if (buffers[0] == LOCAL_GL_NONE || buffers[0] == LOCAL_GL_BACK) { + gl->Screen()->SetDrawBuffer(buffers[0]); return; } return ErrorInvalidOperation("drawBuffers: invalid operation (main framebuffer: buffers[0] must be GL_NONE or GL_BACK)"); } // OK: we are rendering in a framebuffer object if (buffersLength > size_t(mGLMaxDrawBuffers)) {
--- a/dom/canvas/WebGLFramebuffer.cpp +++ b/dom/canvas/WebGLFramebuffer.cpp @@ -504,17 +504,17 @@ WebGLFramebuffer::GetAttachPoint(FBAttac return mStencilAttachment; default: break; } if (attachPoint >= LOCAL_GL_COLOR_ATTACHMENT1) { size_t colorAttachmentId = attachPoint.get() - LOCAL_GL_COLOR_ATTACHMENT0; - if (colorAttachmentId < WebGLContext::kMaxColorAttachments) { + if (colorAttachmentId < (size_t)mContext->mGLMaxColorAttachments) { EnsureColorAttachPoints(colorAttachmentId); return mMoreColorAttachments[colorAttachmentId - 1]; } } MOZ_CRASH("bad `attachPoint` validation"); } @@ -827,27 +827,29 @@ WebGLFramebuffer::CheckAndInitializeAtta mMoreColorAttachments[i].SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); } return true; } void WebGLFramebuffer::EnsureColorAttachPoints(size_t colorAttachmentId) { - MOZ_ASSERT(colorAttachmentId < WebGLContext::kMaxColorAttachments); + size_t maxColorAttachments = mContext->mGLMaxColorAttachments; + + MOZ_ASSERT(colorAttachmentId < maxColorAttachments); if (colorAttachmentId < ColorAttachmentCount()) return; - while (ColorAttachmentCount() < WebGLContext::kMaxColorAttachments) { + while (ColorAttachmentCount() < maxColorAttachments) { GLenum nextAttachPoint = LOCAL_GL_COLOR_ATTACHMENT0 + ColorAttachmentCount(); mMoreColorAttachments.AppendElement(WebGLFBAttachPoint(this, nextAttachPoint)); } - MOZ_ASSERT(ColorAttachmentCount() == WebGLContext::kMaxColorAttachments); + MOZ_ASSERT(ColorAttachmentCount() == maxColorAttachments); } static void FinalizeDrawAndReadBuffers(gl::GLContext* gl, bool isColorBufferDefined) { MOZ_ASSERT(gl, "Expected a valid GLContext ptr."); // GLES don't support DrawBuffer()/ReadBuffer. // According to http://www.opengl.org/wiki/Framebuffer_Object
--- a/dom/fetch/InternalRequest.cpp +++ b/dom/fetch/InternalRequest.cpp @@ -147,19 +147,22 @@ InternalRequest::MapContentPolicyTypeToR context = RequestContext::Internal; break; case nsIContentPolicy::TYPE_XBL: context = RequestContext::Internal; break; case nsIContentPolicy::TYPE_PING: context = RequestContext::Ping; break; - case nsIContentPolicy::TYPE_XMLHTTPREQUEST: + case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST: context = RequestContext::Xmlhttprequest; break; + case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE: + context = RequestContext::Eventsource; + break; case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST: context = RequestContext::Plugin; break; case nsIContentPolicy::TYPE_DTD: context = RequestContext::Internal; break; case nsIContentPolicy::TYPE_FONT: context = RequestContext::Font;
--- a/dom/fetch/InternalRequest.h +++ b/dom/fetch/InternalRequest.h @@ -55,22 +55,22 @@ namespace dom { * prefetch | * script | TYPE_INTERNAL_SCRIPT * sharedworker | TYPE_INTERNAL_SHARED_WORKER * subresource | Not supported by Gecko * style | TYPE_STYLESHEET * track | TYPE_INTERNAL_TRACK * video | TYPE_INTERNAL_VIDEO * worker | TYPE_INTERNAL_WORKER - * xmlhttprequest | TYPE_XMLHTTPREQUEST + * xmlhttprequest | TYPE_INTERNAL_XMLHTTPREQUEST + * eventsource | TYPE_INTERNAL_EVENTSOURCE * xslt | TYPE_XSLT * * TODO: Figure out if TYPE_REFRESH maps to anything useful * TODO: Figure out if TYPE_DTD maps to anything useful - * TODO: Split TYPE_XMLHTTPREQUEST and TYPE_DATAREQUEST for EventSource * TODO: Figure out if TYPE_WEBSOCKET maps to anything useful * TODO: Add a content type for prefetch * TODO: Use the content type for manifest when it becomes available * TODO: Add a content type for location * TODO: Add a content type for hyperlink * TODO: Add a content type for form * TODO: Add a content type for favicon * TODO: Add a content type for download
--- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -361,17 +361,16 @@ bool MediaDecoder::IsInfinite() MediaDecoder::MediaDecoder() : mWatchManager(this, AbstractThread::MainThread()), mDormantSupported(false), mDecoderPosition(0), mPlaybackPosition(0), mLogicalPosition(0.0), mDuration(std::numeric_limits<double>::quiet_NaN()), mMediaSeekable(true), - mSameOriginMedia(false), mReentrantMonitor("media.decoder"), mIgnoreProgressData(false), mInfiniteStream(false), mOwner(nullptr), mPlaybackStatistics(new MediaChannelStatistics()), mPinnedForSeek(false), mShuttingDown(false), mPausedForPlaybackRateNull(false), @@ -407,17 +406,19 @@ MediaDecoder::MediaDecoder() : "MediaDecoder::mEstimatedDuration (Canonical)"), mExplicitDuration(AbstractThread::MainThread(), Maybe<double>(), "MediaDecoder::mExplicitDuration (Canonical)"), mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING, "MediaDecoder::mPlayState (Canonical)"), mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED, "MediaDecoder::mNextState (Canonical)"), mLogicallySeeking(AbstractThread::MainThread(), false, - "MediaDecoder::mLogicallySeeking (Canonical)") + "MediaDecoder::mLogicallySeeking (Canonical)"), + mSameOriginMedia(AbstractThread::MainThread(), false, + "MediaDecoder::mSameOriginMedia (Canonical)") { MOZ_COUNT_CTOR(MediaDecoder); MOZ_ASSERT(NS_IsMainThread()); MediaMemoryTracker::AddMediaDecoder(this); mAudioChannel = AudioChannelService::GetDefaultAudioChannel(); // @@ -771,22 +772,16 @@ void MediaDecoder::DecodeError() void MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin) { MOZ_ASSERT(NS_IsMainThread()); ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); mSameOriginMedia = aSameOrigin; } -bool MediaDecoder::IsSameOriginMedia() -{ - GetReentrantMonitor().AssertCurrentThreadIn(); - return mSameOriginMedia; -} - bool MediaDecoder::IsSeeking() const { MOZ_ASSERT(NS_IsMainThread()); return mLogicallySeeking; } bool MediaDecoder::IsEndedOrShutdown() const {
--- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -567,20 +567,16 @@ public: nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () { self->mPlaybackStatistics->Stop(); }); AbstractThread::MainThread()->Dispatch(r.forget()); } // The actual playback rate computation. The monitor must be held. virtual double ComputePlaybackRate(bool* aReliable); - // Return true when the media is same-origin with the element. The monitor - // must be held. - bool IsSameOriginMedia(); - // Returns true if we can play the entire media through without stopping // to buffer, given the current download and playback rates. bool CanPlayThrough(); void SetAudioChannel(dom::AudioChannel aChannel) { mAudioChannel = aChannel; } dom::AudioChannel GetAudioChannel() { return mAudioChannel; } // Send a new set of metadata to the state machine, to be dispatched to the @@ -937,20 +933,16 @@ protected: virtual int64_t CurrentPosition() { return mCurrentPosition; } // Official duration of the media resource as observed by script. double mDuration; // True if the media is seekable (i.e. supports random access). bool mMediaSeekable; - // True if the media is same-origin with the element. Data can only be - // passed to MediaStreams when this is true. - bool mSameOriginMedia; - /****** * The following member variables can be accessed from any thread. ******/ // Media data resource. nsRefPtr<MediaResource> mResource; private: @@ -1115,16 +1107,20 @@ protected: // OR on the main thread. // Any change to the state must call NotifyAll on the monitor. // This can only be PLAY_STATE_PAUSED or PLAY_STATE_PLAYING. Canonical<PlayState> mNextState; // True if the decoder is seeking. Canonical<bool> mLogicallySeeking; + // True if the media is same-origin with the element. Data can only be + // passed to MediaStreams when this is true. + Canonical<bool> mSameOriginMedia; + public: AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override; AbstractCanonical<double>* CanonicalVolume() { return &mVolume; } AbstractCanonical<double>* CanonicalPlaybackRate() { return &mPlaybackRate; } @@ -1141,13 +1137,16 @@ public: return &mPlayState; } AbstractCanonical<PlayState>* CanonicalNextPlayState() { return &mNextState; } AbstractCanonical<bool>* CanonicalLogicallySeeking() { return &mLogicallySeeking; } + AbstractCanonical<bool>* CanonicalSameOriginMedia() { + return &mSameOriginMedia; + } }; } // namespace mozilla #endif
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -237,16 +237,18 @@ MediaDecoderStateMachine::MediaDecoderSt "MediaDecoderStateMachine::mNextPlayState (Mirror)"), mLogicallySeeking(mTaskQueue, false, "MediaDecoderStateMachine::mLogicallySeeking (Mirror)"), mVolume(mTaskQueue, 1.0, "MediaDecoderStateMachine::mVolume (Mirror)"), mLogicalPlaybackRate(mTaskQueue, 1.0, "MediaDecoderStateMachine::mLogicalPlaybackRate (Mirror)"), mPreservesPitch(mTaskQueue, true, "MediaDecoderStateMachine::mPreservesPitch (Mirror)"), + mSameOriginMedia(mTaskQueue, false, + "MediaDecoderStateMachine::mSameOriginMedia (Mirror)"), mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderStateMachine::mDuration (Canonical"), mIsShutdown(mTaskQueue, false, "MediaDecoderStateMachine::mIsShutdown (Canonical)"), mNextFrameStatus(mTaskQueue, MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED, "MediaDecoderStateMachine::mNextFrameStatus (Canonical)"), mCurrentPosition(mTaskQueue, 0, "MediaDecoderStateMachine::mCurrentPosition (Canonical)") @@ -312,16 +314,17 @@ MediaDecoderStateMachine::Initialization mEstimatedDuration.Connect(mDecoder->CanonicalEstimatedDuration()); mExplicitDuration.Connect(mDecoder->CanonicalExplicitDuration()); mPlayState.Connect(mDecoder->CanonicalPlayState()); mNextPlayState.Connect(mDecoder->CanonicalNextPlayState()); mLogicallySeeking.Connect(mDecoder->CanonicalLogicallySeeking()); mVolume.Connect(mDecoder->CanonicalVolume()); mLogicalPlaybackRate.Connect(mDecoder->CanonicalPlaybackRate()); mPreservesPitch.Connect(mDecoder->CanonicalPreservesPitch()); + mSameOriginMedia.Connect(mDecoder->CanonicalSameOriginMedia()); // Initialize watchers. mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated); mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus); mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus); mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged); mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged); mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged); @@ -368,17 +371,17 @@ int64_t MediaDecoderStateMachine::GetDec } void MediaDecoderStateMachine::SendStreamData() { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()"); - bool finished = mDecodedStream->SendData(mVolume, mDecoder->IsSameOriginMedia()); + bool finished = mDecodedStream->SendData(mVolume, mSameOriginMedia); const auto clockTime = GetClock(); while (true) { const MediaData* a = AudioQueue().PeekFront(); // If we discard audio samples fed to the stream immediately, we will // keep decoding audio samples till the end and consume a lot of memory. // Therefore we only discard those behind the stream clock to throttle @@ -2212,16 +2215,17 @@ MediaDecoderStateMachine::FinishShutdown mEstimatedDuration.DisconnectIfConnected(); mExplicitDuration.DisconnectIfConnected(); mPlayState.DisconnectIfConnected(); mNextPlayState.DisconnectIfConnected(); mLogicallySeeking.DisconnectIfConnected(); mVolume.DisconnectIfConnected(); mLogicalPlaybackRate.DisconnectIfConnected(); mPreservesPitch.DisconnectIfConnected(); + mSameOriginMedia.DisconnectIfConnected(); mDuration.DisconnectAll(); mIsShutdown.DisconnectAll(); mNextFrameStatus.DisconnectAll(); mCurrentPosition.DisconnectAll(); // Shut down the watch manager before shutting down our task queue. mWatchManager.Shutdown();
--- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -1313,16 +1313,20 @@ private: // TODO: The separation between mPlaybackRate and mLogicalPlaybackRate is a // kludge to preserve existing fragile logic while converting this setup to // state-mirroring. Some hero should clean this up. Mirror<double> mLogicalPlaybackRate; // Pitch preservation for the playback rate. Mirror<bool> mPreservesPitch; + // True if the media is same-origin with the element. Data can only be + // passed to MediaStreams when this is true. + Mirror<bool> mSameOriginMedia; + // Duration of the media. This is guaranteed to be non-null after we finish // decoding the first frame. Canonical<media::NullableTimeUnit> mDuration; // Whether we're currently in or transitioning to shutdown state. Canonical<bool> mIsShutdown; // The status of our next frame. Mirrored on the main thread and used to
--- a/dom/media/MediaEventSource.h +++ b/dom/media/MediaEventSource.h @@ -10,16 +10,17 @@ #include "mozilla/AbstractThread.h" #include "mozilla/Atomics.h" #include "mozilla/Mutex.h" #include "mozilla/TypeTraits.h" #include "mozilla/UniquePtr.h" #include "nsISupportsImpl.h" #include "nsTArray.h" +#include "nsThreadUtils.h" namespace mozilla { /** * A thread-safe tool to communicate "revocation" across threads. It is used to * disconnect a listener from the event source to prevent future notifications * from coming. Revoke() can be called on any thread. However, it is recommended * to be called on the target thread to avoid race condition.
--- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -149,20 +149,16 @@ MediaFormatReader::Shutdown() mPlatform = nullptr; return MediaDecoderReader::Shutdown(); } void MediaFormatReader::InitLayersBackendType() { - if (!IsVideoContentType(mDecoder->GetResource()->GetContentType())) { - // Not playing video, we don't care about the layers backend type. - return; - } // Extract the layer manager backend type so that platform decoders // can determine whether it's worthwhile using hardware accelerated // video decoding. MediaDecoderOwner* owner = mDecoder->GetOwner(); if (!owner) { NS_WARNING("MediaFormatReader without a decoder owner, can't get HWAccel"); return; } @@ -413,18 +409,22 @@ MediaFormatReader::EnsureDecodersSetup() // JavaScript player app. Note: we still go through the motions here // even if EME is disabled, so that if script tries and fails to create // a CDM, we can detect that and notify chrome and show some UI // explaining that we failed due to EME being disabled. nsRefPtr<CDMProxy> proxy; { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); proxy = mDecoder->GetCDMProxy(); + MOZ_ASSERT(proxy); + + CDMCaps::AutoLock caps(proxy->Capabilites()); + mInfo.mVideo.mIsRenderedExternally = caps.CanRenderVideo(); + mInfo.mAudio.mIsRenderedExternally = caps.CanRenderAudio(); } - MOZ_ASSERT(proxy); mPlatform = PlatformDecoderModule::CreateCDMWrapper(proxy); NS_ENSURE_TRUE(mPlatform, false); #else // EME not supported. return false; #endif } else {
--- a/dom/media/MediaInfo.h +++ b/dom/media/MediaInfo.h @@ -41,16 +41,17 @@ public: : mId(aId) , mKind(aKind) , mLabel(aLabel) , mLanguage(aLanguage) , mEnabled(aEnabled) , mTrackId(aTrackId) , mDuration(0) , mMediaTime(0) + , mIsRenderedExternally(false) , mType(aType) { MOZ_COUNT_CTOR(TrackInfo); } // Only used for backward compatibility. Do not use in new code. void Init(TrackType aType, const nsAString& aId, @@ -78,16 +79,20 @@ public: TrackID mTrackId; nsAutoCString mMimeType; int64_t mDuration; int64_t mMediaTime; CryptoTrack mCrypto; + // True if the track is gonna be (decrypted)/decoded and + // rendered directly by non-gecko components. + bool mIsRenderedExternally; + virtual AudioInfo* GetAsAudioInfo() { return nullptr; } virtual VideoInfo* GetAsVideoInfo() { return nullptr; } @@ -142,16 +147,17 @@ protected: mLabel = aOther.mLabel; mLanguage = aOther.mLanguage; mEnabled = aOther.mEnabled; mTrackId = aOther.mTrackId; mMimeType = aOther.mMimeType; mDuration = aOther.mDuration; mMediaTime = aOther.mMediaTime; mCrypto = aOther.mCrypto; + mIsRenderedExternally = aOther.mIsRenderedExternally; mType = aOther.mType; MOZ_COUNT_CTOR(TrackInfo); } private: TrackType mType; };
--- a/dom/media/tests/mochitest/head.js +++ b/dom/media/tests/mochitest/head.js @@ -254,23 +254,23 @@ function setupEnvironment() { // We don't care about waiting for this to complete, we just want to ensure // that we don't build up a huge backlog of GC work. SpecialPowers.exactGC(window); } // This is called by steeplechase; which provides the test configuration options // directly to the test through this function. If we're not on steeplechase, // the test is configured directly and immediately. -function run_test(is_initiator) { +function run_test(is_initiator,timeout) { var options = { is_local: is_initiator, is_remote: !is_initiator }; setTimeout(() => { - unexpectedEventArrived(new Error("PeerConnectionTest timed out after 30s")); - }, 30000); + unexpectedEventArrived(new Error("PeerConnectionTest timed out after "+timeout+"s")); + }, timeout); // Also load the steeplechase test code. var s = document.createElement("script"); s.src = "/test.js"; s.onload = () => setTestOptions(options); document.head.appendChild(s); }
--- a/dom/media/tests/mochitest/steeplechase_long/long.js +++ b/dom/media/tests/mochitest/steeplechase_long/long.js @@ -168,52 +168,50 @@ function verifyConnectionStatus(test) { processPcStats(test.pcRemote, 'REMOTE', OPERATIONS); } } /** * Generates a setInterval wrapper command link for use in pc.js command chains * - * The link will repeatedly call the given callback function every interval ms + * This function returns a promise that will resolve once the link repeatedly + * calls the given callback function every interval ms * until duration ms have passed, then it will continue the test. * * @param {function} callback * Function to be called on each interval * @param {number} [interval=1000] * Frequency in milliseconds with which callback will be called * @param {number} [duration=3 hours] * Length of time in milliseconds for which callback will be called - * @param {string} [name='INTERVAL_COMMAND'] - * Name of the generated command link + + */ -function generateIntervalCommand(callback, interval, duration, name) { +function generateIntervalCommand(callback, interval, duration) { interval = interval || 1000; duration = duration || 1000 * 3600 * 3; - name = name || 'INTERVAL_COMMAND'; + + return function INTERVAL_COMMAND(test) { + return new Promise (resolve=>{ + var startTime = Date.now(); + var intervalId = setInterval(function () { + if (callback) { + callback(test); + } - return [ - name, - function (test) { - var startTime = Date.now(); - var intervalId = setInterval(function () { - if (callback) { - callback(test); - } - - var failed = false; - Object.keys(_errorCount).forEach(function (label) { - if (_errorCount[label] > MAX_ERROR_CYCLES) { - ok(false, "Encountered more then " + MAX_ERROR_CYCLES + " cycles" + + var failed = false; + Object.keys(_errorCount).forEach(function (label) { + if (_errorCount[label] > MAX_ERROR_CYCLES) { + ok(false, "Encountered more then " + MAX_ERROR_CYCLES + " cycles" + " with errors on " + label); - failed = true; - } - }); + failed = true; + } + }); var timeElapsed = Date.now() - startTime; if ((timeElapsed >= duration) || failed) { clearInterval(intervalId); - test.next(); + resolve(); } }, interval); - } - ] + }); + }; } -
--- a/dom/media/tests/mochitest/steeplechase_long/steeplechase_long.ini +++ b/dom/media/tests/mochitest/steeplechase_long/steeplechase_long.ini @@ -1,13 +1,12 @@ [DEFAULT] support-files = long.js ../head.js ../mediaStreamPlayback.js ../pc.js ../templates.js ../turnConfig.js + ../network.js -[test_peerConnection_basicAudio_long.html] -[test_peerConnection_basicVideo_long.html] [test_peerConnection_basicAudioVideoCombined_long.html]
--- a/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicAudioVideoCombined_long.html +++ b/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicAudioVideoCombined_long.html @@ -13,23 +13,24 @@ <pre id="test"> <script type="application/javascript"> createHTML({ bug: "1014328", title: "Basic audio/video (combined) peer connection, long running", visible: true }); + var STEEPLECHASE_TIMEOUT = 1000 * 3600 * 3; var test; runNetworkTest(function (options) { options = options || {}; - options.commands = commandsPeerConnection.slice(0); + options.commands = makeDefaultCommands(); options.commands.push(generateIntervalCommand(verifyConnectionStatus, 1000 * 10, - 1000 * 3600 * 3)); + STEEPLECHASE_TIMEOUT)); test = new PeerConnectionTest(options); test.setMediaConstraints([{audio: true, video: true, fake: false}], [{audio: true, video: true, fake: false}]); test.run(); }); </script> </pre>
--- a/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicAudio_long.html +++ b/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicAudio_long.html @@ -13,23 +13,24 @@ <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796892", title: "Basic audio-only peer connection", visible: true }); + var STEEPLECHASE_TIMEOUT = 1000 * 3600 * 3; var test; runNetworkTest(function (options) { options = options || {}; - options.commands = commandsPeerConnection.slice(0); + options.commands = makeDefaultCommands(); options.commands.push(generateIntervalCommand(verifyConnectionStatus, 1000 * 10, - 1000 * 3600 * 3)); + STEEPLECHASE_TIMEOUT)); test = new PeerConnectionTest(options); test.setMediaConstraints([{audio: true, fake: false}], [{audio: true, fake: false}]); test.run(); }); </script> </pre>
--- a/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicVideo_long.html +++ b/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicVideo_long.html @@ -13,23 +13,24 @@ <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796888", title: "Basic video-only peer connection", visible: true }); + var STEEPLECHASE_TIMEOUT = 1000 * 3600 * 3; var test; runNetworkTest(function (options) { options = options || {}; - options.commands = commandsPeerConnection.slice(0); + options.commands = makeDefaultCommands(); options.commands.push(generateIntervalCommand(verifyConnectionStatus, 1000 * 10, - 1000 * 3600 * 3)); + STEEPLECHASE_TIMEOUT)); test = new PeerConnectionTest(options); test.setMediaConstraints([{video: true, fake: false}], [{video: true, fake: false}]); test.run(); }); </script> </pre>
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp @@ -747,17 +747,17 @@ MediaEngineGonkVideoSource::RotateImage( uint32_t half_width = dstWidth / 2; layers::GrallocImage* videoImage = static_cast<layers::GrallocImage*>(image.get()); MOZ_ASSERT(mTextureClientAllocator); RefPtr<layers::TextureClient> textureClient = mTextureClientAllocator->CreateOrRecycleForDrawing(gfx::SurfaceFormat::YUV, gfx::IntSize(dstWidth, dstHeight), - gfx::BackendType::NONE, + layers::BackendSelector::Content, layers::TextureFlags::DEFAULT, layers::ALLOC_DISALLOW_BUFFERTEXTURECLIENT); if (textureClient) { RefPtr<layers::GrallocTextureClientOGL> grallocTextureClient = static_cast<layers::GrallocTextureClientOGL*>(textureClient.get()); android::sp<android::GraphicBuffer> destBuffer = grallocTextureClient->GetGraphicBuffer();
--- a/dom/media/webspeech/recognition/SpeechRecognition.cpp +++ b/dom/media/webspeech/recognition/SpeechRecognition.cpp @@ -124,19 +124,19 @@ SpeechRecognition::SpeechRecognition(nsP mTestConfig.Init(); if (mTestConfig.mEnableTests) { nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); obs->AddObserver(this, SPEECH_RECOGNITION_TEST_EVENT_REQUEST_TOPIC, false); obs->AddObserver(this, SPEECH_RECOGNITION_TEST_END_TOPIC, false); } mEndpointer.set_speech_input_complete_silence_length( - Preferences::GetInt(PREFERENCE_ENDPOINTER_SILENCE_LENGTH, 500000)); + Preferences::GetInt(PREFERENCE_ENDPOINTER_SILENCE_LENGTH, 1250000)); mEndpointer.set_long_speech_input_complete_silence_length( - Preferences::GetInt(PREFERENCE_ENDPOINTER_LONG_SILENCE_LENGTH, 1000000)); + Preferences::GetInt(PREFERENCE_ENDPOINTER_LONG_SILENCE_LENGTH, 2500000)); mEndpointer.set_long_speech_length( Preferences::GetInt(PREFERENCE_ENDPOINTER_SILENCE_LENGTH, 3 * 1000000)); Reset(); } bool SpeechRecognition::StateBetween(FSMState begin, FSMState end) {
--- a/dom/network/NetworkStatsService.jsm +++ b/dom/network/NetworkStatsService.jsm @@ -122,17 +122,17 @@ this.NetworkStatsService = { this.messages.forEach(function(aMsgName) { ppmm.addMessageListener(aMsgName, this); }, this); this._db = new NetworkStatsDB(); // Stats for all interfaces are updated periodically this.timer.initWithCallback(this, this._db.sampleRate, - Ci.nsITimer.TYPE_REPEATING_PRECISE); + Ci.nsITimer.TYPE_REPEATING_PRECISE_CAN_SKIP); // Stats not from netd are firstly stored in the cached. this.cachedStats = Object.create(null); this.cachedStatsDate = new Date(); this.updateQueue = []; this.isQueueRunning = false;
--- a/dom/plugins/ipc/PluginInterposeOSX.mm +++ b/dom/plugins/ipc/PluginInterposeOSX.mm @@ -579,17 +579,17 @@ void OnPluginShowWindow(uint32_t window_ CGRect main_display_bounds = ::CGDisplayBounds(CGMainDisplayID()); if (CGRectEqualToRect(window_bounds, main_display_bounds) && (plugin_fullscreen_windows_set_.find(window_id) == plugin_fullscreen_windows_set_.end())) { plugin_fullscreen_windows_set_.insert(window_id); - nsCocoaUtils::HideOSChromeOnScreen(TRUE, [[NSScreen screens] objectAtIndex:0]); + nsCocoaUtils::HideOSChromeOnScreen(true); } } static void ActivateProcess(pid_t pid) { ProcessSerialNumber process; OSStatus status = ::GetProcessForPID(pid, &process); if (status == noErr) { @@ -602,17 +602,17 @@ static void ActivateProcess(pid_t pid) { // Must be called on the UI thread. // If plugin_pid is -1, the browser will be the active process on return, // otherwise that process will be given focus back before this function returns. static void ReleasePluginFullScreen(pid_t plugin_pid) { // Releasing full screen only works if we are the frontmost process; grab // focus, but give it back to the plugin process if requested. ActivateProcess(base::GetCurrentProcId()); - nsCocoaUtils::HideOSChromeOnScreen(FALSE, [[NSScreen screens] objectAtIndex:0]); + nsCocoaUtils::HideOSChromeOnScreen(false); if (plugin_pid != -1) { ActivateProcess(plugin_pid); } } void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid) { bool had_windows = !plugin_visible_windows_set_.empty();
--- a/dom/push/PushManager.cpp +++ b/dom/push/PushManager.cpp @@ -445,19 +445,23 @@ public: { } bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget(); nsRefPtr<Promise> promise = proxy->GetWorkerPromise(); if (NS_SUCCEEDED(mStatus)) { - nsRefPtr<WorkerPushSubscription> sub = - new WorkerPushSubscription(mEndpoint, mScope); - promise->MaybeResolve(sub); + if (mEndpoint.IsEmpty()) { + promise->MaybeResolve(JS::NullHandleValue); + } else { + nsRefPtr<WorkerPushSubscription> sub = + new WorkerPushSubscription(mEndpoint, mScope); + promise->MaybeResolve(sub); + } } else { promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); } proxy->CleanUp(aCx); return true; } private:
--- a/dom/security/test/cors/mochitest.ini +++ b/dom/security/test/cors/mochitest.ini @@ -4,9 +4,8 @@ support-files = file_CrossSiteXHR_inner.html file_CrossSiteXHR_inner.jar file_CrossSiteXHR_inner_data.sjs file_CrossSiteXHR_server.sjs [test_CrossSiteXHR.html] [test_CrossSiteXHR_cache.html] [test_CrossSiteXHR_origin.html] -skip-if = buildapp == 'b2g' || e10s # last test fails to trigger onload on e10s/b2g
--- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -927,19 +927,19 @@ var interfaceNamesInGlobalScope = "ProcessingInstruction", // IMPORTANT: Do not change this list without review from a DOM peer! "ProgressEvent", // IMPORTANT: Do not change this list without review from a DOM peer! "Promise", // IMPORTANT: Do not change this list without review from a DOM peer! "PropertyNodeList", // IMPORTANT: Do not change this list without review from a DOM peer! - {name: "PushManager", b2g: false, android: false, release: false}, -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "PushSubscription", b2g: false, android: false, release: false}, + {name: "PushManager", b2g: false, android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PushSubscription", b2g: false, android: false}, // IMPORTANT: Do not change this list without review from a DOM peer! "RadioNodeList", // IMPORTANT: Do not change this list without review from a DOM peer! "Range", // IMPORTANT: Do not change this list without review from a DOM peer! "RecordErrorEvent", // IMPORTANT: Do not change this list without review from a DOM peer! "Rect", @@ -965,21 +965,21 @@ var interfaceNamesInGlobalScope = "Screen", // IMPORTANT: Do not change this list without review from a DOM peer! "ScriptProcessorNode", // IMPORTANT: Do not change this list without review from a DOM peer! "ScrollAreaEvent", // IMPORTANT: Do not change this list without review from a DOM peer! "Selection", // IMPORTANT: Do not change this list without review from a DOM peer! - {name: "ServiceWorker", release: false, b2g: false}, -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "ServiceWorkerContainer", release: false, b2g: false}, -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "ServiceWorkerRegistration", release: false, b2g: false}, + {name: "ServiceWorker", b2g: false, android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "ServiceWorkerContainer", b2g: false, android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "ServiceWorkerRegistration", b2g: false, android: false}, // IMPORTANT: Do not change this list without review from a DOM peer! "SettingsLock", // IMPORTANT: Do not change this list without review from a DOM peer! "SettingsManager", // IMPORTANT: Do not change this list without review from a DOM peer! "ShadowRoot", // Bogus, but the test harness forces it on. See bug 1159768. // IMPORTANT: Do not change this list without review from a DOM peer! "SharedWorker",
--- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -91,16 +91,26 @@ static_assert(nsIHttpChannelInternal::CO "RequestMode enumeration value should match Necko CORS mode value."); static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(RequestMode::Cors), "RequestMode enumeration value should match Necko CORS mode value."); static_assert(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT == static_cast<uint32_t>(RequestMode::Cors_with_forced_preflight), "RequestMode enumeration value should match Necko CORS mode value."); static StaticRefPtr<ServiceWorkerManager> gInstance; +// Tracks the "dom.disable_open_click_delay" preference. Modified on main +// thread, read on worker threads. This is set once in the ServiceWorkerManager +// constructor before any service workers are spawned. +// It is updated every time a "notificationclick" event is dispatched. While +// this is done without synchronization, at the worst, the thread will just get +// an older value within which a popup is allowed to be displayed, which will +// still be a valid value since it was set in the constructor. I (:nsm) don't +// think this needs to be synchronized. +Atomic<uint32_t> gDOMDisableOpenClickDelay(0); + struct ServiceWorkerManager::RegistrationDataPerPrincipal { // Ordered list of scopes for glob matching. // Each entry is an absolute URL representing the scope. // Each value of the hash table is an array of an absolute URLs representing // the scopes. // // An array is used for now since the number of controlled scopes per @@ -381,16 +391,18 @@ NS_INTERFACE_MAP_BEGIN(ServiceWorkerMana NS_INTERFACE_MAP_END ServiceWorkerManager::ServiceWorkerManager() : mActor(nullptr) , mShuttingDown(false) { // Register this component to PBackground. MOZ_ALWAYS_TRUE(BackgroundChild::GetOrCreateForCurrentThread(this)); + + gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay"); } ServiceWorkerManager::~ServiceWorkerManager() { // The map will assert if it is not empty when destroyed. mRegistrationInfos.Clear(); MOZ_ASSERT(!mActor); } @@ -1609,25 +1621,26 @@ ServiceWorkerManager::AppendPendingOpera if (!mShuttingDown) { PendingOperation* opt = mPendingOperations.AppendElement(); opt->mRunnable = aRunnable; } } namespace { // Just holds a ref to a ServiceWorker until the Promise is fulfilled. -class KeepAliveHandler final : public PromiseNativeHandler +class KeepAliveHandler : public PromiseNativeHandler { nsMainThreadPtrHandle<ServiceWorker> mServiceWorker; +protected: virtual ~KeepAliveHandler() {} public: - NS_DECL_ISUPPORTS + NS_DECL_THREADSAFE_ISUPPORTS explicit KeepAliveHandler(const nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker) : mServiceWorker(aServiceWorker) {} void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override { @@ -1646,16 +1659,160 @@ public: MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); #endif } }; NS_IMPL_ISUPPORTS0(KeepAliveHandler) +void +DummyCallback(nsITimer* aTimer, void* aClosure) +{ + // Nothing. +} + +class AllowWindowInteractionKeepAliveHandler; + +class ClearWindowAllowedRunnable final : public WorkerRunnable +{ +public: + ClearWindowAllowedRunnable(WorkerPrivate* aWorkerPrivate, + AllowWindowInteractionKeepAliveHandler* aHandler) + : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) + , mHandler(aHandler) + { } + +private: + bool + PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + // WorkerRunnable asserts that the dispatch is from parent thread if + // the busy count modification is WorkerThreadUnchangedBusyCount. + // Since this runnable will be dispatched from the timer thread, we override + // PreDispatch and PostDispatch to skip the check. + return true; + } + + void + PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, + bool aDispatchResult) override + { + // Silence bad assertions. + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; + + nsRefPtr<AllowWindowInteractionKeepAliveHandler> mHandler; +}; + +class AllowWindowInteractionKeepAliveHandler final : public KeepAliveHandler +{ + friend class ClearWindowAllowedRunnable; + nsCOMPtr<nsITimer> mTimer; + + ~AllowWindowInteractionKeepAliveHandler() + { + MOZ_ASSERT(!mTimer); + } + + void + ClearWindowAllowed(WorkerPrivate* aWorkerPrivate) + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + + if (mTimer) { + aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction(); + mTimer->Cancel(); + mTimer = nullptr; + MOZ_ALWAYS_TRUE(aWorkerPrivate->ModifyBusyCountFromWorker(aWorkerPrivate->GetJSContext(), false)); + } + } + + void + StartClearWindowTimer(WorkerPrivate* aWorkerPrivate) + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + MOZ_ASSERT(!mTimer); + + nsresult rv; + nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + nsRefPtr<ClearWindowAllowedRunnable> r = + new ClearWindowAllowedRunnable(aWorkerPrivate, this); + + nsRefPtr<TimerThreadEventTarget> target = + new TimerThreadEventTarget(aWorkerPrivate, r); + + rv = timer->SetTarget(target); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + // The important stuff that *has* to be reversed. + if (NS_WARN_IF(!aWorkerPrivate->ModifyBusyCountFromWorker(aWorkerPrivate->GetJSContext(), true))) { + return; + } + aWorkerPrivate->GlobalScope()->AllowWindowInteraction(); + timer.swap(mTimer); + + // We swap first and then initialize the timer so that even if initializing + // fails, we still clean the busy count and interaction count correctly. + // The timer can't be initialized before modifying the busy count since the + // timer thread could run and call the timeout but the worker may + // already be terminating and modifying the busy count could fail. + rv = mTimer->InitWithFuncCallback(DummyCallback, nullptr, gDOMDisableOpenClickDelay, nsITimer::TYPE_ONE_SHOT); + if (NS_WARN_IF(NS_FAILED(rv))) { + ClearWindowAllowed(aWorkerPrivate); + return; + } + } + +public: + NS_DECL_ISUPPORTS_INHERITED + + AllowWindowInteractionKeepAliveHandler(const nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker, + WorkerPrivate* aWorkerPrivate) + : KeepAliveHandler(aServiceWorker) + { + StartClearWindowTimer(aWorkerPrivate); + } + + void + ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override + { + WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); + ClearWindowAllowed(worker); + KeepAliveHandler::ResolvedCallback(aCx, aValue); + } + + void + RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override + { + WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); + ClearWindowAllowed(worker); + KeepAliveHandler::RejectedCallback(aCx, aValue); + } +}; + +NS_IMPL_ISUPPORTS_INHERITED0(AllowWindowInteractionKeepAliveHandler, KeepAliveHandler) + +bool +ClearWindowAllowedRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) +{ + mHandler->ClearWindowAllowed(aWorkerPrivate); + return true; +} + // Returns a Promise if the event was successfully dispatched and no exceptions // were raised, otherwise returns null. already_AddRefed<Promise> DispatchExtendableEventOnWorkerScope(JSContext* aCx, WorkerGlobalScope* aWorkerScope, ExtendableEvent* aEvent) { MOZ_ASSERT(aWorkerScope); @@ -2376,22 +2533,27 @@ public: nei.mCancelable = true; nsRefPtr<NotificationEvent> event = NotificationEvent::Constructor(target, NS_LITERAL_STRING("notificationclick"), nei, result); if (NS_WARN_IF(result.Failed())) { return false; } + aWorkerPrivate->GlobalScope()->AllowWindowInteraction(); event->SetTrusted(true); nsRefPtr<Promise> waitUntilPromise = DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event); + // If the handler calls WaitUntil(), that will manage its own interaction + // 'stack'. + aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction(); if (waitUntilPromise) { - nsRefPtr<KeepAliveHandler> handler = new KeepAliveHandler(mServiceWorker); + nsRefPtr<AllowWindowInteractionKeepAliveHandler> handler = + new AllowWindowInteractionKeepAliveHandler(mServiceWorker, aWorkerPrivate); waitUntilPromise->AppendNativeHandler(handler); } return true; } }; NS_IMETHODIMP @@ -2407,16 +2569,18 @@ ServiceWorkerManager::SendNotificationCl const nsAString& aData, const nsAString& aBehavior) { OriginAttributes attrs; if (!attrs.PopulateFromSuffix(aOriginSuffix)) { return NS_ERROR_INVALID_ARG; } + gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay"); + nsRefPtr<ServiceWorker> serviceWorker = CreateServiceWorkerForScope(attrs, aScope, nullptr); if (!serviceWorker) { return NS_ERROR_FAILURE; } nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle( new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
--- a/dom/workers/ServiceWorkerWindowClient.cpp +++ b/dom/workers/ServiceWorkerWindowClient.cpp @@ -85,19 +85,17 @@ public: NS_IMETHOD Run() override { AssertIsOnMainThread(); nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId); UniquePtr<ServiceWorkerClientInfo> clientInfo; if (window) { - mozilla::ErrorResult result; - //FIXME(catalinb): Bug 1144660 - check if we are allowed to focus here. - window->Focus(result); + nsContentUtils::DispatchChromeEvent(window->GetExtantDoc(), window->GetOuterWindow(), NS_LITERAL_STRING("DOMServiceWorkerFocusClient"), true, true); clientInfo.reset(new ServiceWorkerClientInfo(window->GetDocument(), window->GetOuterWindow())); } DispatchResult(Move(clientInfo)); return NS_OK; } @@ -137,24 +135,28 @@ ServiceWorkerWindowClient::Focus(ErrorRe nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); MOZ_ASSERT(global); nsRefPtr<Promise> promise = Promise::Create(global, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - nsRefPtr<PromiseWorkerProxy> promiseProxy = - PromiseWorkerProxy::Create(workerPrivate, promise); - if (!promiseProxy->GetWorkerPromise()) { - // Don't dispatch if adding the worker feature failed. - return promise.forget(); - } + if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) { + nsRefPtr<PromiseWorkerProxy> promiseProxy = + PromiseWorkerProxy::Create(workerPrivate, promise); + if (!promiseProxy->GetWorkerPromise()) { + // Don't dispatch if adding the worker feature failed. + return promise.forget(); + } - nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId, - promiseProxy); - aRv = NS_DispatchToMainThread(r); - if (NS_WARN_IF(aRv.Failed())) { - promise->MaybeReject(aRv); + nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId, + promiseProxy); + aRv = NS_DispatchToMainThread(r); + if (NS_WARN_IF(aRv.Failed())) { + promise->MaybeReject(aRv); + } + } else { + promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); } return promise.forget(); }
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -1861,78 +1861,16 @@ private: }; void DummyCallback(nsITimer* aTimer, void* aClosure) { // Nothing! } -class TimerThreadEventTarget final : public nsIEventTarget -{ - ~TimerThreadEventTarget() {} - - WorkerPrivate* mWorkerPrivate; - nsRefPtr<WorkerRunnable> mWorkerRunnable; - -public: - TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate, - WorkerRunnable* aWorkerRunnable) - : mWorkerPrivate(aWorkerPrivate), mWorkerRunnable(aWorkerRunnable) - { - MOZ_ASSERT(aWorkerPrivate); - MOZ_ASSERT(aWorkerRunnable); - } - - NS_DECL_THREADSAFE_ISUPPORTS - -protected: - NS_IMETHOD - DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override - { - nsCOMPtr<nsIRunnable> runnable(aRunnable); - return Dispatch(runnable.forget(), aFlags); - } - - NS_IMETHOD - Dispatch(already_AddRefed<nsIRunnable>&& aRunnable, uint32_t aFlags) override - { - // This should only happen on the timer thread. - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(aFlags == nsIEventTarget::DISPATCH_NORMAL); - - nsRefPtr<TimerThreadEventTarget> kungFuDeathGrip = this; - - // Run the runnable we're given now (should just call DummyCallback()), - // otherwise the timer thread will leak it... If we run this after - // dispatch running the event can race against resetting the timer. - nsCOMPtr<nsIRunnable> runnable(aRunnable); - runnable->Run(); - - // This can fail if we're racing to terminate or cancel, should be handled - // by the terminate or cancel code. - mWorkerRunnable->Dispatch(nullptr); - - return NS_OK; - } - - NS_IMETHOD - IsOnCurrentThread(bool* aIsOnCurrentThread) override - { - MOZ_ASSERT(aIsOnCurrentThread); - - nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; - } -}; - class KillCloseEventRunnable final : public WorkerRunnable { nsCOMPtr<nsITimer> mTimer; class KillScriptRunnable final : public WorkerControlRunnable { public: explicit KillScriptRunnable(WorkerPrivate* aWorkerPrivate) @@ -2376,16 +2314,70 @@ PRThreadFromThread(nsIThread* aThread) } } /* anonymous namespace */ NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, nsRunnable) NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, nsRunnable) +TimerThreadEventTarget::TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate, + WorkerRunnable* aWorkerRunnable) + : mWorkerPrivate(aWorkerPrivate), mWorkerRunnable(aWorkerRunnable) +{ + MOZ_ASSERT(aWorkerPrivate); + MOZ_ASSERT(aWorkerRunnable); +} + +TimerThreadEventTarget::~TimerThreadEventTarget() +{ +} + +NS_IMETHODIMP +TimerThreadEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) +{ + nsCOMPtr<nsIRunnable> runnable(aRunnable); + return Dispatch(runnable.forget(), aFlags); +} + +NS_IMETHODIMP +TimerThreadEventTarget::Dispatch(already_AddRefed<nsIRunnable>&& aRunnable, uint32_t aFlags) +{ + // This should only happen on the timer thread. + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aFlags == nsIEventTarget::DISPATCH_NORMAL); + + nsRefPtr<TimerThreadEventTarget> kungFuDeathGrip = this; + + // Run the runnable we're given now (should just call DummyCallback()), + // otherwise the timer thread will leak it... If we run this after + // dispatch running the event can race against resetting the timer. + nsCOMPtr<nsIRunnable> runnable(aRunnable); + runnable->Run(); + + // This can fail if we're racing to terminate or cancel, should be handled + // by the terminate or cancel code. + mWorkerRunnable->Dispatch(nullptr); + + return NS_OK; +} + +NS_IMETHODIMP +TimerThreadEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) +{ + MOZ_ASSERT(aIsOnCurrentThread); + + nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + NS_IMPL_ISUPPORTS(TimerThreadEventTarget, nsIEventTarget) WorkerLoadInfo::WorkerLoadInfo() : mWindowID(UINT64_MAX) , mServiceWorkerID(0) , mFromWindow(false) , mEvalAllowed(false) , mReportCSPViolations(false)
--- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -1570,11 +1570,35 @@ public: nsIEventTarget* EventTarget() const { return mTarget; } }; +class TimerThreadEventTarget final : public nsIEventTarget +{ + ~TimerThreadEventTarget(); + + WorkerPrivate* mWorkerPrivate; + nsRefPtr<WorkerRunnable> mWorkerRunnable; +public: + NS_DECL_THREADSAFE_ISUPPORTS + + TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate, + WorkerRunnable* aWorkerRunnable); + +protected: + NS_IMETHOD + DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override; + + + NS_IMETHOD + Dispatch(already_AddRefed<nsIRunnable>&& aRunnable, uint32_t aFlags) override; + + NS_IMETHOD + IsOnCurrentThread(bool* aIsOnCurrentThread) override; +}; + END_WORKERS_NAMESPACE #endif /* mozilla_dom_workers_workerprivate_h__ */
--- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -54,17 +54,18 @@ USING_WORKERS_NAMESPACE using mozilla::dom::cache::CacheStorage; using mozilla::dom::indexedDB::IDBFactory; using mozilla::ipc::PrincipalInfo; BEGIN_WORKERS_NAMESPACE WorkerGlobalScope::WorkerGlobalScope(WorkerPrivate* aWorkerPrivate) -: mWorkerPrivate(aWorkerPrivate) +: mWindowInteractionsAllowed(0) +, mWorkerPrivate(aWorkerPrivate) { mWorkerPrivate->AssertIsOnWorkerThread(); } WorkerGlobalScope::~WorkerGlobalScope() { mWorkerPrivate->AssertIsOnWorkerThread(); }
--- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -54,16 +54,18 @@ class WorkerGlobalScope : public DOMEven nsRefPtr<Console> mConsole; nsRefPtr<WorkerLocation> mLocation; nsRefPtr<WorkerNavigator> mNavigator; nsRefPtr<Performance> mPerformance; nsRefPtr<IDBFactory> mIndexedDB; nsRefPtr<cache::CacheStorage> mCacheStorage; + uint32_t mWindowInteractionsAllowed; + protected: WorkerPrivate* mWorkerPrivate; explicit WorkerGlobalScope(WorkerPrivate* aWorkerPrivate); virtual ~WorkerGlobalScope(); public: virtual JSObject* @@ -157,16 +159,35 @@ public: already_AddRefed<Promise> CreateImageBitmap(const ImageBitmapSource& aImage, ErrorResult& aRv); already_AddRefed<Promise> CreateImageBitmap(const ImageBitmapSource& aImage, int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh, ErrorResult& aRv); + + bool + WindowInteractionAllowed() const + { + return mWindowInteractionsAllowed > 0; + } + + void + AllowWindowInteraction() + { + mWindowInteractionsAllowed++; + } + + void + ConsumeWindowInteraction() + { + MOZ_ASSERT(mWindowInteractionsAllowed > 0); + mWindowInteractionsAllowed--; + } }; class DedicatedWorkerGlobalScope final : public WorkerGlobalScope { ~DedicatedWorkerGlobalScope() { } public: explicit DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
--- a/dom/workers/test/serviceworkers/browser_force_refresh.js +++ b/dom/workers/test/serviceworkers/browser_force_refresh.js @@ -12,16 +12,17 @@ function forceRefresh() { EventUtils.synthesizeKey('R', { accelKey: true, shiftKey: true }); } function test() { waitForExplicitFinish(); SpecialPowers.pushPrefEnv({'set': [['dom.serviceWorkers.enabled', true], ['dom.serviceWorkers.exemptFromPerDomainMax', true], ['dom.serviceWorkers.testing.enabled', true], + ['dom.serviceWorkers.interception.enabled', true], ['dom.caches.enabled', true]]}, function() { var url = gTestRoot + 'browser_base_force_refresh.html'; var tab = gBrowser.addTab(url); gBrowser.selectedTab = tab; var cachedLoad = false;
deleted file mode 100644 --- a/dom/workers/test/serviceworkers/client_focus_worker.js +++ /dev/null @@ -1,15 +0,0 @@ -onmessage = function(e) { - if (!e.source) { - dump("ERROR: message doesn't have a source."); - } - - // The client should be a window client - if (e.source instanceof WindowClient) { - // this will dispatch a focus event on the client - e.source.focus().then(function(client) { - client.postMessage(client.focused); - }); - } else { - dump("ERROR: client should be a WindowClient"); - } -};
--- a/dom/workers/test/serviceworkers/fetch/fetch_tests.js +++ b/dom/workers/test/serviceworkers/fetch/fetch_tests.js @@ -91,16 +91,21 @@ fetchXHR('nonresponse.txt', null, functi finish(); }); fetchXHR('nonresponse2.txt', null, function(xhr) { my_ok(xhr.status == 0, "load should not complete"); finish(); }); +fetchXHR('nonpromise.txt', null, function(xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + fetchXHR('headers.txt', function(xhr) { my_ok(xhr.status == 200, "load should be successful"); my_ok(xhr.responseText == "1", "request header checks should have passed"); finish(); }, null, [["X-Test1", "header1"], ["X-Test2", "header2"]]); var expectedUncompressedResponse = ""; for (var i = 0; i < 10; ++i) {
--- a/dom/workers/test/serviceworkers/fetch_event_worker.js +++ b/dom/workers/test/serviceworkers/fetch_event_worker.js @@ -71,16 +71,27 @@ onfetch = function(ev) { else if (ev.request.url.includes("nonresponse.txt")) { ev.respondWith(Promise.resolve(5)); } else if (ev.request.url.includes("nonresponse2.txt")) { ev.respondWith(Promise.resolve({})); } + else if (ev.request.url.includes("nonpromise.txt")) { + try { + // This should coerce to Promise(5) instead of throwing + ev.respondWith(5); + } catch (e) { + // test is expecting failure, so return a success if we get a thrown + // exception + ev.respondWith(new Response('respondWith(5) threw ' + e)); + } + } + else if (ev.request.url.includes("headers.txt")) { var ok = true; ok &= ev.request.headers.get("X-Test1") == "header1"; ok &= ev.request.headers.get("X-Test2") == "header2"; ev.respondWith(Promise.resolve( new Response(ok.toString(), {}) )); }
--- a/dom/workers/test/serviceworkers/mochitest.ini +++ b/dom/workers/test/serviceworkers/mochitest.ini @@ -12,17 +12,16 @@ support-files = simpleregister/index.html simpleregister/ready.html controller/index.html unregister/index.html unregister/unregister.html workerUpdate/update.html sw_clients/simple.html sw_clients/service_worker_controlled.html - sw_clients/focus_stealing_client.html match_all_worker.js match_all_advanced_worker.js worker_unregister.js worker_update.js message_posting_worker.js fetch/index.html fetch/fetch_worker_script.js fetch/fetch_tests.js @@ -85,21 +84,22 @@ support-files = serviceworker_not_sharedworker.js match_all_client/match_all_client_id.html match_all_client_id_worker.js source_message_posting_worker.js scope/scope_worker.js redirect_serviceworker.sjs importscript.sjs importscript_worker.js - client_focus_worker.js bug1151916_worker.js bug1151916_driver.html notificationclick.html notificationclick.js + notificationclick_focus.html + notificationclick_focus.js worker_updatefoundevent.js worker_updatefoundevent2.js updatefoundevent.html empty.js periodic_update_test.js periodic.sjs periodic/frame.html periodic/register.html @@ -158,17 +158,16 @@ support-files = sw_clients/dummy.html [test_app_protocol.html] skip-if = release_build [test_bug1151916.html] [test_claim.html] [test_claim_fetch.html] [test_claim_oninstall.html] -[test_client_focus.html] [test_close.html] [test_controller.html] [test_cross_origin_url_after_redirect.html] [test_empty_serviceworker.html] [test_eval_allowed.html] [test_eval_not_allowed.html] [test_fetch_event.html] [test_force_refresh.html] @@ -222,16 +221,18 @@ skip-if = release_build [test_request_context_track.html] [test_request_context_video.html] [test_request_context_worker.html] [test_request_context_xhr.html] [test_request_context_xslt.html] [test_scopes.html] [test_sandbox_intercept.html] [test_notificationclick.html] +[test_notificationclick_focus.html] +skip-if = toolkit == "android" || toolkit == "gonk" [test_notification_constructor_error.html] [test_notification_get.html] [test_sanitize.html] [test_sanitize_domain.html] [test_service_worker_allowed.html] [test_serviceworker_interfaces.html] [test_serviceworker_not_sharedworker.html] [test_skip_waiting.html]
new file mode 100644 --- /dev/null +++ b/dom/workers/test/serviceworkers/notificationclick_focus.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1144660 - controlled page</title> +<script class="testbody" type="text/javascript"> + var testWindow = parent; + if (opener) { + testWindow = opener; + } + + navigator.serviceWorker.ready.then(function(swr) { + swr.showNotification("Hi there. The ServiceWorker should receive a click event for this."); + }); + + navigator.serviceWorker.onmessage = function(msg) { + dump("GOT Message " + JSON.stringify(msg.data) + "\n"); + testWindow.callback(msg.data.ok); + }; +</script> + +</head> +<body> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/serviceworkers/notificationclick_focus.js @@ -0,0 +1,40 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ +// + +function promisifyTimerFocus(client, delay) { + return new Promise(function(resolve, reject) { + setTimeout(function() { + client.focus().then(resolve, reject); + }, delay); + }); +} + +onnotificationclick = function(e) { + e.waitUntil(self.clients.matchAll().then(function(clients) { + if (clients.length === 0) { + dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n"); + return Promise.resolve(); + } + + var immediatePromise = clients[0].focus(); + var withinTimeout = promisifyTimerFocus(clients[0], 100); + + var afterTimeout = promisifyTimerFocus(clients[0], 2000).then(function() { + throw "Should have failed!"; + }, function() { + return Promise.resolve(); + }); + + return Promise.all([immediatePromise, withinTimeout, afterTimeout]).then(function() { + clients.forEach(function(client) { + client.postMessage({ok: true}); + }); + }).catch(function(e) { + dump("Error " + e + "\n"); + clients.forEach(function(client) { + client.postMessage({ok: false}); + }); + }); + })); +}
--- a/dom/workers/test/serviceworkers/periodic_update_test.js +++ b/dom/workers/test/serviceworkers/periodic_update_test.js @@ -63,12 +63,13 @@ function unregisterSW() { function runTheTest() { SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.interception.enabled", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ]}, function() { start(); }); }
deleted file mode 100644 --- a/dom/workers/test/serviceworkers/sw_clients/focus_stealing_client.html +++ /dev/null @@ -1,33 +0,0 @@ -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<!DOCTYPE HTML> -<html> -<head> - <title>Bug 1130686 - Test service worker client.focus: client </title> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> - <!-- - We load this as an iframe to blur the main test window. - --> -</head> -<body>/ -<p id="display"></p> -<div id="content" style="display: none"></div> -<pre id="test"></pre> -<script class="testbody" type="text/javascript"> - if (!parent) { - info("error: sw_clients/focus_stealing_client.html shouldn't be launched directly!"); - } - - window.onload = function() { - navigator.serviceWorker.ready.then(function() { - parent.postMessage("READY", "*"); - }); - } -</script> -</pre> -</body> -</html> -
--- a/dom/workers/test/serviceworkers/test_app_installation.html +++ b/dom/workers/test/serviceworkers/test_app_installation.html @@ -42,17 +42,18 @@ addLoadEvent(go); function setup() { info('Setting up'); return new Promise((resolve, reject) => { SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.pushPrefEnv({'set': [ ['dom.mozBrowserFramesEnabled', true], ['dom.serviceWorkers.exemptFromPerDomainMax', true], ['dom.serviceWorkers.enabled', true], - ['dom.serviceWorkers.testing.enabled', true] + ['dom.serviceWorkers.testing.enabled', true], + ['dom.serviceWorkers.interception.enabled', true], ]}, () => { SpecialPowers.pushPermissions([ { 'type': 'webapps-manage', 'allow': 1, 'context': document }, { 'type': 'browser', 'allow': 1, 'context': document }, { 'type': 'embed-apps', 'allow': 1, 'context': document } ], () => { SpecialPowers.autoConfirmAppInstall(() => { SpecialPowers.autoConfirmAppUninstall(resolve);
--- a/dom/workers/test/serviceworkers/test_client_focus.html +++ b/dom/workers/test/serviceworkers/test_client_focus.html @@ -4,18 +4,18 @@ --> <!DOCTYPE HTML> <html> <head> <title>Bug 1130686 - Test service worker client.focus </title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> <!-- - This test checks that client.focus is able to restore focus to the main window - when an iframe is holding the focus. + This test checks that client.focus is available. + Actual focusing is tested by test_notificationclick_focus.html since only notification events have permission to change focus. --> </head> <body> <p id="display"></p> <div id="content"></div> <pre id="test"></pre> <script class="testbody" type="text/javascript"> var registration;
--- a/dom/workers/test/serviceworkers/test_eval_allowed.html +++ b/dom/workers/test/serviceworkers/test_eval_allowed.html @@ -29,14 +29,15 @@ SimpleTest.finish(); }); } SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], - ["dom.serviceWorkers.testing.enabled", true] + ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ]}, runTest); </script> </pre> </body> </html>
--- a/dom/workers/test/serviceworkers/test_eval_not_allowed.html +++ b/dom/workers/test/serviceworkers/test_eval_not_allowed.html @@ -32,14 +32,15 @@ SimpleTest.finish(); }); } SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], - ["dom.serviceWorkers.testing.enabled", true] + ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ]}, runTest); </script> </pre> </body> </html>
--- a/dom/workers/test/serviceworkers/test_fetch_event_client_postmessage.html +++ b/dom/workers/test/serviceworkers/test_fetch_event_client_postmessage.html @@ -60,12 +60,14 @@ }).then(SimpleTest.finish); } SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ]}, runTest); </script> </body> </html>
--- a/dom/workers/test/serviceworkers/test_gzip_redirect.html +++ b/dom/workers/test/serviceworkers/test_gzip_redirect.html @@ -71,14 +71,15 @@ ok(false, "Some test failed with error " + e); }).then(SimpleTest.finish); } SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], - ["dom.serviceWorkers.testing.enabled", true] + ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ]}, runTest); </script> </pre> </body> </html>
--- a/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html +++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html @@ -43,15 +43,16 @@ } SimpleTest.waitForExplicitFinish(); onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ["dom.caches.enabled", true], ]}, runTest); }; </script> </pre> </body> </html>
--- a/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html +++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html @@ -43,15 +43,16 @@ } SimpleTest.waitForExplicitFinish(); onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ["dom.caches.enabled", true], ]}, runTest); }; </script> </pre> </body> </html>
--- a/dom/workers/test/serviceworkers/test_notificationclick.html +++ b/dom/workers/test/serviceworkers/test_notificationclick.html @@ -44,13 +44,14 @@ https://bugzilla.mozilla.org/show_bug.cg }; SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], ["dom.webnotifications.workers.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ["notification.prompt.testing", true], ]}, runTest); </script> </body> </html>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notificationclick_focus.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=916893 +--> +<head> + <title>Bug 1144660 - Test client.focus() permissions on notification click</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script> + <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +<script type="text/javascript"> + SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events."); + + function testFrame(src) { + var iframe = document.createElement("iframe"); + iframe.src = src; + window.callback = function(result) { + window.callback = null; + document.body.removeChild(iframe); + iframe = null; + ok(result, "All tests passed."); + MockServices.unregister(); + SimpleTest.finish(); + }; + document.body.appendChild(iframe); + } + + function runTest() { + MockServices.register(); + testFrame('notificationclick_focus.html'); + navigator.serviceWorker.register("notificationclick_focus.js", { scope: "notificationclick_focus.html" }).then(function(reg) { + }, function(e) { + ok(false, "registration should have passed!"); + }); + }; + + SimpleTest.waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ["dom.webnotifications.workers.enabled", true], + ["notification.prompt.testing", true], + ["dom.disable_open_click_delay", 1000], + ]}, runTest); +</script> +</body> +</html>
--- a/dom/workers/test/serviceworkers/test_opaque_intercept.html +++ b/dom/workers/test/serviceworkers/test_opaque_intercept.html @@ -72,15 +72,16 @@ }).then(SimpleTest.finish); } SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ["dom.serviceWorkers.interception.opaque.enabled", true], ["dom.caches.enabled", true], ]}, runTest); </script> </pre> </body> </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect.html +++ b/dom/workers/test/serviceworkers/test_origin_after_redirect.html @@ -43,15 +43,16 @@ } SimpleTest.waitForExplicitFinish(); onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ["dom.caches.enabled", true], ]}, runTest); }; </script> </pre> </body> </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html +++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html @@ -43,15 +43,16 @@ } SimpleTest.waitForExplicitFinish(); onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ["dom.caches.enabled", true], ]}, runTest); }; </script> </pre> </body> </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html +++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html @@ -43,15 +43,16 @@ } SimpleTest.waitForExplicitFinish(); onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ["dom.caches.enabled", true], ]}, runTest); }; </script> </pre> </body> </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html +++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html @@ -43,15 +43,16 @@ } SimpleTest.waitForExplicitFinish(); onload = function() { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], + ['dom.serviceWorkers.interception.enabled', true], ["dom.caches.enabled", true], ]}, runTest); }; </script> </pre> </body> </html>
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js +++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js @@ -169,23 +169,23 @@ var interfaceNamesInGlobalScope = "PerformanceEntry", // IMPORTANT: Do not change this list without review from a DOM peer! "PerformanceMark", // IMPORTANT: Do not change this list without review from a DOM peer! "PerformanceMeasure", // IMPORTANT: Do not change this list without review from a DOM peer! "Promise", // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushEvent", b2g: false, android: false, release: false }, + { name: "PushEvent", b2g: false, android: false}, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushManager", b2g: false, android: false, release: false }, + { name: "PushManager", b2g: false, android: false}, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushMessageData", b2g: false, android: false, release: false }, + { name: "PushMessageData", b2g: false, android: false}, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushSubscription", b2g: false, android: false, release: false }, + { name: "PushSubscription", b2g: false, android: false}, // IMPORTANT: Do not change this list without review from a DOM peer! "Request", // IMPORTANT: Do not change this list without review from a DOM peer! "Response", // IMPORTANT: Do not change this list without review from a DOM peer! "ServiceWorker", // IMPORTANT: Do not change this list without review from a DOM peer! "ServiceWorkerGlobalScope",
--- a/dom/workers/test/serviceworkers/test_third_party_iframes.html +++ b/dom/workers/test/serviceworkers/test_third_party_iframes.html @@ -138,16 +138,17 @@ const COOKIE_BEHAVIOR_REJECTFOREIGN = 1; const COOKIE_BEHAVIOR_REJECT = 2; const COOKIE_BEHAVIOR_LIMITFOREIGN = 3; let steps = [() => { SpecialPowers.pushPrefEnv({"set": [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], + ["dom.serviceWorkers.interception.enabled", true], ["browser.dom.window.dump.enabled", true], ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT] ]}, next); }, () => { testShouldIntercept(next); }, () => { SpecialPowers.pushPrefEnv({"set": [ ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECTFOREIGN]
--- a/dom/workers/test/test_worker_interfaces.js +++ b/dom/workers/test/test_worker_interfaces.js @@ -161,25 +161,25 @@ var interfaceNamesInGlobalScope = "PerformanceEntry", // IMPORTANT: Do not change this list without review from a DOM peer! "PerformanceMark", // IMPORTANT: Do not change this list without review from a DOM peer! "PerformanceMeasure", // IMPORTANT: Do not change this list without review from a DOM peer! "Promise", // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushManager", b2g: false, android: false, release: false }, + { name: "PushManager", b2g: false, android: false}, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushSubscription", b2g: false, android: false, release: false }, + { name: "PushSubscription", b2g: false, android: false}, // IMPORTANT: Do not change this list without review from a DOM peer! "Request", // IMPORTANT: Do not change this list without review from a DOM peer! "Response", // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "ServiceWorkerRegistration", release: false, b2g: false }, + { name: "ServiceWorkerRegistration", b2g: false, android: false }, // IMPORTANT: Do not change this list without review from a DOM peer! "TextDecoder", // IMPORTANT: Do not change this list without review from a DOM peer! "TextEncoder", // IMPORTANT: Do not change this list without review from a DOM peer! "XMLHttpRequest", // IMPORTANT: Do not change this list without review from a DOM peer! "XMLHttpRequestEventTarget",
--- a/dom/xml/XMLDocument.cpp +++ b/dom/xml/XMLDocument.cpp @@ -343,17 +343,17 @@ XMLDocument::Load(const nsAString& aUrl, if (!nsContentUtils::IsSystemPrincipal(principal)) { rv = principal->CheckMayLoad(uri, false, false); if (NS_FAILED(rv)) { aRv.Throw(rv); return false; } int16_t shouldLoad = nsIContentPolicy::ACCEPT; - rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_XMLHTTPREQUEST, + rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST, uri, principal, callingDoc ? callingDoc.get() : static_cast<nsIDocument*>(this), NS_LITERAL_CSTRING("application/xml"), nullptr, &shouldLoad, nsContentUtils::GetContentPolicy(), @@ -440,17 +440,17 @@ XMLDocument::Load(const nsAString& aUrl, nsCOMPtr<nsIChannel> channel; // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, // which in turn keeps STOP button from becoming active rv = NS_NewChannel(getter_AddRefs(channel), uri, callingDoc ? callingDoc.get() : static_cast<nsIDocument*>(this), nsILoadInfo::SEC_NORMAL, - nsIContentPolicy::TYPE_XMLHTTPREQUEST, + nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST, loadGroup, req, nsIRequest::LOAD_BACKGROUND); if (NS_FAILED(rv)) { aRv.Throw(rv); return false; }
--- a/extensions/permissions/nsContentBlocker.cpp +++ b/extensions/permissions/nsContentBlocker.cpp @@ -50,17 +50,19 @@ static const char *kTypeString[] = { "", // TYPE_INTERNAL_WORKER "", // TYPE_INTERNAL_SHARED_WORKER "", // TYPE_INTERNAL_EMBED "", // TYPE_INTERNAL_OBJECT "", // TYPE_INTERNAL_FRAME "", // TYPE_INTERNAL_IFRAME "", // TYPE_INTERNAL_AUDIO "", // TYPE_INTERNAL_VIDEO - "" // TYPE_INTERNAL_TRACK + "", // TYPE_INTERNAL_TRACK + "", // TYPE_INTERNAL_XMLHTTPREQUEST + "" // TYPE_INTERNAL_EVENTSOURCE }; #define NUMBER_OF_TYPES MOZ_ARRAY_LENGTH(kTypeString) uint8_t nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES]; NS_IMPL_ISUPPORTS(nsContentBlocker, nsIContentPolicy, nsIObserver,
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic +++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic @@ -1,9 +1,9 @@ -54837 +54838 0/nm 0th/pt 1/n1 1st/p 1th/tc 2/nm 2nd/p 2th/tc @@ -49506,16 +49506,17 @@ substation/MS substituent/MS substitute/XMGNDS substitution/M substrata substrate/MS substratum/M substructure/SM subsume/DSG +subsumption/S subsurface/M subsystem/SM subteen/SM subtenancy/M subtenant/SM subtend/SDG subterfuge/SM subterranean
--- a/gfx/cairo/libpixman/src/moz.build +++ b/gfx/cairo/libpixman/src/moz.build @@ -74,28 +74,22 @@ DEFINES['PACKAGE'] = 'mozpixman' DEFINES['_USE_MATH_DEFINES'] = True use_mmx = False use_sse2 = False use_vmx = False use_arm_simd_gcc = False use_arm_neon_gcc = False if '86' in CONFIG['OS_TEST']: - if '64' in CONFIG['OS_TEST']: - if CONFIG['GNU_CC']: - use_sse2 = True - else: + use_sse2 = True + if '64' not in CONFIG['OS_TEST']: if CONFIG['_MSC_VER']: use_mmx = True - if CONFIG['HAVE_GCC_ALIGN_ARG_POINTER']: - use_sse2 = True if CONFIG['GNU_CC']: use_mmx = True - if CONFIG['_MSC_VER']: - use_sse2 = True elif 'ppc' in CONFIG['OS_TEST']: if CONFIG['GNU_CC']: use_vmx = True # Apple's arm assembler doesn't support the same syntax as # the standard GNU assembler, so use the C fallback paths for now. # This may be fixable if clang's ARM/iOS assembler improves into a # viable solution in the future. elif 'arm' in CONFIG['OS_TEST']:
--- a/gfx/gl/GLScreenBuffer.cpp +++ b/gfx/gl/GLScreenBuffer.cpp @@ -54,16 +54,17 @@ GLScreenBuffer::Create(GLContext* gl, GLScreenBuffer::GLScreenBuffer(GLContext* gl, const SurfaceCaps& caps, UniquePtr<SurfaceFactory> factory) : mGL(gl) , mCaps(caps) , mFactory(Move(factory)) , mNeedsBlit(true) , mUserReadBufferMode(LOCAL_GL_BACK) + , mUserDrawBufferMode(LOCAL_GL_BACK) , mUserDrawFB(0) , mUserReadFB(0) , mInternalDrawFB(0) , mInternalReadFB(0) #ifdef DEBUG , mInInternalMode_DrawFB(true) , mInInternalMode_ReadFB(true) #endif @@ -440,16 +441,22 @@ GLScreenBuffer::Attach(SharedSurface* su MOZ_ASSERT(SharedSurf() == surf); // Update the ReadBuffer mode. if (mGL->IsSupported(gl::GLFeature::read_buffer)) { BindFB(0); mRead->SetReadBuffer(mUserReadBufferMode); } + // Update the DrawBuffer mode. + if (mGL->IsSupported(gl::GLFeature::draw_buffers)) { + BindFB(0); + SetDrawBuffer(mUserDrawBufferMode); + } + RequireBlit(); return true; } bool GLScreenBuffer::Swap(const gfx::IntSize& size) { @@ -544,16 +551,48 @@ GLScreenBuffer::CreateRead(SharedSurface GLContext* gl = mFactory->mGL; const GLFormats& formats = mFactory->mFormats; const SurfaceCaps& caps = mFactory->ReadCaps(); return ReadBuffer::Create(gl, caps, formats, surf); } void +GLScreenBuffer::SetDrawBuffer(GLenum mode) +{ + MOZ_ASSERT(mGL->IsSupported(gl::GLFeature::draw_buffers)); + MOZ_ASSERT(GetDrawFB() == 0); + + if (!mGL->IsSupported(GLFeature::draw_buffers)) + return; + + mUserDrawBufferMode = mode; + + GLuint fb = mDraw ? mDraw->mFB : mRead->mFB; + GLenum internalMode; + + switch (mode) { + case LOCAL_GL_BACK: + internalMode = (fb == 0) ? LOCAL_GL_BACK + : LOCAL_GL_COLOR_ATTACHMENT0; + break; + + case LOCAL_GL_NONE: + internalMode = LOCAL_GL_NONE; + break; + + default: + MOZ_CRASH("Bad value."); + } + + mGL->MakeCurrent(); + mGL->fDrawBuffers(1, &internalMode); +} + +void GLScreenBuffer::SetReadBuffer(GLenum mode) { MOZ_ASSERT(mGL->IsSupported(gl::GLFeature::read_buffer)); MOZ_ASSERT(GetReadFB() == 0); mUserReadBufferMode = mode; mRead->SetReadBuffer(mUserReadBufferMode); }
--- a/gfx/gl/GLScreenBuffer.h +++ b/gfx/gl/GLScreenBuffer.h @@ -144,16 +144,17 @@ protected: RefPtr<layers::SharedSurfaceTextureClient> mFront; UniquePtr<DrawBuffer> mDraw; UniquePtr<ReadBuffer> mRead; bool mNeedsBlit; GLenum mUserReadBufferMode; + GLenum mUserDrawBufferMode; // Below are the parts that help us pretend to be framebuffer 0: GLuint mUserDrawFB; GLuint mUserReadFB; GLuint mInternalDrawFB; GLuint mInternalReadFB; #ifdef DEBUG @@ -217,16 +218,17 @@ public: void AssureBlitted(); void AfterDrawCall(); void BeforeReadCall(); bool CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); void SetReadBuffer(GLenum userMode); + void SetDrawBuffer(GLenum userMode); /** * Attempts to read pixels from the current bound framebuffer, if * it is backed by a SharedSurface. * * Returns true if the pixel data has been read back, false * otherwise. */
--- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -618,27 +618,27 @@ CairoImage::GetTextureClient(Compositabl // TextureHost is not used by CompositableHost. #ifdef MOZ_WIDGET_GONK RefPtr<TextureClientRecycleAllocator> recycler = aClient->GetTextureClientRecycler(); if (recycler) { textureClient = recycler->CreateOrRecycleForDrawing(surface->GetFormat(), surface->GetSize(), - gfx::BackendType::NONE, + BackendSelector::Content, aClient->GetTextureFlags()); } #endif #endif if (!textureClient) { // gfx::BackendType::NONE means default to content backend textureClient = aClient->CreateTextureClientForDrawing(surface->GetFormat(), surface->GetSize(), - gfx::BackendType::NONE, + BackendSelector::Content, TextureFlags::DEFAULT); } if (!textureClient) { return nullptr; } if (!textureClient->Lock(OpenMode::OPEN_WRITE_ONLY)) { return nullptr;
--- a/gfx/layers/client/CanvasClient.cpp +++ b/gfx/layers/client/CanvasClient.cpp @@ -121,24 +121,23 @@ CanvasClient2D::CreateTextureClientForCa // We want a cairo backend here as we don't want to be copying into // an accelerated backend and we like LockBits to work. This is currently // the most effective way to make this work. return TextureClient::CreateForRawBufferAccess(GetForwarder(), aFormat, aSize, BackendType::CAIRO, mTextureFlags | aFlags); } - gfx::BackendType backend = gfxPlatform::GetPlatform()->GetPreferredCanvasBackend(); #ifdef XP_WIN - return CreateTextureClientForDrawing(aFormat, aSize, backend, aFlags); + return CreateTextureClientForDrawing(aFormat, aSize, BackendSelector::Canvas, aFlags); #else // XXX - We should use CreateTextureClientForDrawing, but we first need // to use double buffering. return TextureClient::CreateForRawBufferAccess(GetForwarder(), - aFormat, aSize, backend, + aFormat, aSize, gfx::BackendType::NONE, mTextureFlags | aFlags); #endif } //////////////////////////////////////////////////////////////////////// CanvasClientSharedSurface::CanvasClientSharedSurface(CompositableForwarder* aLayerForwarder, TextureFlags aFlags)
--- a/gfx/layers/client/CompositableClient.cpp +++ b/gfx/layers/client/CompositableClient.cpp @@ -202,22 +202,22 @@ CompositableClient::CreateBufferTextureC return TextureClient::CreateForRawBufferAccess(GetForwarder(), aFormat, aSize, aMoz2DBackend, aTextureFlags | mTextureFlags); } already_AddRefed<TextureClient> CompositableClient::CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat, gfx::IntSize aSize, - gfx::BackendType aMoz2DBackend, + BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags) { return TextureClient::CreateForDrawing(GetForwarder(), - aFormat, aSize, aMoz2DBackend, + aFormat, aSize, aSelector, aTextureFlags | mTextureFlags, aAllocFlags); } bool CompositableClient::AddTextureClient(TextureClient* aClient) { if(!aClient || !aClient->IsAllocated()) {
--- a/gfx/layers/client/CompositableClient.h +++ b/gfx/layers/client/CompositableClient.h @@ -135,17 +135,17 @@ public: CreateBufferTextureClient(gfx::SurfaceFormat aFormat, gfx::IntSize aSize, gfx::BackendType aMoz2dBackend = gfx::BackendType::NONE, TextureFlags aFlags = TextureFlags::DEFAULT); already_AddRefed<TextureClient> CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat, gfx::IntSize aSize, - gfx::BackendType aMoz2DBackend, + BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT); /** * Establishes the connection with compositor side through IPDL */ virtual bool Connect(ImageContainer* aImageContainer = nullptr);
--- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -288,17 +288,17 @@ ContentClientRemoteBuffer::BuildTextureC CreateBackBuffer(mBufferRect); } void ContentClientRemoteBuffer::CreateBackBuffer(const IntRect& aBufferRect) { // gfx::BackendType::NONE means fallback to the content backend mTextureClient = CreateTextureClientForDrawing( - mSurfaceFormat, mSize, gfx::BackendType::NONE, + mSurfaceFormat, mSize, BackendSelector::Content, mTextureFlags | ExtraTextureFlags(), TextureAllocationFlags::ALLOC_CLEAR_BUFFER ); if (!mTextureClient || !AddTextureClient(mTextureClient)) { AbortTextureClientCreation(); return; }
--- a/gfx/layers/client/ImageClient.cpp +++ b/gfx/layers/client/ImageClient.cpp @@ -211,17 +211,17 @@ ImageClientSingle::UpdateImage(ImageCont #endif } else { MOZ_ASSERT(false, "Bad ImageFormat."); } } else { RefPtr<gfx::SourceSurface> surface = image->GetAsSourceSurface(); MOZ_ASSERT(surface); texture = CreateTextureClientForDrawing(surface->GetFormat(), image->GetSize(), - gfx::BackendType::NONE, mTextureFlags); + BackendSelector::Content, mTextureFlags); if (!texture) { return false; } MOZ_ASSERT(texture->CanExposeDrawTarget()); if (!texture->Lock(OpenMode::OPEN_WRITE_ONLY)) { return false;
--- a/gfx/layers/client/SingleTiledContentClient.cpp +++ b/gfx/layers/client/SingleTiledContentClient.cpp @@ -91,17 +91,17 @@ ClientSingleTiledLayerBuffer::GetSurface mFrameResolution.xScale, mFrameResolution.yScale); } already_AddRefed<TextureClient> ClientSingleTiledLayerBuffer::GetTextureClient() { return mCompositableClient->CreateTextureClientForDrawing( - gfx::ImageFormatToSurfaceFormat(mFormat), mSize, gfx::BackendType::NONE, + gfx::ImageFormatToSurfaceFormat(mFormat), mSize, BackendSelector::Content, TextureFlags::IMMEDIATE_UPLOAD); } void ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion, const nsIntRegion& aDirtyRegion, LayerManager::DrawPaintedLayerCallback aCallback,
--- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -315,75 +315,87 @@ CreateBufferTextureClient(ISurfaceAlloca return result.forget(); } RefPtr<BufferTextureClient> result = new ShmemTextureClient(aAllocator, aFormat, aMoz2DBackend, aTextureFlags); return result.forget(); } +static inline gfx::BackendType +BackendTypeForBackendSelector(BackendSelector aSelector) +{ + switch (aSelector) { + case BackendSelector::Canvas: + return gfxPlatform::GetPlatform()->GetPreferredCanvasBackend(); + case BackendSelector::Content: + return gfxPlatform::GetPlatform()->GetContentBackend(); + default: + MOZ_ASSERT_UNREACHABLE("Unknown backend selector"); + return gfx::BackendType::NONE; + } +}; + // static already_AddRefed<TextureClient> TextureClient::CreateForDrawing(ISurfaceAllocator* aAllocator, gfx::SurfaceFormat aFormat, gfx::IntSize aSize, - gfx::BackendType aMoz2DBackend, + BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags) { - if (aMoz2DBackend == gfx::BackendType::NONE) { - aMoz2DBackend = gfxPlatform::GetPlatform()->GetContentBackend(); - } + gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(aSelector); RefPtr<TextureClient> texture; #if defined(MOZ_WIDGET_GONK) || defined(XP_WIN) int32_t maxTextureSize = aAllocator->GetMaxTextureSize(); #endif #ifdef XP_WIN LayersBackend parentBackend = aAllocator->GetCompositorBackendType(); if (parentBackend == LayersBackend::LAYERS_D3D11 && - ((aMoz2DBackend == gfx::BackendType::DIRECT2D && Factory::GetDirect3D10Device()) || - (aMoz2DBackend == gfx::BackendType::DIRECT2D1_1 && Factory::GetDirect3D11Device())) && + (moz2DBackend == gfx::BackendType::DIRECT2D || + moz2DBackend == gfx::BackendType::DIRECT2D1_1) && aSize.width <= maxTextureSize && aSize.height <= maxTextureSize) { texture = new TextureClientD3D11(aAllocator, aFormat, aTextureFlags); } if (parentBackend == LayersBackend::LAYERS_D3D9 && - aMoz2DBackend == gfx::BackendType::CAIRO && + moz2DBackend == gfx::BackendType::CAIRO && aAllocator->IsSameProcess() && aSize.width <= maxTextureSize && aSize.height <= maxTextureSize && NS_IsMainThread()) { if (gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) { texture = new TextureClientD3D9(aAllocator, aFormat, aTextureFlags); } } if (!texture && aFormat == SurfaceFormat::B8G8R8X8 && aAllocator->IsSameProcess() && - aMoz2DBackend == gfx::BackendType::CAIRO && + moz2DBackend == gfx::BackendType::CAIRO && NS_IsMainThread()) { if (aAllocator->IsSameProcess()) { texture = new TextureClientMemoryDIB(aAllocator, aFormat, aTextureFlags); } else { texture = new TextureClientShmemDIB(aAllocator, aFormat, aTextureFlags); } } #endif #ifdef MOZ_X11 LayersBackend parentBackend = aAllocator->GetCompositorBackendType(); gfxSurfaceType type = gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType(); if (parentBackend == LayersBackend::LAYERS_BASIC && - aMoz2DBackend == gfx::BackendType::CAIRO && + moz2DBackend == gfx::BackendType::CAIRO && type == gfxSurfaceType::Xlib) { texture = new TextureClientX11(aAllocator, aFormat, aTextureFlags); } #ifdef GL_PROVIDER_GLX if (parentBackend == LayersBackend::LAYERS_OPENGL && type == gfxSurfaceType::Xlib && aFormat != SurfaceFormat::A8 && @@ -394,17 +406,17 @@ TextureClient::CreateForDrawing(ISurface #endif #endif #ifdef MOZ_WIDGET_GONK if (!DisableGralloc(aFormat, aSize)) { // Don't allow Gralloc texture clients to exceed the maximum texture size. // BufferTextureClients have code to handle tiling the surface client-side. if (aSize.width <= maxTextureSize && aSize.height <= maxTextureSize) { - texture = new GrallocTextureClientOGL(aAllocator, aFormat, aMoz2DBackend, + texture = new GrallocTextureClientOGL(aAllocator, aFormat, moz2DBackend, aTextureFlags); } } #endif MOZ_ASSERT(!texture || texture->CanExposeDrawTarget(), "texture cannot expose a DrawTarget?"); if (texture && texture->AllocateForSurface(aSize, aAllocFlags)) { @@ -415,17 +427,17 @@ TextureClient::CreateForDrawing(ISurface return nullptr; } if (texture) { NS_WARNING("Failed to allocate a TextureClient, falling back to BufferTextureClient."); } // Can't do any better than a buffer texture client. - texture = CreateBufferTextureClient(aAllocator, aFormat, aTextureFlags, aMoz2DBackend); + texture = CreateBufferTextureClient(aAllocator, aFormat, aTextureFlags, moz2DBackend); if (!texture->AllocateForSurface(aSize, aAllocFlags)) { return nullptr; } return texture.forget(); }
--- a/gfx/layers/client/TextureClient.h +++ b/gfx/layers/client/TextureClient.h @@ -133,16 +133,22 @@ public: * DataSourceSurface beyond the execution of this function. */ virtual void ProcessReadback(gfx::DataSourceSurface *aSourceSurface) = 0; protected: virtual ~TextureReadbackSink() {} }; +enum class BackendSelector +{ + Content, + Canvas +}; + /** * TextureClient is a thin abstraction over texture data that need to be shared * between the content process and the compositor process. It is the * content-side half of a TextureClient/TextureHost pair. A corresponding * TextureHost lives on the compositor-side. * * TextureClient's primary purpose is to present texture data in a way that is * understood by the IPC system. There are two ways to use it: @@ -169,17 +175,17 @@ public: TextureFlags aFlags = TextureFlags::DEFAULT); virtual ~TextureClient(); // Creates and allocates a TextureClient usable with Moz2D. static already_AddRefed<TextureClient> CreateForDrawing(ISurfaceAllocator* aAllocator, gfx::SurfaceFormat aFormat, gfx::IntSize aSize, - gfx::BackendType aMoz2dBackend, + BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags flags = ALLOC_DEFAULT); // Creates and allocates a BufferTextureClient supporting the YCbCr format. static already_AddRefed<BufferTextureClient> CreateForYCbCr(ISurfaceAllocator* aAllocator, gfx::IntSize aYSize, gfx::IntSize aCbCrSize,
--- a/gfx/layers/client/TextureClientPool.cpp +++ b/gfx/layers/client/TextureClientPool.cpp @@ -106,17 +106,17 @@ TextureClientPool::GetTextureClient() // No unused clients in the pool, create one if (gfxPrefs::ForceShmemTiles()) { // gfx::BackendType::NONE means use the content backend textureClient = TextureClient::CreateForRawBufferAccess(mSurfaceAllocator, mFormat, mSize, gfx::BackendType::NONE, TextureFlags::IMMEDIATE_UPLOAD, ALLOC_DEFAULT); } else { textureClient = TextureClient::CreateForDrawing(mSurfaceAllocator, - mFormat, mSize, gfx::BackendType::NONE, TextureFlags::IMMEDIATE_UPLOAD); + mFormat, mSize, BackendSelector::Content, TextureFlags::IMMEDIATE_UPLOAD); } mOutstandingClients++; #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL if (textureClient) { textureClient->mPoolTracker = this; } #endif
--- a/gfx/layers/client/TextureClientRecycleAllocator.cpp +++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp @@ -29,17 +29,17 @@ public: mMaxPooledSize = aMax; } } // Creates and allocates a TextureClient. already_AddRefed<TextureClient> CreateOrRecycleForDrawing(gfx::SurfaceFormat aFormat, gfx::IntSize aSize, - gfx::BackendType aMoz2dBackend, + BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags flags); void Destroy(); void RecycleCallbackImp(TextureClient* aClient); static void RecycleCallback(TextureClient* aClient, void* aClosure); @@ -136,33 +136,29 @@ TextureClientRecycleAllocatorImp::~Textu MOZ_ASSERT(mPooledClients.empty()); MOZ_ASSERT(mInUseClients.empty()); } already_AddRefed<TextureClient> TextureClientRecycleAllocatorImp::CreateOrRecycleForDrawing( gfx::SurfaceFormat aFormat, gfx::IntSize aSize, - gfx::BackendType aMoz2DBackend, + BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags) { // TextureAllocationFlags is actually used only by ContentClient. // This class does not handle ConteClient's TextureClient allocation. MOZ_ASSERT(aAllocFlags == TextureAllocationFlags::ALLOC_DEFAULT || aAllocFlags == TextureAllocationFlags::ALLOC_DISALLOW_BUFFERTEXTURECLIENT); MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE)); aTextureFlags = aTextureFlags | TextureFlags::RECYCLE; // Set recycle flag RefPtr<TextureClientHolder> textureHolder; - if (aMoz2DBackend == gfx::BackendType::NONE) { - aMoz2DBackend = gfxPlatform::GetPlatform()->GetContentBackend(); - } - { MutexAutoLock lock(mLock); if (mDestroyed) { return nullptr; } else if (!mPooledClients.empty()) { textureHolder = mPooledClients.top(); mPooledClients.pop(); // If a pooled TextureClient is not compatible, release it. @@ -178,17 +174,17 @@ TextureClientRecycleAllocatorImp::Create textureHolder->GetTextureClient()->RecycleTexture(aTextureFlags); } } } if (!textureHolder) { // Allocate new TextureClient RefPtr<TextureClient> texture; - texture = TextureClient::CreateForDrawing(this, aFormat, aSize, aMoz2DBackend, + texture = TextureClient::CreateForDrawing(this, aFormat, aSize, aSelector, aTextureFlags, aAllocFlags); if (!texture) { return nullptr; } textureHolder = new TextureClientHolder(texture); } { @@ -256,21 +252,21 @@ TextureClientRecycleAllocator::SetMaxPoo { mAllocator->SetMaxPoolSize(aMax); } already_AddRefed<TextureClient> TextureClientRecycleAllocator::CreateOrRecycleForDrawing( gfx::SurfaceFormat aFormat, gfx::IntSize aSize, - gfx::BackendType aMoz2DBackend, + BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags) { return mAllocator->CreateOrRecycleForDrawing(aFormat, aSize, - aMoz2DBackend, + aSelector, aTextureFlags, aAllocFlags); } } // namespace layers } // namespace mozilla
--- a/gfx/layers/client/TextureClientRecycleAllocator.h +++ b/gfx/layers/client/TextureClientRecycleAllocator.h @@ -33,17 +33,17 @@ public: explicit TextureClientRecycleAllocator(ISurfaceAllocator* aAllocator); void SetMaxPoolSize(uint32_t aMax); // Creates and allocates a TextureClient. already_AddRefed<TextureClient> CreateOrRecycleForDrawing(gfx::SurfaceFormat aFormat, gfx::IntSize aSize, - gfx::BackendType aMoz2dBackend, + BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags flags = ALLOC_DEFAULT); private: RefPtr<TextureClientRecycleAllocatorImp> mAllocator; }; } // namespace layers
--- a/gfx/skia/generate_mozbuild.py +++ b/gfx/skia/generate_mozbuild.py @@ -135,17 +135,19 @@ elif CONFIG['CLANG_CL']: DEFINES['SKIA_IMPLEMENTATION'] = 1 DEFINES['GR_IMPLEMENTATION'] = 1 if CONFIG['GNU_CXX']: CXXFLAGS += [ '-Wno-overloaded-virtual', '-Wno-unused-function', ] - if not CONFIG['CLANG_CXX']: + if CONFIG['CLANG_CXX']: + CXXFLAGS += ['-Wno-inconsistent-missing-override'] + else: CXXFLAGS += ['-Wno-logical-op'] if CONFIG['CPU_ARCH'] == 'arm': SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer'] if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'android', 'gonk', 'qt'): CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
--- a/gfx/skia/moz.build +++ b/gfx/skia/moz.build @@ -663,17 +663,19 @@ elif CONFIG['CLANG_CL']: DEFINES['SKIA_IMPLEMENTATION'] = 1 DEFINES['GR_IMPLEMENTATION'] = 1 if CONFIG['GNU_CXX']: CXXFLAGS += [ '-Wno-overloaded-virtual', '-Wno-unused-function', ] - if not CONFIG['CLANG_CXX']: + if CONFIG['CLANG_CXX']: + CXXFLAGS += ['-Wno-inconsistent-missing-override'] + else: CXXFLAGS += ['-Wno-logical-op'] if CONFIG['CPU_ARCH'] == 'arm': SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer'] if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'android', 'gonk', 'qt'): CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
--- a/js/src/NamespaceImports.h +++ b/js/src/NamespaceImports.h @@ -94,16 +94,17 @@ using JS::CallArgs; using JS::CallNonGenericMethod; using JS::CallReceiver; using JS::CompileOptions; using JS::IsAcceptableThis; using JS::NativeImpl; using JS::OwningCompileOptions; using JS::ReadOnlyCompileOptions; using JS::SourceBufferHolder; +using JS::TransitiveCompileOptions; using JS::Rooted; using JS::RootedFunction; using JS::RootedId; using JS::RootedObject; using JS::RootedScript; using JS::RootedString; using JS::RootedSymbol;
--- a/js/src/aclocal.m4 +++ b/js/src/aclocal.m4 @@ -13,17 +13,16 @@ builtin(include, ../../build/autoconf/pk builtin(include, ../../build/autoconf/nspr.m4)dnl builtin(include, ../../build/autoconf/nspr-build.m4)dnl builtin(include, ../../build/autoconf/codeset.m4)dnl builtin(include, ../../build/autoconf/altoptions.m4)dnl builtin(include, ../../build/autoconf/mozprog.m4)dnl builtin(include, ../../build/autoconf/mozheader.m4)dnl builtin(include, ../../build/autoconf/mozcommonheader.m4)dnl builtin(include, ../../build/autoconf/lto.m4)dnl -builtin(include, ../../build/autoconf/gcc-pr49911.m4)dnl builtin(include, ../../build/autoconf/llvm-pr8927.m4)dnl builtin(include, ../../build/autoconf/frameptr.m4)dnl builtin(include, ../../build/autoconf/compiler-opts.m4)dnl builtin(include, ../../build/autoconf/expandlibs.m4)dnl builtin(include, ../../build/autoconf/arch.m4)dnl builtin(include, ../../build/autoconf/android.m4)dnl builtin(include, ../../build/autoconf/zlib.m4)dnl builtin(include, ../../build/autoconf/linux.m4)dnl
--- a/js/src/configure.in +++ b/js/src/configure.in @@ -2153,17 +2153,16 @@ WINNT|Darwin|Android) STL_FLAGS='-I$(DIST)/stl_wrappers' WRAP_STL_INCLUDES=1 ;; esac AC_SUBST(WRAP_SYSTEM_INCLUDES) AC_SUBST(VISIBILITY_FLAGS) -MOZ_GCC_PR49911 MOZ_LLVM_PR8927 dnl Checks for header files. dnl ======================================================== AC_HEADER_DIRENT case "$target_os" in freebsd*) # for stuff like -lXshm
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5762,23 +5762,23 @@ BytecodeEmitter::emitFunction(ParseNode* fun->lazyScript()->setTreatAsRunOnce(); } else { SharedContext* outersc = sc; if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals()) funbox->setMightAliasLocals(); // inherit mightAliasLocals from parent MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript); - // Inherit most things (principals, version, etc) from the parent. + // Inherit most things (principals, version, etc) from the + // parent. Use default values for the rest. Rooted<JSScript*> parent(cx, script); - CompileOptions options(cx, parser->options()); - options.setMutedErrors(parent->mutedErrors()) - .setNoScriptRval(false) - .setForEval(false) - .setVersion(parent->getVersion()); + MOZ_ASSERT(parent->getVersion() == parser->options().version); + MOZ_ASSERT(parent->mutedErrors() == parser->options().mutedErrors()); + const TransitiveCompileOptions& transitiveOptions = parser->options(); + CompileOptions options(cx, transitiveOptions); Rooted<JSObject*> enclosingScope(cx, enclosingStaticScope()); Rooted<JSObject*> sourceObject(cx, script->sourceObject()); Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options, parent->staticLevel() + 1, sourceObject, funbox->bufStart, funbox->bufEnd)); if (!script)
--- a/js/src/jit-test/lib/class.js +++ b/js/src/jit-test/lib/class.js @@ -1,1 +1,1 @@ -load(libdir + "../../tests/ecma_6/Class/shell.js"); +load(libdir + "../../tests/ecma_6/shell.js");
--- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -36,62 +36,16 @@ using JS::GenericNaN; using JS::ToInt32; // shared CodeGeneratorARM::CodeGeneratorARM(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) : CodeGeneratorShared(gen, graph, masm) { } -bool -CodeGeneratorARM::generatePrologue() -{ - MOZ_ASSERT(masm.framePushed() == 0); - MOZ_ASSERT(!gen->compilingAsmJS()); -#ifdef JS_USE_LINK_REGISTER - masm.pushReturnAddress(); -#endif - - // If profiling, save the current frame pointer to a per-thread global field. - if (isProfilerInstrumentationEnabled()) - masm.profilerEnterFrame(StackPointer, CallTempReg0); - - // Ensure that the Ion frames is properly aligned. - masm.assertStackAlignment(JitStackAlignment, 0); - - // Note that this automatically sets MacroAssembler::framePushed(). - masm.reserveStack(frameSize()); - masm.checkStackAlignment(); - - emitTracelogIonStart(); - - return true; -} - -bool -CodeGeneratorARM::generateEpilogue() -{ - MOZ_ASSERT(!gen->compilingAsmJS()); - masm.bind(&returnLabel_); - - emitTracelogIonStop(); - - masm.freeStack(frameSize()); - MOZ_ASSERT(masm.framePushed() == 0); - - // If profiling, reset the per-thread global lastJitFrame to point to - // the previous frame. - if (isProfilerInstrumentationEnabled()) - masm.profilerExitFrame(); - - masm.pop(pc); - masm.flushBuffer(); - return true; -} - void CodeGeneratorARM::emitBranch(Assembler::Condition cond, MBasicBlock* mirTrue, MBasicBlock* mirFalse) { if (isNextBlock(mirFalse->lir())) { jumpToBlock(mirTrue, cond); } else { jumpToBlock(mirFalse, Assembler::InvertCondition(cond)); jumpToBlock(mirTrue);
--- a/js/src/jit/arm/CodeGenerator-arm.h +++ b/js/src/jit/arm/CodeGenerator-arm.h @@ -18,18 +18,16 @@ class OutOfLineTableSwitch; class CodeGeneratorARM : public CodeGeneratorShared { friend class MoveResolverARM; CodeGeneratorARM* thisFromCtor() {return this;} protected: - // Label for the common return path. - NonAssertingLabel returnLabel_; NonAssertingLabel deoptLabel_; MoveOperand toMoveOperand(LAllocation a) const; void bailoutIf(Assembler::Condition condition, LSnapshot* snapshot); void bailoutFrom(Label* label, LSnapshot* snapshot); void bailout(LSnapshot* snapshot); @@ -57,18 +55,16 @@ class CodeGeneratorARM : public CodeGene bailoutIf(Assembler::Zero, snapshot); } template<class T> void generateUDivModZeroCheck(Register rhs, Register output, Label* done, LSnapshot* snapshot, T* mir); protected: - bool generatePrologue(); - bool generateEpilogue(); bool generateOutOfLineCode(); void emitRoundDouble(FloatRegister src, Register dest, Label* fail); // Emits a branch that directs control flow to the true block if |cond| is // true, and the false block if |cond| is false. void emitBranch(Assembler::Condition cond, MBasicBlock* ifTrue, MBasicBlock* ifFalse);
--- a/js/src/jit/arm/LIR-arm.h +++ b/js/src/jit/arm/LIR-arm.h @@ -5,37 +5,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_arm_LIR_arm_h #define jit_arm_LIR_arm_h namespace js { namespace jit { -class LBox : public LInstructionHelper<2, 1, 0> -{ - MIRType type_; - - public: - LIR_HEADER(Box); - - LBox(const LAllocation& in_payload, MIRType type) - : type_(type) - { - setOperand(0, in_payload); - } - - MIRType type() const { - return type_; - } - const char* extraName() const { - return StringFromMIRType(type_); - } -}; - class LBoxFloatingPoint : public LInstructionHelper<2, 1, 1> { MIRType type_; public: LIR_HEADER(BoxFloatingPoint); LBoxFloatingPoint(const LAllocation& in, const LDefinition& temp, MIRType type) @@ -283,32 +262,16 @@ class LModMaskI : public LInstructionHel return shift_; } MMod* mir() const { return mir_->toMod(); } }; -class LPowHalfD : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(PowHalfD); - LPowHalfD(const LAllocation& input) { - setOperand(0, input); - } - - const LAllocation* input() { - return getOperand(0); - } - const LDefinition* output() { - return getDef(0); - } -}; - // Takes a tableswitch with an integer to decide. class LTableSwitch : public LInstructionHelper<0, 1, 1> { public: LIR_HEADER(TableSwitch); LTableSwitch(const LAllocation& in, const LDefinition& inputCopy, MTableSwitch* ins) { setOperand(0, in);
--- a/js/src/jit/arm64/AtomicOperations-arm64.h +++ b/js/src/jit/arm64/AtomicOperations-arm64.h @@ -10,95 +10,117 @@ #define jit_arm64_AtomicOperations_arm64_h #include "jit/arm64/Architecture-arm64.h" #include "jit/AtomicOperations.h" inline bool js::jit::AtomicOperations::isLockfree8() { - MOZ_CRASH("isLockfree8()"); + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int8_t), 0)); + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int16_t), 0)); + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int32_t), 0)); + return true; } inline void js::jit::AtomicOperations::fenceSeqCst() { - MOZ_CRASH("fenceSeqCst()"); + __atomic_thread_fence(__ATOMIC_SEQ_CST); } template<typename T> inline T js::jit::AtomicOperations::loadSeqCst(T* addr) { - MOZ_CRASH("loadSeqCst()"); + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); + T v; + __atomic_load(addr, &v, __ATOMIC_SEQ_CST); + return v; } template<typename T> inline void js::jit::AtomicOperations::storeSeqCst(T* addr, T val) { - MOZ_CRASH("storeSeqCst()"); + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); + __atomic_store(addr, &val, __ATOMIC_SEQ_CST); } template<typename T> inline T js::jit::AtomicOperations::exchangeSeqCst(T* addr, T val) { - MOZ_CRASH("exchangeSeqCst()"); + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); + T v; + __atomic_exchange(addr, &val, &v, __ATOMIC_SEQ_CST); + return v; } template<typename T> inline T js::jit::AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, T newval) { - MOZ_CRASH("compareExchangeSeqCst()"); + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); + __atomic_compare_exchange(addr, &oldval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return oldval; } template<typename T> inline T js::jit::AtomicOperations::fetchAddSeqCst(T* addr, T val) { - MOZ_CRASH("fetchAddSeqCst()"); + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); + return __atomic_fetch_add(addr, val, __ATOMIC_SEQ_CST); } template<typename T> inline T js::jit::AtomicOperations::fetchSubSeqCst(T* addr, T val) { - MOZ_CRASH("fetchSubSeqCst()"); + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); + return __atomic_fetch_sub(addr, val, __ATOMIC_SEQ_CST); } template<typename T> inline T js::jit::AtomicOperations::fetchAndSeqCst(T* addr, T val) { - MOZ_CRASH("fetchAndSeqCst()"); + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); + return __atomic_fetch_and(addr, val, __ATOMIC_SEQ_CST); } template<typename T> inline T js::jit::AtomicOperations::fetchOrSeqCst(T* addr, T val) { - MOZ_CRASH("fetchOrSeqCst()"); + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); + return __atomic_fetch_or(addr, val, __ATOMIC_SEQ_CST); } template<typename T> inline T js::jit::AtomicOperations::fetchXorSeqCst(T* addr, T val) { - MOZ_CRASH("fetchXorSeqCst()"); + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); + return __atomic_fetch_xor(addr, val, __ATOMIC_SEQ_CST); } template<size_t nbytes> inline void js::jit::RegionLock::acquire(void* addr) { - MOZ_CRASH("acquire()"); + uint32_t zero = 0; + uint32_t one = 1; + while (!__atomic_compare_exchange(&spinlock, &zero, &one, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) + continue; } template<size_t nbytes> inline void js::jit::RegionLock::release(void* addr) { - MOZ_CRASH("release()"); + MOZ_ASSERT(AtomicOperations::loadSeqCst(&spinlock) == 1, "releasing unlocked region lock"); + uint32_t zero = 0; + __atomic_store(&spinlock, &zero, __ATOMIC_SEQ_CST); } #endif // jit_arm64_AtomicOperations_arm64_h
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp +++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp @@ -33,28 +33,16 @@ using JS::GenericNaN; // shared CodeGeneratorARM64::CodeGeneratorARM64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) : CodeGeneratorShared(gen, graph, masm) { } bool -CodeGeneratorARM64::generatePrologue() -{ - MOZ_CRASH("generatePrologue"); -} - -bool -CodeGeneratorARM64::generateEpilogue() -{ - MOZ_CRASH("generateEpilogue"); -} - -bool CodeGeneratorARM64::generateOutOfLineCode() { MOZ_CRASH("generateOutOfLineCode"); } void CodeGeneratorARM64::emitBranch(Assembler::Condition cond, MBasicBlock* mirTrue, MBasicBlock* mirFalse) {
--- a/js/src/jit/arm64/CodeGenerator-arm64.h +++ b/js/src/jit/arm64/CodeGenerator-arm64.h @@ -21,18 +21,16 @@ class CodeGeneratorARM64 : public CodeGe friend class MoveResolverARM64; CodeGeneratorARM64* thisFromCtor() { return this; } public: CodeGeneratorARM64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm); protected: - // Label for the common return path. - NonAssertingLabel returnLabel_; NonAssertingLabel deoptLabel_; // FIXME: VIXL Operand does not match the platform-agnostic Operand, // which is just a union of possible arguments. inline Operand ToOperand(const LAllocation& a) { MOZ_CRASH("ToOperand"); } inline Operand ToOperand(const LAllocation* a) { @@ -68,18 +66,16 @@ class CodeGeneratorARM64 : public CodeGe return bailoutIf(c, snapshot); } void bailoutIfFalseBool(Register reg, LSnapshot* snapshot) { masm.test32(reg, Imm32(0xFF)); return bailoutIf(Assembler::Zero, snapshot); } protected: - bool generatePrologue(); - bool generateEpilogue(); bool generateOutOfLineCode(); void emitRoundDouble(FloatRegister src, Register dest, Label* fail); // Emits a branch that directs control flow to the true block if |cond| is // true, and the false block if |cond| is false. void emitBranch(Assembler::Condition cond, MBasicBlock* ifTrue, MBasicBlock* ifFalse);
--- a/js/src/jit/arm64/LIR-arm64.h +++ b/js/src/jit/arm64/LIR-arm64.h @@ -5,37 +5,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_arm64_LIR_arm64_h #define jit_arm64_LIR_arm64_h namespace js { namespace jit { -class LBox : public LInstructionHelper<1, 1, 0> -{ - MIRType type_; - - public: - LIR_HEADER(Box); - - LBox(const LAllocation& in_payload, MIRType type) - : type_(type) - { - setOperand(0, in_payload); - } - - MIRType type() const { - return type_; - } - const char* extraName() const { - return StringFromMIRType(type_); - } -}; - class LUnboxBase : public LInstructionHelper<1, 1, 0> { public: LUnboxBase(const LAllocation& input) { setOperand(0, input); } static const size_t Input = 0; @@ -264,32 +243,16 @@ class LModMaskI : public LInstructionHel return shift_; } MMod* mir() const { return mir_->toMod(); } }; -class LPowHalfD : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(PowHalfD); - LPowHalfD(const LAllocation& input) { - setOperand(0, input); - } - - const LAllocation* input() { - return getOperand(0); - } - const LDefinition* output() { - return getDef(0); - } -}; - // Takes a tableswitch with an integer to decide class LTableSwitch : public LInstructionHelper<0, 1, 1> { public: LIR_HEADER(TableSwitch); LTableSwitch(const LAllocation& in, const LDefinition& inputCopy, MTableSwitch* ins) { setOperand(0, in);
--- a/js/src/jit/arm64/Lowering-arm64.cpp +++ b/js/src/jit/arm64/Lowering-arm64.cpp @@ -13,23 +13,16 @@ #include "jit/shared/Lowering-shared-inl.h" using namespace js; using namespace js::jit; using mozilla::FloorLog2; void -LIRGeneratorARM64::useBox(LInstruction* lir, size_t n, MDefinition* mir, - LUse::Policy policy, bool useAtStart) -{ - MOZ_CRASH("useBox"); -} - -void LIRGeneratorARM64::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register) { MOZ_CRASH("useBoxFixed"); } LAllocation LIRGeneratorARM64::useByteOpRegister(MDefinition* mir) {
--- a/js/src/jit/arm64/Lowering-arm64.h +++ b/js/src/jit/arm64/Lowering-arm64.h @@ -17,18 +17,16 @@ class LIRGeneratorARM64 : public LIRGene public: LIRGeneratorARM64(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph) : LIRGeneratorShared(gen, graph, lirGraph) { } protected: // Adds a box input to an instruction, setting operand |n| to the type and // |n+1| to the payload. - void useBox(LInstruction* lir, size_t n, MDefinition* mir, - LUse::Policy policy = LUse::REGISTER, bool useAtStart = false); void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register reg2); LAllocation useByteOpRegister(MDefinition* mir); LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir); inline LDefinition tempToUnbox() { return temp(); }
--- a/js/src/jit/mips/CodeGenerator-mips.cpp +++ b/js/src/jit/mips/CodeGenerator-mips.cpp @@ -54,58 +54,16 @@ CodeGeneratorMIPS::ToAddress(const LAllo // shared CodeGeneratorMIPS::CodeGeneratorMIPS(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) : CodeGeneratorShared(gen, graph, masm) { } -bool -CodeGeneratorMIPS::generatePrologue() -{ - MOZ_ASSERT(masm.framePushed() == 0); - MOZ_ASSERT(!gen->compilingAsmJS()); - - // If profiling, save the current frame pointer to a per-thread global field. - if (isProfilerInstrumentationEnabled()) - masm.profilerEnterFrame(StackPointer, CallTempReg0); - - // Ensure that the Ion frames is properly aligned. - masm.assertStackAlignment(JitStackAlignment, 0); - - // Note that this automatically sets MacroAssembler::framePushed(). - masm.reserveStack(frameSize()); - masm.checkStackAlignment(); - - emitTracelogIonStart(); - - return true; -} - -bool -CodeGeneratorMIPS::generateEpilogue() -{ - MOZ_ASSERT(!gen->compilingAsmJS()); - masm.bind(&returnLabel_); - - emitTracelogIonStop(); - - masm.freeStack(frameSize()); - MOZ_ASSERT(masm.framePushed() == 0); - - // If profiling, reset the per-thread global lastJitFrame to point to - // the previous frame. - if (isProfilerInstrumentationEnabled()) - masm.profilerExitFrame(); - - masm.ret(); - return true; -} - void CodeGeneratorMIPS::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, MBasicBlock* mir, Assembler::DoubleCondition cond) { // Skip past trivial blocks. mir = skipTrivialBlocks(mir); Label* label = mir->lir()->label();
--- a/js/src/jit/mips/CodeGenerator-mips.h +++ b/js/src/jit/mips/CodeGenerator-mips.h @@ -20,18 +20,16 @@ class CodeGeneratorMIPS : public CodeGen { friend class MoveResolverMIPS; CodeGeneratorMIPS* thisFromCtor() { return this; } protected: - // Label for the common return path. - NonAssertingLabel returnLabel_; NonAssertingLabel deoptLabel_; inline Address ToAddress(const LAllocation& a); inline Address ToAddress(const LAllocation* a); MoveOperand toMoveOperand(LAllocation a) const; template <typename T1, typename T2> @@ -70,18 +68,16 @@ class CodeGeneratorMIPS : public CodeGen masm.branchTest32(Assembler::Zero, reg, Imm32(0xFF), &bail); bailoutFrom(&bail, snapshot); } void bailoutFrom(Label* label, LSnapshot* snapshot); void bailout(LSnapshot* snapshot); protected: - bool generatePrologue(); - bool generateEpilogue(); bool generateOutOfLineCode(); template <typename T> void branchToBlock(Register lhs, T rhs, MBasicBlock* mir, Assembler::Condition cond) { mir = skipTrivialBlocks(mir); Label* label = mir->lir()->label();
--- a/js/src/jit/mips/LIR-mips.h +++ b/js/src/jit/mips/LIR-mips.h @@ -5,37 +5,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_mips_LIR_mips_h #define jit_mips_LIR_mips_h namespace js { namespace jit { -class LBox : public LInstructionHelper<2, 1, 0> -{ - MIRType type_; - - public: - LIR_HEADER(Box); - - LBox(const LAllocation& in_payload, MIRType type) - : type_(type) - { - setOperand(0, in_payload); - } - - MIRType type() const { - return type_; - } - const char* extraName() const { - return StringFromMIRType(type_); - } -}; - class LBoxFloatingPoint : public LInstructionHelper<2, 1, 1> { MIRType type_; public: LIR_HEADER(BoxFloatingPoint); LBoxFloatingPoint(const LAllocation& in, const LDefinition& temp, MIRType type) @@ -228,32 +207,16 @@ class LModMaskI : public LInstructionHel return shift_; } MMod* mir() const { return mir_->toMod(); } }; -class LPowHalfD : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(PowHalfD); - LPowHalfD(const LAllocation& input) { - setOperand(0, input); - } - - const LAllocation* input() { - return getOperand(0); - } - const LDefinition* output() { - return getDef(0); - } -}; - // Takes a tableswitch with an integer to decide class LTableSwitch : public LInstructionHelper<0, 1, 2> { public: LIR_HEADER(TableSwitch); LTableSwitch(const LAllocation& in, const LDefinition& inputCopy, const LDefinition& jumpTablePointer, MTableSwitch* ins) {
--- a/js/src/jit/none/CodeGenerator-none.h +++ b/js/src/jit/none/CodeGenerator-none.h @@ -10,18 +10,16 @@ #include "jit/shared/CodeGenerator-shared.h" namespace js { namespace jit { class CodeGeneratorNone : public CodeGeneratorShared { public: - NonAssertingLabel returnLabel_; - CodeGeneratorNone(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) : CodeGeneratorShared(gen, graph, masm) { MOZ_CRASH(); } MoveOperand toMoveOperand(LAllocation) const { MOZ_CRASH(); } template <typename T1, typename T2> @@ -30,18 +28,16 @@ class CodeGeneratorNone : public CodeGen void bailoutTest32(Assembler::Condition, Register, T, LSnapshot*) { MOZ_CRASH(); } template <typename T1, typename T2> void bailoutCmpPtr(Assembler::Condition, T1, T2, LSnapshot*) { MOZ_CRASH(); } void bailoutTestPtr(Assembler::Condition, Register, Register, LSnapshot*) { MOZ_CRASH(); } void bailoutIfFalseBool(Register, LSnapshot*) { MOZ_CRASH(); } void bailoutFrom(Label*, LSnapshot*) { MOZ_CRASH(); } void bailout(LSnapshot*) { MOZ_CRASH(); } void bailoutIf(Assembler::Condition, LSnapshot*) { MOZ_CRASH(); } - bool generatePrologue() { MOZ_CRASH(); } - bool generateEpilogue() { MOZ_CRASH(); } bool generateOutOfLineCode() { MOZ_CRASH(); } void testNullEmitBranch(Assembler::Condition, ValueOperand, MBasicBlock*, MBasicBlock*) { MOZ_CRASH(); } void testUndefinedEmitBranch(Assembler::Condition, ValueOperand, MBasicBlock*, MBasicBlock*) { MOZ_CRASH(); } void testObjectEmitBranch(Assembler::Condition, ValueOperand, MBasicBlock*, MBasicBlock*) {
--- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -49,16 +49,17 @@ CodeGeneratorShared::CodeGeneratorShared snapshots_(), recovers_(), deoptTable_(nullptr), #ifdef DEBUG pushedArgs_(0), #endif lastOsiPointOffset_(0), safepoints_(graph->totalSlotCount(), (gen->info().nargs() + 1) * sizeof(Value)), + returnLabel_(), nativeToBytecodeMap_(nullptr), nativeToBytecodeMapSize_(0), nativeToBytecodeTableOffset_(0), nativeToBytecodeNumRegions_(0), nativeToBytecodeScriptList_(nullptr), nativeToBytecodeScriptListLength_(0), trackedOptimizationsMap_(nullptr), trackedOptimizationsMapSize_(0), @@ -105,16 +106,64 @@ CodeGeneratorShared::CodeGeneratorShared // asm.js code. frameClass_ = FrameSizeClass::None(); } else { frameClass_ = FrameSizeClass::FromDepth(frameDepth_); } } bool +CodeGeneratorShared::generatePrologue() +{ + MOZ_ASSERT(masm.framePushed() == 0); + MOZ_ASSERT(!gen->compilingAsmJS()); + +#ifdef JS_USE_LINK_REGISTER + masm.pushReturnAddress(); +#endif + + // If profiling, save the current frame pointer to a per-thread global field. + if (isProfilerInstrumentationEnabled()) + masm.profilerEnterFrame(masm.getStackPointer(), CallTempReg0); + + // Ensure that the Ion frame is properly aligned. + masm.assertStackAlignment(JitStackAlignment, 0); + + // Note that this automatically sets MacroAssembler::framePushed(). + masm.reserveStack(frameSize()); + masm.checkStackAlignment(); + + emitTracelogIonStart(); + return true; +} + +bool +CodeGeneratorShared::generateEpilogue() +{ + MOZ_ASSERT(!gen->compilingAsmJS()); + masm.bind(&returnLabel_); + + emitTracelogIonStop(); + + masm.freeStack(frameSize()); + MOZ_ASSERT(masm.framePushed() == 0); + + // If profiling, reset the per-thread global lastJitFrame to point to + // the previous frame. + if (isProfilerInstrumentationEnabled()) + masm.profilerExitFrame(); + + masm.ret(); + + // On systems that use a constant pool, this is a good time to emit. + masm.flushBuffer(); + return true; +} + +bool CodeGeneratorShared::generateOutOfLineCode() { for (size_t i = 0; i < outOfLineCode_.length(); i++) { // Add native => bytecode mapping entries for OOL sites. // Not enabled on asm.js yet since asm doesn't contain bytecode mappings. if (!gen->compilingAsmJS()) { if (!addNativeToBytecodeEntry(outOfLineCode_[i]->bytecodeSite())) return false;
--- a/js/src/jit/shared/CodeGenerator-shared.h +++ b/js/src/jit/shared/CodeGenerator-shared.h @@ -100,16 +100,19 @@ class CodeGeneratorShared : public LElem // Patchable backedges generated for loops. Vector<PatchableBackedgeInfo, 0, SystemAllocPolicy> patchableBackedges_; #ifdef JS_TRACE_LOGGING js::Vector<CodeOffsetLabel, 0, SystemAllocPolicy> patchableTraceLoggers_; js::Vector<CodeOffsetLabel, 0, SystemAllocPolicy> patchableTLScripts_; #endif + // Label for the common return path. + NonAssertingLabel returnLabel_; + public: struct NativeToBytecode { CodeOffsetLabel nativeOffset; InlineScriptTree* tree; jsbytecode* pc; }; protected: @@ -445,16 +448,19 @@ class CodeGeneratorShared : public LElem inline OutOfLineCode* oolCallVM(const VMFunction& fun, LInstruction* ins, const ArgSeq& args, const StoreOutputTo& out); void addCache(LInstruction* lir, size_t cacheIndex); size_t addCacheLocations(const CacheLocationList& locs, size_t* numLocs); ReciprocalMulConstants computeDivisionConstants(uint32_t d, int maxLog); protected: + bool generatePrologue(); + bool generateEpilogue(); + void addOutOfLineCode(OutOfLineCode* code, const MInstruction* mir); void addOutOfLineCode(OutOfLineCode* code, const BytecodeSite* site); bool generateOutOfLineCode(); Label* labelForBackedgeWithImplicitCheck(MBasicBlock* mir); // Generate a jump to the start of the specified block, adding information // if this is a loop backedge. Use this in place of jumping directly to
--- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -12,17 +12,36 @@ #include "jit/AtomicOp.h" #include "jit/shared/Assembler-shared.h" // This file declares LIR instructions that are common to every platform. namespace js { namespace jit { -class Range; +class LBox : public LInstructionHelper<BOX_PIECES, 1, 0> +{ + MIRType type_; + + public: + LIR_HEADER(Box); + + LBox(const LAllocation& payload, MIRType type) + : type_(type) + { + setOperand(0, payload); + } + + MIRType type() const { + return type_; + } + const char* extraName() const { + return StringFromMIRType(type_); + } +}; template <size_t Temps, size_t ExtraUses = 0> class LBinaryMath : public LInstructionHelper<1, 2 + ExtraUses, Temps> { public: const LAllocation* lhs() { return this->getOperand(0); } @@ -3719,16 +3738,36 @@ class LFloat32x4ToInt32x4 : public LInst const LDefinition* temp() { return getTemp(0); } const MSimdConvert* mir() const { return mir_->toSimdConvert(); } }; +// Double raised to a half power. +class LPowHalfD : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(PowHalfD); + explicit LPowHalfD(const LAllocation& input) { + setOperand(0, input); + } + + const LAllocation* input() { + return getOperand(0); + } + const LDefinition* output() { + return getDef(0); + } + MPowHalf* mir() const { + return mir_->toPowHalf(); + } +}; + // No-op instruction that is used to hold the entry snapshot. This simplifies // register allocation as it doesn't need to sniff the snapshot out of the // LIRGraph. class LStart : public LInstructionHelper<0, 0, 0> { public: LIR_HEADER(Start) };
--- a/js/src/jit/x64/LIR-x64.h +++ b/js/src/jit/x64/LIR-x64.h @@ -5,38 +5,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_x64_LIR_x64_h #define jit_x64_LIR_x64_h namespace js { namespace jit { -// Given a typed input, returns an untyped box. -class LBox : public LInstructionHelper<1, 1, 0> -{ - MIRType type_; - - public: - LIR_HEADER(Box) - - LBox(MIRType type, const LAllocation& payload) - : type_(type) - { - setOperand(0, payload); - } - - MIRType type() const { - return type_; - } - const char* extraName() const { - return StringFromMIRType(type_); - } -}; - // Given an untyped input, guards on whether it's a specific type and returns // the unboxed payload. class LUnboxBase : public LInstructionHelper<1, 1, 0> { public: explicit LUnboxBase(const LAllocation& input) { setOperand(0, input); }
--- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -56,17 +56,17 @@ LIRGeneratorX64::visitBox(MBox* box) if (opd->isConstant() && box->canEmitAtUses()) { emitAtUses(box); return; } if (opd->isConstant()) { define(new(alloc()) LValue(opd->toConstant()->value()), box, LDefinition(LDefinition::BOX)); } else { - LBox* ins = new(alloc()) LBox(opd->type(), useRegister(opd)); + LBox* ins = new(alloc()) LBox(useRegister(opd), opd->type()); define(ins, box, LDefinition(LDefinition::BOX)); } } void LIRGeneratorX64::visitUnbox(MUnbox* unbox) { MDefinition* box = unbox->getOperand(0);
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -34,59 +34,16 @@ using JS::GenericNaN; namespace js { namespace jit { CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) : CodeGeneratorShared(gen, graph, masm) { } -bool -CodeGeneratorX86Shared::generatePrologue() -{ - MOZ_ASSERT(masm.framePushed() == 0); - MOZ_ASSERT(!gen->compilingAsmJS()); - - // If profiling, save the current frame pointer to a per-thread global field. - if (isProfilerInstrumentationEnabled()) - masm.profilerEnterFrame(StackPointer, CallTempReg0); - - // Ensure that the Ion frames is properly aligned. - masm.assertStackAlignment(JitStackAlignment, 0); - - // Note that this automatically sets MacroAssembler::framePushed(). - masm.reserveStack(frameSize()); - - emitTracelogIonStart(); - - return true; -} - -bool -CodeGeneratorX86Shared::generateEpilogue() -{ - MOZ_ASSERT(!gen->compilingAsmJS()); - - masm.bind(&returnLabel_); - - emitTracelogIonStop(); - - // Pop the stack we allocated at the start of the function. - masm.freeStack(frameSize()); - MOZ_ASSERT(masm.framePushed() == 0); - - // If profiling, reset the per-thread global lastJitFrame to point to - // the previous frame. - if (isProfilerInstrumentationEnabled()) - masm.profilerExitFrame(); - - masm.ret(); - return true; -} - void OutOfLineBailout::accept(CodeGeneratorX86Shared* codegen) { codegen->visitOutOfLineBailout(this); } void CodeGeneratorX86Shared::emitBranch(Assembler::Condition cond, MBasicBlock* mirTrue,
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -92,18 +92,16 @@ class CodeGeneratorX86Shared : public Co }; // Functions for emitting bounds-checking code with branches. MOZ_WARN_UNUSED_RESULT uint32_t emitAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, const MInstruction* ins, Register ptr, Label* fail); void cleanupAfterAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, Register ptr); - // Label for the common return path. - NonAssertingLabel returnLabel_; NonAssertingLabel deoptLabel_; MoveOperand toMoveOperand(LAllocation a) const; void bailoutIf(Assembler::Condition condition, LSnapshot* snapshot); void bailoutIf(Assembler::DoubleCondition condition, LSnapshot* snapshot); void bailoutFrom(Label* label, LSnapshot* snapshot); void bailout(LSnapshot* snapshot); @@ -143,18 +141,16 @@ class CodeGeneratorX86Shared : public Co void bailoutCvttss2si(FloatRegister src, Register dest, LSnapshot* snapshot) { // Same trick as explained in the above comment. masm.vcvttss2si(src, dest); masm.cmp32(dest, Imm32(1)); bailoutIf(Assembler::Overflow, snapshot); } protected: - bool generatePrologue(); - bool generateEpilogue(); bool generateOutOfLineCode(); void emitCompare(MCompare::CompareType type, const LAllocation* left, const LAllocation* right); // Emits a branch that directs control flow to the true block if |cond| is // true, and the false block if |cond| is false. void emitBranch(Assembler::Condition cond, MBasicBlock* ifTrue, MBasicBlock* ifFalse, Assembler::NaNCond ifNaN = Assembler::NaN_HandledByCond);
--- a/js/src/jit/x86-shared/LIR-x86-shared.h +++ b/js/src/jit/x86-shared/LIR-x86-shared.h @@ -213,36 +213,16 @@ class LModPowTwoI : public LInstructionH const LDefinition* remainder() { return getDef(0); } MMod* mir() const { return mir_->toMod(); } }; -// Double raised to a half power. -class LPowHalfD : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(PowHalfD) - explicit LPowHalfD(const LAllocation& input) { - setOperand(0, input); - } - - const LAllocation* input() { - return getOperand(0); - } - const LDefinition* output() { - return getDef(0); - } - MPowHalf* mir() const { - return mir_->toPowHalf(); - } -}; - // Takes a tableswitch with an integer to decide class LTableSwitch : public LInstructionHelper<0, 1, 2> { public: LIR_HEADER(TableSwitch) LTableSwitch(const LAllocation& in, const LDefinition& inputCopy, const LDefinition& jumpTablePointer, MTableSwitch* ins)
--- a/js/src/jit/x86/LIR-x86.h +++ b/js/src/jit/x86/LIR-x86.h @@ -5,37 +5,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_x86_LIR_x86_h #define jit_x86_LIR_x86_h namespace js { namespace jit { -class LBox : public LInstructionHelper<2, 1, 0> -{ - MIRType type_; - - public: - LIR_HEADER(Box); - - LBox(const LAllocation& in_payload, MIRType type) - : type_(type) - { - setOperand(0, in_payload); - } - - MIRType type() const { - return type_; - } - const char* extraName() const { - return StringFromMIRType(type_); - } -}; - class LBoxFloatingPoint : public LInstructionHelper<2, 1, 1> { MIRType type_; public: LIR_HEADER(BoxFloatingPoint); LBoxFloatingPoint(const LAllocation& in, const LDefinition& temp, MIRType type)
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3808,41 +3808,47 @@ AutoFile::open(JSContext* cx, const char JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_OPEN, filename, "No such file or directory"); return false; } } return true; } -JSObject * const JS::ReadOnlyCompileOptions::nullObjectPtr = nullptr; - void -JS::ReadOnlyCompileOptions::copyPODOptions(const ReadOnlyCompileOptions& rhs) -{ +JS::TransitiveCompileOptions::copyPODTransitiveOptions(const TransitiveCompileOptions& rhs) +{ + mutedErrors_ = rhs.mutedErrors_; version = rhs.version; versionSet = rhs.versionSet; utf8 = rhs.utf8; - lineno = rhs.lineno; - column = rhs.column; - forEval = rhs.forEval; - noScriptRval = rhs.noScriptRval; selfHostingMode = rhs.selfHostingMode; canLazilyParse = rhs.canLazilyParse; strictOption = rhs.strictOption; extraWarningsOption = rhs.extraWarningsOption; werrorOption = rhs.werrorOption; asmJSOption = rhs.asmJSOption; forceAsync = rhs.forceAsync; installedFile = rhs.installedFile; sourceIsLazy = rhs.sourceIsLazy; introductionType = rhs.introductionType; introductionLineno = rhs.introductionLineno; introductionOffset = rhs.introductionOffset; hasIntroductionInfo = rhs.hasIntroductionInfo; +}; + +void +JS::ReadOnlyCompileOptions::copyPODOptions(const ReadOnlyCompileOptions& rhs) +{ + copyPODTransitiveOptions(rhs); + lineno = rhs.lineno; + column = rhs.column; + isRunOnce = rhs.isRunOnce; + forEval = rhs.forEval; + noScriptRval = rhs.noScriptRval; } JS::OwningCompileOptions::OwningCompileOptions(JSContext* cx) : ReadOnlyCompileOptions(), runtime(GetRuntime(cx)), elementRoot(cx), elementAttributeNameRoot(cx), introductionScriptRoot(cx) @@ -3857,17 +3863,16 @@ JS::OwningCompileOptions::~OwningCompile js_free(const_cast<char*>(introducerFilename_)); } bool JS::OwningCompileOptions::copy(JSContext* cx, const ReadOnlyCompileOptions& rhs) { copyPODOptions(rhs); - setMutedErrors(rhs.mutedErrors()); setElement(rhs.element()); setElementAttributeName(rhs.elementAttributeName()); setIntroductionScript(rhs.introductionScript()); return setFileAndLine(cx, rhs.filename(), rhs.lineno) && setSourceMapURL(cx, rhs.sourceMapURL()) && setIntroducerFilename(cx, rhs.introducerFilename()); } @@ -4265,18 +4270,17 @@ CompileFunction(JSContext* cx, const Rea if (!fun) return false; // Make sure the static scope chain matches up when we have a // non-syntactic scope. MOZ_ASSERT_IF(!enclosingDynamicScope->is<GlobalObject>(), HasNonSyntacticStaticScopeChain(enclosingStaticScope)); - CompileOptions options(cx, optionsArg); - if (!frontend::CompileFunctionBody(cx, fun, options, formals, srcBuf, enclosingStaticScope)) + if (!frontend::CompileFunctionBody(cx, fun, optionsArg, formals, srcBuf, enclosingStaticScope)) return false; return true; } JS_PUBLIC_API(bool) JS::CompileFunction(JSContext* cx, AutoObjectVector& scopeChain, const ReadOnlyCompileOptions& options,
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3379,42 +3379,48 @@ namespace JS { * compilation options where a worker thread can find them, and then return * immediately. The worker thread will come along at some later point, and use * the options. * * The compiler itself just needs to be able to access a collection of options; * it doesn't care who owns them, or what's keeping them alive. It does its own * addrefs/copies/tracing/etc. * - * So, we have a class hierarchy that reflects these three use cases: + * Furthermore, in some cases compile options are propagated from one entity to + * another (e.g. from a scriipt to a function defined in that script). This + * involves copying over some, but not all, of the options. + * + * So, we have a class hierarchy that reflects these four use cases: * - * - ReadOnlyCompileOptions is the common base class. It can be used by code - * that simply needs to access options set elsewhere, like the compiler. + * - TransitiveCompileOptions is the common base class, representing options + * that should get propagated from a script to functions defined in that + * script. This is never instantiated directly. + * + * - ReadOnlyCompileOptions is the only subclass of TransitiveCompileOptions, + * representing a full set of compile options. It can be used by code that + * simply needs to access options set elsewhere, like the compiler. This, + * again, is never instantiated directly. * * - The usual CompileOptions class must be stack-allocated, and holds * non-owning references to the filename, element, and so on. It's derived * from ReadOnlyCompileOptions, so the compiler can use it. * * - OwningCompileOptions roots / copies / reference counts of all its values, * and unroots / frees / releases them when it is destructed. It too is * derived from ReadOnlyCompileOptions, so the compiler accepts it. */ /* * The common base class for the CompileOptions hierarchy. * - * Use this in code that only needs to access compilation options created - * elsewhere, like the compiler. Don't instantiate this class (the constructor - * is protected anyway); instead, create instances only of the derived classes: - * CompileOptions and OwningCompileOptions. - */ -class JS_FRIEND_API(ReadOnlyCompileOptions) + * Use this in code that needs to propagate compile options from one compilation + * unit to another. + */ +class JS_FRIEND_API(TransitiveCompileOptions) { - friend class CompileOptions; - protected: // The Web Platform allows scripts to be loaded from arbitrary cross-origin // sources. This allows an attack by which a malicious website loads a // sensitive file (say, a bank statement) cross-origin (using the user's // cookies), and sniffs the generated syntax errors (via a window.onerror // handler) for juicy morsels of its contents. // // To counter this attack, HTML5 specifies that script errors should be @@ -3426,29 +3432,24 @@ class JS_FRIEND_API(ReadOnlyCompileOptio const char* filename_; const char* introducerFilename_; const char16_t* sourceMapURL_; // This constructor leaves 'version' set to JSVERSION_UNKNOWN. The structure // is unusable until that's set to something more specific; the derived // classes' constructors take care of that, in ways appropriate to their // purpose. - ReadOnlyCompileOptions() + TransitiveCompileOptions() : mutedErrors_(false), filename_(nullptr), introducerFilename_(nullptr), sourceMapURL_(nullptr), version(JSVERSION_UNKNOWN), versionSet(false), utf8(false), - lineno(1), - column(0), - isRunOnce(false), - forEval(false), - noScriptRval(false), selfHostingMode(false), canLazilyParse(true), strictOption(false), extraWarningsOption(false), werrorOption(false), asmJSOption(false), forceAsync(false), installedFile(false), @@ -3456,58 +3457,100 @@ class JS_FRIEND_API(ReadOnlyCompileOptio introductionType(nullptr), introductionLineno(0), introductionOffset(0), hasIntroductionInfo(false) { } // Set all POD options (those not requiring reference counts, copies, // rooting, or other hand-holding) to their values in |rhs|. + void copyPODTransitiveOptions(const TransitiveCompileOptions& rhs); + + public: + // Read-only accessors for non-POD options. The proper way to set these + // depends on the derived type. + bool mutedErrors() const { return mutedErrors_; } + const char* filename() const { return filename_; } + const char* introducerFilename() const { return introducerFilename_; } + const char16_t* sourceMapURL() const { return sourceMapURL_; } + virtual JSObject* element() const = 0; + virtual JSString* elementAttributeName() const = 0; + virtual JSScript* introductionScript() const = 0; + + // POD options. + JSVersion version; + bool versionSet; + bool utf8; + bool selfHostingMode; + bool canLazilyParse; + bool strictOption; + bool extraWarningsOption; + bool werrorOption; + bool asmJSOption; + bool forceAsync; + bool installedFile; // 'true' iff pre-compiling js file in packaged app + bool sourceIsLazy; + + // |introductionType| is a statically allocated C string: + // one of "eval", "Function", or "GeneratorFunction". + const char* introductionType; + unsigned introductionLineno; + uint32_t introductionOffset; + bool hasIntroductionInfo; + + private: + void operator=(const TransitiveCompileOptions&) = delete; +}; + +/* + * The class representing a full set of compile options. + * + * Use this in code that only needs to access compilation options created + * elsewhere, like the compiler. Don't instantiate this class (the constructor + * is protected anyway); instead, create instances only of the derived classes: + * CompileOptions and OwningCompileOptions. + */ +class JS_FRIEND_API(ReadOnlyCompileOptions) : public TransitiveCompileOptions +{ + friend class CompileOptions; + + protected: + ReadOnlyCompileOptions() + : TransitiveCompileOptions(), + lineno(1), + column(0), + isRunOnce(false), + forEval(false), + noScriptRval(false) + { } + + // Set all POD options (those not requiring reference counts, copies, + // rooting, or other hand-holding) to their values in |rhs|. void copyPODOptions(const ReadOnlyCompileOptions& rhs); public: // Read-only accessors for non-POD options. The proper way to set these // depends on the derived type. bool mutedErrors() const { return mutedErrors_; } const char* filename() const { return filename_; } const char* introducerFilename() const { return introducerFilename_; } const char16_t* sourceMapURL() const { return sourceMapURL_; } virtual JSObject* element() const = 0; virtual JSString* elementAttributeName() const = 0; virtual JSScript* introductionScript() const = 0; // POD options. - JSVersion version; - bool versionSet; - bool utf8; unsigned lineno; unsigned column; // isRunOnce only applies to non-function scripts. bool isRunOnce; bool forEval; bool noScriptRval; - bool selfHostingMode; - bool canLazilyParse; - bool strictOption; - bool extraWarningsOption; - bool werrorOption; - bool asmJSOption; - bool forceAsync; - bool installedFile; // 'true' iff pre-compiling js file in packaged app - bool sourceIsLazy; - - // |introductionType| is a statically allocated C string: - // one of "eval", "Function", or "GeneratorFunction". - const char* introductionType; - unsigned introductionLineno; - uint32_t introductionOffset; - bool hasIntroductionInfo; private: - static JSObject * const nullObjectPtr; void operator=(const ReadOnlyCompileOptions&) = delete; }; /* * Compilation options, with dynamic lifetime. An instance of this type * makes a copy of / holds / roots all dynamically allocated resources * (principals; elements; strings) that it refers to. Its destructor frees * / drops / unroots them. This is heavier than CompileOptions, below, but @@ -3612,18 +3655,32 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Comp public: explicit CompileOptions(JSContext* cx, JSVersion version = JSVERSION_UNKNOWN); CompileOptions(js::ContextFriendFields* cx, const ReadOnlyCompileOptions& rhs) : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx), introductionScriptRoot(cx) { copyPODOptions(rhs); - mutedErrors_ = rhs.mutedErrors_; filename_ = rhs.filename(); + introducerFilename_ = rhs.introducerFilename(); + sourceMapURL_ = rhs.sourceMapURL(); + elementRoot = rhs.element(); + elementAttributeNameRoot = rhs.elementAttributeName(); + introductionScriptRoot = rhs.introductionScript(); + } + + CompileOptions(js::ContextFriendFields* cx, const TransitiveCompileOptions& rhs) + : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx), + introductionScriptRoot(cx) + { + copyPODTransitiveOptions(rhs); + + filename_ = rhs.filename(); + introducerFilename_ = rhs.introducerFilename(); sourceMapURL_ = rhs.sourceMapURL(); elementRoot = rhs.element(); elementAttributeNameRoot = rhs.elementAttributeName(); introductionScriptRoot = rhs.introductionScript(); } JSObject* element() const override { return elementRoot; } JSString* elementAttributeName() const override { return elementAttributeNameRoot; }
--- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -32,16 +32,17 @@ typedef AutoVectorRooter<jsid> AutoIdVec class CallArgs; template <typename T> class Rooted; class JS_FRIEND_API(CompileOptions); class JS_FRIEND_API(ReadOnlyCompileOptions); class JS_FRIEND_API(OwningCompileOptions); +class JS_FRIEND_API(TransitiveCompileOptions); class JS_PUBLIC_API(CompartmentOptions); class Value; struct Zone; } /* namespace JS */ namespace js {
--- a/js/src/tests/ecma_6/Class/shell.js +++ b/js/src/tests/ecma_6/Class/shell.js @@ -1,23 +1,14 @@ // NOTE: This only turns on 1.8.5 in shell builds. The browser requires the // futzing in js/src/tests/browser.js (which only turns on 1.8, the most // the browser supports). if (typeof version != 'undefined') version(185); -function classesEnabled() { - try { - new Function("class Foo { constructor() { } }"); - return true; - } catch (e if e instanceof SyntaxError) { - return false; - } -} - function assertThrownErrorContains(thunk, substr) { try { thunk(); } catch (e) { if (e.message.indexOf(substr) !== -1) return; throw new Error("Expected error containing " + substr + ", got " + e); }
--- a/js/src/tests/ecma_6/Reflect/apply.js +++ b/js/src/tests/ecma_6/Reflect/apply.js @@ -1,17 +1,20 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ // Reflect.apply calls functions. assertEq(Reflect.apply(Math.floor, undefined, [1.75]), 1); // Reflect.apply requires a target object that's callable. -class clsX { constructor() {} } // classes are not callable -var nonCallable = [{}, [], clsX]; +var nonCallable = [{}, []]; +if (classesEnabled()) { + // classes are not callable + nonCallable.push(eval("(class clsX { constructor() {} })")); +} for (var value of nonCallable) { assertThrowsInstanceOf(() => Reflect.apply(nonCallable), TypeError); } // When target is not callable, Reflect.apply does not try to get argumentList.length before throwing. var hits = 0; var bogusArgumentList = {get length() { hit++; throw "FAIL";}}; assertThrowsInstanceOf(() => Reflect.apply({callable: false}, null, bogusArgumentList),
--- a/js/src/tests/ecma_6/Reflect/construct.js +++ b/js/src/tests/ecma_6/Reflect/construct.js @@ -25,26 +25,16 @@ assertDeepEq(Reflect.construct(f, [3]), f.prototype = Array.prototype; assertDeepEq(Reflect.construct(f, [3]), new f(3)); // Bound functions: var bound = f.bind(null, "carrot"); assertDeepEq(Reflect.construct(bound, []), new bound); // Classes: -function classesEnabled(testCode = "class Foo { constructor() {} }") { - try { - new Function(testCode); - return true; - } catch (e) { - if (!(e instanceof SyntaxError)) - throw e; - return false; - } -} if (classesEnabled()) { eval(`{ class Base { constructor(...args) { this.args = args; this.newTarget = new.target; } }
--- a/js/src/tests/ecma_6/shell.js +++ b/js/src/tests/ecma_6/shell.js @@ -210,8 +210,19 @@ if (typeof assertWarning === 'undefined' enableLastWarning(); func(); var warning = getLastWarning(); assertEq(warning !== null, true); assertEq(warning.name, name); disableLastWarning(); } } + +function classesEnabled(testCode = "class Foo { constructor() {} }") { + try { + new Function(testCode); + return true; + } catch (e) { + if (!(e instanceof SyntaxError)) + throw e; + return false; + } +}
--- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -968,17 +968,17 @@ RestyleManager::ProcessRestyledFrames(ns aChangeList.ChangeAt(index, &changeData); if (changeData->mFrame) { propTable->Delete(changeData->mFrame, ChangeListProperty()); } #ifdef DEBUG // reget frame from content since it may have been regenerated... if (changeData->mContent) { - if (!css::CommonAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent)) { + if (!CommonAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent)) { nsIFrame* frame = changeData->mContent->GetPrimaryFrame(); if (frame) { DebugVerifyStyleTree(frame); } } } else if (!changeData->mFrame || changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) { NS_WARNING("Unable to test style tree integrity -- no content node " @@ -2653,17 +2653,17 @@ ElementRestyler::AddLayerChangesForAnima { // Bug 847286 - We should have separate animation generation counters // on layers for transitions and animations and use != comparison below // rather than a > comparison. uint64_t frameGeneration = RestyleManager::GetMaxAnimationGenerationForFrame(mFrame); nsChangeHint hint = nsChangeHint(0); - const auto& layerInfo = css::CommonAnimationManager::sLayerAnimationInfo; + const auto& layerInfo = CommonAnimationManager::sLayerAnimationInfo; for (size_t i = 0; i < ArrayLength(layerInfo); i++) { Layer* layer = FrameLayerBuilder::GetDedicatedLayer(mFrame, layerInfo[i].mLayerType); if (layer && frameGeneration > layer->GetAnimationGeneration()) { // If we have a transform layer but don't have any transform style, we // probably just removed the transform but haven't destroyed the layer // yet. In this case we will add the appropriate change hint // (nsChangeHint_UpdateContainingBlock) when we compare style contexts
--- a/layout/base/nsCSSRenderingBorders.cpp +++ b/layout/base/nsCSSRenderingBorders.cpp @@ -1171,21 +1171,18 @@ ComputeCornerSkirtSize(Float aAlpha1, Fl aSizeResult = slopeScale - sqrtf(discrim); aSlopeResult = slope; } } // Draws a border radius with possibly different sides. // A skirt is drawn underneath the corner intersection to hide possible // seams when anti-aliased drawing is used. -// As an optimization, this tries to combine the drawing of the side itself -// with the drawing of the border radius where possible. static void DrawBorderRadius(DrawTarget* aDrawTarget, - const Point& aSideStart, Float aSideWidth, mozilla::css::Corner c, const Point& aOuterCorner, const Point& aInnerCorner, const twoFloats& aCornerMultPrev, const twoFloats& aCornerMultNext, const Size& aCornerDims, const Size& aOuterRadius, const Size& aInnerRadius, const Color& aFirstColor, const Color& aSecondColor, Float aSkirtSize, Float aSkirtSlope) { @@ -1216,24 +1213,17 @@ DrawBorderRadius(DrawTarget* aDrawTarget // Inner radius center Point innerCenter = aInnerCorner + (aCornerMultPrev + aCornerMultNext) * aInnerRadius; RefPtr<PathBuilder> builder; RefPtr<Path> path; if (aFirstColor.a > 0) { builder = aDrawTarget->CreatePathBuilder(); - // Combine stroke with corner if color matches. - if (aSideWidth > 0) { - builder->MoveTo(aSideStart + aCornerMultNext * aSideWidth); - builder->LineTo(aSideStart); - builder->LineTo(outerCornerStart); - } else { - builder->MoveTo(outerCornerStart); - } + builder->MoveTo(outerCornerStart); } if (aFirstColor != aSecondColor) { // Start and end angles of corner quadrant Float startAngle = (c * M_PI) / 2.0f - M_PI, endAngle = startAngle + M_PI / 2.0f, outerSplitAngle, innerSplitAngle; Point outerSplit, innerSplit; @@ -1303,21 +1293,18 @@ DrawBorderRadius(DrawTarget* aDrawTarget path = builder->Finish(); aDrawTarget->Fill(path, ColorPattern(aFirstColor)); } } // Draw a corner with possibly different sides. // A skirt is drawn underneath the corner intersection to hide possible // seams when anti-aliased drawing is used. -// As an optimization, this tries to combine the drawing of the side itself -// with the drawing of the corner where possible. static void DrawCorner(DrawTarget* aDrawTarget, - const Point& aSideStart, Float aSideWidth, mozilla::css::Corner c, const Point& aOuterCorner, const Point& aInnerCorner, const twoFloats& aCornerMultPrev, const twoFloats& aCornerMultNext, const Size& aCornerDims, const Color& aFirstColor, const Color& aSecondColor, Float aSkirtSize, Float aSkirtSlope) { // Corner box start point @@ -1325,23 +1312,17 @@ DrawCorner(DrawTarget* aDrawTarget, // Corner box end point Point cornerEnd = aOuterCorner + aCornerMultNext * aCornerDims; RefPtr<PathBuilder> builder; RefPtr<Path> path; if (aFirstColor.a > 0) { builder = aDrawTarget->CreatePathBuilder(); - // Combine stroke with corner if color matches. - if (aSideWidth > 0) { - builder->MoveTo(aSideStart + aCornerMultNext * aSideWidth); - builder->LineTo(aSideStart); - } else { - builder->MoveTo(cornerStart); - } + builder->MoveTo(cornerStart); } if (aFirstColor != aSecondColor) { // Draw first half with first color if (aFirstColor.a > 0) { builder->LineTo(aOuterCorner); // Draw skirt as part of first half if (aSkirtSize > 0) { @@ -1393,18 +1374,18 @@ nsCSSBorderRenderer::DrawNoCompositeColo Rect strokeRect = mOuterRect; strokeRect.Deflate(Margin(mBorderWidths[0] / 2.0, mBorderWidths[1] / 2.0, mBorderWidths[2] / 2.0, mBorderWidths[3] / 2.0)); NS_FOR_CSS_SIDES(i) { // We now draw the current side and the CW corner following it. // The CCW corner of this side was already drawn in the previous iteration. - // The side will either be drawn as an explicit stroke or combined - // with the drawing of the CW corner. + // The side will be drawn as an explicit stroke, and the CW corner will be + // filled separately. // If the next side does not have a matching color, then we split the // corner into two halves, one of each side's color and draw both. // Thus, the CCW corner of the next side will end up drawn here. // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw) mozilla::css::Corner c = mozilla::css::Corner((i+1) % 4); mozilla::css::Corner prevCorner = mozilla::css::Corner(i); @@ -1438,55 +1419,51 @@ nsCSSBorderRenderer::DrawNoCompositeColo Point outerCorner = mOuterRect.AtCorner(c); Point innerCorner = mInnerRect.AtCorner(c); // start and end points of border side stroke between corners Point sideStart = mOuterRect.AtCorner(prevCorner) + cornerMults[i2] * mBorderCornerDimensions[prevCorner]; Point sideEnd = outerCorner + cornerMults[i] * mBorderCornerDimensions[c]; - // if the side is inverted, don't draw it - if (-(sideEnd - sideStart).DotProduct(cornerMults[i]) <= 0) { - sideWidth = 0.0f; + // check if the side is visible and not inverted + if (sideWidth > 0 && firstColor.a > 0 && + -(sideEnd - sideStart).DotProduct(cornerMults[i]) > 0) { + mDrawTarget->StrokeLine(sideStart + centerAdjusts[i] * sideWidth, + sideEnd + centerAdjusts[i] * sideWidth, + ColorPattern(firstColor), + StrokeOptions(sideWidth)); } Float skirtSize = 0.0f, skirtSlope = 0.0f; // the sides don't match, so compute a skirt if (firstColor != secondColor && mPresContextType != nsPresContext::eContext_Print) { Point cornerDir = outerCorner - innerCorner; ComputeCornerSkirtSize(firstColor.a, secondColor.a, cornerDir.DotProduct(cornerMults[i]), cornerDir.DotProduct(cornerMults[i3]), skirtSize, skirtSlope); } if (!mBorderRadii[c].IsEmpty()) { // the corner has a border radius DrawBorderRadius(mDrawTarget, - sideStart, sideWidth, c, outerCorner, innerCorner, cornerMults[i], cornerMults[i3], mBorderCornerDimensions[c], mBorderRadii[c], innerRadii[c], firstColor, secondColor, skirtSize, skirtSlope); } else if (!mBorderCornerDimensions[c].IsEmpty()) { // a corner with no border radius DrawCorner(mDrawTarget, - sideStart, sideWidth, c, outerCorner, innerCorner, cornerMults[i], cornerMults[i3], mBorderCornerDimensions[c], firstColor, secondColor, skirtSize, skirtSlope); - } else if (sideWidth > 0 && firstColor.a > 0) { - // if there is no corner, then stroke the border side separately - mDrawTarget->StrokeLine(sideStart + centerAdjusts[i] * sideWidth, - sideEnd + centerAdjusts[i] * sideWidth, - ColorPattern(firstColor), - StrokeOptions(sideWidth)); } } } void nsCSSBorderRenderer::DrawRectangularCompositeColors() { nsBorderColors *currentColors[4];
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -376,30 +376,30 @@ nsLayoutUtils::HasAnimationsForComposito nsCSSProperty aProperty) { nsPresContext* presContext = aFrame->PresContext(); return presContext->AnimationManager()->GetAnimationsForCompositor(aFrame, aProperty) || presContext->TransitionManager()->GetAnimationsForCompositor(aFrame, aProperty); } bool -nsLayoutUtils::HasAnimations(const nsIFrame* aFrame, - nsCSSProperty aProperty) +nsLayoutUtils::HasCurrentAnimationOfProperty(const nsIFrame* aFrame, + nsCSSProperty aProperty) { nsPresContext* presContext = aFrame->PresContext(); AnimationCollection* collection = presContext->AnimationManager()->GetAnimationCollection(aFrame); if (collection && - collection->HasAnimationOfProperty(aProperty)) { + collection->HasCurrentAnimationOfProperty(aProperty)) { return true; } collection = presContext->TransitionManager()->GetAnimationCollection(aFrame); if (collection && - collection->HasAnimationOfProperty(aProperty)) { + collection->HasCurrentAnimationOfProperty(aProperty)) { return true; } return false; } bool nsLayoutUtils::HasCurrentAnimations(const nsIFrame* aFrame) { @@ -459,17 +459,17 @@ GetSuitableScale(float aMaxScale, float static void GetMinAndMaxScaleForAnimationProperty(const nsIFrame* aFrame, AnimationCollection* aAnimations, gfxSize& aMaxScale, gfxSize& aMinScale) { for (size_t animIdx = aAnimations->mAnimations.Length(); animIdx-- != 0; ) { dom::Animation* anim = aAnimations->mAnimations[animIdx]; - if (!anim->GetEffect() || anim->GetEffect()->IsFinishedTransition()) { + if (!anim->IsRelevant()) { continue; } dom::KeyframeEffectReadOnly* effect = anim->GetEffect(); for (size_t propIdx = effect->Properties().Length(); propIdx-- != 0; ) { AnimationProperty& prop = effect->Properties()[propIdx]; if (prop.mProperty == eCSSProperty_transform) { for (uint32_t segIdx = prop.mSegments.Length(); segIdx-- != 0; ) { AnimationPropertySegment& segment = prop.mSegments[segIdx];
--- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -2155,20 +2155,21 @@ public: /** * Returns true if the frame has animations or transitions that can be * performed on the compositor. */ static bool HasAnimationsForCompositor(const nsIFrame* aFrame, nsCSSProperty aProperty); /** - * Returns true if the frame has animations or transitions for the - * property. + * Returns true if the frame has current (i.e. running or scheduled-to-run) + * animations or transitions for the property. */ - static bool HasAnimations(const nsIFrame* aFrame, nsCSSProperty aProperty); + static bool HasCurrentAnimationOfProperty(const nsIFrame* aFrame, + nsCSSProperty aProperty); /** * Returns true if the frame has any current animations. * A current animation is any animation that has not yet finished playing * including paused animations. */ static bool HasCurrentAnimations(const nsIFrame* aFrame);
--- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -355,28 +355,30 @@ nsPresContext::LastRelease() if (mMissingFonts) { mMissingFonts->Clear(); } } NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationManager); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument); // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager); // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLangService); // a service NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrefChangedTimer); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationManager); NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument); NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering? // NS_RELEASE(tmp->mLanguage); // an atom // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service // NS_IMPL_CYCLE_COLLECTION_UNLINK(mLangService); // a service NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings); tmp->Destroy();
--- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1245,16 +1245,21 @@ PresShell::Destroy() NS_ASSERTION(mDocument->GetShell() == this, "Wrong shell?"); mDocument->DeleteShell(); if (mDocument->HasAnimationController()) { mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd); } } + if (mPresContext) { + mPresContext->AnimationManager()->ClearEventQueue(); + mPresContext->TransitionManager()->ClearEventQueue(); + } + // Revoke any pending events. We need to do this and cancel pending reflows // before we destroy the frame manager, since apparently frame destruction // sometimes spins the event queue when plug-ins are involved(!). rd->RemoveLayoutFlushObserver(this); if (mHiddenInvalidationObserverRefreshDriver) { mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this); }
--- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -59,16 +59,17 @@ #include "mozilla/ipc/PBackgroundChild.h" #include "nsIIPCBackgroundChildCreateCallback.h" #include "mozilla/layout/VsyncChild.h" #include "VsyncSource.h" #include "mozilla/VsyncDispatcher.h" #include "nsThreadUtils.h" #include "mozilla/unused.h" #include "mozilla/TimelineConsumers.h" +#include "nsAnimationManager.h" #ifdef MOZ_NUWA_PROCESS #include "ipc/Nuwa.h" #endif using namespace mozilla; using namespace mozilla::widget; using namespace mozilla::ipc; @@ -1484,16 +1485,80 @@ nsRefreshDriver::DispatchPendingEvents() // Swap out the current pending events nsTArray<PendingEvent> pendingEvents(Move(mPendingEvents)); for (PendingEvent& event : pendingEvents) { bool dummy; event.mTarget->DispatchEvent(event.mEvent, &dummy); } } +namespace { + enum class AnimationEventType { + CSSAnimations, + CSSTransitions + }; + + struct DispatchAnimationEventParams { + AnimationEventType mEventType; + nsRefreshDriver* mRefreshDriver; + }; +} + +static bool +DispatchAnimationEventsOnSubDocuments(nsIDocument* aDocument, + void* aParams) +{ + MOZ_ASSERT(aParams, "Animation event parameters should be set"); + auto params = static_cast<DispatchAnimationEventParams*>(aParams); + + nsIPresShell* shell = aDocument->GetShell(); + if (!shell) { + return true; + } + + nsPresContext* context = shell->GetPresContext(); + if (!context || context->RefreshDriver() != params->mRefreshDriver) { + return true; + } + + nsCOMPtr<nsIDocument> kungFuDeathGrip(aDocument); + + if (params->mEventType == AnimationEventType::CSSAnimations) { + context->AnimationManager()->DispatchEvents(); + } else { + context->TransitionManager()->DispatchEvents(); + } + aDocument->EnumerateSubDocuments(DispatchAnimationEventsOnSubDocuments, + aParams); + + return true; +} + +void +nsRefreshDriver::DispatchAnimationEvents() +{ + if (!mPresContext) { + return; + } + + nsIDocument* doc = mPresContext->Document(); + + // Dispatch transition events first since transitions conceptually sit + // below animations in terms of compositing order. + DispatchAnimationEventParams params { AnimationEventType::CSSTransitions, + this }; + DispatchAnimationEventsOnSubDocuments(doc, ¶ms); + if (!mPresContext) { + return; + } + + params.mEventType = AnimationEventType::CSSAnimations; + DispatchAnimationEventsOnSubDocuments(doc, ¶ms); +} + void nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime) { // Grab all of our frame request callbacks up front. nsTArray<DocumentFrameCallbacks> frameRequestCallbacks(mFrameRequestCallbackDocs.Length() + mThrottledFrameRequestCallbackDocs.Length()); @@ -1659,16 +1724,17 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, StopTimer(); return; } } if (i == 0) { // This is the Flush_Style case. + DispatchAnimationEvents(); DispatchPendingEvents(); RunFrameRequestCallbacks(aNowTime); if (mPresContext && mPresContext->GetPresShell()) { bool tracingStyleFlush = false; nsAutoTArray<nsIPresShell*, 16> observers; observers.AppendElements(mStyleFlushObservers); for (uint32_t j = observers.Length();
--- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -317,16 +317,17 @@ private: } mozilla::Maybe<mozilla::TimeStamp> mStartTime; RequestTable mEntries; }; typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable; void DispatchPendingEvents(); + void DispatchAnimationEvents(); void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime); void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime); enum EnsureTimerStartedFlags { eNone = 0, eAdjustingTimer = 1 << 0, eAllowTimeToGoBackwards = 1 << 1
--- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -305,17 +305,17 @@ nsLayoutStatics::Initialize() ServiceWorkerRegistrar::Initialize(); #ifdef MOZ_B2G RequestSyncWifiService::Init(); #endif #ifdef DEBUG nsStyleContext::Initialize(); - mozilla::css::CommonAnimationManager::Initialize(); + mozilla::CommonAnimationManager::Initialize(); #endif MediaDecoder::InitStatics(); PromiseDebugging::Init(); layers::CompositorLRU::Init();
new file mode 100644 --- /dev/null +++ b/layout/generic/crashtests/1183431.html @@ -0,0 +1,6 @@ +<!DOCTYPE> +<html> +<body> +<div style="writing-mode: vertical-lr;"><div style="position: fixed;"></div></div> +</body> +</html>
--- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -582,8 +582,9 @@ asserts(0-3) load 1134667.html asserts(0-3) load 1134667.html load 1146103.html load 1146107.html load 1146114.html load 1156222.html load 1157011.html load 1169420-1.html load 1169420-2.html +load 1183431.html
--- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1941,17 +1941,18 @@ nsIFrame::BuildDisplayListForStackingCon // we're painting, and we're not animating opacity. Don't do this // if we're going to compute plugin geometry, since opacity-0 plugins // need to have display items built for them. bool needEventRegions = aBuilder->IsBuildingLayerEventRegions() && StyleVisibility()->GetEffectivePointerEvents(this) != NS_STYLE_POINTER_EVENTS_NONE; if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() && !aBuilder->WillComputePluginGeometry() && !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) && - !nsLayoutUtils::HasAnimations(this, eCSSProperty_opacity) && + !nsLayoutUtils::HasCurrentAnimationOfProperty(this, + eCSSProperty_opacity) && !needEventRegions) { return; } if (disp->mWillChangeBitField != 0) { aBuilder->AddToWillChangeBudget(this, GetSize()); }
--- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -990,19 +990,19 @@ nsHTMLReflowState::ApplyRelativePosition StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame); if (ssc) { *aPosition = ssc->ComputePosition(aFrame); } } } nsIFrame* -nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, - nscoord& aCBIStartEdge, - nscoord& aCBISize) +nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, + nscoord& aCBIStartEdge, + LogicalSize& aCBSize) { aFrame = aFrame->GetContainingBlock(); NS_ASSERTION(aFrame != frame, "How did that happen?"); /* Now aFrame is the containing block we want */ /* Check whether the containing block is currently being reflowed. If so, use the info from the reflow state. */ @@ -1016,26 +1016,26 @@ nsHTMLReflowState::GetHypotheticalBoxCon state = nullptr; } WritingMode wm = aFrame->GetWritingMode(); if (state) { WritingMode stateWM = state->GetWritingMode(); aCBIStartEdge = state->ComputedLogicalBorderPadding().ConvertTo(wm, stateWM).IStart(wm); - aCBISize = state->ComputedSize(wm).ISize(wm); + aCBSize = state->ComputedSize(wm); } else { /* Didn't find a reflow state for aFrame. Just compute the information we want, on the assumption that aFrame already knows its size. This really ought to be true by now. */ NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW), "aFrame shouldn't be in reflow; we'll lie if it is"); LogicalMargin borderPadding = aFrame->GetLogicalUsedBorderAndPadding(wm); aCBIStartEdge = borderPadding.IStart(wm); - aCBISize = aFrame->ISize(wm) - borderPadding.IStartEnd(wm); + aCBSize = aFrame->GetLogicalSize(wm) - borderPadding.Size(wm); } return aFrame; } // When determining the hypothetical box that would have been if the element // had been in the flow we may not be able to exactly determine both the IStart // and IEnd edges. For example, if the element is a non-replaced inline-level @@ -1075,81 +1075,91 @@ GetIntrinsicSizeFor(nsIFrame* aFrame, ns if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(aIntrinsicSize))) { success = (aIntrinsicSize != nsSize(0, 0)); } } return success; } /** - * aInsideBoxSizing returns the part of the horizontal padding, border, - * and margin that goes inside the edge given by box-sizing; + * aInsideBoxSizing returns the part of the padding, border, and margin + * in the aAxis dimension that goes inside the edge given by box-sizing; * aOutsideBoxSizing returns the rest. */ void -nsHTMLReflowState::CalculateInlineBorderPaddingMargin( - nscoord aContainingBlockISize, +nsHTMLReflowState::CalculateBorderPaddingMargin( + LogicalAxis aAxis, + nscoord aContainingBlockSize, nscoord* aInsideBoxSizing, nscoord* aOutsideBoxSizing) { WritingMode wm = GetWritingMode(); - mozilla::css::Side inlineStart = wm.PhysicalSide(eLogicalSideIStart); - mozilla::css::Side inlineEnd = wm.PhysicalSide(eLogicalSideIEnd); + mozilla::css::Side startSide = + wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeStart)); + mozilla::css::Side endSide = + wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeEnd)); - const LogicalMargin& border = - LogicalMargin(wm, mStyleBorder->GetComputedBorder()); - LogicalMargin padding(wm), margin(wm); + nsMargin styleBorder = mStyleBorder->GetComputedBorder(); + nscoord borderStartEnd = + styleBorder.Side(startSide) + styleBorder.Side(endSide); + + nscoord paddingStartEnd, marginStartEnd; // See if the style system can provide us the padding directly nsMargin stylePadding; if (mStylePadding->GetPadding(stylePadding)) { - padding = LogicalMargin(wm, stylePadding); + paddingStartEnd = + stylePadding.Side(startSide) + stylePadding.Side(endSide); } else { - // We have to compute the inline start and end values - padding.IStart(wm) = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockISize, - mStylePadding->mPadding.Get(inlineStart)); - padding.IEnd(wm) = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockISize, - mStylePadding->mPadding.Get(inlineEnd)); + // We have to compute the start and end values + nscoord start, end; + start = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockSize, + mStylePadding->mPadding.Get(startSide)); + end = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockSize, + mStylePadding->mPadding.Get(endSide)); + paddingStartEnd = start + end; } // See if the style system can provide us the margin directly nsMargin styleMargin; if (mStyleMargin->GetMargin(styleMargin)) { - margin = LogicalMargin(wm, styleMargin); + marginStartEnd = + styleMargin.Side(startSide) + styleMargin.Side(endSide); } else { - // We have to compute the left and right values - if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(inlineStart)) { + nscoord start, end; + // We have to compute the start and end values + if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(startSide)) { // XXX FIXME (or does CalculateBlockSideMargins do this?) - margin.IStart(wm) = 0; // just ignore + start = 0; // just ignore } else { - margin.IStart(wm) = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockISize, - mStyleMargin->mMargin.Get(inlineStart)); + start = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockSize, + mStyleMargin->mMargin.Get(startSide)); } - if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(inlineEnd)) { + if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(endSide)) { // XXX FIXME (or does CalculateBlockSideMargins do this?) - margin.IEnd(wm) = 0; // just ignore + end = 0; // just ignore } else { - margin.IEnd(wm) = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockISize, - mStyleMargin->mMargin.Get(inlineEnd)); + end = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockSize, + mStyleMargin->mMargin.Get(endSide)); } + marginStartEnd = start + end; } - nscoord outside = - padding.IStartEnd(wm) + border.IStartEnd(wm) + margin.IStartEnd(wm); + nscoord outside = paddingStartEnd + borderStartEnd + marginStartEnd; nscoord inside = 0; switch (mStylePosition->mBoxSizing) { case NS_STYLE_BOX_SIZING_BORDER: - inside += border.IStartEnd(wm); + inside += borderStartEnd; // fall through case NS_STYLE_BOX_SIZING_PADDING: - inside += padding.IStartEnd(wm); + inside += paddingStartEnd; } outside -= inside; *aInsideBoxSizing = inside; *aOutsideBoxSizing = outside; return; } /** @@ -1173,40 +1183,48 @@ static bool AreAllEarlierInFlowFramesEmp } } *aFound = false; return true; } // Calculate the hypothetical box that the element would have if it were in // the flow. The values returned are relative to the padding edge of the -// absolute containing block, in the actual containing block's writing mode. -// cbrs->frame is the actual containing block +// absolute containing block. The writing-mode of the hypothetical box will +// have the same block direction as the absolute containing block, but may +// differ in inline-bidi direction. +// In the code below, |cbrs->frame| is the absolute containing block, while +// |containingBlock| is the nearest block container of the placeholder frame, +// which may be different from the absolute containing block. void nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, nsIFrame* aPlaceholderFrame, const nsHTMLReflowState* cbrs, nsHypotheticalBox& aHypotheticalBox, nsIAtom* aFrameType) { NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE, "mOriginalDisplay has not been properly initialized"); // Find the nearest containing block frame to the placeholder frame, // and its inline-start edge and width. - nscoord blockIStartContentEdge, blockContentISize; + nscoord blockIStartContentEdge; + // Dummy writing mode for blockContentSize, will be changed as needed by + // GetHypotheticalBoxContainer. + WritingMode cbwm = cbrs->GetWritingMode(); + LogicalSize blockContentSize(cbwm); nsIFrame* containingBlock = GetHypotheticalBoxContainer(aPlaceholderFrame, blockIStartContentEdge, - blockContentISize); + blockContentSize); + // Now blockContentSize is in containingBlock's writing mode. // If it's a replaced element and it has a 'auto' value for //'inline size', see if we can get the intrinsic size. This will allow // us to exactly determine both the inline edges WritingMode wm = containingBlock->GetWritingMode(); - aHypotheticalBox.mWritingMode = wm; nsStyleCoord styleISize = mStylePosition->ISize(wm); bool isAutoISize = styleISize.GetUnit() == eStyleUnit_Auto; nsSize intrinsicSize; bool knowIntrinsicSize = false; if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) { // See if we can get the intrinsic size of the element knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize, aFrameType); @@ -1225,50 +1243,50 @@ nsHTMLReflowState::CalculateHypothetical } else { // It's either a replaced inline-level element or a block-level element // Determine the total amount of inline direction // border/padding/margin that the element would have had if it had // been in the flow. Note that we ignore any 'auto' and 'inherit' // values nscoord insideBoxSizing, outsideBoxSizing; - CalculateInlineBorderPaddingMargin(blockContentISize, - &insideBoxSizing, &outsideBoxSizing); + CalculateBorderPaddingMargin(eLogicalAxisInline, + blockContentSize.ISize(wm), + &insideBoxSizing, &outsideBoxSizing); if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) { // It's a replaced element with an 'auto' inline size so the box // inline size is its intrinsic size plus any border/padding/margin if (knowIntrinsicSize) { boxISize = LogicalSize(wm, intrinsicSize).ISize(wm) + outsideBoxSizing + insideBoxSizing; knowBoxISize = true; } } else if (isAutoISize) { // The box inline size is the containing block inline size - boxISize = blockContentISize; + boxISize = blockContentSize.ISize(wm); knowBoxISize = true; } else { // We need to compute it. It's important we do this, because if it's // percentage based this computed value may be different from the computed // value calculated using the absolute containing block width - boxISize = ComputeISizeValue(blockContentISize, + boxISize = ComputeISizeValue(blockContentSize.ISize(wm), insideBoxSizing, outsideBoxSizing, styleISize) + insideBoxSizing + outsideBoxSizing; knowBoxISize = true; } } // Get the placeholder x-offset and y-offset in the coordinate // space of its containing block // XXXbz the placeholder is not fully reflowed yet if our containing block is // relatively positioned... - WritingMode cbwm = cbrs->GetWritingMode(); nsSize containerSize = containingBlock->GetStateBits() & NS_FRAME_IN_REFLOW ? cbrs->ComputedSizeAsContainerIfConstrained() : containingBlock->GetSize(); LogicalPoint placeholderOffset(wm, aPlaceholderFrame->GetOffsetTo(containingBlock), containerSize); // First, determine the hypothetical box's mBStart. We want to check the @@ -1363,17 +1381,18 @@ nsHTMLReflowState::CalculateHypothetical aHypotheticalBox.mIEnd = aHypotheticalBox.mIStart + boxISize; #ifdef DEBUG aHypotheticalBox.mIEndIsExact = true; #endif } else { // We can't compute the inline-end edge because we don't know the desired // inline-size. So instead use the end content edge of the block parent, // but remember it's not exact - aHypotheticalBox.mIEnd = blockIStartContentEdge + blockContentISize; + aHypotheticalBox.mIEnd = + blockIStartContentEdge + blockContentSize.ISize(wm); #ifdef DEBUG aHypotheticalBox.mIEndIsExact = false; #endif } // The current coordinate space is that of the nearest block to the placeholder. // Convert to the coordinate space of the absolute containing block // One weird thing here is that for fixed-positioned elements we want to do @@ -1406,33 +1425,99 @@ nsHTMLReflowState::CalculateHypothetical } while (containingBlock != cbrs->frame); } else { // XXXldb We need to either ignore scrolling for the absolute // positioning case too (and take the incompatibility) or figure out // how to make these positioned elements actually *move* when we // scroll, and thus avoid the resulting incremental reflow bugs. cbOffset = containingBlock->GetOffsetTo(cbrs->frame); } - nsSize cbrsSize = - cbrs->ComputedPhysicalSize() + - cbrs->ComputedLogicalBorderPadding().Size(cbwm).GetPhysicalSize(cbwm); + nsSize cbrsSize = cbrs->ComputedSizeAsContainerIfConstrained(); LogicalPoint logCBOffs(wm, cbOffset, cbrsSize - containerSize); aHypotheticalBox.mIStart += logCBOffs.I(wm); aHypotheticalBox.mIEnd += logCBOffs.I(wm); aHypotheticalBox.mBStart += logCBOffs.B(wm); // The specified offsets are relative to the absolute containing block's // padding edge and our current values are relative to the border edge, so // translate. LogicalMargin border = cbrs->ComputedLogicalBorderPadding() - cbrs->ComputedLogicalPadding(); border = border.ConvertTo(wm, cbrs->GetWritingMode()); aHypotheticalBox.mIStart -= border.IStart(wm); aHypotheticalBox.mIEnd -= border.IStart(wm); aHypotheticalBox.mBStart -= border.BStart(wm); + + // At this point, we have computed aHypotheticalBox using the writing mode + // of the placeholder's containing block. + + if (cbwm.GetBlockDir() != wm.GetBlockDir()) { + // If the block direction we used in calculating aHypotheticalBox does not + // match the absolute containing block's, we need to convert here so that + // aHypotheticalBox is usable in relation to the absolute containing block. + // This requires computing or measuring the abspos frame's block-size, + // which is not otherwise required/used here (as aHypotheticalBox records + // only the block-start coordinate). + + // This is similar to the inline-size calculation for a replaced + // inline-level element or a block-level element (above), except that + // 'auto' sizing is handled differently in the block direction for non- + // replaced elements and replaced elements lacking an intrinsic size. + + // Determine the total amount of block direction + // border/padding/margin that the element would have had if it had + // been in the flow. Note that we ignore any 'auto' and 'inherit' + // values. + nscoord insideBoxSizing, outsideBoxSizing; + CalculateBorderPaddingMargin(eLogicalAxisBlock, + blockContentSize.BSize(wm), + &insideBoxSizing, &outsideBoxSizing); + + nscoord boxBSize; + nsStyleCoord styleBSize = mStylePosition->BSize(wm); + bool isAutoBSize = styleBSize.GetUnit() == eStyleUnit_Auto; + if (isAutoBSize) { + if (NS_FRAME_IS_REPLACED(mFrameType) && knowIntrinsicSize) { + // It's a replaced element with an 'auto' block size so the box + // block size is its intrinsic size plus any border/padding/margin + boxBSize = LogicalSize(wm, intrinsicSize).BSize(wm) + + outsideBoxSizing + insideBoxSizing; + } else { + // XXX Bug 1191801 + // Figure out how to get the correct boxBSize here (need to reflow the + // positioned frame?) + boxBSize = 0; + } + } else { + // We need to compute it. It's important we do this, because if it's + // percentage-based this computed value may be different from the + // computed value calculated using the absolute containing block height. + boxBSize = ComputeBSizeValue(blockContentSize.BSize(wm), + insideBoxSizing, styleBSize) + + insideBoxSizing + outsideBoxSizing; + } + + LogicalSize boxSize(wm, knowBoxISize ? boxISize : 0, boxBSize); + + LogicalPoint origin(wm, aHypotheticalBox.mIStart, + aHypotheticalBox.mBStart); + origin = origin.ConvertTo(cbwm, wm, cbrsSize - + boxSize.GetPhysicalSize(wm)); + + aHypotheticalBox.mIStart = origin.I(cbwm); + aHypotheticalBox.mIEnd = aHypotheticalBox.mIStart + + boxSize.ConvertTo(cbwm, wm).ISize(cbwm); +#ifdef DEBUG + aHypotheticalBox.mIEndIsExact = false; // it may be fake +#endif + aHypotheticalBox.mBStart = origin.B(cbwm); + aHypotheticalBox.mWritingMode = cbwm; + } else { + aHypotheticalBox.mWritingMode = wm; + } } void nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, const nsHTMLReflowState* cbrs, const LogicalSize& aCBSize, nsIAtom* aFrameType) { @@ -1518,18 +1603,16 @@ nsHTMLReflowState::InitAbsoluteConstrain } else { offsets.BEnd(cbwm) = nsLayoutUtils:: ComputeBSizeDependentValue(cbSize.BSize(cbwm), mStylePosition->mOffset.GetBEnd(cbwm)); } if (bStartIsAuto && bEndIsAuto) { // Treat 'top' like 'static-position' - NS_ASSERTION(hypotheticalBox.mWritingMode.GetBlockDir() == cbwm.GetBlockDir(), - "block direction mismatch"); offsets.BStart(cbwm) = hypotheticalBox.mBStart; bStartIsAuto = false; } SetComputedLogicalOffsets(offsets.ConvertTo(wm, cbwm)); bool iSizeIsAuto = eStyleUnit_Auto == mStylePosition->ISize(cbwm).GetUnit(); bool bSizeIsAuto = eStyleUnit_Auto == mStylePosition->BSize(cbwm).GetUnit();
--- a/layout/generic/nsHTMLReflowState.h +++ b/layout/generic/nsHTMLReflowState.h @@ -904,40 +904,49 @@ protected: void InitConstraints(nsPresContext* aPresContext, const mozilla::LogicalSize& aContainingBlockSize, const nsMargin* aBorder, const nsMargin* aPadding, nsIAtom* aFrameType); // Returns the nearest containing block or block frame (whether or not // it is a containing block) for the specified frame. Also returns - // the inline-start edge and inline size of the containing block's + // the inline-start edge and logical size of the containing block's // content area. // These are returned in the coordinate space of the containing block. nsIFrame* GetHypotheticalBoxContainer(nsIFrame* aFrame, nscoord& aCBIStartEdge, - nscoord& aCBISize); + mozilla::LogicalSize& aCBSize); + // Calculate a "hypothetical box" position where the placeholder frame + // (for a position:fixed/absolute element) would have been placed if it were + // positioned statically. The hypothetical box will have a writing mode with + // the same block direction as the absolute containing block (cbrs->frame), + // though it may differ in inline-bidi direction. void CalculateHypotheticalBox(nsPresContext* aPresContext, nsIFrame* aPlaceholderFrame, const nsHTMLReflowState* cbrs, nsHypotheticalBox& aHypotheticalBox, nsIAtom* aFrameType); void InitAbsoluteConstraints(nsPresContext* aPresContext, const nsHTMLReflowState* cbrs, const mozilla::LogicalSize& aContainingBlockSize, nsIAtom* aFrameType); // Calculates the computed values for the 'min-Width', 'max-Width', // 'min-Height', and 'max-Height' properties, and stores them in the assorted // data members void ComputeMinMaxValues(const mozilla::LogicalSize& aContainingBlockSize); - void CalculateInlineBorderPaddingMargin(nscoord aContainingBlockISize, - nscoord* aInsideBoxSizing, - nscoord* aOutsideBoxSizing); + // aInsideBoxSizing returns the part of the padding, border, and margin + // in the aAxis dimension that goes inside the edge given by box-sizing; + // aOutsideBoxSizing returns the rest. + void CalculateBorderPaddingMargin(mozilla::LogicalAxis aAxis, + nscoord aContainingBlockSize, + nscoord* aInsideBoxSizing, + nscoord* aOutsideBoxSizing); void CalculateBlockSideMargins(nsIAtom* aFrameType); }; #endif /* nsHTMLReflowState_h___ */
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1-ref.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; + writing-mode: vertical-lr; position: absolute; left: 0; top: 0; } +.test { background: #aaa; position: absolute; left: 0; top: 0; width: 2em; height: 100%; } +.rel { position: relative; } +img { position: absolute; left: 0; top: 34px; + width: 30px; height: 40px; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="rel"> + <div class="test"><span class="abc">abc</span><img src=""></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1a.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vlr"> + <div class="test"><span class="abc">abc</span><img src=""></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1b.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vlr rel"> + <div class="test"><span class="abc">abc</span><img src=""></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1c.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vlr"> + <div class="test rel"><span class="abc">abc</span><img src=""></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2-ref.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; + writing-mode: vertical-lr; position: absolute; left: 0; top: 0; } +.test { background: #aaa; position: absolute; left: 0; top: 0; width: 2em; height: 100%; } +.rel { position: relative; } +img { position: absolute; left: 0; top: 34px; + background: green; border: 5px solid yellow; } +</style> +<body> +<div class="rel"> + <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2a.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +img { position: absolute; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vlr"> + <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2b.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +img { position: absolute; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vlr rel"> + <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2c.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +img { position: absolute; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vlr"> + <div class="test rel"><span class="abc">abc</span><img src="blue-32x32.png"></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3-ref.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; } +.vlr { writing-mode: vertical-lr; } +span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; } +p { margin: 0; padding: 2px; border: 2px solid green; inline-size: -moz-fit-content; } +</style> +<body> +<div> + <div class="vlr"><span>abc def</span><p>xyzzy</p></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3a.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; } +p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; } +</style> +<body> +<div class="vlr"> + <div><span>abc def</span><p>xyzzy</p></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3b.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; } +p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; } +</style> +<body> +<div class="vlr rel"> + <div><span>abc def</span><p>xyzzy</p></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3c.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; } +p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; } +</style> +<body> +<div class="vlr"> + <div class="rel"><span>abc def</span><p>xyzzy</p></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4-ref.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; + position: absolute; right: 0; top: 0; } +.test { writing-mode: vertical-rl; background: #aaa; + position: absolute; right: 0; top: 0; width: 2em; height: 100%; } +.rel { position: relative; } +img { position: absolute; right: 0; top: 34px; + width: 30px; height: 40px; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="rel"> + <div class="test"><span class="abc">abc</span><img src=""></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4a.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vrl"> + <div class="test"><span class="abc">abc</span><img src=""></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4b.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vrl rel"> + <div class="test"><span class="abc">abc</span><img src=""></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4c.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vrl"> + <div class="test rel"><span class="abc">abc</span><img src=""></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5-ref.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; + position: absolute; right: 0; top: 0; } +.test { writing-mode: vertical-rl; background: #aaa; + position: absolute; right: 0; top: 0; width: 2em; height: 100%; } +.rel { position: relative; } +img { position: absolute; right: 0; top: 34px; + background: green; border: 5px solid yellow; } +</style> +<body> +<div class="rel"> + <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5a.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +img { position: absolute; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vrl"> + <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5b.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +img { position: absolute; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vrl rel"> + <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5c.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +img { position: absolute; background: green; border: 5px solid yellow; } +</style> +<body> +<div class="vrl"> + <div class="test rel"><span class="abc">abc</span><img src="blue-32x32.png"></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6-ref.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; } +.vrl { writing-mode: vertical-rl; } +span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; } +p { margin: 0; padding: 2px; border: 2px solid green; inline-size: -moz-fit-content; } +</style> +<body> +<div class="vrl"> + <div><span>abc def</span><p>xyzzy</p></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6a.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; } +p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; } +</style> +<body> +<div class="vrl"> + <div><span>abc def</span><p>xyzzy</p></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6b.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; } +p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; } +</style> +<body> +<div class="vrl rel"> + <div><span>abc def</span><p>xyzzy</p></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6c.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; } +p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; } +</style> +<body> +<div class="vrl"> + <div class="rel"><span>abc def</span><p>xyzzy</p></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7-ref.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; + writing-mode: vertical-lr; position: absolute; left: 0; top: 0; } +.test { background: #aaa; position: absolute; left: 0; top: 0; width: 2em; height: 100%; } +.rel { position: relative; } +iframe { + position: absolute; left: 0; top: 34px; + background: yellow; border: 5px solid green; + width: 150px; height: 300px; /* XXX this is probably wrong, pending CSSWG clarification */ +} +</style> +<body> +<div class="rel"> + <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7a.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +iframe { position: absolute; background: yellow; border: 5px solid green; } +</style> +<body> +<div class="vlr"> + <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7b.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +iframe { position: absolute; background: yellow; border: 5px solid green; } +</style> +<body> +<div class="vlr rel"> + <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7c.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vlr { writing-mode: vertical-lr; } +.rel { position: relative; } +iframe { position: absolute; background: yellow; border: 5px solid green; } +</style> +<body> +<div class="vlr"> + <div class="test rel"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8-ref.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; + writing-mode: vertical-rl; position: absolute; right: 0; top: 0; } +.test { background: #aaa; position: absolute; right: 0; top: 0; width: 2em; height: 100%; } +.rel { position: relative; } +iframe { + position: absolute; right: 0; top: 34px; + background: yellow; border: 5px solid green; + width: 150px; height: 300px; /* XXX this is probably wrong, pending CSSWG clarification */ +} +</style> +<body> +<div class="rel"> + <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8a.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +iframe { position: absolute; background: yellow; border: 5px solid green; } +</style> +<body> +<div class="vrl"> + <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8b.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +iframe { position: absolute; background: yellow; border: 5px solid green; } +</style> +<body> +<div class="vrl rel"> + <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div> +</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8c.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<meta charset="utf-8"> +<style> +body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; } +.abc { display: inline-block; border: 2px solid red; inline-size: 30px; } +.test { background: #aaa; width: 2em; } +.vrl { writing-mode: vertical-rl; } +.rel { position: relative; } +iframe { position: absolute; background: yellow; border: 5px solid green; } +</style> +<body> +<div class="vrl"> + <div class="test rel"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div> +</div>
copy from layout/reftests/writing-mode/blue-32x32.png copy to layout/reftests/writing-mode/abspos/blue-32x32.png
--- a/layout/reftests/writing-mode/abspos/reftest.list +++ b/layout/reftests/writing-mode/abspos/reftest.list @@ -94,8 +94,33 @@ fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d, fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-082.xht s71-abs-pos-non-replaced-vrl-082-ref.xht fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-084.xht s71-abs-pos-non-replaced-vrl-084-ref.xht fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-086.xht s71-abs-pos-non-replaced-vrl-086-ref.xht fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-088.xht s71-abs-pos-non-replaced-vrl-088-ref.xht fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-090.xht s71-abs-pos-non-replaced-vrl-090-ref.xht fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-092.xht s71-abs-pos-non-replaced-vrl-092-ref.xht fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-094.xht s71-abs-pos-non-replaced-vrl-094-ref.xht fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-096.xht s71-abs-pos-non-replaced-vrl-096-ref.xht + +== 1183431-orthogonal-modes-1a.html 1183431-orthogonal-modes-1-ref.html +== 1183431-orthogonal-modes-1b.html 1183431-orthogonal-modes-1-ref.html +== 1183431-orthogonal-modes-1c.html 1183431-orthogonal-modes-1-ref.html +== 1183431-orthogonal-modes-2a.html 1183431-orthogonal-modes-2-ref.html +== 1183431-orthogonal-modes-2b.html 1183431-orthogonal-modes-2-ref.html +== 1183431-orthogonal-modes-2c.html 1183431-orthogonal-modes-2-ref.html +== 1183431-orthogonal-modes-3a.html 1183431-orthogonal-modes-3-ref.html +== 1183431-orthogonal-modes-3b.html 1183431-orthogonal-modes-3-ref.html +== 1183431-orthogonal-modes-3c.html 1183431-orthogonal-modes-3-ref.html +== 1183431-orthogonal-modes-4a.html 1183431-orthogonal-modes-4-ref.html +== 1183431-orthogonal-modes-4b.html 1183431-orthogonal-modes-4-ref.html +== 1183431-orthogonal-modes-4c.html 1183431-orthogonal-modes-4-ref.html +== 1183431-orthogonal-modes-5a.html 1183431-orthogonal-modes-5-ref.html +== 1183431-orthogonal-modes-5b.html 1183431-orthogonal-modes-5-ref.html +== 1183431-orthogonal-modes-5c.html 1183431-orthogonal-modes-5-ref.html +fails == 1183431-orthogonal-modes-6a.html 1183431-orthogonal-modes-6-ref.html # bug 1191801 +== 1183431-orthogonal-modes-6b.html 1183431-orthogonal-modes-6-ref.html +== 1183431-orthogonal-modes-6c.html 1183431-orthogonal-modes-6-ref.html +== 1183431-orthogonal-modes-7a.html 1183431-orthogonal-modes-7-ref.html +== 1183431-orthogonal-modes-7b.html 1183431-orthogonal-modes-7-ref.html +== 1183431-orthogonal-modes-7c.html 1183431-orthogonal-modes-7-ref.html +fails == 1183431-orthogonal-modes-8a.html 1183431-orthogonal-modes-8-ref.html # bug 1191801 +== 1183431-orthogonal-modes-8b.html 1183431-orthogonal-modes-8-ref.html +== 1183431-orthogonal-modes-8c.html 1183431-orthogonal-modes-8-ref.html
--- a/layout/style/AnimationCommon.cpp +++ b/layout/style/AnimationCommon.cpp @@ -46,18 +46,16 @@ IsGeometricProperty(nsCSSProperty aPrope case eCSSProperty_top: case eCSSProperty_width: return true; default: return false; } } -namespace css { - CommonAnimationManager::CommonAnimationManager(nsPresContext *aPresContext) : mPresContext(aPresContext) , mIsObservingRefreshDriver(false) { PR_INIT_CLIST(&mElementCollections); } CommonAnimationManager::~CommonAnimationManager() @@ -170,32 +168,26 @@ CommonAnimationManager::GetAnimationColl } AnimationCollection* CommonAnimationManager::GetAnimationsForCompositor(const nsIFrame* aFrame, nsCSSProperty aProperty) { AnimationCollection* collection = GetAnimationCollection(aFrame); if (!collection || - !collection->HasAnimationOfProperty(aProperty) || + !collection->HasCurrentAnimationOfProperty(aProperty) || !collection->CanPerformOnCompositorThread( AnimationCollection::CanAnimate_AllowPartial)) { return nullptr; } // This animation can be done on the compositor. return collection; } -/* - * nsISupports implementation - */ - -NS_IMPL_ISUPPORTS(CommonAnimationManager, nsIStyleRuleProcessor) - nsRestyleHint CommonAnimationManager::HasStateDependentStyle(StateRuleProcessorData* aData) { return nsRestyleHint(0); } nsRestyleHint CommonAnimationManager::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) @@ -443,17 +435,17 @@ CommonAnimationManager::LayerAnimationRe } return nullptr; } #ifdef DEBUG /* static */ void CommonAnimationManager::Initialize() { - const auto& info = css::CommonAnimationManager::sLayerAnimationInfo; + const auto& info = CommonAnimationManager::sLayerAnimationInfo; for (size_t i = 0; i < ArrayLength(info); i++) { auto record = info[i]; MOZ_ASSERT(nsCSSProps::PropHasFlags(record.mProperty, CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR), "CSS property with entry in sLayerAnimationInfo does not " "have the CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR flag"); } @@ -537,18 +529,16 @@ AnimValuesStyleRule::List(FILE* out, int AppendUTF16toUTF8(value, str); str.AppendLiteral("; "); } str.AppendLiteral("}\n"); fprintf_stderr(out, "%s", str.get()); } #endif -} // namespace css - bool AnimationCollection::CanAnimatePropertyOnCompositor( const dom::Element *aElement, nsCSSProperty aProperty, CanAnimateFlags aFlags) { bool shouldLog = nsLayoutUtils::IsAnimationLoggingEnabled(); if (!gfxPlatform::OffMainThreadCompositingEnabled()) { @@ -700,41 +690,56 @@ AnimationCollection::PostUpdateLayerAnim for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) { const auto& properties = mAnimations[animIdx]->GetEffect()->Properties(); for (size_t propIdx = properties.Length(); propIdx-- != 0; ) { nsCSSProperty prop = properties[propIdx].mProperty; if (nsCSSProps::PropHasFlags(prop, CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR) && !propsHandled.HasProperty(prop)) { propsHandled.AddProperty(prop); - nsChangeHint changeHint = css::CommonAnimationManager:: + nsChangeHint changeHint = CommonAnimationManager:: LayerAnimationRecordFor(prop)->mChangeHint; dom::Element* element = GetElementToRestyle(); if (element) { mManager->mPresContext->RestyleManager()-> PostRestyleEvent(element, nsRestyleHint(0), changeHint); } } } } } bool -AnimationCollection::HasAnimationOfProperty(nsCSSProperty aProperty) const +AnimationCollection::HasCurrentAnimationOfProperty(nsCSSProperty + aProperty) const { - for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) { - const KeyframeEffectReadOnly* effect = mAnimations[animIdx]->GetEffect(); - if (effect && effect->HasAnimationOfProperty(aProperty) && - !effect->IsFinishedTransition()) { + for (Animation* animation : mAnimations) { + if (animation->HasCurrentEffect() && + animation->GetEffect()->HasAnimationOfProperty(aProperty)) { return true; } } return false; } +/*static*/ nsString +AnimationCollection::PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType) +{ + switch (aPseudoType) { + case nsCSSPseudoElements::ePseudo_before: + return NS_LITERAL_STRING("::before"); + case nsCSSPseudoElements::ePseudo_after: + return NS_LITERAL_STRING("::after"); + default: + MOZ_ASSERT(aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement, + "Unexpected pseudo type"); + return EmptyString(); + } +} + mozilla::dom::Element* AnimationCollection::GetElementToRestyle() const { if (IsForElement()) { return mElement; } nsIFrame* primaryFrame = mElement->GetPrimaryFrame(); @@ -924,21 +929,28 @@ AnimationCollection::CanThrottleAnimatio if (!element) { return false; } nsIFrame* frame = nsLayoutUtils::GetStyleFrame(element); if (!frame) { return false; } - - const auto& info = css::CommonAnimationManager::sLayerAnimationInfo; + const auto& info = CommonAnimationManager::sLayerAnimationInfo; for (size_t i = 0; i < ArrayLength(info); i++) { auto record = info[i]; - if (!HasAnimationOfProperty(record.mProperty)) { + // We only need to worry about *current* animations here. + // - If we have a newly-finished animation, Animation::CanThrottle will + // detect that and force an unthrottled sample. + // - If we have a newly-idle animation, then whatever caused the animation + // to be idle will update the animation generation so we'll return false + // from the layer generation check below for any other running compositor + // animations (and if no other compositor animations exist we won't get + // this far). + if (!HasCurrentAnimationOfProperty(record.mProperty)) { continue; } Layer* layer = FrameLayerBuilder::GetDedicatedLayer( frame, record.mLayerType); if (!layer || mAnimationGeneration > layer->GetAnimationGeneration()) { return false; } @@ -992,9 +1004,29 @@ AnimationCollection::HasCurrentAnimation effect->HasAnimationOfProperties(aProperties, aPropertyCount)) { return true; } } return false; } +nsPresContext* +OwningElementRef::GetRenderedPresContext() const +{ + if (!mElement) { + return nullptr; + } + + nsIDocument* doc = mElement->GetComposedDoc(); + if (!doc) { + return nullptr; + } + + nsIPresShell* shell = doc->GetShell(); + if (!shell) { + return nullptr; + } + + return shell->GetPresContext(); +} + } // namespace mozilla
--- a/layout/style/AnimationCommon.h +++ b/layout/style/AnimationCommon.h @@ -8,16 +8,17 @@ #include "nsIStyleRuleProcessor.h" #include "nsIStyleRule.h" #include "nsRefreshDriver.h" #include "prclist.h" #include "nsChangeHint.h" #include "nsCSSProperty.h" #include "nsDisplayList.h" // For nsDisplayItem::Type +#include "mozilla/EventDispatcher.h" #include "mozilla/MemoryReporting.h" #include "mozilla/StyleAnimationValue.h" #include "mozilla/dom/Animation.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/Nullable.h" #include "nsStyleStruct.h" #include "mozilla/Attributes.h" #include "mozilla/Assertions.h" @@ -30,45 +31,40 @@ class nsIFrame; class nsPresContext; namespace mozilla { class RestyleTracker; struct AnimationCollection; -namespace css { - bool IsGeometricProperty(nsCSSProperty aProperty); class CommonAnimationManager : public nsIStyleRuleProcessor, public nsARefreshObserver { public: explicit CommonAnimationManager(nsPresContext *aPresContext); - // nsISupports - NS_DECL_ISUPPORTS - // nsIStyleRuleProcessor (parts) virtual nsRestyleHint HasStateDependentStyle(StateRuleProcessorData* aData) override; virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) override; virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override; virtual nsRestyleHint HasAttributeDependentStyle(AttributeRuleProcessorData* aData, RestyleHintData& aRestyleHintDataResult) override; virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) override; virtual void RulesMatching(ElementRuleProcessorData* aData) override; virtual void RulesMatching(PseudoElementRuleProcessorData* aData) override; virtual void RulesMatching(AnonBoxRuleProcessorData* aData) override; #ifdef MOZ_XUL virtual void RulesMatching(XULTreeRuleProcessorData* aData) override; #endif - virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) + virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_MUST_OVERRIDE override; - virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) + virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_MUST_OVERRIDE override; #ifdef DEBUG static void Initialize(); #endif // NOTE: This can return null after Disconnect(). nsPresContext* PresContext() const { return mPresContext; } @@ -76,17 +72,17 @@ public: /** * Notify the manager that the pres context is going away. */ void Disconnect(); // Tell the restyle tracker about all the styles that we're currently // animating, so that it can update the animation rule for these // elements. - void AddStyleUpdatesTo(mozilla::RestyleTracker& aTracker); + void AddStyleUpdatesTo(RestyleTracker& aTracker); AnimationCollection* GetAnimations(dom::Element *aElement, nsCSSPseudoElements::Type aPseudoType, bool aCreateIfNeeded); // Returns true if aContent or any of its ancestors has an animation // or transition. @@ -105,23 +101,23 @@ public: // has been updated. void NotifyCollectionUpdated(AnimationCollection& aCollection); enum FlushFlags { Can_Throttle, Cannot_Throttle }; - nsIStyleRule* GetAnimationRule(mozilla::dom::Element* aElement, + nsIStyleRule* GetAnimationRule(dom::Element* aElement, nsCSSPseudoElements::Type aPseudoType); static bool ExtractComputedValueForTransition( nsCSSProperty aProperty, nsStyleContext* aStyleContext, - mozilla::StyleAnimationValue& aComputedValue); + StyleAnimationValue& aComputedValue); // For CSS properties that may be animated on a separate layer, represents // a record of the corresponding layer type and change hint. struct LayerAnimationRecord { nsCSSProperty mProperty; nsDisplayItem::Type mLayerType; nsChangeHint mChangeHint; }; @@ -137,17 +133,17 @@ public: // on such properties. static const LayerAnimationRecord* LayerAnimationRecordFor(nsCSSProperty aProperty); protected: virtual ~CommonAnimationManager(); // For ElementCollectionRemoved - friend struct mozilla::AnimationCollection; + friend struct AnimationCollection; void AddElementCollection(AnimationCollection* aCollection); void ElementCollectionRemoved() { MaybeStartOrStopObservingRefreshDriver(); } void RemoveAllElementCollections(); // We should normally only call MaybeStartOrStopObservingRefreshDriver in // situations where we will also queue events since otherwise we may stop // getting refresh driver ticks before we queue the necessary events. @@ -198,63 +194,60 @@ public: NS_DECL_ISUPPORTS // nsIStyleRule implementation virtual void MapRuleInfoInto(nsRuleData* aRuleData) override; #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - void AddValue(nsCSSProperty aProperty, - mozilla::StyleAnimationValue &aStartValue) + void AddValue(nsCSSProperty aProperty, StyleAnimationValue &aStartValue) { PropertyValuePair v = { aProperty, aStartValue }; mPropertyValuePairs.AppendElement(v); } // Caller must fill in returned value. - mozilla::StyleAnimationValue* AddEmptyValue(nsCSSProperty aProperty) + StyleAnimationValue* AddEmptyValue(nsCSSProperty aProperty) { PropertyValuePair *p = mPropertyValuePairs.AppendElement(); p->mProperty = aProperty; return &p->mValue; } struct PropertyValuePair { nsCSSProperty mProperty; - mozilla::StyleAnimationValue mValue; + StyleAnimationValue mValue; }; void AddPropertiesToSet(nsCSSPropertySet& aSet) const { for (size_t i = 0, i_end = mPropertyValuePairs.Length(); i < i_end; ++i) { const PropertyValuePair &cv = mPropertyValuePairs[i]; aSet.AddProperty(cv.mProperty); } } private: ~AnimValuesStyleRule() {} InfallibleTArray<PropertyValuePair> mPropertyValuePairs; }; -} // namespace css - typedef InfallibleTArray<nsRefPtr<dom::Animation>> AnimationPtrArray; enum EnsureStyleRuleFlags { EnsureStyleRule_IsThrottled, EnsureStyleRule_IsNotThrottled }; struct AnimationCollection : public PRCList { AnimationCollection(dom::Element *aElement, nsIAtom *aElementProperty, - mozilla::css::CommonAnimationManager *aManager) + CommonAnimationManager *aManager) : mElement(aElement) , mElementProperty(aElementProperty) , mManager(aManager) , mAnimationGeneration(0) , mCheckGeneration(0) , mNeedsRefreshes(true) #ifdef DEBUG , mCalledPropertyDtor(false) @@ -280,19 +273,19 @@ struct AnimationCollection : public PRCL static void PropertyDtor(void *aObject, nsIAtom *aPropertyName, void *aPropertyValue, void *aData); void Tick(); void EnsureStyleRuleFor(TimeStamp aRefreshTime, EnsureStyleRuleFlags aFlags); - bool CanThrottleTransformChanges(mozilla::TimeStamp aTime); + bool CanThrottleTransformChanges(TimeStamp aTime); - bool CanThrottleAnimation(mozilla::TimeStamp aTime); + bool CanThrottleAnimation(TimeStamp aTime); enum CanAnimateFlags { // Testing for width, height, top, right, bottom, or left. CanAnimate_HasGeometricProperty = 1, // Allow the case where OMTA is allowed in general, but not for the // specified property. CanAnimate_AllowPartial = 2 }; @@ -322,17 +315,17 @@ public: // // Note that this does not test whether the element's layer uses // off-main-thread compositing, although it does check whether // off-main-thread compositing is enabled as a whole. bool CanPerformOnCompositorThread(CanAnimateFlags aFlags) const; void PostUpdateLayerAnimations(); - bool HasAnimationOfProperty(nsCSSProperty aProperty) const; + bool HasCurrentAnimationOfProperty(nsCSSProperty aProperty) const; bool IsForElement() const { // rather than for a pseudo-element return mElementProperty == nsGkAtoms::animationsProperty || mElementProperty == nsGkAtoms::transitionsProperty; } bool IsForBeforePseudo() const { return mElementProperty == nsGkAtoms::animationsOfBeforeProperty || @@ -351,46 +344,35 @@ public: } bool IsForAnimations() const { return mElementProperty == nsGkAtoms::animationsProperty || mElementProperty == nsGkAtoms::animationsOfBeforeProperty || mElementProperty == nsGkAtoms::animationsOfAfterProperty; } - nsString PseudoElement() const - { - if (IsForElement()) { - return EmptyString(); - } - if (IsForBeforePseudo()) { - return NS_LITERAL_STRING("::before"); - } - MOZ_ASSERT(IsForAfterPseudo(), - "::before & ::after should be the only pseudo-elements here"); - return NS_LITERAL_STRING("::after"); - } - nsCSSPseudoElements::Type PseudoElementType() const { if (IsForElement()) { return nsCSSPseudoElements::ePseudo_NotPseudoElement; } if (IsForBeforePseudo()) { return nsCSSPseudoElements::ePseudo_before; } MOZ_ASSERT(IsForAfterPseudo(), "::before & ::after should be the only pseudo-elements here"); return nsCSSPseudoElements::ePseudo_after; } - mozilla::dom::Element* GetElementToRestyle() const; + static nsString PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType); + + dom::Element* GetElementToRestyle() const; void PostRestyleForAnimation(nsPresContext *aPresContext) { - mozilla::dom::Element* element = GetElementToRestyle(); + dom::Element* element = GetElementToRestyle(); if (element) { nsRestyleHint hint = IsForTransitions() ? eRestyle_CSSTransitions : eRestyle_CSSAnimations; aPresContext->PresShell()->RestyleForAnimation(element, hint); } } void NotifyAnimationUpdated(); @@ -399,28 +381,28 @@ public: const nsIContent* aContent = nullptr); dom::Element *mElement; // the atom we use in mElement's prop table (must be a static atom, // i.e., in an atom list) nsIAtom *mElementProperty; - mozilla::css::CommonAnimationManager *mManager; + CommonAnimationManager *mManager; - mozilla::AnimationPtrArray mAnimations; + AnimationPtrArray mAnimations; // This style rule contains the style data for currently animating // values. It only matches when styling with animation. When we // style without animation, we need to not use it so that we can // detect any new changes; if necessary we restyle immediately // afterwards with animation. // NOTE: If we don't need to apply any styles, mStyleRule will be // null, but mStyleRuleRefreshTime will still be valid. - nsRefPtr<mozilla::css::AnimValuesStyleRule> mStyleRule; + nsRefPtr<AnimValuesStyleRule> mStyleRule; // RestyleManager keeps track of the number of animation // 'mini-flushes' (see nsTransitionManager::UpdateAllThrottledStyles()). // mAnimationGeneration is the sequence number of the last flush where a // transition/animation changed. We keep a similar count on the // corresponding layer so we can check that the layer is up to date with // the animation manager. uint64_t mAnimationGeneration; @@ -502,16 +484,90 @@ public: return mPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement || (mPseudoType == nsCSSPseudoElements::ePseudo_before && aOther.mPseudoType == nsCSSPseudoElements::ePseudo_after); } bool IsSet() const { return !!mElement; } + void GetElement(dom::Element*& aElement, + nsCSSPseudoElements::Type& aPseudoType) const { + aElement = mElement; + aPseudoType = mPseudoType; + } + + nsPresContext* GetRenderedPresContext() const; + private: dom::Element* MOZ_NON_OWNING_REF mElement; nsCSSPseudoElements::Type mPseudoType; }; +template <class EventInfo> +class DelayedEventDispatcher +{ +public: + void QueueEvent(EventInfo&& aEventInfo) + { + mPendingEvents.AppendElement(mozilla::Forward<EventInfo>(aEventInfo)); + } + + // Takes a reference to the owning manager's pres context so it can + // detect if the pres context is destroyed while dispatching one of + // the events. + void DispatchEvents(nsPresContext* const & aPresContext) + { + if (!aPresContext || mPendingEvents.IsEmpty()) { + return; + } + + EventArray events; + mPendingEvents.SwapElements(events); + // FIXME: Sort events here in timeline order, then document order + for (EventInfo& info : events) { + EventDispatcher::Dispatch(info.mElement, aPresContext, &info.mEvent); + + if (!aPresContext) { + break; + } + } + } + + void ClearEventQueue() { mPendingEvents.Clear(); } + bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); } + + // Methods for supporting cycle-collection + void Traverse(nsCycleCollectionTraversalCallback* aCallback, + const char* aName) + { + for (EventInfo& info : mPendingEvents) { + ImplCycleCollectionTraverse(*aCallback, info.mElement, aName); + } + } + void Unlink() { mPendingEvents.Clear(); } + +protected: + typedef nsTArray<EventInfo> EventArray; + + EventArray mPendingEvents; +}; + +template <class EventInfo> +inline void +ImplCycleCollectionUnlink(DelayedEventDispatcher<EventInfo>& aField) +{ + aField.Unlink(); +} + +template <class EventInfo> +inline void +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, + DelayedEventDispatcher<EventInfo>& aField, + const char* aName, + uint32_t aFlags = 0) +{ + aField.Traverse(&aCallback, aName); +} + } // namespace mozilla #endif /* !defined(mozilla_css_AnimationCommon_h) */
--- a/layout/style/nsAnimationManager.cpp +++ b/layout/style/nsAnimationManager.cpp @@ -2,17 +2,16 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsAnimationManager.h" #include "nsTransitionManager.h" #include "mozilla/dom/CSSAnimationBinding.h" -#include "mozilla/EventDispatcher.h" #include "mozilla/MemoryReporting.h" #include "mozilla/StyleAnimationValue.h" #include "mozilla/dom/DocumentTimeline.h" #include "mozilla/dom/KeyframeEffect.h" #include "nsPresContext.h" #include "nsStyleSet.h" #include "nsStyleChangeList.h" @@ -26,16 +25,18 @@ using namespace mozilla; using namespace mozilla::css; using mozilla::dom::Animation; using mozilla::dom::AnimationPlayState; using mozilla::dom::KeyframeEffectReadOnly; using mozilla::dom::CSSAnimation; +////////////////////////// CSSAnimation //////////////////////////// + JSObject* CSSAnimation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return dom::CSSAnimationBinding::Wrap(aCx, this, aGivenProto); } mozilla::dom::Promise* CSSAnimation::GetReady(ErrorResult& aRv) @@ -108,16 +109,23 @@ CSSAnimation::PauseFromStyle() // and then updated animation-play-state. It's an unusual case and there's // no obvious way to pass on the exception information so we just silently // fail for now. if (rv.Failed()) { NS_WARNING("Unexpected exception pausing animation - silently failing"); } } +void +CSSAnimation::Tick() +{ + Animation::Tick(); + QueueEvents(); +} + bool CSSAnimation::HasLowerCompositeOrderThan(const Animation& aOther) const { // 0. Object-equality case if (&aOther == this) { return false; } @@ -153,22 +161,50 @@ CSSAnimation::HasLowerCompositeOrderThan return mOwningElement.LessThan(otherAnimation->OwningElement()); } // 4. (Same element and pseudo): Sort by position in animation-name return mSequenceNum < otherAnimation->mSequenceNum; } void -CSSAnimation::QueueEvents(EventArray& aEventsToDispatch) +CSSAnimation::QueueEvents() { if (!mEffect) { return; } + // CSS animations dispatch events at their owning element. This allows + // script to repurpose a CSS animation to target a different element, + // to use a group effect (which has no obvious "target element"), or + // to remove the animation effect altogether whilst still getting + // animation events. + // + // It does mean, however, that for a CSS animation that has no owning + // element (e.g. it was created using the CSSAnimation constructor or + // disassociated from CSS) no events are fired. If it becomes desirable + // for these animations to still fire events we should spec the concept + // of the "original owning element" or "event target" and allow script + // to set it when creating a CSSAnimation object. + if (!mOwningElement.IsSet()) { + return; + } + + dom::Element* owningElement; + nsCSSPseudoElements::Type owningPseudoType; + mOwningElement.GetElement(owningElement, owningPseudoType); + MOZ_ASSERT(owningElement, "Owning element should be set"); + + // Get the nsAnimationManager so we can queue events on it + nsPresContext* presContext = mOwningElement.GetRenderedPresContext(); + if (!presContext) { + return; + } + nsAnimationManager* manager = presContext->AnimationManager(); + ComputedTiming computedTiming = mEffect->GetComputedTiming(); if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Null) { return; // do nothing } // Note that script can change the start time, so we have to handle moving // backwards through the animation as well as forwards. An 'animationstart' @@ -195,38 +231,33 @@ CSSAnimation::QueueEvents(EventArray& aE if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Before) { mPreviousPhaseOrIteration = PREVIOUS_PHASE_BEFORE; } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Active) { mPreviousPhaseOrIteration = computedTiming.mCurrentIteration; } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase_After) { mPreviousPhaseOrIteration = PREVIOUS_PHASE_AFTER; } - dom::Element* target; - nsCSSPseudoElements::Type targetPseudoType; - mEffect->GetTarget(target, targetPseudoType); - uint32_t message; if (!wasActive && isActive) { message = NS_ANIMATION_START; } else if (wasActive && !isActive) { message = NS_ANIMATION_END; } else if (wasActive && isActive && !isSameIteration) { message = NS_ANIMATION_ITERATION; } else if (skippedActivePhase) { // First notifying for start of 0th iteration by appending an // 'animationstart': StickyTimeDuration elapsedTime = std::min(StickyTimeDuration(mEffect->InitialAdvance()), computedTiming.mActiveDuration); - AnimationEventInfo ei(target, mAnimationName, NS_ANIMATION_START, - elapsedTime, - PseudoTypeAsString(targetPseudoType)); - aEventsToDispatch.AppendElement(ei); + manager->QueueEvent( + AnimationEventInfo(owningElement, mAnimationName, NS_ANIMATION_START, + elapsedTime, owningPseudoType)); // Then have the shared code below append an 'animationend': message = NS_ANIMATION_END; } else { return; // No events need to be sent } StickyTimeDuration elapsedTime; @@ -236,64 +267,43 @@ CSSAnimation::QueueEvents(EventArray& aE computedTiming.mCurrentIteration; elapsedTime = StickyTimeDuration(std::max(iterationStart, mEffect->InitialAdvance())); } else { MOZ_ASSERT(message == NS_ANIMATION_END); elapsedTime = computedTiming.mActiveDuration; } - AnimationEventInfo ei(target, mAnimationName, message, elapsedTime, - PseudoTypeAsString(targetPseudoType)); - aEventsToDispatch.AppendElement(ei); + manager->QueueEvent( + AnimationEventInfo(owningElement, mAnimationName, message, elapsedTime, + owningPseudoType)); } CommonAnimationManager* CSSAnimation::GetAnimationManager() const { nsPresContext* context = GetPresContext(); if (!context) { return nullptr; } return context->AnimationManager(); } -/* static */ nsString -CSSAnimation::PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType) -{ - switch (aPseudoType) { - case nsCSSPseudoElements::ePseudo_before: - return NS_LITERAL_STRING("::before"); - case nsCSSPseudoElements::ePseudo_after: - return NS_LITERAL_STRING("::after"); - default: - return EmptyString(); - } -} +////////////////////////// nsAnimationManager //////////////////////////// + +NS_IMPL_CYCLE_COLLECTION(nsAnimationManager, mEventDispatcher) -void -nsAnimationManager::UpdateStyleAndEvents(AnimationCollection* aCollection, - TimeStamp aRefreshTime, - EnsureStyleRuleFlags aFlags) -{ - aCollection->EnsureStyleRuleFor(aRefreshTime, aFlags); - QueueEvents(aCollection, mPendingEvents); -} +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnimationManager) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAnimationManager) -void -nsAnimationManager::QueueEvents(AnimationCollection* aCollection, - EventArray& aEventsToDispatch) -{ - for (size_t animIdx = aCollection->mAnimations.Length(); animIdx-- != 0; ) { - CSSAnimation* anim = aCollection->mAnimations[animIdx]->AsCSSAnimation(); - MOZ_ASSERT(anim, "Expected a collection of CSS Animations"); - anim->QueueEvents(aEventsToDispatch); - } -} +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAnimationManager) + NS_INTERFACE_MAP_ENTRY(nsIStyleRuleProcessor) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRuleProcessor) +NS_INTERFACE_MAP_END void nsAnimationManager::MaybeUpdateCascadeResults(AnimationCollection* aCollection) { for (size_t animIdx = aCollection->mAnimations.Length(); animIdx-- != 0; ) { CSSAnimation* anim = aCollection->mAnimations[animIdx]->AsCSSAnimation(); if (anim->IsInEffect() != anim->mInEffectForCascadeResults) { // Update our own cascade results. @@ -328,17 +338,17 @@ nsAnimationManager::MaybeUpdateCascadeRe /* virtual */ size_t nsAnimationManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf); // Measurement of the following members may be added later if DMD finds it is // worthwhile: - // - mPendingEvents + // - mEventDispatcher } /* virtual */ size_t nsAnimationManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); } @@ -498,23 +508,22 @@ nsAnimationManager::CheckAnimationRule(n // Cancel removed animations for (size_t newAnimIdx = newAnimations.Length(); newAnimIdx-- != 0; ) { newAnimations[newAnimIdx]->CancelFromStyle(); } UpdateCascadeResults(aStyleContext, collection); TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh(); - UpdateStyleAndEvents(collection, refreshTime, - EnsureStyleRule_IsNotThrottled); - // We don't actually dispatch the mPendingEvents now. We'll either + collection->EnsureStyleRuleFor(refreshTime, EnsureStyleRule_IsNotThrottled); + // We don't actually dispatch the pending events now. We'll either // dispatch them the next time we get a refresh driver notification // or the next time somebody calls // nsPresShell::FlushPendingNotifications. - if (!mPendingEvents.IsEmpty()) { + if (mEventDispatcher.HasQueuedEvents()) { mPresContext->Document()->SetNeedStyleFlush(); } return GetAnimationRule(aElement, aStyleContext->GetPseudoType()); } struct KeyframeData { float mKey; @@ -962,42 +971,25 @@ nsAnimationManager::FlushAnimations(Flus nsAutoAnimationMutationBatch mb(collection->mElement); collection->Tick(); bool canThrottleTick = aFlags == Can_Throttle && collection->CanPerformOnCompositorThread( AnimationCollection::CanAnimateFlags(0)) && collection->CanThrottleAnimation(now); - nsRefPtr<css::AnimValuesStyleRule> oldStyleRule = collection->mStyleRule; - UpdateStyleAndEvents(collection, now, canThrottleTick - ? EnsureStyleRule_IsThrottled - : EnsureStyleRule_IsNotThrottled); + nsRefPtr<AnimValuesStyleRule> oldStyleRule = collection->mStyleRule; + collection->EnsureStyleRuleFor(now, canThrottleTick + ? EnsureStyleRule_IsThrottled + : EnsureStyleRule_IsNotThrottled); if (oldStyleRule != collection->mStyleRule) { collection->PostRestyleForAnimation(mPresContext); } else { didThrottle = true; } } if (didThrottle) { mPresContext->Document()->SetNeedStyleFlush(); } MaybeStartOrStopObservingRefreshDriver(); - - DispatchEvents(); // may destroy us } - -void -nsAnimationManager::DoDispatchEvents() -{ - EventArray events; - mPendingEvents.SwapElements(events); - for (uint32_t i = 0, i_end = events.Length(); i < i_end; ++i) { - AnimationEventInfo &info = events[i]; - EventDispatcher::Dispatch(info.mElement, mPresContext, &info.mEvent); - - if (!mPresContext) { - break; - } - } -}
--- a/layout/style/nsAnimationManager.h +++ b/layout/style/nsAnimationManager.h @@ -27,36 +27,34 @@ class Promise; struct AnimationEventInfo { nsRefPtr<mozilla::dom::Element> mElement; mozilla::InternalAnimationEvent mEvent; AnimationEventInfo(mozilla::dom::Element *aElement, const nsSubstring& aAnimationName, uint32_t aMessage, const mozilla::StickyTimeDuration& aElapsedTime, - const nsAString& aPseudoElement) + nsCSSPseudoElements::Type aPseudoType) : mElement(aElement), mEvent(true, aMessage) { // XXX Looks like nobody initialize WidgetEvent::time mEvent.animationName = aAnimationName; mEvent.elapsedTime = aElapsedTime.ToSeconds(); - mEvent.pseudoElement = aPseudoElement; + mEvent.pseudoElement = AnimationCollection::PseudoTypeAsString(aPseudoType); } // InternalAnimationEvent doesn't support copy-construction, so we need // to ourselves in order to work with nsTArray AnimationEventInfo(const AnimationEventInfo &aOther) : mElement(aOther.mElement), mEvent(true, aOther.mEvent.message) { mEvent.AssignAnimationEventData(aOther.mEvent, false); } }; -typedef InfallibleTArray<AnimationEventInfo> EventArray; - namespace dom { class CSSAnimation final : public Animation { public: explicit CSSAnimation(nsIGlobalObject* aGlobal, const nsSubstring& aAnimationName) : dom::Animation(aGlobal) @@ -96,16 +94,18 @@ public: void PauseFromStyle(); void CancelFromStyle() override { mOwningElement = OwningElementRef(); Animation::CancelFromStyle(); MOZ_ASSERT(mSequenceNum == kUnsequenced); } + void Tick() override; + bool IsStylePaused() const { return mIsStylePaused; } bool HasLowerCompositeOrderThan(const Animation& aOther) const override; bool IsUsingCustomCompositeOrder() const override { return mOwningElement.IsSet(); } @@ -116,18 +116,16 @@ public: } void CopyAnimationIndex(const CSSAnimation& aOther) { MOZ_ASSERT(IsUsingCustomCompositeOrder() && aOther.IsUsingCustomCompositeOrder()); mSequenceNum = aOther.mSequenceNum; } - void QueueEvents(EventArray& aEventsToDispatch); - // Returns the element or pseudo-element whose animation-name property // this CSSAnimation corresponds to (if any). This is used for determining // the relative composite order of animations generated from CSS markup. // // Typically this will be the same as the target element of the keyframe // effect associated with this animation. However, it can differ in the // following circumstances: // @@ -158,19 +156,19 @@ public: bool mInEffectForCascadeResults; protected: virtual ~CSSAnimation() { MOZ_ASSERT(!mOwningElement.IsSet(), "Owning element should be cleared " "before a CSS animation is destroyed"); } - virtual css::CommonAnimationManager* GetAnimationManager() const override; + virtual CommonAnimationManager* GetAnimationManager() const override; - static nsString PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType); + void QueueEvents(); nsString mAnimationName; // The (pseudo-)element whose computed animation-name refers to this // animation (if any). OwningElementRef mOwningElement; // When combining animation-play-state with play() / pause() the following @@ -233,29 +231,26 @@ protected: // whose start we last notified on. uint64_t mPreviousPhaseOrIteration; }; } /* namespace dom */ } /* namespace mozilla */ class nsAnimationManager final - : public mozilla::css::CommonAnimationManager + : public mozilla::CommonAnimationManager { public: explicit nsAnimationManager(nsPresContext *aPresContext) - : mozilla::css::CommonAnimationManager(aPresContext) + : mozilla::CommonAnimationManager(aPresContext) { } - void UpdateStyleAndEvents(mozilla::AnimationCollection* aEA, - mozilla::TimeStamp aRefreshTime, - mozilla::EnsureStyleRuleFlags aFlags); - void QueueEvents(mozilla::AnimationCollection* aEA, - mozilla::EventArray &aEventsToDispatch); + NS_DECL_CYCLE_COLLECTION_CLASS(nsAnimationManager) + NS_DECL_CYCLE_COLLECTING_ISUPPORTS void MaybeUpdateCascadeResults(mozilla::AnimationCollection* aCollection); // nsIStyleRuleProcessor (parts) virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_MUST_OVERRIDE override; virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_MUST_OVERRIDE override; @@ -275,59 +270,63 @@ public: * * aStyleContext may be a style context for aElement or for its * :before or :after pseudo-element. */ nsIStyleRule* CheckAnimationRule(nsStyleContext* aStyleContext, mozilla::dom::Element* aElement); /** + * Add a pending event. + */ + void QueueEvent(mozilla::AnimationEventInfo&& aEventInfo) + { + mEventDispatcher.QueueEvent( + mozilla::Forward<mozilla::AnimationEventInfo>(aEventInfo)); + } + + /** * Dispatch any pending events. We accumulate animationend and * animationiteration events only during refresh driver notifications * (and dispatch them at the end of such notifications), but we * accumulate animationstart events at other points when style * contexts are created. */ - void DispatchEvents() { - // Fast-path the common case: no events - if (!mPendingEvents.IsEmpty()) { - DoDispatchEvents(); - } - } + void DispatchEvents() { mEventDispatcher.DispatchEvents(mPresContext); } + void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); } protected: + virtual ~nsAnimationManager() {} + virtual nsIAtom* GetAnimationsAtom() override { return nsGkAtoms::animationsProperty; } virtual nsIAtom* GetAnimationsBeforeAtom() override { return nsGkAtoms::animationsOfBeforeProperty; } virtual nsIAtom* GetAnimationsAfterAtom() override { return nsGkAtoms::animationsOfAfterProperty; } virtual bool IsAnimationManager() override { return true; } + mozilla::DelayedEventDispatcher<mozilla::AnimationEventInfo> mEventDispatcher; + private: void BuildAnimations(nsStyleContext* aStyleContext, mozilla::dom::Element* aTarget, mozilla::dom::AnimationTimeline* aTimeline, mozilla::AnimationPtrArray& aAnimations); bool BuildSegment(InfallibleTArray<mozilla::AnimationPropertySegment>& aSegments, nsCSSProperty aProperty, const mozilla::StyleAnimation& aAnimation, float aFromKey, nsStyleContext* aFromContext, mozilla::css::Declaration* aFromDeclaration, float aToKey, nsStyleContext* aToContext); static void UpdateCascadeResults(nsStyleContext* aStyleContext, mozilla::AnimationCollection* aElementAnimations); - - // The guts of DispatchEvents - void DoDispatchEvents(); - - mozilla::EventArray mPendingEvents; }; #endif /* !defined(nsAnimationManager_h_) */
--- a/layout/style/nsTransitionManager.cpp +++ b/layout/style/nsTransitionManager.cpp @@ -7,25 +7,23 @@ /* Code to start and animate CSS transitions. */ #include "nsTransitionManager.h" #include "nsAnimationManager.h" #include "mozilla/dom/CSSTransitionBinding.h" #include "nsIContent.h" #include "nsStyleContext.h" -#include "nsCSSProps.h" #include "mozilla/MemoryReporting.h" #include "mozilla/TimeStamp.h" #include "nsRefreshDriver.h" #include "nsRuleProcessorData.h" #include "nsRuleWalker.h" #include "nsCSSPropertySet.h" #include "mozilla/EventDispatcher.h" -#include "mozilla/ContentEvents.h" #include "mozilla/StyleAnimationValue.h" #include "mozilla/dom/DocumentTimeline.h" #include "mozilla/dom/Element.h" #include "nsIFrame.h" #include "Layers.h" #include "FrameLayerBuilder.h" #include "nsCSSProps.h" #include "nsDisplayList.h" @@ -40,21 +38,16 @@ using mozilla::dom::Animation; using mozilla::dom::KeyframeEffectReadOnly; using namespace mozilla; using namespace mozilla::css; double ElementPropertyTransition::CurrentValuePortion() const { - // It would be easy enough to handle finished transitions by using a - // progress of 1 but currently we should not be called for finished - // transitions. - MOZ_ASSERT(!IsFinishedTransition(), - "Getting the value portion of a finished transition"); MOZ_ASSERT(!GetLocalTime().IsNull(), "Getting the value portion of an animation that's not being " "sampled"); // Transitions use a fill mode of 'backwards' so GetComputedTiming will // never return a null time progress due to being *before* the animation // interval. However, it might be possible that we're behind on flushing // causing us to get called *after* the animation interval. So, just in @@ -69,19 +62,17 @@ ElementPropertyTransition::CurrentValueP MOZ_ASSERT(mProperties.Length() == 1, "Should have one animation property for a transition"); MOZ_ASSERT(mProperties[0].mSegments.Length() == 1, "Animation property should have one segment for a transition"); return mProperties[0].mSegments[0].mTimingFunction .GetValue(computedTiming.mProgress); } -/***************************************************************************** - * CSSTransition * - *****************************************************************************/ +////////////////////////// CSSTransition //////////////////////////// JSObject* CSSTransition::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return dom::CSSTransitionBinding::Wrap(aCx, this, aGivenProto); } void @@ -116,16 +107,52 @@ CSSTransition::GetAnimationManager() con nsPresContext* context = GetPresContext(); if (!context) { return nullptr; } return context->TransitionManager(); } +void +CSSTransition::QueueEvents() +{ + AnimationPlayState playState = PlayState(); + bool newlyFinished = !mWasFinishedOnLastTick && + playState == AnimationPlayState::Finished; + mWasFinishedOnLastTick = playState == AnimationPlayState::Finished; + + if (!newlyFinished || !mEffect || !mOwningElement.IsSet()) { + return; + } + + dom::Element* owningElement; + nsCSSPseudoElements::Type owningPseudoType; + mOwningElement.GetElement(owningElement, owningPseudoType); + MOZ_ASSERT(owningElement, "Owning element should be set"); + + nsPresContext* presContext = mOwningElement.GetRenderedPresContext(); + if (!presContext) { + return; + } + nsTransitionManager* manager = presContext->TransitionManager(); + + manager->QueueEvent( + TransitionEventInfo(owningElement, TransitionProperty(), + mEffect->Timing().mIterationDuration, + owningPseudoType)); +} + +void +CSSTransition::Tick() +{ + Animation::Tick(); + QueueEvents(); +} + nsCSSProperty CSSTransition::TransitionProperty() const { // FIXME: Once we support replacing/removing the effect (bug 1049975) // we'll need to store the original transition property so we keep // returning the same value in that case. dom::KeyframeEffectReadOnly* effect = GetEffect(); MOZ_ASSERT(effect && effect->AsTransition(), @@ -171,19 +198,27 @@ CSSTransition::HasLowerCompositeOrderTha return mSequenceNum < otherTransition->mSequenceNum; } // 5. (Same transition generation): Sort by transition property return nsCSSProps::GetStringValue(TransitionProperty()) < nsCSSProps::GetStringValue(otherTransition->TransitionProperty()); } -/***************************************************************************** - * nsTransitionManager * - *****************************************************************************/ +////////////////////////// nsTransitionManager //////////////////////////// + +NS_IMPL_CYCLE_COLLECTION(nsTransitionManager, mEventDispatcher) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTransitionManager) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTransitionManager) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTransitionManager) + NS_INTERFACE_MAP_ENTRY(nsIStyleRuleProcessor) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END void nsTransitionManager::StyleContextChanged(dom::Element *aElement, nsStyleContext *aOldStyleContext, nsRefPtr<nsStyleContext>* aNewStyleContext /* inout */) { nsStyleContext* newStyleContext = *aNewStyleContext; @@ -401,17 +436,17 @@ nsTransitionManager::StyleContextChanged // did not start a new transition (because delay and // duration are both zero, or because the new value is not // interpolable); a new transition would have segment.mToValue // matching currentValue !ExtractComputedValueForTransition(prop.mProperty, afterChangeStyle, currentValue) || currentValue != segment.mToValue) { // stop the transition - if (!anim->GetEffect()->IsFinishedTransition()) { + if (anim->HasCurrentEffect()) { collection->UpdateAnimationGeneration(mPresContext); } anim->CancelFromStyle(); animations.RemoveElementAt(i); } } while (i != 0); if (animations.IsEmpty()) { @@ -530,17 +565,17 @@ nsTransitionManager::ConsiderStartingTra "Should have one animation property segment for a transition"); if (haveCurrentTransition && haveValues && oldPT->Properties()[0].mSegments[0].mToValue == endValue) { // GetAnimationRule already called RestyleForAnimation. return; } if (!shouldAnimate) { - if (haveCurrentTransition && !oldPT->IsFinishedTransition()) { + if (haveCurrentTransition) { // We're in the middle of a transition, and just got a non-transition // style change to something that we can't animate. This might happen // because we got a non-transition style change changing to the current // in-progress value (which is particularly easy to cause when we're // currently in the 'transition-delay'). It also might happen because we // just got a style change to a value that can't be interpolated. AnimationPtrArray& animations = aElementTransitions->mAnimations; animations[currentIndex]->CancelFromStyle(); @@ -567,17 +602,17 @@ nsTransitionManager::ConsiderStartingTra } StyleAnimationValue startForReversingTest = startValue; double reversePortion = 1.0; // If the new transition reverses an existing one, we'll need to // handle the timing differently. if (haveCurrentTransition && - !oldPT->IsFinishedTransition() && + aElementTransitions->mAnimations[currentIndex]->HasCurrentEffect() && oldPT->mStartForReversingTest == endValue) { // Compute the appropriate negative transition-delay such that right // now we'd end up at the current position. double valuePortion = oldPT->CurrentValuePortion() * oldPT->mReversePortion + (1.0 - oldPT->mReversePortion); // A timing function with negative y1 (or y2!) might make // valuePortion negative. In this case, we still want to apply our @@ -700,25 +735,25 @@ nsTransitionManager::PruneCompletedTrans // FIXME (bug 1158431): Really, we should also cancel running // transitions whose destination doesn't match as well. AnimationPtrArray& animations = collection->mAnimations; size_t i = animations.Length(); MOZ_ASSERT(i != 0, "empty transitions list?"); do { --i; Animation* anim = animations[i]; - dom::KeyframeEffectReadOnly* effect = anim->GetEffect(); - if (!effect->IsFinishedTransition()) { + if (anim->HasCurrentEffect()) { continue; } - MOZ_ASSERT(effect && effect->Properties().Length() == 1, + dom::KeyframeEffectReadOnly* effect = anim->GetEffect(); + MOZ_ASSERT(effect->Properties().Length() == 1, "Should have one animation property for a transition"); - MOZ_ASSERT(effect && effect->Properties()[0].mSegments.Length() == 1, + MOZ_ASSERT(effect->Properties()[0].mSegments.Length() == 1, "Animation property should have one segment for a transition"); const AnimationProperty& prop = effect->Properties()[0]; const AnimationPropertySegment& segment = prop.mSegments[0]; // Since effect is a finished transition, we know it didn't // influence style. StyleAnimationValue currentValue; if (!ExtractComputedValueForTransition(prop.mProperty, aNewStyleContext, @@ -836,50 +871,28 @@ nsTransitionManager::UpdateCascadeResult /* * nsIStyleRuleProcessor implementation */ /* virtual */ size_t nsTransitionManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mEventDispatcher } /* virtual */ size_t nsTransitionManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); } -struct TransitionEventInfo { - nsCOMPtr<nsIContent> mElement; - InternalTransitionEvent mEvent; - - TransitionEventInfo(nsIContent *aElement, nsCSSProperty aProperty, - TimeDuration aDuration, const nsAString& aPseudoElement) - : mElement(aElement) - , mEvent(true, NS_TRANSITION_END) - { - // XXX Looks like nobody initialize WidgetEvent::time - mEvent.propertyName = - NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty)); - mEvent.elapsedTime = aDuration.ToSeconds(); - mEvent.pseudoElement = aPseudoElement; - } - - // InternalTransitionEvent doesn't support copy-construction, so we need - // to ourselves in order to work with nsTArray - TransitionEventInfo(const TransitionEventInfo &aOther) - : mElement(aOther.mElement) - , mEvent(true, NS_TRANSITION_END) - { - mEvent.AssignTransitionEventData(aOther.mEvent, false); - } -}; - /* virtual */ void nsTransitionManager::WillRefresh(mozilla::TimeStamp aTime) { MOZ_ASSERT(mPresContext, "refresh driver should not notify additional observers " "after pres context has been destroyed"); if (!mPresContext->GetPresShell()) { // Someone might be keeping mPresContext alive past the point @@ -896,17 +909,16 @@ nsTransitionManager::WillRefresh(mozilla void nsTransitionManager::FlushTransitions(FlushFlags aFlags) { if (PR_CLIST_IS_EMPTY(&mElementCollections)) { // no transitions, leave early return; } - nsTArray<TransitionEventInfo> events; TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh(); bool didThrottle = false; // Trim transitions that have completed, post restyle events for frames that // are still transitioning, and start transitions with delays. { PRCList *next = PR_LIST_HEAD(&mElementCollections); while (next != &mElementCollections) { AnimationCollection* collection = static_cast<AnimationCollection*>(next); @@ -920,68 +932,34 @@ nsTransitionManager::FlushTransitions(Fl AnimationCollection::CanAnimateFlags(0)) && collection->CanThrottleAnimation(now); MOZ_ASSERT(collection->mElement->GetCrossShadowCurrentDoc() == mPresContext->Document(), "Element::UnbindFromTree should have " "destroyed the element transitions object"); - size_t i = collection->mAnimations.Length(); - MOZ_ASSERT(i != 0, "empty transitions list?"); - bool transitionStartedOrEnded = false; - do { - --i; - Animation* anim = collection->mAnimations[i]; - if (!anim->GetEffect()->IsFinishedTransition()) { - MOZ_ASSERT(anim->GetEffect(), "Transitions should have an effect"); - ComputedTiming computedTiming = - anim->GetEffect()->GetComputedTiming(); - if (computedTiming.mPhase == ComputedTiming::AnimationPhase_After) { - nsCSSProperty prop = - anim->GetEffect()->AsTransition()->TransitionProperty(); - TimeDuration duration = - anim->GetEffect()->Timing().mIterationDuration; - events.AppendElement( - TransitionEventInfo(collection->mElement, prop, - duration, - collection->PseudoElement())); - - // Leave this transition in the list for one more refresh - // cycle, since we haven't yet processed its style change, and - // if we also have (already, or will have from processing - // transitionend events or other refresh driver notifications) - // a non-animation style change that would affect it, we need - // to know not to start a new transition for the transition - // from the almost-completed value to the final value. - anim->GetEffect()->SetIsFinishedTransition(true); - collection->UpdateAnimationGeneration(mPresContext); - transitionStartedOrEnded = true; - } else if ((computedTiming.mPhase == - ComputedTiming::AnimationPhase_Active) && - canThrottleTick && - !anim->IsRunningOnCompositor()) { - // Start a transition with a delay where we should start the - // transition proper. - collection->UpdateAnimationGeneration(mPresContext); - transitionStartedOrEnded = true; - } - } - } while (i != 0); + // Look for any transitions that can't be throttled because they + // may be starting or stopping + for (auto iter = collection->mAnimations.cbegin(); + canThrottleTick && iter != collection->mAnimations.cend(); + ++iter) { + canThrottleTick &= (*iter)->CanThrottle(); + } // We need to restyle even if the transition rule no longer // applies (in which case we just made it not apply). MOZ_ASSERT(collection->mElementProperty == nsGkAtoms::transitionsProperty || collection->mElementProperty == nsGkAtoms::transitionsOfBeforeProperty || collection->mElementProperty == nsGkAtoms::transitionsOfAfterProperty, "Unexpected element property; might restyle too much"); - if (!canThrottleTick || transitionStartedOrEnded) { + if (!canThrottleTick) { collection->PostRestyleForAnimation(mPresContext); } else { didThrottle = true; } if (collection->mAnimations.IsEmpty()) { collection->Destroy(); // |collection| is now a dangling pointer! @@ -990,18 +968,9 @@ nsTransitionManager::FlushTransitions(Fl } } if (didThrottle) { mPresContext->Document()->SetNeedStyleFlush(); } MaybeStartOrStopObservingRefreshDriver(); - - for (uint32_t i = 0, i_end = events.Length(); i < i_end; ++i) { - TransitionEventInfo &info = events[i]; - EventDispatcher::Dispatch(info.mElement, mPresContext, &info.mEvent); - - if (!mPresContext) { - break; - } - } }
--- a/layout/style/nsTransitionManager.h +++ b/layout/style/nsTransitionManager.h @@ -4,20 +4,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* Code to start and animate CSS transitions. */ #ifndef nsTransitionManager_h_ #define nsTransitionManager_h_ #include "mozilla/Attributes.h" +#include "mozilla/ContentEvents.h" #include "mozilla/MemoryReporting.h" #include "mozilla/dom/Animation.h" #include "mozilla/dom/KeyframeEffect.h" #include "AnimationCommon.h" +#include "nsCSSProps.h" #include "nsCSSPseudoElements.h" class nsIGlobalObject; class nsStyleContext; class nsPresContext; class nsCSSPropertySet; namespace mozilla { @@ -77,16 +79,17 @@ struct ElementPropertyTransition : publi namespace dom { class CSSTransition final : public Animation { public: explicit CSSTransition(nsIGlobalObject* aGlobal) : dom::Animation(aGlobal) + , mWasFinishedOnLastTick(false) { } JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; CSSTransition* AsCSSTransition() override { return this; } const CSSTransition* AsCSSTransition() const override { return this; } @@ -110,16 +113,18 @@ public: void CancelFromStyle() override { mOwningElement = OwningElementRef(); Animation::CancelFromStyle(); MOZ_ASSERT(mSequenceNum == kUnsequenced); } + void Tick() override; + nsCSSProperty TransitionProperty() const; bool HasLowerCompositeOrderThan(const Animation& aOther) const override; bool IsUsingCustomCompositeOrder() const override { return mOwningElement.IsSet(); } @@ -157,36 +162,71 @@ public: protected: virtual ~CSSTransition() { MOZ_ASSERT(!mOwningElement.IsSet(), "Owning element should be cleared " "before a CSS transition is destroyed"); } - virtual css::CommonAnimationManager* GetAnimationManager() const override; + virtual CommonAnimationManager* GetAnimationManager() const override; + + void QueueEvents(); // The (pseudo-)element whose computed transition-property refers to this // transition (if any). OwningElementRef mOwningElement; + + bool mWasFinishedOnLastTick; }; } // namespace dom + +struct TransitionEventInfo { + nsCOMPtr<nsIContent> mElement; + InternalTransitionEvent mEvent; + + TransitionEventInfo(nsIContent *aElement, nsCSSProperty aProperty, + TimeDuration aDuration, + nsCSSPseudoElements::Type aPseudoType) + : mElement(aElement) + , mEvent(true, NS_TRANSITION_END) + { + // XXX Looks like nobody initialize WidgetEvent::time + mEvent.propertyName = + NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty)); + mEvent.elapsedTime = aDuration.ToSeconds(); + mEvent.pseudoElement = AnimationCollection::PseudoTypeAsString(aPseudoType); + } + + // InternalTransitionEvent doesn't support copy-construction, so we need + // to ourselves in order to work with nsTArray + TransitionEventInfo(const TransitionEventInfo &aOther) + : mElement(aOther.mElement) + , mEvent(true, NS_TRANSITION_END) + { + mEvent.AssignTransitionEventData(aOther.mEvent, false); + } +}; + } // namespace mozilla class nsTransitionManager final - : public mozilla::css::CommonAnimationManager + : public mozilla::CommonAnimationManager { public: explicit nsTransitionManager(nsPresContext *aPresContext) - : mozilla::css::CommonAnimationManager(aPresContext) + : mozilla::CommonAnimationManager(aPresContext) , mInAnimationOnlyStyleUpdate(false) { } + NS_DECL_CYCLE_COLLECTION_CLASS(nsTransitionManager) + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + typedef mozilla::AnimationCollection AnimationCollection; /** * StyleContextChanged * * To be called from nsFrameManager::ReResolveStyleContext when the * style of an element has changed, to initiate transitions from * that style change. For style contexts with :before and :after @@ -234,17 +274,28 @@ public: virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_MUST_OVERRIDE override; // nsARefreshObserver virtual void WillRefresh(mozilla::TimeStamp aTime) override; void FlushTransitions(FlushFlags aFlags); + void QueueEvent(mozilla::TransitionEventInfo&& aEventInfo) + { + mEventDispatcher.QueueEvent( + mozilla::Forward<mozilla::TransitionEventInfo>(aEventInfo)); + } + + void DispatchEvents() { mEventDispatcher.DispatchEvents(mPresContext); } + void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); } + protected: + virtual ~nsTransitionManager() {} + virtual nsIAtom* GetAnimationsAtom() override { return nsGkAtoms::transitionsProperty; } virtual nsIAtom* GetAnimationsBeforeAtom() override { return nsGkAtoms::transitionsOfBeforeProperty; } virtual nsIAtom* GetAnimationsAfterAtom() override { return nsGkAtoms::transitionsOfAfterProperty; @@ -257,11 +308,14 @@ private: mozilla::dom::Element* aElement, AnimationCollection*& aElementTransitions, nsStyleContext* aOldStyleContext, nsStyleContext* aNewStyleContext, bool* aStartedAny, nsCSSPropertySet* aWhichStarted); bool mInAnimationOnlyStyleUpdate; + + mozilla::DelayedEventDispatcher<mozilla::TransitionEventInfo> + mEventDispatcher; }; #endif /* !defined(nsTransitionManager_h_) */
--- a/layout/style/test/animation_utils.js +++ b/layout/style/test/animation_utils.js @@ -50,17 +50,17 @@ function advance_clock(milliseconds) { gElem.addEventListener("animationiteration", listener, false); gElem.addEventListener("animationend", listener, false); } function check_events(eventsExpected, desc) { // This function checks that the list of eventsExpected matches // the received events -- but it only checks the properties that // are present on eventsExpected. - is(gEventsReceived.length, gEventsReceived.length, + is(gEventsReceived.length, eventsExpected.length, "number of events received for " + desc); for (var i = 0, i_end = Math.min(eventsExpected.length, gEventsReceived.length); i != i_end; ++i) { var exp = eventsExpected[i]; var rec = gEventsReceived[i]; for (var prop in exp) { if (prop == "elapsedTime") {
--- a/layout/style/test/test_animations_omta.html +++ b/layout/style/test/test_animations_omta.html @@ -946,17 +946,17 @@ addAsyncAnimTest(function *() { omta_is_approx("transform", { ty: 100 * gTF.ease_out(0.81) }, 0.01, RunningOn.Compositor, "animation-iteration-count test 3 at 23.8s"); advance_clock(200); omta_is_approx("transform", { ty: 100 * gTF.ease_out(0.8) }, 0.01, RunningOn.Compositor, "animation-iteration-count test 3 at 24s"); advance_clock(200); - omta_is("opacity", 1, RunningOn.Compositor, + omta_is("opacity", 1, RunningOn.Either, "animation-iteration-count test 2 at 25s"); omta_is_approx("transform", { ty: 100 * gTF.ease_out(0.8) }, 0.01, RunningOn.Compositor, "animation-iteration-count test 3 at 25s"); done_div(); new_div("animation: anim4 ease-in-out 5s 1.6 forwards"); yield waitForPaintsFlushed(); @@ -2340,10 +2340,43 @@ addAsyncAnimTest(function *() { omta_is("transform", { tx: 50 }, RunningOn.Compositor, "transformnone animation at 0ms"); advance_clock(500); omta_is("transform", { tx: 0 }, RunningOn.Compositor, "transformnone animation at 500ms, interpolating none values"); done_div(); }); +addAsyncAnimTest(function *() { + new_div("transform: translate(100px); transition: transform 10s 5s linear"); + yield waitForPaintsFlushed(); + gDiv.style.transform = "translate(200px)"; + yield waitForPaintsFlushed(); + omta_is("transform", { tx: 100 }, RunningOn.MainThread, + "transition runs on main thread during delay"); + // At the *very* start of the transition the start value of the transition + // will match the underlying transform value. Various optimizations in + // RestyleManager may recognize this a "no change" and filter out the + // transition meaning that the animation doesn't get added to the compositor + // thread until the first time the value changes. As a result, we fast-forward + // a little past the beginning and then wait for the animation to be sent + // to the compositor. + advance_clock(5100); + yield waitForPaints(); + omta_is("transform", { tx: 101 }, RunningOn.Compositor, + "transition runs on compositor at start of active interval"); + advance_clock(4900); + omta_is("transform", { tx: 150 }, RunningOn.Compositor, + "transition runs on compositor at during active interval"); + advance_clock(5000); + // Currently the compositor will apply a forwards fill until it gets told by + // the main thread to clear the animation. As a result we should wait for + // paints before checking that the animated value does *not* appear on the + // compositor thread. + yield waitForPaints(); + omta_is("transform", { tx: 200 }, RunningOn.MainThread, + "transition runs on main thread at end of active interval"); + + done_div(); +}); + </script> </html>
deleted file mode 100644 --- a/memory/jemalloc/Makefile.in +++ /dev/null @@ -1,9 +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 $(topsrcdir)/config/rules.mk - -ifdef GNU_CC -CFLAGS += -std=gnu99 -endif
--- a/memory/jemalloc/moz.build +++ b/memory/jemalloc/moz.build @@ -56,14 +56,17 @@ if CONFIG['_MSC_VER']: if not CONFIG['HAVE_INTTYPES_H']: LOCAL_INCLUDES += ['src/include/msvc_compat/C99'] if CONFIG['OS_TARGET'] == 'Linux': # For mremap DEFINES['_GNU_SOURCE'] = True if CONFIG['MOZ_NUWA_PROCESS'] and CONFIG['MOZ_JEMALLOC3']: - DEFINES['pthread_mutex_lock'] = '__real_pthread_mutex_lock'; + DEFINES['pthread_mutex_lock'] = '__real_pthread_mutex_lock' + +if CONFIG['GNU_CC']: + CFLAGS += ['-std=gnu99'] DEFINES['abort'] = 'moz_abort' GENERATED_INCLUDES += ['src/include'] LOCAL_INCLUDES += ['src/include']
--- a/mfbt/Atomics.h +++ b/mfbt/Atomics.h @@ -262,38 +262,22 @@ struct IntrinsicAddSub : public Intrinsi template<typename T, MemoryOrdering Order> struct IntrinsicAddSub<T*, Order> : public IntrinsicBase<T*, Order> { typedef IntrinsicBase<T*, Order> Base; static T* add(typename Base::ValueType& aPtr, ptrdiff_t aVal) { - return aPtr.fetch_add(fixupAddend(aVal), Base::OrderedOp::AtomicRMWOrder); + return aPtr.fetch_add(aVal, Base::OrderedOp::AtomicRMWOrder); } static T* sub(typename Base::ValueType& aPtr, ptrdiff_t aVal) { - return aPtr.fetch_sub(fixupAddend(aVal), Base::OrderedOp::AtomicRMWOrder); - } -private: - /* - * GCC 4.6's <atomic> header has a bug where adding X to an - * atomic<T*> is not the same as adding X to a T*. Hence the need - * for this function to provide the correct addend. - */ - static ptrdiff_t fixupAddend(ptrdiff_t aVal) - { -#if defined(__clang__) || defined(_MSC_VER) - return aVal; -#elif defined(__GNUC__) && !MOZ_GCC_VERSION_AT_LEAST(4, 7, 0) - return aVal * sizeof(T); -#else - return aVal; -#endif + return aPtr.fetch_sub(aVal, Base::OrderedOp::AtomicRMWOrder); } }; template<typename T, MemoryOrdering Order> struct IntrinsicIncDec : public IntrinsicAddSub<T, Order> { typedef IntrinsicBase<T, Order> Base;
--- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -921,19 +921,13 @@ pref("caret.manages-android-actionbar", // Disable sending console to logcat on release builds. #ifdef RELEASE_BUILD pref("consoleservice.logcat", false); #else pref("consoleservice.logcat", true); #endif -// Enable Service Workers for Android on non-release builds -#ifndef RELEASE_BUILD -pref("dom.serviceWorkers.enabled", true); -pref("dom.serviceWorkers.interception.enabled", true); -#endif - // Enable Cardboard VR on mobile, assuming VR at all is enabled pref("dom.vr.cardboard.enabled", true); // TODO: Disabled until bug 1190301 is fixed. pref("browser.tabs.showAudioPlayingIcon", false);
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -437,17 +437,22 @@ var BrowserApp = { deck: null, startup: function startup() { window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess(); dump("zerdatime " + Date.now() + " - browser chrome startup finished."); this.deck = document.getElementById("browsers"); - BrowserEventHandler.init(); + // This check and BrowserEventHandler should be removed once we have + // switched over to the C++ APZ code + if (!AppConstants.MOZ_ANDROID_APZ) { + BrowserEventHandler.init(); + } + ViewportHandler.init(); Services.androidBridge.browserApp = this; Services.obs.addObserver(this, "Locale:OS", false); Services.obs.addObserver(this, "Locale:Changed", false); Services.obs.addObserver(this, "Tab:Load", false); Services.obs.addObserver(this, "Tab:Selected", false);
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -134,17 +134,16 @@ pref("dom.permissions.enabled", false); // Whether or not Web Workers are enabled. pref("dom.workers.enabled", true); // The number of workers per domain allowed to run concurrently. pref("dom.workers.maxPerDomain", 20); // Whether or not Shared Web Workers are enabled. pref("dom.workers.sharedWorkers.enabled", true); -// Service workers pref("dom.serviceWorkers.enabled", false); // Allow service workers to intercept network requests using the fetch event pref("dom.serviceWorkers.interception.enabled", false); // Allow service workers to intercept opaque (cross origin) responses pref("dom.serviceWorkers.interception.opaque.enabled", false); @@ -4455,29 +4454,22 @@ pref("dom.sms.maxReadAheadEntries", 0); // WebContacts pref("dom.mozContacts.enabled", false); // WebAlarms pref("dom.mozAlarms.enabled", false); // Push -#if !defined(MOZ_B2G) && !defined(ANDROID) -// Desktop prefs -#ifdef RELEASE_BUILD pref("dom.push.enabled", false); -#else -pref("dom.push.enabled", true); + +#if !defined(RELEASE_BUILD) +pref("dom.push.debug", true); #endif -#else -// Mobile prefs -pref("dom.push.enabled", false); -#endif - -pref("dom.push.debug", false); + pref("dom.push.serverURL", "wss://push.services.mozilla.com/"); pref("dom.push.userAgentID", ""); // The maximum number of notifications that a service worker can receive // without user interaction. pref("dom.push.maxQuotaPerSubscription", 16); // Is the network connection allowed to be up?
--- a/netwerk/cache2/CacheFileIOManager.cpp +++ b/netwerk/cache2/CacheFileIOManager.cpp @@ -422,55 +422,38 @@ CacheFileHandles::RemoveHandle(CacheFile if (entry->IsEmpty()) { LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x " "list is empty, removing entry %p", LOGSHA1(entry->Hash()), entry)); mTable.RemoveEntry(*entry->Hash()); } } -static PLDHashOperator -GetAllHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure) -{ - nsTArray<nsRefPtr<CacheFileHandle> > *array = - static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure); - - aEntry->GetHandles(*array); - return PL_DHASH_NEXT; -} - void CacheFileHandles::GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval) { MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased()); - mTable.EnumerateEntries(&GetAllHandlesEnum, _retval); -} - -static PLDHashOperator -GetActiveHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure) -{ - nsTArray<nsRefPtr<CacheFileHandle> > *array = - static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure); - - nsRefPtr<CacheFileHandle> handle = aEntry->GetNewestHandle(); - MOZ_ASSERT(handle); - - if (!handle->IsDoomed()) { - array->AppendElement(handle); + for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) { + iter.Get()->GetHandles(*_retval); } - - return PL_DHASH_NEXT; } void CacheFileHandles::GetActiveHandles( nsTArray<nsRefPtr<CacheFileHandle> > *_retval) { MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased()); - mTable.EnumerateEntries(&GetActiveHandlesEnum, _retval); + for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) { + nsRefPtr<CacheFileHandle> handle = iter.Get()->GetNewestHandle(); + MOZ_ASSERT(handle); +