diff --git a/CLOBBER b/CLOBBER index bca2226c6513..ab48dbb49640 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Bug 1128037 needed a clobber to properly build on OS X +Bug 1155494 seems to need a clobber to pick up a change the ipdl parser. diff --git a/browser/base/content/browser-fullScreen.js b/browser/base/content/browser-fullScreen.js index d764655a42c8..c74943b352f4 100644 --- a/browser/base/content/browser-fullScreen.js +++ b/browser/base/content/browser-fullScreen.js @@ -10,6 +10,7 @@ var FullScreen = { // called when we go into full screen, even if initiated by a web page script window.addEventListener("fullscreen", this, true); window.messageManager.addMessageListener("MozEnteredDomFullscreen", this); + window.messageManager.addMessageListener("MozExitedDomFullscreen", this); if (window.fullScreen) this.toggle(); @@ -17,6 +18,7 @@ var FullScreen = { uninit: function() { window.messageManager.removeMessageListener("MozEnteredDomFullscreen", this); + window.messageManager.removeMessageListener("MozExitedDomFullscreen", this); this.cleanup(); }, @@ -70,23 +72,16 @@ var FullScreen = { document.addEventListener("keypress", this._keyToggleCallback, false); document.addEventListener("popupshown", this._setPopupOpen, false); document.addEventListener("popuphidden", this._setPopupOpen, false); + this._shouldAnimate = true; // We don't animate the toolbar collapse if in DOM full-screen mode, // as the size of the content area would still be changing after the // mozfullscreenchange event fired, which could confuse content script. - this._shouldAnimate = !document.mozFullScreen; - this.mouseoverToggle(false); + this.hideNavToolbox(document.mozFullScreen); } else { - // The user may quit fullscreen during an animation - this._cancelAnimation(); - gNavToolbox.style.marginTop = ""; - if (this._isChromeCollapsed) - this.mouseoverToggle(true); + this.showNavToolbox(false); // This is needed if they use the context menu to quit fullscreen this._isPopupOpen = false; - - document.documentElement.removeAttribute("inDOMFullscreen"); - this.cleanup(); } }, @@ -127,6 +122,16 @@ var FullScreen = { windowUtils.remoteFrameFullscreenChanged(browser, data.origin); } this.enterDomFullscreen(browser, data.origin); + } else if (aMessage.name == "MozExitedDomFullscreen") { + document.documentElement.removeAttribute("inDOMFullscreen"); + this.cleanupDomFullscreen(); + this.showNavToolbox(); + // If we are still in fullscreen mode, re-hide + // the toolbox with animation. + if (window.fullScreen) { + this._shouldAnimate = true; + this.hideNavToolbox(); + } } }, @@ -172,8 +177,8 @@ var FullScreen = { // Cancel any "hide the toolbar" animation which is in progress, and make // the toolbar hide immediately. - this._cancelAnimation(); - this.mouseoverToggle(false); + this.hideNavToolbox(true); + this._fullScrToggler.hidden = true; }, cleanup: function () { @@ -183,18 +188,22 @@ var FullScreen = { document.removeEventListener("popupshown", this._setPopupOpen, false); document.removeEventListener("popuphidden", this._setPopupOpen, false); - this.cancelWarning(); - gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen); - gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen); - gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen); - if (!this.useLionFullScreen) - window.removeEventListener("activate", this); - - window.messageManager - .broadcastAsyncMessage("DOMFullscreen:Cleanup"); + this.cleanupDomFullscreen(); } }, + cleanupDomFullscreen: function () { + this.cancelWarning(); + gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen); + gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen); + gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen); + if (!this.useLionFullScreen) + window.removeEventListener("activate", this); + + window.messageManager + .broadcastAsyncMessage("DOMFullscreen:Cleanup"); + }, + getMouseTargetRect: function() { return this._mouseTargetRect; @@ -203,23 +212,22 @@ var FullScreen = { // Event callbacks _expandCallback: function() { - FullScreen.mouseoverToggle(true); + FullScreen.showNavToolbox(); }, onMouseEnter: function() { - FullScreen.mouseoverToggle(false); + FullScreen.hideNavToolbox(); }, _keyToggleCallback: function(aEvent) { // if we can use the keyboard (eg Ctrl+L or Ctrl+E) to open the toolbars, we // should provide a way to collapse them too. if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) { - FullScreen._shouldAnimate = false; - FullScreen.mouseoverToggle(false, true); + FullScreen.hideNavToolbox(true); } // F6 is another shortcut to the address bar, but its not covered in OpenLocation() else if (aEvent.keyCode == aEvent.DOM_VK_F6) - FullScreen.mouseoverToggle(true); + FullScreen.showNavToolbox(); }, // Checks whether we are allowed to collapse the chrome @@ -273,47 +281,6 @@ var FullScreen = { // Animate the toolbars disappearing _shouldAnimate: true, - _isAnimating: false, - _animationTimeout: 0, - _animationHandle: 0, - _animateUp: function() { - // check again, the user may have done something before the animation was due to start - if (!window.fullScreen || !this._safeToCollapse(false)) { - this._isAnimating = false; - this._shouldAnimate = true; - return; - } - - this._animateStartTime = window.mozAnimationStartTime; - if (!this._animationHandle) - this._animationHandle = window.mozRequestAnimationFrame(this); - }, - - sample: function (timeStamp) { - const duration = 1500; - const timePassed = timeStamp - this._animateStartTime; - const pos = timePassed >= duration ? 1 : - 1 - Math.pow(1 - timePassed / duration, 4); - - if (pos >= 1) { - // We've animated enough - this._cancelAnimation(); - gNavToolbox.style.marginTop = ""; - this.mouseoverToggle(false); - return; - } - - gNavToolbox.style.marginTop = (gNavToolbox.boxObject.height * pos * -1) + "px"; - this._animationHandle = window.mozRequestAnimationFrame(this); - }, - - _cancelAnimation: function() { - window.mozCancelAnimationFrame(this._animationHandle); - this._animationHandle = 0; - clearTimeout(this._animationTimeout); - this._isAnimating = false; - this._shouldAnimate = false; - }, cancelWarning: function(event) { if (!this.warningBox) @@ -470,38 +437,18 @@ var FullScreen = { 3000); }, - mouseoverToggle: function(aShow, forceHide) - { - // Don't do anything if: - // a) we're already in the state we want, - // b) we're animating and will become collapsed soon, or - // c) we can't collapse because it would be undesirable right now - if (aShow != this._isChromeCollapsed || (!aShow && this._isAnimating) || - (!aShow && !this._safeToCollapse(forceHide))) - return; + showNavToolbox: function(trackMouse = true) { + this._fullScrToggler.hidden = true; + gNavToolbox.removeAttribute("fullscreenShouldAnimate"); + gNavToolbox.style.marginTop = ""; - // browser.fullscreen.animateUp - // 0 - never animate up - // 1 - animate only for first collapse after entering fullscreen (default for perf's sake) - // 2 - animate every time it collapses - if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 0) - this._shouldAnimate = false; - - if (!aShow && this._shouldAnimate) { - this._isAnimating = true; - this._shouldAnimate = false; - this._animationTimeout = setTimeout(this._animateUp.bind(this), 800); + if (!this._isChromeCollapsed) { return; } - // Hiding/collapsing the toolbox interferes with the tab bar's scrollbox, - // so we just move it off-screen instead. See bug 430687. - gNavToolbox.style.marginTop = - aShow ? "" : -gNavToolbox.getBoundingClientRect().height + "px"; - - this._fullScrToggler.hidden = aShow || document.mozFullScreen; - - if (aShow) { + // Track whether mouse is near the toolbox + this._isChromeCollapsed = false; + if (trackMouse) { let rect = gBrowser.mPanelContainer.getBoundingClientRect(); this._mouseTargetRect = { top: rect.top + 50, @@ -510,13 +457,49 @@ var FullScreen = { right: rect.right }; MousePosTracker.addListener(this); - } else { - MousePosTracker.removeListener(this); + } + }, + + hideNavToolbox: function(forceHide = false) { + this._fullScrToggler.hidden = document.mozFullScreen; + if (this._isChromeCollapsed) { + if (forceHide) { + gNavToolbox.removeAttribute("fullscreenShouldAnimate"); + } + return; + } + if (!this._safeToCollapse(forceHide)) { + this._fullScrToggler.hidden = true; + return; } - this._isChromeCollapsed = !aShow; - if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 2) + // browser.fullscreen.animateUp + // 0 - never animate up + // 1 - animate only for first collapse after entering fullscreen (default for perf's sake) + // 2 - animate every time it collapses + let animateUp = gPrefService.getIntPref("browser.fullscreen.animateUp"); + if (animateUp == 0) { + this._shouldAnimate = false; + } else if (animateUp == 2) { this._shouldAnimate = true; + } + if (this._shouldAnimate && !forceHide) { + gNavToolbox.setAttribute("fullscreenShouldAnimate", true); + this._shouldAnimate = false; + // Hide the fullscreen toggler until the transition ends. + let listener = () => { + gNavToolbox.removeEventListener("transitionend", listener, true); + if (this._isChromeCollapsed) + this._fullScrToggler.hidden = false; + }; + gNavToolbox.addEventListener("transitionend", listener, true); + this._fullScrToggler.hidden = true; + } + + gNavToolbox.style.marginTop = + -gNavToolbox.getBoundingClientRect().height + "px"; + this._isChromeCollapsed = true; + MousePosTracker.removeListener(this); }, showXULChrome: function(aTag, aShow) diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index da971482e14a..a9341fc27a17 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -300,6 +300,10 @@ toolbar[customizing] > .overflow-button { visibility: collapse; } +#navigator-toolbox[fullscreenShouldAnimate] { + transition: 1.5s margin-top ease-out; +} + /* Rules to help integrate SDK widgets */ toolbaritem[sdkstylewidget="true"] > toolbarbutton, toolbarpaletteitem > toolbaritem[sdkstylewidget="true"] > iframe, diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 7691440d1809..ac73e1a2a8cc 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1955,7 +1955,7 @@ function loadOneOrMoreURIs(aURIString) function focusAndSelectUrlBar() { if (gURLBar) { if (window.fullScreen) - FullScreen.mouseoverToggle(true); + FullScreen.showNavToolbox(); gURLBar.select(); if (document.activeElement == gURLBar.inputField) @@ -3418,7 +3418,7 @@ const BrowserSearch = { } if (searchBar) { if (window.fullScreen) - FullScreen.mouseoverToggle(true); + FullScreen.showNavToolbox(); searchBar.select(); } openSearchPageIfFieldIsNotActive(searchBar); diff --git a/browser/base/content/tab-content.js b/browser/base/content/tab-content.js index db943faca904..e8011b5e2c05 100644 --- a/browser/base/content/tab-content.js +++ b/browser/base/content/tab-content.js @@ -590,6 +590,7 @@ let DOMFullscreenHandler = { addMessageListener("DOMFullscreen:Approved", this); addMessageListener("DOMFullscreen:CleanUp", this); addEventListener("MozEnteredDomFullscreen", this); + addEventListener("MozExitedDomFullscreen", this); }, receiveMessage: function(aMessage) { @@ -615,6 +616,8 @@ let DOMFullscreenHandler = { sendAsyncMessage("MozEnteredDomFullscreen", { origin: this._fullscreenDoc.nodePrincipal.origin, }); + } else if (aEvent.type == "MozExitedDomFullscreen") { + sendAsyncMessage("MozExitedDomFullscreen"); } } }; diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index b059746c81b6..4db0d3ad1891 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -477,3 +477,4 @@ support-files = [browser_bug1124271_readerModePinnedTab.js] support-files = readerModeArticle.html +[browser_domFullscreen_fullscreenMode.js] diff --git a/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js new file mode 100644 index 000000000000..7ca92c8c3abd --- /dev/null +++ b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js @@ -0,0 +1,208 @@ +"use strict"; + +let gMessageManager; + +function frameScript() { + addMessageListener("Test:RequestFullscreen", () => { + content.document.body.mozRequestFullScreen(); + }); + addMessageListener("Test:ExitFullscreen", () => { + content.document.mozCancelFullScreen(); + }); + addMessageListener("Test:QueryFullscreenState", () => { + sendAsyncMessage("Test:FullscreenState", { + inDOMFullscreen: content.document.mozFullScreen, + inFullscreen: content.fullScreen + }); + }); + content.document.addEventListener("mozfullscreenchange", () => { + sendAsyncMessage("Test:FullscreenChanged", { + inDOMFullscreen: content.document.mozFullScreen, + inFullscreen: content.fullScreen + }); + }); + function waitUntilActive() { + let doc = content.document; + if (doc.docShell.isActive && doc.hasFocus()) { + sendAsyncMessage("Test:Activated"); + } else { + setTimeout(waitUntilActive, 10); + } + } + waitUntilActive(); +} + +function listenOneMessage(aMsg, aListener) { + function listener({ data }) { + gMessageManager.removeMessageListener(aMsg, listener); + aListener(data); + } + gMessageManager.addMessageListener(aMsg, listener); +} + +function listenOneEvent(aEvent, aListener) { + function listener(evt) { + removeEventListener(aEvent, listener); + aListener(evt); + } + addEventListener(aEvent, listener); +} + +function queryFullscreenState() { + return new Promise(resolve => { + listenOneMessage("Test:FullscreenState", resolve); + gMessageManager.sendAsyncMessage("Test:QueryFullscreenState"); + }); +} + +function captureUnexpectedFullscreenChange() { + ok(false, "catched an unexpected fullscreen change"); +} + +const FS_CHANGE_DOM = 1 << 0; +const FS_CHANGE_SIZE = 1 << 1; +const FS_CHANGE_BOTH = FS_CHANGE_DOM | FS_CHANGE_SIZE; + +function waitForFullscreenChanges(aFlags) { + return new Promise(resolve => { + let fullscreenData = null; + let sizemodeChanged = false; + function tryResolve() { + if ((!(aFlags & FS_CHANGE_DOM) || fullscreenData) && + (!(aFlags & FS_CHANGE_SIZE) || sizemodeChanged)) { + if (!fullscreenData) { + queryFullscreenState().then(resolve); + } else { + resolve(fullscreenData); + } + } + } + if (aFlags & FS_CHANGE_SIZE) { + listenOneEvent("sizemodechange", () => { + sizemodeChanged = true; + tryResolve(); + }); + } + if (aFlags & FS_CHANGE_DOM) { + gMessageManager.removeMessageListener( + "Test:FullscreenChanged", captureUnexpectedFullscreenChange); + listenOneMessage("Test:FullscreenChanged", data => { + gMessageManager.addMessageListener( + "Test:FullscreenChanged", captureUnexpectedFullscreenChange); + fullscreenData = data; + tryResolve(); + }); + } + }); +} + +let gTests = [ + { + desc: "document method", + affectsFullscreenMode: false, + exitFunc: () => { + gMessageManager.sendAsyncMessage("Test:ExitFullscreen"); + } + }, + { + desc: "escape key", + affectsFullscreenMode: false, + exitFunc: () => { + executeSoon(() => EventUtils.synthesizeKey("VK_ESCAPE", {})); + } + }, + { + desc: "F11 key", + affectsFullscreenMode: true, + exitFunc: function () { + executeSoon(() => EventUtils.synthesizeKey("VK_F11", {})); + } + } +]; + +add_task(function* () { + let tab = gBrowser.addTab("about:robots"); + let browser = tab.linkedBrowser; + gBrowser.selectedTab = tab; + yield waitForDocLoadComplete(); + + registerCleanupFunction(() => { + if (browser.contentWindow.fullScreen) { + BrowserFullScreen(); + } + gBrowser.removeTab(tab); + }); + + gMessageManager = browser.messageManager; + gMessageManager.loadFrameScript( + "data:,(" + frameScript.toString() + ")();", false); + gMessageManager.addMessageListener( + "Test:FullscreenChanged", captureUnexpectedFullscreenChange); + + // Wait for the document being activated, so that + // fullscreen request won't be denied. + yield new Promise(resolve => listenOneMessage("Test:Activated", resolve)); + + for (let test of gTests) { + info("Testing exit DOM fullscreen via " + test.desc); + + var { inDOMFullscreen, inFullscreen } = yield queryFullscreenState(); + ok(!inDOMFullscreen, "Shouldn't have been in DOM fullscreen"); + ok(!inFullscreen, "Shouldn't have been in fullscreen"); + + /* DOM fullscreen without fullscreen mode */ + + // Enter DOM fullscreen + gMessageManager.sendAsyncMessage("Test:RequestFullscreen"); + var { inDOMFullscreen, inFullscreen } = + yield waitForFullscreenChanges(FS_CHANGE_BOTH); + ok(inDOMFullscreen, "Should now be in DOM fullscreen"); + ok(inFullscreen, "Should now be in fullscreen"); + + // Exit DOM fullscreen + test.exitFunc(); + var { inDOMFullscreen, inFullscreen } = + yield waitForFullscreenChanges(FS_CHANGE_BOTH); + ok(!inDOMFullscreen, "Should no longer be in DOM fullscreen"); + ok(!inFullscreen, "Should no longer be in fullscreen"); + + /* DOM fullscreen with fullscreen mode */ + + // Enter fullscreen mode + // Need to be asynchronous because sizemodechange event could be + // dispatched synchronously, which would cause the event listener + // miss that event and wait infinitely. + executeSoon(() => BrowserFullScreen()); + var { inDOMFullscreen, inFullscreen } = + yield waitForFullscreenChanges(FS_CHANGE_SIZE); + ok(!inDOMFullscreen, "Shouldn't have been in DOM fullscreen"); + ok(inFullscreen, "Should now be in fullscreen mode"); + + // Enter DOM fullscreen + gMessageManager.sendAsyncMessage("Test:RequestFullscreen"); + var { inDOMFullscreen, inFullscreen } = + yield waitForFullscreenChanges(FS_CHANGE_DOM); + ok(inDOMFullscreen, "Should now be in DOM fullscreen"); + ok(inFullscreen, "Should still be in fullscreen"); + + // Exit DOM fullscreen + test.exitFunc(); + var { inDOMFullscreen, inFullscreen } = + yield waitForFullscreenChanges(test.affectsFullscreenMode ? + FS_CHANGE_BOTH : FS_CHANGE_DOM); + ok(!inDOMFullscreen, "Should no longer be in DOM fullscreen"); + if (test.affectsFullscreenMode) { + ok(!inFullscreen, "Should no longer be in fullscreen mode"); + } else { + ok(inFullscreen, "Should still be in fullscreen mode"); + } + + /* Cleanup */ + + // Exit fullscreen mode if we are still in + if (browser.contentWindow.fullScreen) { + executeSoon(() => BrowserFullScreen()); + yield waitForFullscreenChanges(FS_CHANGE_SIZE); + } + } +}); diff --git a/browser/base/content/test/general/browser_windowopen_reflows.js b/browser/base/content/test/general/browser_windowopen_reflows.js index 8d34636f79ec..072852e44b5a 100644 --- a/browser/base/content/test/general/browser_windowopen_reflows.js +++ b/browser/base/content/test/general/browser_windowopen_reflows.js @@ -23,6 +23,9 @@ const EXPECTED_REFLOWS = [ // Sometimes sessionstore collects data during this test, which causes a sync reflow // (https://bugzilla.mozilla.org/show_bug.cgi?id=892154 will fix this) "ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm", + + // We may get a resize event, see bug 1149555. + "PreviewController.prototype.wasResizedSinceLastPreview@resource:///modules/WindowsPreviewPerTab.jsm" ]; if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") { diff --git a/browser/base/content/test/social/browser_social_chatwindow_resize.js b/browser/base/content/test/social/browser_social_chatwindow_resize.js index 6f2a2477d0ab..a779dc8d5593 100644 --- a/browser/base/content/test/social/browser_social_chatwindow_resize.js +++ b/browser/base/content/test/social/browser_social_chatwindow_resize.js @@ -72,8 +72,8 @@ var tests = { [chatWidth*2+popupWidth+2, 2, "big enough to fit 2 - nub remains visible as first is still hidden"], [chatWidth*3+popupWidth-2, 2, "one smaller than the size necessary to display all three - first still hidden"], [chatWidth*3+popupWidth+2, 3, "big enough to fit all - all exposed (which removes the nub)"], - [chatWidth*3+2, 3, "now the nub is hidden we can resize back down to chatWidth*3 before overflow."], - [chatWidth*3-2, 2, "2 pixels less and the first is again collapsed (and the nub re-appears)"], + [chatWidth*3+4, 3, "now the nub is hidden we can resize back down to chatWidth*3 before overflow."], + [chatWidth*3-4, 2, "4 pixels less and the first is again collapsed (and the nub re-appears)"], [chatWidth*2+popupWidth+2, 2, "back down to just big enough to fit 2"], [chatWidth*2+popupWidth-2, 1, "back down to just not enough to fit 2"], [chatWidth*3+popupWidth+2, 3, "now a large jump to make all 3 visible (ie, affects 2)"], diff --git a/browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js b/browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js index 7849eec59880..8659c60aaa47 100644 --- a/browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js +++ b/browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js @@ -4,6 +4,11 @@ "use strict"; +function isFullscreenSizeMode() { + let sizemode = document.documentElement.getAttribute("sizemode"); + return sizemode == "fullscreen"; +} + // Observers should be disabled when in customization mode. add_task(function() { // Open and close the panel to make sure that the @@ -17,9 +22,10 @@ add_task(function() { let fullscreenButton = document.getElementById("fullscreen-button"); ok(!fullscreenButton.checked, "Fullscreen button should not be checked when not in fullscreen.") + ok(!isFullscreenSizeMode(), "Should not be in fullscreen sizemode before we enter fullscreen."); BrowserFullScreen(); - yield waitForCondition(function() fullscreenButton.checked); + yield waitForCondition(() => isFullscreenSizeMode()); ok(fullscreenButton.checked, "Fullscreen button should be checked when in fullscreen.") yield startCustomizing(); @@ -34,6 +40,6 @@ add_task(function() { BrowserFullScreen(); fullscreenButton = document.getElementById("fullscreen-button"); - yield waitForCondition(function() !fullscreenButton.checked); + yield waitForCondition(() => !isFullscreenSizeMode()); ok(!fullscreenButton.checked, "Fullscreen button should not be checked when not in fullscreen.") }); diff --git a/browser/components/pocket/panels/js/saved.js b/browser/components/pocket/panels/js/saved.js index ec278be156f9..4baef0e5abe5 100644 --- a/browser/components/pocket/panels/js/saved.js +++ b/browser/components/pocket/panels/js/saved.js @@ -105,7 +105,7 @@ var PKT_SAVED_OVERLAY = function (options) }; this.startCloseTimer = function(manualtime) { - /*var settime = manualtime ? manualtime : myself.autocloseTiming; + var settime = manualtime ? manualtime : myself.autocloseTiming; if (typeof myself.autocloseTimer == 'number') { clearTimeout(myself.autocloseTimer); @@ -117,7 +117,7 @@ var PKT_SAVED_OVERLAY = function (options) myself.preventCloseTimerCancel = false; myself.closePopup(); } - }, settime);*/ + }, settime); }; this.stopCloseTimer = function() { diff --git a/browser/components/preferences/in-content/sync.js b/browser/components/preferences/in-content/sync.js index 6ff4e8cb623d..230aa9a73463 100644 --- a/browser/components/preferences/in-content/sync.js +++ b/browser/components/preferences/in-content/sync.js @@ -301,9 +301,15 @@ let gSyncPane = { // If the account is verified the next promise in the chain will // fetch profile data. return data.verified; - }).then(shouldGetProfile => { - if (shouldGetProfile) { - return fxAccounts.getSignedInUserProfile(); + }).then(isVerified => { + if (isVerified) { + let enabled; + try { + enabled = Services.prefs.getBoolPref("identity.fxaccounts.profile_image.enabled"); + } catch (ex) {} + if (enabled) { + return fxAccounts.getSignedInUserProfile(); + } } }).then(data => { if (data && data.avatar) { diff --git a/browser/components/selfsupport/SelfSupportService.js b/browser/components/selfsupport/SelfSupportService.js index d7e7f8c38c82..9a44377d4f5b 100644 --- a/browser/components/selfsupport/SelfSupportService.js +++ b/browser/components/selfsupport/SelfSupportService.js @@ -68,6 +68,11 @@ MozSelfSupportInterface.prototype = { resetPref: function(name) { Services.prefs.clearUserPref(name); }, + + resetSearchEngines: function() { + Services.search.restoreDefaultEngines(); + Services.search.resetToOriginalDefaultEngine(); + }, } this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MozSelfSupportInterface]); diff --git a/browser/components/selfsupport/test/browser_selfsupportAPI.js b/browser/components/selfsupport/test/browser_selfsupportAPI.js index 93ca34a78740..2a54d4ae651c 100644 --- a/browser/components/selfsupport/test/browser_selfsupportAPI.js +++ b/browser/components/selfsupport/test/browser_selfsupportAPI.js @@ -1,6 +1,6 @@ Cu.import("resource://gre/modules/Preferences.jsm"); -function test() { +function test_resetPref() { const prefNewName = "browser.newpref.fake"; Assert.ok(!Preferences.has(prefNewName), "pref should not exist"); @@ -45,3 +45,44 @@ function test() { // clearUserPref can't undo its action // see discussion in bug 1075160 } + +function test_resetSearchEngines() +{ + const defaultEngineOriginal = Services.search.defaultEngine; + const visibleEnginesOriginal = Services.search.getVisibleEngines(); + + // 1. do nothing on unchanged search configuration + MozSelfSupport.resetSearchEngines(); + Assert.equal(Services.search.defaultEngine, defaultEngineOriginal, "default engine should be reset"); + Assert.deepEqual(Services.search.getVisibleEngines(), visibleEnginesOriginal, + "default visible engines set should be reset"); + + // 2. change the default search engine + const defaultEngineNew = visibleEnginesOriginal[3]; + Assert.notEqual(defaultEngineOriginal, defaultEngineNew, "new default engine should be different from original"); + Services.search.defaultEngine = defaultEngineNew; + Assert.equal(Services.search.defaultEngine, defaultEngineNew, "default engine should be set to new"); + MozSelfSupport.resetSearchEngines(); + Assert.equal(Services.search.defaultEngine, defaultEngineOriginal, "default engine should be reset"); + Assert.deepEqual(Services.search.getVisibleEngines(), visibleEnginesOriginal, + "default visible engines set should be reset"); + + // 3. remove an engine + const engineRemoved = visibleEnginesOriginal[2]; + Services.search.removeEngine(engineRemoved); + Assert.ok(Services.search.getVisibleEngines().indexOf(engineRemoved) == -1, + "removed engine should not be visible any more"); + MozSelfSupport.resetSearchEngines(); + Assert.equal(Services.search.defaultEngine, defaultEngineOriginal, "default engine should be reset"); + Assert.deepEqual(Services.search.getVisibleEngines(), visibleEnginesOriginal, + "default visible engines set should be reset"); + + // 4. add an angine + // we don't remove user-added engines as they are only used if selected +} + +function test() +{ + test_resetPref(); + test_resetSearchEngines(); +} diff --git a/caps/nsPrincipal.cpp b/caps/nsPrincipal.cpp index 8c39a0dc5784..7f3159eabff6 100644 --- a/caps/nsPrincipal.cpp +++ b/caps/nsPrincipal.cpp @@ -638,7 +638,6 @@ IsOnFullDomainWhitelist(nsIURI* aURI) // 0th entry only active when testing: NS_LITERAL_CSTRING("test1.example.org"), NS_LITERAL_CSTRING("map.baidu.com"), - NS_LITERAL_CSTRING("music.baidu.com"), NS_LITERAL_CSTRING("3g.163.com"), NS_LITERAL_CSTRING("3glogo.gtimg.com"), // for 3g.163.com NS_LITERAL_CSTRING("info.3g.qq.com"), // for 3g.qq.com @@ -646,6 +645,16 @@ IsOnFullDomainWhitelist(nsIURI* aURI) NS_LITERAL_CSTRING("img.m.baidu.com"), // for [shucheng|ks].baidu.com NS_LITERAL_CSTRING("m.mogujie.com"), NS_LITERAL_CSTRING("touch.qunar.com"), + NS_LITERAL_CSTRING("mjs.sinaimg.cn"), // for sina.cn + NS_LITERAL_CSTRING("static.qiyi.com"), // for m.iqiyi.com + NS_LITERAL_CSTRING("www.kuaidi100.com"), // for m.kuaidi100.com + NS_LITERAL_CSTRING("m.pc6.com"), + NS_LITERAL_CSTRING("m.haosou.com"), + NS_LITERAL_CSTRING("m.mi.com"), + NS_LITERAL_CSTRING("wappass.baidu.com"), + NS_LITERAL_CSTRING("m.video.baidu.com"), + NS_LITERAL_CSTRING("m.video.baidu.com"), + NS_LITERAL_CSTRING("imgcache.gtimg.cn"), // for m.v.qq.com }; static const size_t sNumFullDomainsOnWhitelist = MOZ_ARRAY_LENGTH(sFullDomainsOnWhitelist); diff --git a/dom/base/URL.cpp b/dom/base/URL.cpp index f82404feb8dc..9c927ae20d73 100644 --- a/dom/base/URL.cpp +++ b/dom/base/URL.cpp @@ -18,6 +18,7 @@ #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsIURL.h" +#include "nsContentUtils.h" namespace mozilla { namespace dom { @@ -42,7 +43,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END -URL::URL(nsIURI* aURI) +URL::URL(already_AddRefed aURI) : mURI(aURI) { } @@ -57,56 +58,55 @@ URL::WrapObject(JSContext* aCx, JS::Handle aGivenProto, JS::MutableHa URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, URL& aBase, ErrorResult& aRv) { - nsresult rv; - nsCOMPtr ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv)); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return nullptr; - } - - nsCOMPtr uri; - rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aUrl), nullptr, aBase.GetURI(), - getter_AddRefs(uri)); - if (NS_FAILED(rv)) { - nsAutoString label(aUrl); - aRv.ThrowTypeError(MSG_INVALID_URL, &label); - return nullptr; - } - - nsRefPtr url = new URL(uri); - return url.forget(); + return Constructor(aUrl, aBase.GetURI(), aRv); } /* static */ already_AddRefed URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, - const nsAString& aBase, ErrorResult& aRv) + const Optional& aBase, ErrorResult& aRv) { - nsresult rv; - nsCOMPtr ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv)); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return nullptr; + if (aBase.WasPassed()) { + return Constructor(aUrl, aBase.Value(), aRv); } + return Constructor(aUrl, nullptr, aRv); +} + +/* static */ already_AddRefed +URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, + const nsAString& aBase, ErrorResult& aRv) +{ + return Constructor(aUrl, aBase, aRv); +} + +/* static */ already_AddRefed +URL::Constructor(const nsAString& aUrl, const nsAString& aBase, + ErrorResult& aRv) +{ nsCOMPtr baseUri; - rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aBase), nullptr, nullptr, - getter_AddRefs(baseUri)); - if (NS_FAILED(rv)) { - nsAutoString label(aBase); - aRv.ThrowTypeError(MSG_INVALID_URL, &label); + nsresult rv = NS_NewURI(getter_AddRefs(baseUri), aBase, nullptr, nullptr, + nsContentUtils::GetIOService()); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.ThrowTypeError(MSG_INVALID_URL, &aBase); return nullptr; } + return Constructor(aUrl, baseUri, aRv); +} + +/* static */ +already_AddRefed +URL::Constructor(const nsAString& aUrl, nsIURI* aBase, ErrorResult& aRv) +{ nsCOMPtr uri; - rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aUrl), nullptr, baseUri, - getter_AddRefs(uri)); - if (NS_FAILED(rv)) { - nsAutoString label(aUrl); - aRv.ThrowTypeError(MSG_INVALID_URL, &label); + nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl, nullptr, aBase, + nsContentUtils::GetIOService()); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.ThrowTypeError(MSG_INVALID_URL, &aUrl); return nullptr; } - nsRefPtr url = new URL(uri); + nsRefPtr url = new URL(uri.forget()); return url.forget(); } diff --git a/dom/base/URL.h b/dom/base/URL.h index 81d315d78daa..f1ef8be9abb1 100644 --- a/dom/base/URL.h +++ b/dom/base/URL.h @@ -39,7 +39,7 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS(URL) - explicit URL(nsIURI* aURI); + explicit URL(already_AddRefed aURI); // WebIDL methods bool @@ -49,8 +49,16 @@ public: Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, URL& aBase, ErrorResult& aRv); static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, + const Optional& aBase, ErrorResult& aRv); + // Versions of Constructor that we can share with workers and other code. + static already_AddRefed Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, const nsAString& aBase, ErrorResult& aRv); + static already_AddRefed + Constructor(const nsAString& aUrl, const nsAString& aBase, ErrorResult& aRv); + static already_AddRefed + Constructor(const nsAString& aUrl, nsIURI* aBase, ErrorResult& aRv); static void CreateObjectURL(const GlobalObject& aGlobal, File& aBlob, diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index ba762e8106ee..44e39e0b60ab 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -11266,6 +11266,12 @@ ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc) NS_ASSERTION(!root->IsFullScreenDoc(), "Fullscreen root should no longer be a fullscreen doc..."); + // Dispatch MozExitedDomFullscreen to the last document in + // the list since we want this event to follow the same path + // MozEnteredDomFullscreen dispatched. + nsRefPtr asyncDispatcher = new AsyncEventDispatcher( + changed.LastElement(), NS_LITERAL_STRING("MozExitedDomFullscreen"), true, true); + asyncDispatcher->PostDOMEvent(); // Move the top-level window out of fullscreen mode. SetWindowFullScreen(root, false); } @@ -11422,6 +11428,9 @@ nsDocument::RestorePreviousFullScreenState() // move the top-level window out of fullscreen mode. NS_ASSERTION(!GetFullscreenRootDocument(this)->IsFullScreenDoc(), "Should have cleared all docs' stacks"); + nsRefPtr asyncDispatcher = new AsyncEventDispatcher( + this, NS_LITERAL_STRING("MozExitedDomFullscreen"), true, true); + asyncDispatcher->PostDOMEvent(); SetWindowFullScreen(this, false); } } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index cf773990ad83..5020665fc9f6 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1084,6 +1084,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) mAddActiveEventFuzzTime(true), mIsFrozen(false), mFullScreen(false), + mFullscreenMode(false), mIsClosed(false), mInClose(false), mHavePendingClose(false), @@ -6082,6 +6083,22 @@ nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust, gfx: if (mFullScreen == aFullScreen) return NS_OK; + // If a fullscreen is originated from chrome, we are switching to + // the fullscreen mode, otherwise, we are entering DOM fullscreen. + // Note that although entering DOM fullscreen could also cause + // consequential calls to this method, those calls will be skipped + // at the condition above. + if (aRequireTrust) { + mFullscreenMode = aFullScreen; + } else { + // If we are exiting from DOM fullscreen while we + // initially make the window fullscreen because of + // fullscreen mode, don't restore the window. + if (!aFullScreen && mFullscreenMode) { + return NS_OK; + } + } + // dispatch a "fullscreen" DOM event so that XUL apps can // respond visually if we are kicked into full screen mode if (!DispatchCustomEvent(NS_LITERAL_STRING("fullscreen"))) { diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 8d41e3668011..5ef8a39d4152 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -1538,6 +1538,7 @@ protected: // These members are only used on outer window objects. Make sure // you never set any of these on an inner object! bool mFullScreen : 1; + bool mFullscreenMode : 1; bool mIsClosed : 1; bool mInClose : 1; // mHavePendingClose means we've got a termination function set to diff --git a/dom/base/test/test_reentrant_flush.html b/dom/base/test/test_reentrant_flush.html index fe1088526efd..74ecbf23ac2d 100644 --- a/dom/base/test/test_reentrant_flush.html +++ b/dom/base/test/test_reentrant_flush.html @@ -48,12 +48,11 @@ addLoadEvent(function() { // Now flush out layout on the subdocument, to trigger the resize handler is(bod.getBoundingClientRect().width, 50, "Width of body should still be 50px"); - - is(resizeHandlerRan, true, "Resize handler should have run"); - - win.removeEventListener("resize", handleResize, false); - - SimpleTest.finish(); + window.requestAnimationFrame(function() { + is(resizeHandlerRan, true, "Resize handler should have run"); + win.removeEventListener("resize", handleResize, false); + SimpleTest.finish(); + }); }); diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 160aa57a1a28..eee66a176253 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -146,7 +146,7 @@ ErrorResult::ThrowErrorWithMessage(va_list ap, const dom::ErrNum errorNumber, MOZ_ASSERT(argCount <= 10); argCount = std::min(argCount, 10); while (argCount--) { - message->mArgs.AppendElement(*va_arg(ap, nsString*)); + message->mArgs.AppendElement(*va_arg(ap, const nsAString*)); } mMessage = message; #ifdef DEBUG diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 0aff220953a3..6fc2edf2801d 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -1049,7 +1049,7 @@ WrapNewBindingNonWrapperCachedObject(JSContext* cx, // We can end up here in all sorts of compartments, per above. Make // sure to JS_WrapValue! rval.set(JS::ObjectValue(*obj)); - return JS_WrapValue(cx, rval); + return MaybeWrapObjectValue(cx, rval); } // Create a JSObject wrapping "value", for cases when "value" is a @@ -1097,7 +1097,7 @@ WrapNewBindingNonWrapperCachedObject(JSContext* cx, // We can end up here in all sorts of compartments, per above. Make // sure to JS_WrapValue! rval.set(JS::ObjectValue(*obj)); - return JS_WrapValue(cx, rval); + return MaybeWrapObjectValue(cx, rval); } // Helper for smart pointers (nsRefPtr/nsCOMPtr). diff --git a/dom/cache/CacheStorage.cpp b/dom/cache/CacheStorage.cpp index a999e8e9a3f9..54563616203c 100644 --- a/dom/cache/CacheStorage.cpp +++ b/dom/cache/CacheStorage.cpp @@ -314,6 +314,30 @@ CacheStorage::PrefEnabled(JSContext* aCx, JSObject* aObj) return Cache::PrefEnabled(aCx, aObj); } +// static +already_AddRefed +CacheStorage::Constructor(const GlobalObject& aGlobal, + CacheStorageNamespace aNamespace, + nsIPrincipal* aPrincipal, ErrorResult& aRv) +{ + if (NS_WARN_IF(!NS_IsMainThread())) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + // TODO: remove Namespace in favor of CacheStorageNamespace + static_assert(DEFAULT_NAMESPACE == (uint32_t)CacheStorageNamespace::Content, + "Default namespace should match webidl Content enum"); + static_assert(CHROME_ONLY_NAMESPACE == (uint32_t)CacheStorageNamespace::Chrome, + "Chrome namespace should match webidl Chrome enum"); + static_assert(NUMBER_OF_NAMESPACES == (uint32_t)CacheStorageNamespace::EndGuard_, + "Number of namespace should match webidl endguard enum"); + + Namespace ns = static_cast(aNamespace); + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + return CreateOnMainThread(ns, global, aPrincipal, aRv); +} + nsISupports* CacheStorage::GetParentObject() const { diff --git a/dom/cache/CacheStorage.h b/dom/cache/CacheStorage.h index ff227e61e127..f646fcd72786 100644 --- a/dom/cache/CacheStorage.h +++ b/dom/cache/CacheStorage.h @@ -7,7 +7,6 @@ #ifndef mozilla_dom_cache_CacheStorage_h #define mozilla_dom_cache_CacheStorage_h -#include "mozilla/dom/CacheBinding.h" #include "mozilla/dom/cache/Types.h" #include "mozilla/dom/cache/TypeUtils.h" #include "nsAutoPtr.h" @@ -29,6 +28,7 @@ namespace ipc { namespace dom { +enum class CacheStorageNamespace : uint32_t; class Promise; namespace workers { @@ -64,6 +64,11 @@ public: already_AddRefed Delete(const nsAString& aKey, ErrorResult& aRv); already_AddRefed Keys(ErrorResult& aRv); + // chrome-only webidl interface methods + static already_AddRefed + Constructor(const GlobalObject& aGlobal, CacheStorageNamespace aNamespace, + nsIPrincipal* aPrincipal, ErrorResult& aRv); + // binding methods static bool PrefEnabled(JSContext* aCx, JSObject* aObj); diff --git a/dom/cache/moz.build b/dom/cache/moz.build index 5862c86d3986..3f672aafff39 100644 --- a/dom/cache/moz.build +++ b/dom/cache/moz.build @@ -94,3 +94,7 @@ FINAL_LIBRARY = 'xul' MOCHITEST_MANIFESTS += [ 'test/mochitest/mochitest.ini', ] + +MOCHITEST_CHROME_MANIFESTS += [ + 'test/mochitest/chrome.ini', +] diff --git a/dom/cache/test/mochitest/chrome.ini b/dom/cache/test/mochitest/chrome.ini new file mode 100644 index 000000000000..d607bf2d8173 --- /dev/null +++ b/dom/cache/test/mochitest/chrome.ini @@ -0,0 +1 @@ +[test_chrome_constructor.html] diff --git a/dom/cache/test/mochitest/test_chrome_constructor.html b/dom/cache/test/mochitest/test_chrome_constructor.html new file mode 100644 index 000000000000..f888d0614cfb --- /dev/null +++ b/dom/cache/test/mochitest/test_chrome_constructor.html @@ -0,0 +1,43 @@ + + + + + Validate Interfaces Exposed to Workers + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest.ini b/dom/canvas/test/webgl-mochitest.ini index c9e8c2b33d50..78339ed07574 100644 --- a/dom/canvas/test/webgl-mochitest.ini +++ b/dom/canvas/test/webgl-mochitest.ini @@ -21,6 +21,7 @@ skip-if = android_version == '10' || android_version == '18' #Android 2.3 and 4. [webgl-mochitest/test_noprog_draw.html] [webgl-mochitest/test_privileged_exts.html] [webgl-mochitest/test_texsubimage_float.html] +[webgl-mochitest/test_uninit_data.html] [webgl-mochitest/test_webgl_available.html] skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests #[webgl-mochitest/test_webgl_color_buffer_float.html] diff --git a/dom/canvas/test/webgl-mochitest/test_uninit_data.html b/dom/canvas/test/webgl-mochitest/test_uninit_data.html new file mode 100644 index 000000000000..541cfdbe51db --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/test_uninit_data.html @@ -0,0 +1,84 @@ + + + + + + Test contents of uninitialized buffers + + + + + + + + + + diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 869c45eeaaac..cb054eec40e4 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -1528,6 +1528,9 @@ EventStateManager::BeginTrackingDragGesture(nsPresContext* aPresContext, getter_AddRefs(mGestureDownContent)); mGestureDownFrameOwner = inDownFrame->GetContent(); + if (!mGestureDownFrameOwner) { + mGestureDownFrameOwner = mGestureDownContent; + } } mGestureModifiers = inDownEvent->modifiers; mGestureDownButtons = inDownEvent->buttons; diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp index efd4aa40907e..9116dfad082e 100644 --- a/dom/html/nsHTMLDocument.cpp +++ b/dom/html/nsHTMLDocument.cpp @@ -1566,13 +1566,6 @@ nsHTMLDocument::Open(JSContext* cx, return nullptr; } - // We can't depend on channels implementing property bags, so do our - // base URI manually after reset. - - if (rv.Failed()) { - return nullptr; - } - if (callerChannel) { nsLoadFlags callerLoadFlags; rv = callerChannel->GetLoadFlags(&callerLoadFlags); diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 3cc711e5c0d3..22e545baec7d 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -51,7 +51,7 @@ interface nsIJSRAIIHelper; interface nsIContentPermissionRequest; interface nsIObserver; -[scriptable, uuid(9bb4a34f-de8f-43d3-a58c-10757cac95a0)] +[scriptable, uuid(1a75c351-d115-4d51-94df-731dd1723a1f)] interface nsIDOMWindowUtils : nsISupports { /** @@ -613,6 +613,11 @@ interface nsIDOMWindowUtils : nsISupports { */ const unsigned long MOUSESCROLL_PREFER_WIDGET_AT_POINT = 0x00000001; + /** + * Interpret the scroll delta values as lines rather than pixels. + */ + const unsigned long MOUSESCROLL_SCROLL_LINES = 0x00000002; + /** * The platform specific values of aAdditionalFlags. Must be over 0x00010000. */ diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index c836a937a8bc..8e12205b2c3d 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -545,7 +545,7 @@ child: CacheFileDescriptor(nsString path, FileDescriptor fd); UpdateDimensions(IntRect rect, ScreenIntSize size, ScreenOrientation orientation, - LayoutDeviceIntPoint chromeDisp) compress; + LayoutDeviceIntPoint chromeDisp) compressall; UpdateFrame(FrameMetrics frame); diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index a5a8e3dfda42..39bab3e59cbf 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -934,11 +934,20 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType, if (aReason == MediaDecoderReader::WAITING_FOR_DATA) { MOZ_ASSERT(mReader->IsWaitForDataSupported(), "Readers that send WAITING_FOR_DATA need to implement WaitForData"); + nsRefPtr self = this; WaitRequestRef(aType).Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::WaitForData, aType) - ->RefableThen(TaskQueue(), __func__, this, - &MediaDecoderStateMachine::OnWaitForDataResolved, - &MediaDecoderStateMachine::OnWaitForDataRejected)); + ->RefableThen(TaskQueue(), __func__, + [self] (MediaData::Type aType) -> void { + ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor()); + self->WaitRequestRef(aType).Complete(); + self->DispatchDecodeTasksIfNeeded(); + }, + [self] (WaitForDataRejectValue aRejection) -> void { + ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor()); + self->WaitRequestRef(aRejection.mType).Complete(); + })); + return; } @@ -1901,12 +1910,24 @@ MediaDecoderStateMachine::InitiateSeek() Reset(); // Do the seek. + nsRefPtr self = this; mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime, GetEndTime()) - ->RefableThen(TaskQueue(), __func__, this, - &MediaDecoderStateMachine::OnSeekCompleted, - &MediaDecoderStateMachine::OnSeekFailed)); + ->RefableThen(TaskQueue(), __func__, + [self] (int64_t) -> void { + ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor()); + self->mSeekRequest.Complete(); + // We must decode the first samples of active streams, so we can determine + // the new stream time. So dispatch tasks to do that. + self->mDecodeToSeekTarget = true; + self->DispatchDecodeTasksIfNeeded(); + }, [self] (nsresult aResult) -> void { + ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor()); + self->mSeekRequest.Complete(); + MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest"); + self->DecodeError(); + })); } nsresult @@ -2386,30 +2407,6 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame() return NS_OK; } -void -MediaDecoderStateMachine::OnSeekCompleted(int64_t aTime) -{ - MOZ_ASSERT(OnTaskQueue()); - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - mSeekRequest.Complete(); - - // We must decode the first samples of active streams, so we can determine - // the new stream time. So dispatch tasks to do that. - mDecodeToSeekTarget = true; - - DispatchDecodeTasksIfNeeded(); -} - -void -MediaDecoderStateMachine::OnSeekFailed(nsresult aResult) -{ - MOZ_ASSERT(OnTaskQueue()); - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - mSeekRequest.Complete(); - MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest"); - DecodeError(); -} - void MediaDecoderStateMachine::SeekCompleted() { diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 2fbbc24cfcd8..c382490d06f2 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -427,22 +427,6 @@ public: OnNotDecoded(MediaData::VIDEO_DATA, aReason); } - void OnSeekCompleted(int64_t aTime); - void OnSeekFailed(nsresult aResult); - - void OnWaitForDataResolved(MediaData::Type aType) - { - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - WaitRequestRef(aType).Complete(); - DispatchDecodeTasksIfNeeded(); - } - - void OnWaitForDataRejected(WaitForDataRejectValue aRejection) - { - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - WaitRequestRef(aRejection.mType).Complete(); - } - // Resets all state related to decoding and playback, emptying all buffers // and aborting all pending operations on the decode task queue. void Reset(); diff --git a/dom/media/MediaPromise.h b/dom/media/MediaPromise.h index 61c914f5e35f..efd17839e735 100644 --- a/dom/media/MediaPromise.h +++ b/dom/media/MediaPromise.h @@ -49,6 +49,31 @@ class MediaPromise public: typedef ResolveValueT ResolveValueType; typedef RejectValueT RejectValueType; + class ResolveOrRejectValue + { + public: + void SetResolve(ResolveValueType& aResolveValue) + { + MOZ_ASSERT(IsNothing()); + mResolveValue.emplace(aResolveValue); + } + + void SetReject(RejectValueType& aRejectValue) + { + MOZ_ASSERT(IsNothing()); + mRejectValue.emplace(aRejectValue); + } + + bool IsResolve() const { return mResolveValue.isSome(); } + bool IsReject() const { return mRejectValue.isSome(); } + bool IsNothing() const { return mResolveValue.isNothing() && mRejectValue.isNothing(); } + ResolveValueType& ResolveValue() { return mResolveValue.ref(); } + RejectValueType& RejectValue() { return mRejectValue.ref(); } + + private: + Maybe mResolveValue; + Maybe mRejectValue; + }; NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPromise) @@ -119,64 +144,74 @@ protected: class ThenValueBase : public Consumer { public: - class ResolveRunnable : public nsRunnable + class ResolveOrRejectRunnable : public nsRunnable { public: - ResolveRunnable(ThenValueBase* aThenValue, ResolveValueType aResolveValue) + ResolveOrRejectRunnable(ThenValueBase* aThenValue, ResolveOrRejectValue& aValue) : mThenValue(aThenValue) - , mResolveValue(aResolveValue) {} + , mValue(aValue) {} - ~ResolveRunnable() + ~ResolveOrRejectRunnable() { MOZ_DIAGNOSTIC_ASSERT(!mThenValue || mThenValue->IsDisconnected()); } NS_IMETHODIMP Run() { - PROMISE_LOG("ResolveRunnable::Run() [this=%p]", this); - mThenValue->DoResolve(mResolveValue); + PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this); + mThenValue->DoResolveOrReject(mValue); mThenValue = nullptr; return NS_OK; } private: nsRefPtr mThenValue; - ResolveValueType mResolveValue; + ResolveOrRejectValue mValue; }; - class RejectRunnable : public nsRunnable + explicit ThenValueBase(AbstractThread* aResponseTarget, const char* aCallSite) + : mResponseTarget(aResponseTarget), mCallSite(aCallSite) {} + + void Dispatch(MediaPromise *aPromise) { - public: - RejectRunnable(ThenValueBase* aThenValue, RejectValueType aRejectValue) - : mThenValue(aThenValue) - , mRejectValue(aRejectValue) {} + aPromise->mMutex.AssertCurrentThreadOwns(); + MOZ_ASSERT(!aPromise->IsPending()); - ~RejectRunnable() - { - MOZ_DIAGNOSTIC_ASSERT(!mThenValue || mThenValue->IsDisconnected()); - } + nsRefPtr runnable = + static_cast(new (typename ThenValueBase::ResolveOrRejectRunnable)(this, aPromise->mValue)); + PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]", + aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", ThenValueBase::mCallSite, + runnable.get(), aPromise, this); - NS_IMETHODIMP Run() - { - PROMISE_LOG("RejectRunnable::Run() [this=%p]", this); - mThenValue->DoReject(mRejectValue); - mThenValue = nullptr; - return NS_OK; - } + // Promise consumers are allowed to disconnect the Consumer object and + // then shut down the thread or task queue that the promise result would + // be dispatched on. So we unfortunately can't assert that promise + // dispatch succeeds. :-( + mResponseTarget->Dispatch(runnable.forget(), AbstractThread::DontAssertDispatchSuccess); + } - private: - nsRefPtr mThenValue; - RejectValueType mRejectValue; - }; - - explicit ThenValueBase(const char* aCallSite) : mCallSite(aCallSite) {} - - virtual void Dispatch(MediaPromise *aPromise) = 0; + virtual void Disconnect() override + { + MOZ_ASSERT(ThenValueBase::mResponseTarget->IsCurrentThreadIn()); + MOZ_DIAGNOSTIC_ASSERT(!Consumer::mComplete); + Consumer::mDisconnected = true; + } protected: - virtual void DoResolve(ResolveValueType aResolveValue) = 0; - virtual void DoReject(RejectValueType aRejectValue) = 0; + virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) = 0; + void DoResolveOrReject(ResolveOrRejectValue& aValue) + { + Consumer::mComplete = true; + if (Consumer::mDisconnected) { + PROMISE_LOG("ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]", this); + return; + } + + DoResolveOrRejectInternal(aValue); + } + + nsRefPtr mResponseTarget; // May be released on any thread. const char* mCallSite; }; @@ -207,51 +242,20 @@ protected: } template - class ThenValue : public ThenValueBase + class MethodThenValue : public ThenValueBase { public: - ThenValue(AbstractThread* aResponseTarget, ThisType* aThisVal, - ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod, - const char* aCallSite) - : ThenValueBase(aCallSite) - , mResponseTarget(aResponseTarget) + MethodThenValue(AbstractThread* aResponseTarget, ThisType* aThisVal, + ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod, + const char* aCallSite) + : ThenValueBase(aResponseTarget, aCallSite) , mThisVal(aThisVal) , mResolveMethod(aResolveMethod) , mRejectMethod(aRejectMethod) {} - void Dispatch(MediaPromise *aPromise) override - { - aPromise->mMutex.AssertCurrentThreadOwns(); - MOZ_ASSERT(!aPromise->IsPending()); - bool resolved = aPromise->mResolveValue.isSome(); - nsRefPtr runnable = - resolved ? static_cast(new (typename ThenValueBase::ResolveRunnable)(this, aPromise->mResolveValue.ref())) - : static_cast(new (typename ThenValueBase::RejectRunnable)(this, aPromise->mRejectValue.ref())); - PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]", - resolved ? "Resolving" : "Rejecting", ThenValueBase::mCallSite, - runnable.get(), aPromise, this); - - // Promise consumers are allowed to disconnect the Consumer object and - // then shut down the thread or task queue that the promise result would - // be dispatched on. So we unfortunately can't assert that promise - // dispatch succeeds. :-( - mResponseTarget->Dispatch(runnable.forget(), AbstractThread::DontAssertDispatchSuccess); - } - -#ifdef DEBUG - void AssertOnDispatchThread() - { - MOZ_ASSERT(mResponseTarget->IsCurrentThreadIn()); - } -#else - void AssertOnDispatchThread() {} -#endif - virtual void Disconnect() override { - AssertOnDispatchThread(); - MOZ_DIAGNOSTIC_ASSERT(!Consumer::mComplete); - Consumer::mDisconnected = true; + ThenValueBase::Disconnect(); // If a Consumer has been disconnected, we don't guarantee that the // resolve/reject runnable will be dispatched. Null out our refcounted @@ -260,32 +264,13 @@ protected: } protected: - virtual void DoResolve(ResolveValueType aResolveValue) override + virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override { - Consumer::mComplete = true; - if (Consumer::mDisconnected) { - MOZ_ASSERT(!mThisVal); - PROMISE_LOG("ThenValue::DoResolve disconnected - bailing out [this=%p]", this); - return; + if (aValue.IsResolve()) { + InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aValue.ResolveValue()); + } else { + InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aValue.RejectValue()); } - InvokeCallbackMethod(mThisVal.get(), mResolveMethod, aResolveValue); - - // Null out mThisVal after invoking the callback so that any references are - // released predictably on the dispatch thread. Otherwise, it would be - // released on whatever thread last drops its reference to the ThenValue, - // which may or may not be ok. - mThisVal = nullptr; - } - - virtual void DoReject(RejectValueType aRejectValue) override - { - Consumer::mComplete = true; - if (Consumer::mDisconnected) { - MOZ_ASSERT(!mThisVal); - PROMISE_LOG("ThenValue::DoReject disconnected - bailing out [this=%p]", this); - return; - } - InvokeCallbackMethod(mThisVal.get(), mRejectMethod, aRejectValue); // Null out mThisVal after invoking the callback so that any references are // released predictably on the dispatch thread. Otherwise, it would be @@ -295,31 +280,96 @@ protected: } private: - nsRefPtr mResponseTarget; // May be released on any thread. nsRefPtr mThisVal; // Only accessed and refcounted on dispatch thread. ResolveMethodType mResolveMethod; RejectMethodType mRejectMethod; }; + + // NB: We could use std::function here instead of a template if it were supported. :-( + template + class FunctionThenValue : public ThenValueBase + { + public: + FunctionThenValue(AbstractThread* aResponseTarget, + ResolveFunction&& aResolveFunction, + RejectFunction&& aRejectFunction, + const char* aCallSite) + : ThenValueBase(aResponseTarget, aCallSite) + { + mResolveFunction.emplace(Move(aResolveFunction)); + mRejectFunction.emplace(Move(aRejectFunction)); + } + + virtual void Disconnect() override + { + ThenValueBase::Disconnect(); + + // If a Consumer has been disconnected, we don't guarantee that the + // resolve/reject runnable will be dispatched. Destroy our callbacks + // now so that any references in closures are released predictable on + // the dispatch thread. + mResolveFunction.reset(); + mRejectFunction.reset(); + } + + protected: + virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override + { + if (aValue.IsResolve()) { + mResolveFunction.ref()(aValue.ResolveValue()); + } else { + mRejectFunction.ref()(aValue.RejectValue()); + } + + // Destroy callbacks after invocation so that any references in closures are + // released predictably on the dispatch thread. Otherwise, they would be + // released on whatever thread last drops its reference to the ThenValue, + // which may or may not be ok. + mResolveFunction.reset(); + mRejectFunction.reset(); + } + + private: + Maybe mResolveFunction; // Only accessed and deleted on dispatch thread. + Maybe mRejectFunction; // Only accessed and deleted on dispatch thread. + }; + +public: + void ThenInternal(AbstractThread* aResponseThread, ThenValueBase* aThenValue, + const char* aCallSite) + { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(aResponseThread->IsDispatchReliable()); + MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveConsumer); + mHaveConsumer = true; + PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]", + aCallSite, this, aThenValue, (int) IsPending()); + if (!IsPending()) { + aThenValue->Dispatch(this); + } else { + mThenValues.AppendElement(aThenValue); + } + } + public: template already_AddRefed RefableThen(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal, ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod) { - MutexAutoLock lock(mMutex); - MOZ_ASSERT(aResponseThread->IsDispatchReliable()); - MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveConsumer); - mHaveConsumer = true; - nsRefPtr thenValue = new ThenValue( + nsRefPtr thenValue = new MethodThenValue( aResponseThread, aThisVal, aResolveMethod, aRejectMethod, aCallSite); - PROMISE_LOG("%s invoking Then() [this=%p, thenValue=%p, aThisVal=%p, isPending=%d]", - aCallSite, this, thenValue.get(), aThisVal, (int) IsPending()); - if (!IsPending()) { - thenValue->Dispatch(this); - } else { - mThenValues.AppendElement(thenValue); - } + ThenInternal(aResponseThread, thenValue, aCallSite); + return thenValue.forget(); + } + template + already_AddRefed RefableThen(AbstractThread* aResponseThread, const char* aCallSite, + ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction) + { + nsRefPtr thenValue = new FunctionThenValue(aResponseThread, + Move(aResolveFunction), Move(aRejectFunction), aCallSite); + ThenInternal(aResponseThread, thenValue, aCallSite); return thenValue.forget(); } @@ -332,6 +382,15 @@ public: return; } + template + void Then(AbstractThread* aResponseThread, const char* aCallSite, + ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction) + { + nsRefPtr c = + RefableThen(aResponseThread, aCallSite, Move(aResolveFunction), Move(aRejectFunction)); + return; + } + void ChainTo(already_AddRefed aChainedPromise, const char* aCallSite) { MutexAutoLock lock(mMutex); @@ -348,7 +407,7 @@ public: } protected: - bool IsPending() { return mResolveValue.isNothing() && mRejectValue.isNothing(); } + bool IsPending() { return mValue.IsNothing(); } void DispatchAll() { mMutex.AssertCurrentThreadOwns(); @@ -366,10 +425,10 @@ protected: void ForwardTo(Private* aOther) { MOZ_ASSERT(!IsPending()); - if (mResolveValue.isSome()) { - aOther->Resolve(mResolveValue.ref(), ""); + if (mValue.IsResolve()) { + aOther->Resolve(mValue.ResolveValue(), ""); } else { - aOther->Reject(mRejectValue.ref(), ""); + aOther->Reject(mValue.RejectValue(), ""); } } @@ -383,8 +442,7 @@ protected: const char* mCreationSite; // For logging Mutex mMutex; - Maybe mResolveValue; - Maybe mRejectValue; + ResolveOrRejectValue mValue; nsTArray> mThenValues; nsTArray> mChainedPromises; bool mHaveConsumer; @@ -402,7 +460,7 @@ public: MutexAutoLock lock(mMutex); MOZ_ASSERT(IsPending()); PROMISE_LOG("%s resolving MediaPromise (%p created at %s)", aResolveSite, this, mCreationSite); - mResolveValue.emplace(aResolveValue); + mValue.SetResolve(aResolveValue); DispatchAll(); } @@ -411,7 +469,7 @@ public: MutexAutoLock lock(mMutex); MOZ_ASSERT(IsPending()); PROMISE_LOG("%s rejecting MediaPromise (%p created at %s)", aRejectSite, this, mCreationSite); - mRejectValue.emplace(aRejectValue); + mValue.SetReject(aRejectValue); DispatchAll(); } }; diff --git a/dom/media/fmp4/SharedDecoderManager.cpp b/dom/media/fmp4/SharedDecoderManager.cpp index bc908f258f26..7c35f02d724c 100644 --- a/dom/media/fmp4/SharedDecoderManager.cpp +++ b/dom/media/fmp4/SharedDecoderManager.cpp @@ -98,9 +98,12 @@ SharedDecoderManager::CreateVideoDecoder( mPDM = nullptr; return nullptr; } - mPDM = aPDM; nsresult rv = mDecoder->Init(); - NS_ENSURE_SUCCESS(rv, nullptr); + if (NS_FAILED(rv)) { + mDecoder = nullptr; + return nullptr; + } + mPDM = aPDM; } nsRefPtr proxy(new SharedDecoderProxy(this, aCallback)); @@ -147,11 +150,19 @@ void SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy) { if (aProxy && mActiveProxy == aProxy) { - mWaitForInternalDrain = true; - mActiveProxy->Drain(); MonitorAutoLock mon(mMonitor); - while (mWaitForInternalDrain) { - mon.Wait(); + mWaitForInternalDrain = true; + nsresult rv; + { + // We don't want to hold the lock while calling Drain() has some + // platform implementations call DrainComplete() immediately. + MonitorAutoUnlock mon(mMonitor); + rv = mActiveProxy->Drain(); + } + if (NS_SUCCEEDED(rv)) { + while (mWaitForInternalDrain) { + mon.Wait(); + } } mActiveProxy->Flush(); mActiveProxy = nullptr; @@ -161,13 +172,15 @@ SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy) void SharedDecoderManager::DrainComplete() { - if (mWaitForInternalDrain) { + { MonitorAutoLock mon(mMonitor); - mWaitForInternalDrain = false; - mon.NotifyAll(); - } else { - mActiveCallback->DrainComplete(); + if (mWaitForInternalDrain) { + mWaitForInternalDrain = false; + mon.NotifyAll(); + return; + } } + mActiveCallback->DrainComplete(); } void diff --git a/dom/media/fmp4/apple/AppleVDADecoder.cpp b/dom/media/fmp4/apple/AppleVDADecoder.cpp index d5e903006788..bac166b2bd11 100644 --- a/dom/media/fmp4/apple/AppleVDADecoder.cpp +++ b/dom/media/fmp4/apple/AppleVDADecoder.cpp @@ -364,6 +364,7 @@ AppleVDADecoder::SubmitFrame(MediaRawData* aSample) if (rv != noErr) { NS_WARNING("AppleVDADecoder: Couldn't pass frame to decoder"); + mCallback->Error(); return NS_ERROR_FAILURE; } diff --git a/dom/media/fmp4/apple/AppleVTDecoder.cpp b/dom/media/fmp4/apple/AppleVTDecoder.cpp index ecc62300bdb7..dd023c0a8201 100644 --- a/dom/media/fmp4/apple/AppleVTDecoder.cpp +++ b/dom/media/fmp4/apple/AppleVTDecoder.cpp @@ -168,8 +168,7 @@ PlatformCallback(void* decompressionOutputRefCon, return; } if (flags & kVTDecodeInfo_FrameDropped) { - NS_WARNING(" ...frame dropped..."); - return; + NS_WARNING(" ...frame tagged as dropped..."); } MOZ_ASSERT(CFGetTypeID(image) == CVPixelBufferGetTypeID(), "VideoToolbox returned an unexpected image type"); @@ -246,7 +245,9 @@ AppleVTDecoder::SubmitFrame(MediaRawData* aSample) CreateAppleFrameRef(aSample), &infoFlags); if (rv != noErr && !(infoFlags & kVTDecodeInfo_FrameDropped)) { + LOG("AppleVTDecoder: Error %d VTDecompressionSessionDecodeFrame", rv); NS_WARNING("Couldn't pass frame to decoder"); + mCallback->Error(); return NS_ERROR_FAILURE; } diff --git a/dom/tests/mochitest/bugs/test_sizetocontent_clamp.html b/dom/tests/mochitest/bugs/test_sizetocontent_clamp.html index 95f1eff6604a..517f99e66cbe 100644 --- a/dom/tests/mochitest/bugs/test_sizetocontent_clamp.html +++ b/dom/tests/mochitest/bugs/test_sizetocontent_clamp.html @@ -37,23 +37,21 @@ var innerWidthMax = (isWin8 ? 125 : 100); function test() { var w = window.open('data:text/html,null', null, 'width=300,height=300'); - var nbResize = 0; SimpleTest.waitForFocus(function() { w.onresize = function() { - nbResize++; - - if (nbResize == 1) { + if (!(w.innerWidth + epsilon >= innerWidthMin && + w.innerWidth - epsilon <= innerWidthMax)) { + // We need still another resize event. return; } - - ok(w.innerWidth + epsilon >= innerWidthMin && w.innerWidth - epsilon <= innerWidthMax, - "innerWidth should be between " + innerWidthMin + " and " + innerWidthMax); - ok(w.innerHeight + epsilon >= 100 && w.innerHeight - epsilon <= 100, - "innerHeight should be around 100"); - - // It's not clear why 2 events are coming... - is(nbResize, 2, "We should get 2 events."); + if (!(w.innerHeight + epsilon >= 100 && + w.innerHeight - epsilon <= 100)) { + // ditto + return; + } + ok(true, "innerWidth should be between " + innerWidthMin + " and " + innerWidthMax); + ok(true, "innerHeight should be around 100"); w.close(); diff --git a/dom/tests/mochitest/chrome/MozEnteredDomFullscreen_chrome.xul b/dom/tests/mochitest/chrome/MozDomFullscreen_chrome.xul similarity index 85% rename from dom/tests/mochitest/chrome/MozEnteredDomFullscreen_chrome.xul rename to dom/tests/mochitest/chrome/MozDomFullscreen_chrome.xul index 63fa30e71ec4..5673b1b1c5fa 100644 --- a/dom/tests/mochitest/chrome/MozEnteredDomFullscreen_chrome.xul +++ b/dom/tests/mochitest/chrome/MozDomFullscreen_chrome.xul @@ -60,7 +60,18 @@ function thirdEntry(event) { is(event.target, gOuterDoc, "Third MozEnteredDomFullscreen should be targeted at outer doc"); ok(gOuterDoc.mozFullScreenElement != null, "Outer doc return to fullscreen after cancel fullscreen in inner doc"); window.removeEventListener("MozEnteredDomFullscreen", thirdEntry, false); + window.removeEventListener("MozExitedDomFullscreen", earlyExit, false); + window.addEventListener("MozExitedDomFullscreen", lastExit, false); gOuterDoc.mozCancelFullScreen(); +} + +function earlyExit(event) { + ok(false, "MozExitedDomFullscreen should only be triggered after cancel all fullscreen"); +} + +function lastExit(event) { + is(event.target, gOuterDoc, "MozExitedDomFullscreen should be targeted at the last exited doc"); + ok(gOuterDoc.mozFullScreenElement == null, "Fullscreen should have been fully exited"); window.opener.wrappedJSObject.done(); } @@ -71,6 +82,7 @@ function start() { gOuterDoc = gBrowser.contentDocument; gBrowser.contentWindow.focus(); window.addEventListener("MozEnteredDomFullscreen", firstEntry, false); + window.addEventListener("MozExitedDomFullscreen", earlyExit, false); gOuterDoc.body.mozRequestFullScreen(); }); } @@ -78,6 +90,6 @@ function start() { ]]> - + diff --git a/dom/tests/mochitest/chrome/chrome.ini b/dom/tests/mochitest/chrome/chrome.ini index 77119435002d..52950fd53f42 100644 --- a/dom/tests/mochitest/chrome/chrome.ini +++ b/dom/tests/mochitest/chrome/chrome.ini @@ -4,7 +4,7 @@ support-files = 489127.html DOMWindowCreated_chrome.xul DOMWindowCreated_content.html - MozEnteredDomFullscreen_chrome.xul + MozDomFullscreen_chrome.xul child_focus_frame.html file_DOM_element_instanceof.xul file_bug799299.xul @@ -49,7 +49,7 @@ skip-if = os == 'linux' [test_indexedSetter.html] [test_moving_nodeList.xul] [test_moving_xhr.xul] -[test_MozEnteredDomFullscreen_event.xul] +[test_MozDomFullscreen_event.xul] # disabled on OS X for intermittent failures--bug-798848 skip-if = toolkit == 'cocoa' [test_nodesFromRect.html] diff --git a/dom/tests/mochitest/chrome/test_MozEnteredDomFullscreen_event.xul b/dom/tests/mochitest/chrome/test_MozDomFullscreen_event.xul similarity index 93% rename from dom/tests/mochitest/chrome/test_MozEnteredDomFullscreen_event.xul rename to dom/tests/mochitest/chrome/test_MozDomFullscreen_event.xul index d9ab22684b57..076e9db4e49c 100644 --- a/dom/tests/mochitest/chrome/test_MozEnteredDomFullscreen_event.xul +++ b/dom/tests/mochitest/chrome/test_MozDomFullscreen_event.xul @@ -36,7 +36,7 @@ SpecialPowers.pushPrefEnv({"set": [['full-screen-api.enabled', true], ['full-screen-api.allow-trusted-requests-only', false]]}, setup); function setup() { - newwindow = window.open("MozEnteredDomFullscreen_chrome.xul", "_blank","chrome,resizable=yes,width=400,height=400"); + newwindow = window.open("MozDomFullscreen_chrome.xul", "_blank","chrome,resizable=yes,width=400,height=400"); } function done() diff --git a/dom/tests/mochitest/general/file_MozEnteredDomFullscreen.html b/dom/tests/mochitest/general/file_MozDomFullscreen.html similarity index 100% rename from dom/tests/mochitest/general/file_MozEnteredDomFullscreen.html rename to dom/tests/mochitest/general/file_MozDomFullscreen.html diff --git a/dom/tests/mochitest/general/mochitest.ini b/dom/tests/mochitest/general/mochitest.ini index 492a2565189c..5cd5f731391e 100644 --- a/dom/tests/mochitest/general/mochitest.ini +++ b/dom/tests/mochitest/general/mochitest.ini @@ -1,7 +1,7 @@ [DEFAULT] support-files = 497633.html - file_MozEnteredDomFullscreen.html + file_MozDomFullscreen.html file_bug628069.html file_clonewrapper.html file_domWindowUtils_scrollbarSize.html diff --git a/dom/webidl/Cache.webidl b/dom/webidl/Cache.webidl index 7df69a8f6e0c..b2c698bb0703 100644 --- a/dom/webidl/Cache.webidl +++ b/dom/webidl/Cache.webidl @@ -13,32 +13,32 @@ [Exposed=(Window,Worker), Func="mozilla::dom::cache::Cache::PrefEnabled"] interface Cache { -[Throws] -Promise match(RequestInfo request, optional CacheQueryOptions options); -[Throws] -Promise> matchAll(optional RequestInfo request, optional CacheQueryOptions options); -[Throws] -Promise add(RequestInfo request); -[Throws] -Promise addAll(sequence requests); -[Throws] -Promise put(RequestInfo request, Response response); -[Throws] -Promise delete(RequestInfo request, optional CacheQueryOptions options); -[Throws] -Promise> keys(optional RequestInfo request, optional CacheQueryOptions options); + [NewObject] + Promise match(RequestInfo request, optional CacheQueryOptions options); + [NewObject] + Promise> matchAll(optional RequestInfo request, optional CacheQueryOptions options); + [NewObject] + Promise add(RequestInfo request); + [NewObject] + Promise addAll(sequence requests); + [NewObject] + Promise put(RequestInfo request, Response response); + [NewObject] + Promise delete(RequestInfo request, optional CacheQueryOptions options); + [NewObject] + Promise> keys(optional RequestInfo request, optional CacheQueryOptions options); }; dictionary CacheQueryOptions { -boolean ignoreSearch = false; -boolean ignoreMethod = false; -boolean ignoreVary = false; -DOMString cacheName; + boolean ignoreSearch = false; + boolean ignoreMethod = false; + boolean ignoreVary = false; + DOMString cacheName; }; dictionary CacheBatchOperation { -DOMString type; -Request request; -Response response; -CacheQueryOptions options; + DOMString type; + Request request; + Response response; + CacheQueryOptions options; }; diff --git a/dom/webidl/CacheStorage.webidl b/dom/webidl/CacheStorage.webidl index ef845a64a1a5..cf5bad44b201 100644 --- a/dom/webidl/CacheStorage.webidl +++ b/dom/webidl/CacheStorage.webidl @@ -10,17 +10,25 @@ // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#cache-storage +interface Principal; + [Exposed=(Window,Worker), + ChromeConstructor(CacheStorageNamespace namespace, Principal principal), Func="mozilla::dom::cache::CacheStorage::PrefEnabled"] interface CacheStorage { -[Throws] -Promise match(RequestInfo request, optional CacheQueryOptions options); -[Throws] -Promise has(DOMString cacheName); -[Throws] -Promise open(DOMString cacheName); -[Throws] -Promise delete(DOMString cacheName); -[Throws] -Promise> keys(); + [NewObject] + Promise match(RequestInfo request, optional CacheQueryOptions options); + [NewObject] + Promise has(DOMString cacheName); + [NewObject] + Promise open(DOMString cacheName); + [NewObject] + Promise delete(DOMString cacheName); + [NewObject] + Promise> keys(); +}; + +// chrome-only, gecko specific extension +enum CacheStorageNamespace { + "content", "chrome" }; diff --git a/dom/webidl/MozSelfSupport.webidl b/dom/webidl/MozSelfSupport.webidl index 7ba73ba1f6c5..c4345bcc088b 100644 --- a/dom/webidl/MozSelfSupport.webidl +++ b/dom/webidl/MozSelfSupport.webidl @@ -50,4 +50,9 @@ interface MozSelfSupport * The name of the pref to reset. */ void resetPref(DOMString name); + + /** + * Resets original search engines, and resets the default one. + */ + void resetSearchEngines(); }; diff --git a/dom/webidl/URL.webidl b/dom/webidl/URL.webidl index 7fe970104372..dd28dec22ff9 100644 --- a/dom/webidl/URL.webidl +++ b/dom/webidl/URL.webidl @@ -14,7 +14,7 @@ // [Constructor(DOMString url, optional (URL or DOMString) base = "about:blank")] [Constructor(DOMString url, URL base), - Constructor(DOMString url, optional DOMString base = "about:blank"), + Constructor(DOMString url, optional DOMString base), Exposed=(Window,Worker)] interface URL { }; diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 5e50d394e4c3..9693ff629ade 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -16,6 +16,7 @@ #include "nsIHttpHeaderVisitor.h" #include "nsINetworkInterceptController.h" #include "nsIMutableArray.h" +#include "nsIUploadChannel2.h" #include "nsPIDOMWindow.h" #include "nsScriptLoader.h" #include "nsDebug.h" @@ -2357,6 +2358,7 @@ class FetchEventRunnable : public WorkerRunnable RequestMode mRequestMode; RequestCredentials mRequestCredentials; nsContentPolicyType mContentPolicyType; + nsCOMPtr mUploadStream; public: FetchEventRunnable(WorkerPrivate* aWorkerPrivate, nsMainThreadPtrHandle& aChannel, @@ -2451,6 +2453,13 @@ public: mContentPolicyType = loadInfo->GetContentPolicyType(); + nsCOMPtr uploadChannel = do_QueryInterface(httpChannel); + if (uploadChannel) { + MOZ_ASSERT(!mUploadStream); + rv = uploadChannel->CloneUploadStream(getter_AddRefs(mUploadStream)); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; } @@ -2510,8 +2519,6 @@ private: reqInit.mHeaders.Construct(); reqInit.mHeaders.Value().SetAsHeaders() = headers; - //TODO(jdm): set request body - reqInit.mMode.Construct(mRequestMode); reqInit.mCredentials.Construct(mRequestCredentials); @@ -2525,6 +2532,8 @@ private: MOZ_ASSERT(internalReq); internalReq->SetCreatedByFetchEvent(); + internalReq->SetBody(mUploadStream); + request->SetContentPolicyType(mContentPolicyType); RootedDictionary init(aCx); diff --git a/dom/workers/URL.cpp b/dom/workers/URL.cpp index 21e10359c2b3..012c68270fe0 100644 --- a/dom/workers/URL.cpp +++ b/dom/workers/URL.cpp @@ -34,7 +34,7 @@ class URLProxy final public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URLProxy) - explicit URLProxy(mozilla::dom::URL* aURL) + explicit URLProxy(already_AddRefed aURL) : mURL(aURL) { AssertIsOnMainThread(); @@ -228,7 +228,7 @@ class ConstructorRunnable : public WorkerMainThreadRunnable private: const nsString mURL; - const nsString mBase; + nsString mBase; // IsVoid() if we have no base URI string. nsRefPtr mBaseProxy; mozilla::ErrorResult& mRv; @@ -236,13 +236,17 @@ private: public: ConstructorRunnable(WorkerPrivate* aWorkerPrivate, - const nsAString& aURL, const nsAString& aBase, + const nsAString& aURL, const Optional& aBase, mozilla::ErrorResult& aRv) : WorkerMainThreadRunnable(aWorkerPrivate) , mURL(aURL) - , mBase(aBase) , mRv(aRv) { + if (aBase.WasPassed()) { + mBase = aBase.Value(); + } else { + mBase.SetIsVoid(true); + } mWorkerPrivate->AssertIsOnWorkerThread(); } @@ -254,6 +258,7 @@ public: , mBaseProxy(aBaseProxy) , mRv(aRv) { + mBase.SetIsVoid(true); mWorkerPrivate->AssertIsOnWorkerThread(); } @@ -262,35 +267,20 @@ public: { AssertIsOnMainThread(); - nsresult rv; - nsCOMPtr ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv)); - if (NS_FAILED(rv)) { - mRv.Throw(rv); - return true; - } - - nsCOMPtr baseURL; - - if (!mBaseProxy) { - rv = ioService->NewURI(NS_ConvertUTF16toUTF8(mBase), nullptr, nullptr, - getter_AddRefs(baseURL)); - if (NS_FAILED(rv)) { - mRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return true; - } + nsRefPtr url; + if (mBaseProxy) { + url = mozilla::dom::URL::Constructor(mURL, mBaseProxy->URI(), mRv); + } else if (!mBase.IsVoid()) { + url = mozilla::dom::URL::Constructor(mURL, mBase, mRv); } else { - baseURL = mBaseProxy->URI(); + url = mozilla::dom::URL::Constructor(mURL, nullptr, mRv); } - nsCOMPtr url; - rv = ioService->NewURI(NS_ConvertUTF16toUTF8(mURL), nullptr, baseURL, - getter_AddRefs(url)); - if (NS_FAILED(rv)) { - mRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + if (mRv.Failed()) { return true; } - mRetval = new URLProxy(new mozilla::dom::URL(url)); + mRetval = new URLProxy(url.forget()); return true; } @@ -522,18 +512,21 @@ URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, nsRefPtr runnable = new ConstructorRunnable(workerPrivate, aUrl, aBase.GetURLProxy(), aRv); - if (!runnable->Dispatch(cx)) { - JS_ReportPendingException(cx); - } + return FinishConstructor(cx, workerPrivate, runnable, aRv); +} - nsRefPtr proxy = runnable->GetURLProxy(); - if (!proxy) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return nullptr; - } +// static +already_AddRefed +URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, + const Optional& aBase, ErrorResult& aRv) +{ + JSContext* cx = aGlobal.Context(); + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx); - nsRefPtr url = new URL(workerPrivate, proxy); - return url.forget(); + nsRefPtr runnable = + new ConstructorRunnable(workerPrivate, aUrl, aBase, aRv); + + return FinishConstructor(cx, workerPrivate, runnable, aRv); } // static @@ -544,20 +537,34 @@ URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, JSContext* cx = aGlobal.Context(); WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx); + Optional base; + base = &aBase; nsRefPtr runnable = - new ConstructorRunnable(workerPrivate, aUrl, aBase, aRv); + new ConstructorRunnable(workerPrivate, aUrl, base, aRv); - if (!runnable->Dispatch(cx)) { - JS_ReportPendingException(cx); + return FinishConstructor(cx, workerPrivate, runnable, aRv); +} + +// static +already_AddRefed +URL::FinishConstructor(JSContext* aCx, WorkerPrivate* aPrivate, + ConstructorRunnable* aRunnable, ErrorResult& aRv) +{ + if (!aRunnable->Dispatch(aCx)) { + JS_ReportPendingException(aCx); } - nsRefPtr proxy = runnable->GetURLProxy(); + if (aRv.Failed()) { + return nullptr; + } + + nsRefPtr proxy = aRunnable->GetURLProxy(); if (!proxy) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return nullptr; } - nsRefPtr url = new URL(workerPrivate, proxy); + nsRefPtr url = new URL(aPrivate, proxy); return url.forget(); } diff --git a/dom/workers/URL.h b/dom/workers/URL.h index 1c15c3b63405..85e1fcbd6a53 100644 --- a/dom/workers/URL.h +++ b/dom/workers/URL.h @@ -23,6 +23,7 @@ struct objectURLOptions; BEGIN_WORKERS_NAMESPACE class URLProxy; +class ConstructorRunnable; class URL final : public mozilla::dom::URLSearchParamsObserver { @@ -52,6 +53,9 @@ public: Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, URL& aBase, ErrorResult& aRv); static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, + const Optional& aBase, ErrorResult& aRv); + static already_AddRefed Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, const nsAString& aBase, ErrorResult& aRv); @@ -123,6 +127,10 @@ private: return mURLProxy; } + static already_AddRefed + FinishConstructor(JSContext* aCx, WorkerPrivate* aPrivate, + ConstructorRunnable* aRunnable, ErrorResult& aRv); + void CreateSearchParamsIfNeeded(); void SetSearchInternal(const nsAString& aSearch); diff --git a/dom/workers/test/serviceworkers/fetch/fetch_tests.js b/dom/workers/test/serviceworkers/fetch/fetch_tests.js index 15bc0bbed451..b256ca3b65d4 100644 --- a/dom/workers/test/serviceworkers/fetch/fetch_tests.js +++ b/dom/workers/test/serviceworkers/fetch/fetch_tests.js @@ -180,3 +180,73 @@ fetch('http://example.com/cors-for-no-cors', { mode: "no-cors" }) my_ok(false, "intercepted non-opaque response for no-cors request should resolve to opaque response. It should not fail."); finish(); }); + +function arrayBufferFromString(str) { + var arr = new Uint8Array(str.length); + for (var i = 0; i < str.length; ++i) { + arr[i] = str.charCodeAt(i); + } + return arr; +} + +expectAsyncResult(); +fetch(new Request('body-simple', {method: 'POST', body: 'my body'})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +fetch(new Request('body-arraybufferview', {method: 'POST', body: arrayBufferFromString('my body')})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the ArrayBufferView body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +fetch(new Request('body-arraybuffer', {method: 'POST', body: arrayBufferFromString('my body').buffer})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the ArrayBuffer body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +var usp = new URLSearchParams(); +usp.set("foo", "bar"); +usp.set("baz", "qux"); +fetch(new Request('body-urlsearchparams', {method: 'POST', body: usp})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'foo=bar&baz=quxfoo=bar&baz=qux', "the URLSearchParams body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +var fd = new FormData(); +fd.set("foo", "bar"); +fd.set("baz", "qux"); +fetch(new Request('body-formdata', {method: 'POST', body: fd})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body.indexOf("Content-Disposition: form-data; name=\"foo\"\r\n\r\nbar") < + body.indexOf("Content-Disposition: form-data; name=\"baz\"\r\n\r\nqux"), + "the FormData body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +fetch(new Request('body-blob', {method: 'POST', body: new Blob(new String('my body'))})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the Blob body of the intercepted fetch should be visible in the SW"); + finish(); +}); diff --git a/dom/workers/test/serviceworkers/fetch_event_worker.js b/dom/workers/test/serviceworkers/fetch_event_worker.js index 5f35bf2fa309..ff7bc98b599b 100644 --- a/dom/workers/test/serviceworkers/fetch_event_worker.js +++ b/dom/workers/test/serviceworkers/fetch_event_worker.js @@ -172,4 +172,10 @@ onfetch = function(ev) { ev.respondWith(fetch(ev.request.url)); } } + + else if (ev.request.url.includes("body-")) { + ev.respondWith(ev.request.text().then(function (body) { + return new Response(body + body); + })); + } } diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html index 3ba95a4f4b4b..60a5dfd1f22e 100644 --- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html +++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html @@ -94,12 +94,28 @@ SimpleTest.waitForExplicitFinish(); onload = function() { - SpecialPowers.pushPrefEnv({"set": [ + // The handling of "dom.caches.enabled" here is a bit complicated. What we + // want to happen is that Cache is always enabled in service workers. So + // if service workers are disabled by default we want to force on both + // service workers and "dom.caches.enabled". But if service workers are + // enabled by default, we do not want to mess with the "dom.caches.enabled" + // value, since that would defeat the purpose of the test. Use a subframe + // to decide whether service workers are enabled by default, so we don't + // force creation of our own Navigator object before our prefs are set. + var prefs = [ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ["dom.caches.enabled", true] - ]}, runTest); + ]; + + var subframe = document.createElement("iframe"); + document.body.appendChild(subframe); + if (!("serviceWorker" in subframe.contentWindow.navigator)) { + prefs.push(["dom.caches.enabled", true]); + } + subframe.remove(); + + SpecialPowers.pushPrefEnv({"set": prefs}, runTest); }; diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js index 13caa0baf8a8..935b6108b1f7 100644 --- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js +++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js @@ -91,9 +91,9 @@ var interfaceNamesInGlobalScope = // IMPORTANT: Do not change this list without review from a DOM peer! "BroadcastChannel", // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "Cache", release: false }, + "Cache", // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "CacheStorage", release: false }, + "CacheStorage", // IMPORTANT: Do not change this list without review from a DOM peer! "Client", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html index 7df52e74c3ee..3361eba08a3e 100644 --- a/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html +++ b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html @@ -17,24 +17,18 @@ var registration; var promise; - SimpleTest.requestCompleteLog(); - function start() { - info("start got called"); return navigator.serviceWorker.register("worker_updatefoundevent.js", { scope: "./updatefoundevent.html" }) .then((swr) => registration = swr); } function startWaitForUpdateFound() { - info("startWaitForUpdateFound got called"); registration.onupdatefound = function(e) { - info("onupdatefound"); } promise = new Promise(function(resolve, reject) { window.onmessage = function(e) { - info("Got message " + e.data); if (e.data == "finish") { ok(true, "Received updatefound"); @@ -52,18 +46,16 @@ } function registerNext() { - info("registerNext got called"); return navigator.serviceWorker.register("worker_updatefoundevent2.js", { scope: "./updatefoundevent.html" }); } function waitForUpdateFound() { - info("waitForUpdateFound got called"); return promise; } function unregister() { - info("unregister got called"); + window.onmessage = null; return registration.unregister().then(function(result) { ok(result, "Unregister should return true."); }); @@ -85,7 +77,6 @@ ["dom.serviceWorkers.exemptFromPerDomainMax", true], ["dom.serviceWorkers.enabled", true], ["dom.serviceWorkers.testing.enabled", true], - ["browser.dom.window.dump.enabled", true], ]}, runTest); diff --git a/dom/workers/test/serviceworkers/worker_updatefoundevent.js b/dom/workers/test/serviceworkers/worker_updatefoundevent.js index 2474c31cca18..a297bf455674 100644 --- a/dom/workers/test/serviceworkers/worker_updatefoundevent.js +++ b/dom/workers/test/serviceworkers/worker_updatefoundevent.js @@ -6,10 +6,8 @@ onactivate = function(e) { e.waitUntil(new Promise(function(resolve, reject) { registration.onupdatefound = function(e) { - dump("Update found for scope " + registration.scope + "\n"); clients.matchAll().then(function(clients) { if (!clients.length) { - dump("No clients found\n"); reject("No clients found"); } diff --git a/dom/workers/test/serviceworkers/worker_updatefoundevent2.js b/dom/workers/test/serviceworkers/worker_updatefoundevent2.js index 681206711e48..da4c592aad80 100644 --- a/dom/workers/test/serviceworkers/worker_updatefoundevent2.js +++ b/dom/workers/test/serviceworkers/worker_updatefoundevent2.js @@ -1,2 +1 @@ // Not useful. -dump("worker_updatefoundevent2.js loaded\n"); diff --git a/gfx/layers/apz/test/apz_test_native_event_utils.js b/gfx/layers/apz/test/apz_test_native_event_utils.js new file mode 100644 index 000000000000..391f7647172c --- /dev/null +++ b/gfx/layers/apz/test/apz_test_native_event_utils.js @@ -0,0 +1,72 @@ +// Utilities for synthesizing of native events. + +function getPlatform() { + if (navigator.platform.indexOf("Win") == 0) { + return "windows"; + } + if (navigator.platform.indexOf("Mac") == 0) { + return "mac"; + } + if (navigator.platform.indexOf("Linux") == 0) { + return "linux"; + } + return "unknown"; +} + +function nativeVerticalWheelEventMsg() { + switch (getPlatform()) { + case "windows": return 0x020A; // WM_MOUSEWHEEL + case "mac": return 0; // value is unused, can be anything + } + throw "Native wheel events not supported on platform " + getPlatform(); +} + +function nativeHorizontalWheelEventMsg() { + switch (getPlatform()) { + case "windows": return 0x020E; // WM_MOUSEHWHEEL + case "mac": return 0; // value is unused, can be anything + } + throw "Native wheel events not supported on platform " + getPlatform(); +} + +// Synthesizes a native mousewheel event and returns immediately. This does not +// guarantee anything; you probably want to use one of the other functions below +// which actually wait for results. +// aX and aY are relative to |window|'s top-left. aDeltaX and aDeltaY +// are pixel deltas, and aObserver can be left undefined if not needed. +function synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, aObserver) { + aX += window.mozInnerScreenX; + aY += window.mozInnerScreenY; + if (aDeltaX && aDeltaY) { + throw "Simultaneous wheeling of horizontal and vertical is not supported on all platforms."; + } + var msg = aDeltaX ? nativeHorizontalWheelEventMsg() : nativeVerticalWheelEventMsg(); + _getDOMWindowUtils().sendNativeMouseScrollEvent(aX, aY, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver); + return true; +} + +// Synthesizes a native mousewheel event and invokes the callback once the +// request has been successfully made to the OS. This does not necessarily +// guarantee that the OS generates the event we requested. See +// synthesizeNativeWheel for details on the parameters. +function synthesizeNativeWheelAndWaitForObserver(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) { + var observer = { + observe: function(aSubject, aTopic, aData) { + if (aCallback && aTopic == "mousescrollevent") { + setTimeout(aCallback, 0); + } + } + }; + return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, observer); +} + +// Synthesizes a native mousewheel event and invokes the callback once the +// wheel event is dispatched to the window. See synthesizeNativeWheel for +// details on the parameters. +function synthesizeNativeWheelAndWaitForEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) { + window.addEventListener("wheel", function wheelWaiter(e) { + window.removeEventListener("wheel", wheelWaiter); + setTimeout(aCallback, 0); + }); + return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY); +} diff --git a/gfx/layers/apz/test/mochitest.ini b/gfx/layers/apz/test/mochitest.ini index 52cf7e04f204..5d42cd251538 100644 --- a/gfx/layers/apz/test/mochitest.ini +++ b/gfx/layers/apz/test/mochitest.ini @@ -1,9 +1,13 @@ [DEFAULT] support-files = apz_test_utils.js + apz_test_native_event_utils.js helper_bug982141.html helper_bug1151663.html +tags = apz [test_bug982141.html] skip-if = toolkit != 'gonk' # bug 991198 [test_bug1151663.html] skip-if = toolkit != 'gonk' # bug 991198 +[test_wheel_scroll.html] +skip-if = toolkit != 'windows' && toolkit != 'cocoa' diff --git a/gfx/layers/apz/test/test_wheel_scroll.html b/gfx/layers/apz/test/test_wheel_scroll.html new file mode 100644 index 000000000000..f3b4ff8e590a --- /dev/null +++ b/gfx/layers/apz/test/test_wheel_scroll.html @@ -0,0 +1,109 @@ + + + + + Test for Bug 1013412 + + + + + + + +Mozilla Bug 1161206 +

+
+

Scrolling the page should be async, but scrolling over the dark circle should not scroll the page and instead rotate the white ball.

+
+
+
+
+
+
+
+
+
+ + + diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.cpp b/gfx/layers/d3d9/DeviceManagerD3D9.cpp index a978d10bad16..b584cc7968c9 100644 --- a/gfx/layers/d3d9/DeviceManagerD3D9.cpp +++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp @@ -206,7 +206,7 @@ DeviceManagerD3D9::Init() if (!mNv3DVUtils) { mNv3DVUtils = new Nv3DVUtils(); if (!mNv3DVUtils) { - NS_WARNING("Could not create a new instance of Nv3DVUtils.\n"); + NS_WARNING("Could not create a new instance of Nv3DVUtils."); } } diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index e6a7c9187072..a9cc16473520 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -666,7 +666,7 @@ CompositorParent::CompositorParent(nsIWidget* aWidget, } if (UseVsyncComposition()) { - NS_WARNING("Enabling vsync compositor\n"); + NS_WARNING("Enabling vsync compositor"); mCompositorScheduler = new CompositorVsyncScheduler(this, aWidget); } else { mCompositorScheduler = new CompositorSoftwareTimerScheduler(this); diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp index f421def3afd8..ae00d07f58f0 100644 --- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -1087,7 +1087,7 @@ CompositorOGL::DrawQuad(const Rect& aRect, TextureSourceOGL* sourceCb = sourceYCbCr->GetSubSource(Cb)->AsSourceOGL(); TextureSourceOGL* sourceCr = sourceYCbCr->GetSubSource(Cr)->AsSourceOGL(); - if (!sourceY && !sourceCb && !sourceCr) { + if (!sourceY || !sourceCb || !sourceCr) { NS_WARNING("Invalid layer texture."); return; } diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp index 8370e35cbf51..e204baa9ec24 100644 --- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -497,7 +497,7 @@ gfxAndroidPlatform::CreateHardwareVsyncSource() VsyncSource::Display& display = vsyncSource->GetGlobalDisplay(); display.EnableVsync(); if (!display.IsVsyncEnabled()) { - NS_WARNING("Error enabling gonk vsync. Falling back to software vsync\n"); + NS_WARNING("Error enabling gonk vsync. Falling back to software vsync"); return gfxPlatform::CreateHardwareVsyncSource(); } display.DisableVsync(); diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index aea05ef40b05..f63d8e75f8fb 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -2344,7 +2344,7 @@ gfxPlatform::UsesOffMainThreadCompositing() already_AddRefed gfxPlatform::CreateHardwareVsyncSource() { - NS_WARNING("Hardware Vsync support not yet implemented. Falling back to software timers\n"); + NS_WARNING("Hardware Vsync support not yet implemented. Falling back to software timers"); nsRefPtr softwareVsync = new SoftwareVsyncSource(); return softwareVsync.forget(); } diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp index 512c8446606b..ffe6ed69ec70 100644 --- a/gfx/thebes/gfxPlatformMac.cpp +++ b/gfx/thebes/gfxPlatformMac.cpp @@ -482,7 +482,7 @@ public: // situations. According to the docs, it is compatible with all displays running on the computer // But if we have different monitors at different display rates, we may hit issues. if (CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink) != kCVReturnSuccess) { - NS_WARNING("Could not create a display link with all active displays. Retrying\n"); + NS_WARNING("Could not create a display link with all active displays. Retrying"); CVDisplayLinkRelease(mDisplayLink); mDisplayLink = nullptr; @@ -586,7 +586,7 @@ gfxPlatformMac::CreateHardwareVsyncSource() VsyncSource::Display& primaryDisplay = osxVsyncSource->GetGlobalDisplay(); primaryDisplay.EnableVsync(); if (!primaryDisplay.IsVsyncEnabled()) { - NS_WARNING("OS X Vsync source not enabled. Falling back to software vsync.\n"); + NS_WARNING("OS X Vsync source not enabled. Falling back to software vsync."); return gfxPlatform::CreateHardwareVsyncSource(); } diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index a45df24fdfd6..525bd26862ee 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -1319,7 +1319,7 @@ gfxUtils::WriteAsPNG(SourceSurface* aSurface, const char* aFile) } } if (!file) { - NS_WARNING("Failed to open file to create PNG!\n"); + NS_WARNING("Failed to open file to create PNG!"); return; } } diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index eae2a1c4d913..5a6fbca2d677 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -2135,7 +2135,7 @@ public: void ScheduleSoftwareVsync(TimeStamp aVsyncTimestamp) { MOZ_ASSERT(IsInVsyncThread()); - NS_WARNING("DwmComposition dynamically disabled, falling back to software timers\n"); + NS_WARNING("DwmComposition dynamically disabled, falling back to software timers"); TimeStamp nextVsync = aVsyncTimestamp + mSoftwareVsyncRate; TimeDuration delay = nextVsync - TimeStamp::Now(); @@ -2246,14 +2246,14 @@ gfxWindowsPlatform::CreateHardwareVsyncSource() { MOZ_RELEASE_ASSERT(NS_IsMainThread()); if (!WinUtils::dwmIsCompositionEnabledPtr) { - NS_WARNING("Dwm composition not available, falling back to software vsync\n"); + NS_WARNING("Dwm composition not available, falling back to software vsync"); return gfxPlatform::CreateHardwareVsyncSource(); } BOOL dwmEnabled = false; WinUtils::dwmIsCompositionEnabledPtr(&dwmEnabled); if (!dwmEnabled) { - NS_WARNING("DWM not enabled, falling back to software vsync\n"); + NS_WARNING("DWM not enabled, falling back to software vsync"); return gfxPlatform::CreateHardwareVsyncSource(); } diff --git a/image/decoders/nsJPEGDecoder.cpp b/image/decoders/nsJPEGDecoder.cpp index 9d7eeb04e642..c6f09f6fff4f 100644 --- a/image/decoders/nsJPEGDecoder.cpp +++ b/image/decoders/nsJPEGDecoder.cpp @@ -719,16 +719,16 @@ nsJPEGDecoder::OutputScanlines(bool* suspend) } } - if (top != mInfo.output_scanline) { + if (mDownscaler && mDownscaler->HasInvalidation()) { + DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect(); + PostInvalidation(invalidRect.mOriginalSizeRect, + Some(invalidRect.mTargetSizeRect)); + MOZ_ASSERT(!mDownscaler->HasInvalidation()); + } else if (!mDownscaler && top != mInfo.output_scanline) { PostInvalidation(nsIntRect(0, top, mInfo.output_width, - mInfo.output_scanline - top), - mDownscaler ? Some(mDownscaler->TakeInvalidRect()) - : Nothing()); + mInfo.output_scanline - top)); } - - MOZ_ASSERT(!mDownscaler || !mDownscaler->HasInvalidation(), - "Didn't send downscaler's invalidation"); } // Override the standard error method in the IJG JPEG decoder code. diff --git a/image/src/Decoder.cpp b/image/src/Decoder.cpp index 31e3adf5fb1f..e9b95f278fa0 100644 --- a/image/src/Decoder.cpp +++ b/image/src/Decoder.cpp @@ -473,7 +473,8 @@ Decoder::InternalAddFrame(uint32_t aFrameNum, return RawAccessFrameRef(); } - if (!SurfaceCache::CanHold(aTargetSize)) { + const uint32_t bytesPerPixel = aPaletteDepth == 0 ? 4 : 1; + if (!SurfaceCache::CanHold(aFrameRect.Size(), bytesPerPixel)) { NS_WARNING("Trying to add frame that's too large for the SurfaceCache"); return RawAccessFrameRef(); } diff --git a/image/src/Downscaler.cpp b/image/src/Downscaler.cpp index a0839e54a091..40459c5e12cb 100644 --- a/image/src/Downscaler.cpp +++ b/image/src/Downscaler.cpp @@ -69,6 +69,8 @@ Downscaler::BeginFrame(const nsIntSize& aOriginalSize, "Invalid original size"); mOriginalSize = aOriginalSize; + mScale = gfxSize(double(mOriginalSize.width) / mTargetSize.width, + double(mOriginalSize.height) / mTargetSize.height); mOutputBuffer = aOutputBuffer; mHasAlpha = aHasAlpha; @@ -183,17 +185,25 @@ Downscaler::HasInvalidation() const return mCurrentOutLine > mPrevInvalidatedLine; } -nsIntRect +DownscalerInvalidRect Downscaler::TakeInvalidRect() { if (MOZ_UNLIKELY(!HasInvalidation())) { - return nsIntRect(); + return DownscalerInvalidRect(); } - nsIntRect invalidRect(0, mPrevInvalidatedLine, - mTargetSize.width, - mCurrentOutLine - mPrevInvalidatedLine); + DownscalerInvalidRect invalidRect; + + // Compute the target size invalid rect. + invalidRect.mTargetSizeRect = + nsIntRect(0, mPrevInvalidatedLine, + mTargetSize.width, mCurrentOutLine - mPrevInvalidatedLine); mPrevInvalidatedLine = mCurrentOutLine; + + // Compute the original size invalid rect. + invalidRect.mOriginalSizeRect = invalidRect.mTargetSizeRect; + invalidRect.mOriginalSizeRect.ScaleRoundOut(mScale.width, mScale.height); + return invalidRect; } diff --git a/image/src/Downscaler.h b/image/src/Downscaler.h index 67c88e2c8bab..b052f3da6da0 100644 --- a/image/src/Downscaler.h +++ b/image/src/Downscaler.h @@ -24,6 +24,16 @@ namespace skia { namespace mozilla { namespace image { +/** + * DownscalerInvalidRect wraps two invalidation rects: one in terms of the + * original image size, and one in terms of the target size. + */ +struct DownscalerInvalidRect +{ + nsIntRect mOriginalSizeRect; + nsIntRect mTargetSizeRect; +}; + /** * Downscaler is a high-quality, streaming image downscaler based upon Skia's * scaling implementation. @@ -47,6 +57,7 @@ public: const nsIntSize& OriginalSize() const { return mOriginalSize; } const nsIntSize& TargetSize() const { return mTargetSize; } + const gfxSize& Scale() const { return mScale; } /** * Begins a new frame and reinitializes the Downscaler. @@ -73,7 +84,7 @@ public: bool HasInvalidation() const; /// Takes the Downscaler's current invalid rect and resets it. - nsIntRect TakeInvalidRect(); + DownscalerInvalidRect TakeInvalidRect(); /** * Resets the Downscaler's position in the image, for a new progressive pass @@ -88,6 +99,7 @@ private: nsIntSize mOriginalSize; nsIntSize mTargetSize; + gfxSize mScale; uint8_t* mOutputBuffer; diff --git a/image/src/SurfaceCache.cpp b/image/src/SurfaceCache.cpp index ca50a93280a3..ead7bf2e87a1 100644 --- a/image/src/SurfaceCache.cpp +++ b/image/src/SurfaceCache.cpp @@ -32,6 +32,7 @@ #include "nsSize.h" #include "nsTArray.h" #include "prsystem.h" +#include "ShutdownTracker.h" #include "SVGImageContext.h" using std::max; @@ -66,9 +67,10 @@ static StaticRefPtr sInstance; typedef size_t Cost; static Cost -ComputeCost(const IntSize& aSize) +ComputeCost(const IntSize& aSize, uint32_t aBytesPerPixel) { - return aSize.width * aSize.height * 4; // width * height * 4 bytes (32bpp) + MOZ_ASSERT(aBytesPerPixel == 1 || aBytesPerPixel == 4); + return aSize.width * aSize.height * aBytesPerPixel; } /** @@ -526,7 +528,8 @@ public: } else { // Our call to AddObject must have failed in StartTracking; most likely // we're in XPCOM shutdown right now. - NS_WARNING("Not expiration-tracking an unlocked surface!"); + NS_WARN_IF_FALSE(ShutdownTracker::ShutdownHasStarted(), + "Not expiration-tracking an unlocked surface!"); } DebugOnly foundInCosts = mCosts.RemoveElementSorted(costEntry); @@ -1001,18 +1004,18 @@ SurfaceCache::Insert(imgFrame* aSurface, } MutexAutoLock lock(sInstance->GetMutex()); - Cost cost = ComputeCost(aSurfaceKey.Size()); + Cost cost = ComputeCost(aSurface->GetSize(), aSurface->GetBytesPerPixel()); return sInstance->Insert(aSurface, cost, aImageKey, aSurfaceKey, aLifetime); } /* static */ bool -SurfaceCache::CanHold(const IntSize& aSize) +SurfaceCache::CanHold(const IntSize& aSize, uint32_t aBytesPerPixel /* = 4 */) { if (!sInstance) { return false; } - Cost cost = ComputeCost(aSize); + Cost cost = ComputeCost(aSize, aBytesPerPixel); return sInstance->CanHold(cost); } diff --git a/image/src/SurfaceCache.h b/image/src/SurfaceCache.h index 3107c2938693..5dcc41fe2961 100644 --- a/image/src/SurfaceCache.h +++ b/image/src/SurfaceCache.h @@ -288,10 +288,13 @@ struct SurfaceCache * for sure the cache can't hold it. * * @param aSize The dimensions of a surface in pixels. + * @param aBytesPerPixel How many bytes each pixel of the surface requires. + * Defaults to 4, which is appropriate for RGBA or RGBX + * images. * * @return false if the surface cache can't hold a surface of that size. */ - static bool CanHold(const IntSize& aSize); + static bool CanHold(const IntSize& aSize, uint32_t aBytesPerPixel = 4); static bool CanHold(size_t aSize); /** diff --git a/image/src/imgFrame.h b/image/src/imgFrame.h index 24c22d44221a..3343dcd923f3 100644 --- a/image/src/imgFrame.h +++ b/image/src/imgFrame.h @@ -236,6 +236,14 @@ public: */ void WaitUntilComplete() const; + /** + * Returns the number of bytes per pixel this imgFrame requires. This is a + * worst-case value that does not take into account the effects of format + * changes caused by Optimize(), since an imgFrame is not optimized throughout + * its lifetime. + */ + uint32_t GetBytesPerPixel() const { return GetIsPaletted() ? 1 : 4; } + IntSize GetImageSize() const { return mImageSize; } nsIntRect GetRect() const; IntSize GetSize() const { return mSize; } diff --git a/intl/uconv/ucvlatin/nsMacGujaratiToUnicode.cpp b/intl/uconv/ucvlatin/nsMacGujaratiToUnicode.cpp index 0d01e33e4d55..21b3020f55eb 100644 --- a/intl/uconv/ucvlatin/nsMacGujaratiToUnicode.cpp +++ b/intl/uconv/ucvlatin/nsMacGujaratiToUnicode.cpp @@ -17,6 +17,7 @@ nsMacGujaratiToUnicodeConstructor(nsISupports *aOuter, REFNSIID aIID, #include "macgujarati.ut" }; + Telemetry::Accumulate(Telemetry::DECODER_INSTANTIATED_MACGUJARATI, true); return CreateOneByteDecoder((uMappingTable*) &g_utMappingTable, aOuter, aIID, aResult); } diff --git a/ipc/chromium/src/chrome/common/ipc_message.cc b/ipc/chromium/src/chrome/common/ipc_message.cc index 7771e7fb6c5b..1e4216a59a92 100644 --- a/ipc/chromium/src/chrome/common/ipc_message.cc +++ b/ipc/chromium/src/chrome/common/ipc_message.cc @@ -49,6 +49,8 @@ Message::Message(int32_t routing_id, msgid_t type, PriorityValue priority, header()->flags = priority; if (compression == COMPRESSION_ENABLED) header()->flags |= COMPRESS_BIT; + else if (compression == COMPRESSION_ALL) + header()->flags |= COMPRESSALL_BIT; #if defined(OS_POSIX) header()->num_fds = 0; #endif diff --git a/ipc/chromium/src/chrome/common/ipc_message.h b/ipc/chromium/src/chrome/common/ipc_message.h index e7e91fd97ab3..b95a2d8861c6 100644 --- a/ipc/chromium/src/chrome/common/ipc_message.h +++ b/ipc/chromium/src/chrome/common/ipc_message.h @@ -56,7 +56,8 @@ class Message : public Pickle { enum MessageCompression { COMPRESSION_NONE, - COMPRESSION_ENABLED + COMPRESSION_ENABLED, + COMPRESSION_ALL }; virtual ~Message(); @@ -99,8 +100,12 @@ class Message : public Pickle { } // True if compression is enabled for this message. - bool compress() const { - return (header()->flags & COMPRESS_BIT) != 0; + MessageCompression compress_type() const { + return (header()->flags & COMPRESS_BIT) ? + COMPRESSION_ENABLED : + (header()->flags & COMPRESSALL_BIT) ? + COMPRESSION_ALL : + COMPRESSION_NONE; } // Set this on a reply to a synchronous message. @@ -285,6 +290,7 @@ class Message : public Pickle { HAS_SENT_TIME_BIT = 0x0080, INTERRUPT_BIT = 0x0100, COMPRESS_BIT = 0x0200, + COMPRESSALL_BIT = 0x0400, }; struct Header : Pickle::Header { diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 9606ac381d9b..a8307640b0fe 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -612,6 +612,19 @@ MessageChannel::ShouldDeferMessage(const Message& aMsg) return mSide == ParentSide && aMsg.transaction_id() != mCurrentTransaction; } +// Predicate that is true for messages that should be consolidated if 'compress' is set. +class MatchingKinds { + typedef IPC::Message Message; + Message::msgid_t mType; + int32_t mRoutingId; +public: + MatchingKinds(Message::msgid_t aType, int32_t aRoutingId) : + mType(aType), mRoutingId(aRoutingId) {} + bool operator()(const Message &msg) { + return msg.type() == mType && msg.routing_id() == mRoutingId; + } +}; + void MessageChannel::OnMessageReceivedFromLink(const Message& aMsg) { @@ -653,17 +666,35 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg) } // Prioritized messages cannot be compressed. - MOZ_ASSERT(!aMsg.compress() || aMsg.priority() == IPC::Message::PRIORITY_NORMAL); + MOZ_ASSERT_IF(aMsg.compress_type() != IPC::Message::COMPRESSION_NONE, + aMsg.priority() == IPC::Message::PRIORITY_NORMAL); - bool compress = (aMsg.compress() && !mPending.empty() && - mPending.back().type() == aMsg.type() && - mPending.back().routing_id() == aMsg.routing_id()); - if (compress) { - // This message type has compression enabled, and the back of the - // queue was the same message type and routed to the same destination. - // Replace it with the newer message. - MOZ_ASSERT(mPending.back().compress()); - mPending.pop_back(); + bool compress = false; + if (aMsg.compress_type() == IPC::Message::COMPRESSION_ENABLED) { + compress = (!mPending.empty() && + mPending.back().type() == aMsg.type() && + mPending.back().routing_id() == aMsg.routing_id()); + if (compress) { + // This message type has compression enabled, and the back of the + // queue was the same message type and routed to the same destination. + // Replace it with the newer message. + MOZ_ASSERT(mPending.back().compress_type() == + IPC::Message::COMPRESSION_ENABLED); + mPending.pop_back(); + } + } else if (aMsg.compress_type() == IPC::Message::COMPRESSION_ALL) { + // Check the message queue for another message with this type/destination. + auto it = std::find_if(mPending.rbegin(), mPending.rend(), + MatchingKinds(aMsg.type(), aMsg.routing_id())); + if (it != mPending.rend()) { + // This message type has compression enabled, and the queue holds + // a message with the same message type and routed to the same destination. + // Erase it. Note that, since we always compress these redundancies, There Can + // Be Only One. + compress = true; + MOZ_ASSERT((*it).compress_type() == IPC::Message::COMPRESSION_ALL); + mPending.erase((++it).base()); + } } bool shouldWakeUp = AwaitingInterruptReply() || diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index 167886d7bec7..888ca5909b2c 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -1860,8 +1860,11 @@ def _generateMessageClass(clsname, msgid, priority, prettyName, compress): cls.addstmt(StmtDecl(Decl(idenum, ''))) # make the message constructor - if compress: + if compress == 'compress': compression = ExprVar('COMPRESSION_ENABLED') + elif compress: + assert compress == 'compressall' + compression = ExprVar('COMPRESSION_ALL') else: compression = ExprVar('COMPRESSION_NONE') if priority == ipdl.ast.NORMAL_PRIORITY: diff --git a/ipc/ipdl/ipdl/parser.py b/ipc/ipdl/ipdl/parser.py index e55c873ae1f4..a0ca80d43b74 100644 --- a/ipc/ipdl/ipdl/parser.py +++ b/ipc/ipdl/ipdl/parser.py @@ -123,6 +123,7 @@ reserved = set(( 'child', 'class', 'compress', + 'compressall', '__delete__', 'delete', # reserve 'delete' to prevent its use 'from', @@ -546,12 +547,17 @@ def p_MessageOutParams(p): p[0] = p[3] def p_OptionalMessageCompress(p): - """OptionalMessageCompress : COMPRESS + """OptionalMessageCompress : MessageCompress | """ if 1 == len(p): p[0] = '' else: - p[0] = 'compress' + p[0] = p[1] + +def p_MessageCompress(p): + """MessageCompress : COMPRESS + | COMPRESSALL""" + p[0] = p[1] ##-------------------- ## State machine diff --git a/ipc/ipdl/ipdl/type.py b/ipc/ipdl/ipdl/type.py index 40762f216cfe..d022f0118044 100644 --- a/ipc/ipdl/ipdl/type.py +++ b/ipc/ipdl/ipdl/type.py @@ -1101,7 +1101,7 @@ class GatherDecls(TcheckVisitor): msgtype = MessageType(md.priority, md.sendSemantics, md.direction, ctor=isctor, dtor=isdtor, cdtype=cdtype, - compress=(md.compress == 'compress')) + compress=md.compress) # replace inparam Param nodes with proper Decls def paramToDecl(param): diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h index 268670a3a9cd..03d3f6a37cc4 100644 --- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -204,8 +204,8 @@ struct JS_PUBLIC_API(NullPtr) static void * const constNullValue; }; -JS_FRIEND_API(void) HeapCellPostBarrier(js::gc::Cell** cellp); -JS_FRIEND_API(void) HeapCellRelocate(js::gc::Cell** cellp); +JS_FRIEND_API(void) HeapObjectPostBarrier(JSObject** objp); +JS_FRIEND_API(void) HeapObjectRelocate(JSObject** objp); #ifdef JS_DEBUG /* @@ -214,9 +214,13 @@ JS_FRIEND_API(void) HeapCellRelocate(js::gc::Cell** cellp); */ extern JS_FRIEND_API(void) AssertGCThingMustBeTenured(JSObject* obj); +extern JS_FRIEND_API(void) +AssertGCThingIsNotAnObjectSubclass(js::gc::Cell* cell); #else inline void AssertGCThingMustBeTenured(JSObject* obj) {} +inline void +AssertGCThingIsNotAnObjectSubclass(js::gc::Cell* cell) {} #endif /* @@ -639,7 +643,10 @@ struct GCMethods { static T* initial() { return nullptr; } static bool needsPostBarrier(T* v) { return false; } - static void postBarrier(T** vp) {} + static void postBarrier(T** vp) { + if (vp) + JS::AssertGCThingIsNotAnObjectSubclass(reinterpret_cast(vp)); + } static void relocate(T** vp) {} }; @@ -657,10 +664,10 @@ struct GCMethods return v != nullptr && gc::IsInsideNursery(reinterpret_cast(v)); } static void postBarrier(JSObject** vp) { - JS::HeapCellPostBarrier(reinterpret_cast(vp)); + JS::HeapObjectPostBarrier(vp); } static void relocate(JSObject** vp) { - JS::HeapCellRelocate(reinterpret_cast(vp)); + JS::HeapObjectRelocate(vp); } }; @@ -672,10 +679,10 @@ struct GCMethods return v != nullptr && gc::IsInsideNursery(reinterpret_cast(v)); } static void postBarrier(JSFunction** vp) { - JS::HeapCellPostBarrier(reinterpret_cast(vp)); + JS::HeapObjectPostBarrier(reinterpret_cast(vp)); } static void relocate(JSFunction** vp) { - JS::HeapCellRelocate(reinterpret_cast(vp)); + JS::HeapObjectRelocate(reinterpret_cast(vp)); } }; diff --git a/js/src/gc/Barrier.cpp b/js/src/gc/Barrier.cpp index 01aca5d3c42a..17d8d44abe1e 100644 --- a/js/src/gc/Barrier.cpp +++ b/js/src/gc/Barrier.cpp @@ -75,3 +75,33 @@ template void js::PreBarrierFunctor::operator()(JSObject*); template void js::PreBarrierFunctor::operator()(JSString*); template void js::PreBarrierFunctor::operator()(JS::Symbol*); template void js::PreBarrierFunctor::operator()(JSString*); + +JS_PUBLIC_API(void) +JS::HeapObjectPostBarrier(JSObject** objp) +{ + MOZ_ASSERT(objp); + MOZ_ASSERT(*objp); + js::InternalGCMethods::postBarrierRelocate(objp); +} + +JS_PUBLIC_API(void) +JS::HeapObjectRelocate(JSObject** objp) +{ + MOZ_ASSERT(objp); + MOZ_ASSERT(*objp); + js::InternalGCMethods::postBarrierRemove(objp); +} + +JS_PUBLIC_API(void) +JS::HeapValuePostBarrier(JS::Value* valuep) +{ + MOZ_ASSERT(valuep); + js::InternalGCMethods::postBarrierRelocate(valuep); +} + +JS_PUBLIC_API(void) +JS::HeapValueRelocate(JS::Value* valuep) +{ + MOZ_ASSERT(valuep); + js::InternalGCMethods::postBarrierRemove(valuep); +} diff --git a/js/src/gc/StoreBuffer.cpp b/js/src/gc/StoreBuffer.cpp index 32ba20fdbeed..4ee431a6f9b1 100644 --- a/js/src/gc/StoreBuffer.cpp +++ b/js/src/gc/StoreBuffer.cpp @@ -202,50 +202,6 @@ StoreBuffer::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::GCSi sizes->storeBufferGenerics += bufferGeneric.sizeOfExcludingThis(mallocSizeOf); } -JS_PUBLIC_API(void) -JS::HeapCellPostBarrier(js::gc::Cell** cellp) -{ - MOZ_ASSERT(cellp); - MOZ_ASSERT(*cellp); - StoreBuffer* storeBuffer = (*cellp)->storeBuffer(); - if (storeBuffer) - storeBuffer->putRelocatableCellFromAnyThread(cellp); -} - -JS_PUBLIC_API(void) -JS::HeapCellRelocate(js::gc::Cell** cellp) -{ - /* Called with old contents of *cellp before overwriting. */ - MOZ_ASSERT(cellp); - MOZ_ASSERT(*cellp); - JSRuntime* runtime = (*cellp)->runtimeFromMainThread(); - runtime->gc.storeBuffer.removeRelocatableCellFromAnyThread(cellp); -} - -JS_PUBLIC_API(void) -JS::HeapValuePostBarrier(JS::Value* valuep) -{ - MOZ_ASSERT(valuep); - MOZ_ASSERT(valuep->isMarkable()); - if (valuep->isObject()) { - StoreBuffer* storeBuffer = valuep->toObject().storeBuffer(); - if (storeBuffer) - storeBuffer->putRelocatableValueFromAnyThread(valuep); - } -} - -JS_PUBLIC_API(void) -JS::HeapValueRelocate(JS::Value* valuep) -{ - /* Called with old contents of *valuep before overwriting. */ - MOZ_ASSERT(valuep); - MOZ_ASSERT(valuep->isMarkable()); - if (valuep->isString() && valuep->toString()->isPermanentAtom()) - return; - JSRuntime* runtime = static_cast(valuep->toGCThing())->runtimeFromMainThread(); - runtime->gc.storeBuffer.removeRelocatableValueFromAnyThread(valuep); -} - template struct StoreBuffer::MonoTypeBuffer; template struct StoreBuffer::MonoTypeBuffer; template struct StoreBuffer::MonoTypeBuffer; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 628b08620623..98bda507e939 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -6816,6 +6816,13 @@ JS::AssertGCThingMustBeTenured(JSObject* obj) (!IsNurseryAllocable(obj->asTenured().getAllocKind()) || obj->getClass()->finalize)); } +JS_FRIEND_API(void) +JS::AssertGCThingIsNotAnObjectSubclass(Cell* cell) +{ + MOZ_ASSERT(cell); + MOZ_ASSERT(cell->getTraceKind() != JSTRACE_OBJECT); +} + JS_FRIEND_API(void) js::gc::AssertGCThingHasType(js::gc::Cell* cell, JSGCTraceKind kind) { diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 4248e7fe76e0..a87dceb572e8 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -9579,7 +9579,7 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( FCItemIterator iter(aItems); do { // Advance iter past children that don't want to be wrapped - if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState)) { + if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState, containerType)) { // Hit the end of the items without finding any remaining children that // need to be wrapped. We're finished! return; @@ -9601,7 +9601,8 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( FCItemIterator afterWhitespaceIter(iter); bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState); bool nextChildNeedsAnonItem = - !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(aState); + !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(aState, + containerType); if (!nextChildNeedsAnonItem) { // There's nothing after the whitespace that we need to wrap, so we @@ -9615,7 +9616,7 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( // we jump back to the beginning of the loop to skip over that child // (and anything else non-wrappable after it) MOZ_ASSERT(!iter.IsDone() && - !iter.item().NeedsAnonFlexOrGridItem(aState), + !iter.item().NeedsAnonFlexOrGridItem(aState, containerType), "hitEnd and/or nextChildNeedsAnonItem lied"); continue; } @@ -9625,7 +9626,7 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( // anonymous flex/grid item. Now we see how many children after it also want // to be wrapped in an anonymous flex/grid item. FCItemIterator endIter(iter); // iterator to find the end of the group - endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState); + endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, containerType); NS_ASSERTION(iter != endIter, "Should've had at least one wrappable child to seek past"); @@ -11831,8 +11832,9 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, // Check if we're adding to-be-wrapped content right *after* an existing // anonymous flex or grid item (which would need to absorb this content). + nsIAtom* containerType = aFrame->GetType(); if (aPrevSibling && IsAnonymousFlexOrGridItem(aPrevSibling) && - iter.item().NeedsAnonFlexOrGridItem(aState)) { + iter.item().NeedsAnonFlexOrGridItem(aState, containerType)) { RecreateFramesForContent(aFrame->GetContent(), true, REMOVE_FOR_RECONSTRUCTION, nullptr); return true; @@ -11844,7 +11846,7 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, // Jump to the last entry in the list iter.SetToEnd(); iter.Prev(); - if (iter.item().NeedsAnonFlexOrGridItem(aState)) { + if (iter.item().NeedsAnonFlexOrGridItem(aState, containerType)) { RecreateFramesForContent(aFrame->GetContent(), true, REMOVE_FOR_RECONSTRUCTION, nullptr); return true; @@ -11869,10 +11871,11 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, FCItemIterator iter(aItems); // Skip over things that _do_ need an anonymous flex item, because // they're perfectly happy to go here -- they won't cause a reframe. - if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState)) { + nsIFrame* containerFrame = aFrame->GetParent(); + if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState, + containerFrame->GetType())) { // We hit something that _doesn't_ need an anonymous flex item! // Rebuild the flex container to bust it out. - nsIFrame* containerFrame = aFrame->GetParent(); RecreateFramesForContent(containerFrame->GetContent(), true, REMOVE_FOR_RECONSTRUCTION, nullptr); return true; @@ -12324,14 +12327,17 @@ Iterator::SkipItemsNotWantingParentType(ParentType aParentType) bool nsCSSFrameConstructor::FrameConstructionItem:: - NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState) + NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState, + nsIAtom* aContainerType) { if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) { // This will be an inline non-replaced box. return true; } - if (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) && + // Bug 874718: Flex containers still wrap placeholders; Grid containers don't. + if (aContainerType == nsGkAtoms::flexContainerFrame && + !(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) && aState.GetGeometricParent(mStyleContext->StyleDisplay(), nullptr)) { // We're abspos or fixedpos, which means we'll spawn a placeholder which // we'll need to wrap in an anonymous flex item. So, we just treat @@ -12346,10 +12352,11 @@ nsCSSFrameConstructor::FrameConstructionItem:: inline bool nsCSSFrameConstructor::FrameConstructionItemList:: Iterator::SkipItemsThatNeedAnonFlexOrGridItem( - const nsFrameConstructorState& aState) + const nsFrameConstructorState& aState, + nsIAtom* aContainerType) { NS_PRECONDITION(!IsDone(), "Shouldn't be done yet"); - while (item().NeedsAnonFlexOrGridItem(aState)) { + while (item().NeedsAnonFlexOrGridItem(aState, aContainerType)) { Next(); if (IsDone()) { return true; @@ -12361,10 +12368,11 @@ Iterator::SkipItemsThatNeedAnonFlexOrGridItem( inline bool nsCSSFrameConstructor::FrameConstructionItemList:: Iterator::SkipItemsThatDontNeedAnonFlexOrGridItem( - const nsFrameConstructorState& aState) + const nsFrameConstructorState& aState, + nsIAtom* aContainerType) { NS_PRECONDITION(!IsDone(), "Shouldn't be done yet"); - while (!(item().NeedsAnonFlexOrGridItem(aState))) { + while (!(item().NeedsAnonFlexOrGridItem(aState, aContainerType))) { Next(); if (IsDone()) { return true; diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index a48c9c63dc73..121ea0ab2aa7 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -931,13 +931,13 @@ private: // Return whether the iterator is done after doing that. // The iterator must not be done when this is called. inline bool SkipItemsThatNeedAnonFlexOrGridItem( - const nsFrameConstructorState& aState); + const nsFrameConstructorState& aState, nsIAtom* aContainerType); // Skip to the first frame that is a non-replaced inline or is // positioned. Return whether the iterator is done after doing that. // The iterator must not be done when this is called. inline bool SkipItemsThatDontNeedAnonFlexOrGridItem( - const nsFrameConstructorState& aState); + const nsFrameConstructorState& aState, nsIAtom* aContainerType); // Skip over all items that do not want a ruby parent. Return whether // the iterator is done after doing that. The iterator must not be done @@ -1074,7 +1074,8 @@ private: // Indicates whether (when in a flex or grid container) this item needs // to be wrapped in an anonymous block. - bool NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState); + bool NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState, + nsIAtom* aContainerType); // Don't call this unless the frametree really depends on the answer! // Especially so for generated content, where we don't want to reframe diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index fe929b3f5522..f4e082436900 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -138,10 +138,10 @@ typedef struct CapturingContentInfo { mozilla::StaticRefPtr mContent; } CapturingContentInfo; -// d910f009-d209-74c1-6b04-30c83c051c78 +// b7b89561-4f03-44b3-9afa-b47e7f313ffb #define NS_IPRESSHELL_IID \ - { 0x025264c6, 0x0b12, 0x4804, \ - { 0xa3, 0x3e, 0xb7, 0x73, 0xf2, 0x19, 0x48, 0x90 } } + { 0xb7b89561, 0x4f03, 0x44b3, \ + { 0x9a, 0xfa, 0xb4, 0x7e, 0x7f, 0x31, 0x3f, 0xfb } } // debug VerifyReflow flags #define VERIFY_REFLOW_ON 0x01 @@ -1550,6 +1550,8 @@ public: // Whether we should assume all images are visible. virtual bool AssumeAllImagesVisible() = 0; + virtual void FireResizeEvent() = 0; + /** * Refresh observer management. */ diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 1a4fb2a3bc93..d1be08b4f3be 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1216,6 +1216,9 @@ PresShell::Destroy() // 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(!). + if (mResizeEventPending) { + rd->RemoveResizeEventFlushObserver(this); + } rd->RemoveLayoutFlushObserver(this); if (mHiddenInvalidationObserverRefreshDriver) { mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this); @@ -1225,12 +1228,6 @@ PresShell::Destroy() rd->RevokeViewManagerFlush(); } - mResizeEvent.Revoke(); - if (mAsyncResizeTimerIsActive) { - mAsyncResizeEventTimer->Cancel(); - mAsyncResizeTimerIsActive = false; - } - CancelAllPendingReflows(); CancelPostedReflowCallbacks(); @@ -1998,12 +1995,6 @@ PresShell::sPaintSuppressionCallback(nsITimer *aTimer, void* aPresShell) self->UnsuppressPainting(); } -void -PresShell::AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell) -{ - static_cast(aPresShell)->FireResizeEvent(); -} - nsresult PresShell::ResizeReflowOverride(nscoord aWidth, nscoord aHeight) { @@ -2088,26 +2079,9 @@ PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight) nsRect(0, 0, aWidth, rootFrame->GetRect().height)); } - if (!mIsDestroying && !mResizeEvent.IsPending() && - !mAsyncResizeTimerIsActive) { - if (mInResize) { - if (!mAsyncResizeEventTimer) { - mAsyncResizeEventTimer = do_CreateInstance("@mozilla.org/timer;1"); - } - if (mAsyncResizeEventTimer) { - mAsyncResizeTimerIsActive = true; - mAsyncResizeEventTimer->InitWithFuncCallback(AsyncResizeEventCallback, - this, 15, - nsITimer::TYPE_ONE_SHOT); - } - } else { - nsRefPtr > resizeEvent = - NS_NewRunnableMethod(this, &PresShell::FireResizeEvent); - if (NS_SUCCEEDED(NS_DispatchToCurrentThread(resizeEvent))) { - mResizeEvent = resizeEvent; - mDocument->SetNeedStyleFlush(); - } - } + if (!mIsDestroying && !mResizeEventPending) { + mResizeEventPending = true; + GetPresContext()->RefreshDriver()->AddResizeEventFlushObserver(this); } return NS_OK; //XXX this needs to be real. MMP @@ -2116,25 +2090,19 @@ PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight) void PresShell::FireResizeEvent() { - if (mAsyncResizeTimerIsActive) { - mAsyncResizeTimerIsActive = false; - mAsyncResizeEventTimer->Cancel(); - } - mResizeEvent.Revoke(); - - if (mIsDocumentGone) + if (mIsDocumentGone) { return; + } + + mResizeEventPending = false; //Send resize event from here. WidgetEvent event(true, NS_RESIZE_EVENT); nsEventStatus status = nsEventStatus_eIgnore; - nsPIDOMWindow *window = mDocument->GetWindow(); + nsPIDOMWindow* window = mDocument->GetWindow(); if (window) { - nsCOMPtr kungFuDeathGrip(this); - mInResize = true; EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status); - mInResize = false; } } @@ -4215,13 +4183,6 @@ PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush) // hold weak refs when calling FlushPendingNotifications(). :( nsCOMPtr kungFuDeathGrip(this); - if (mResizeEvent.IsPending()) { - FireResizeEvent(); - if (mIsDestroying) { - return; - } - } - // We need to make sure external resource documents are flushed too (for // example, svg filters that reference a filter in an external document // need the frames in the external document to be constructed for the diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index 51417fc1b431..33708762c5ab 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -390,6 +390,8 @@ public: void SetNextPaintCompressed() { mNextPaintCompressed = true; } + virtual void FireResizeEvent() override; + protected: virtual ~PresShell(); @@ -700,9 +702,6 @@ protected: mozilla::LayoutDeviceIntPoint& aTargetPt, nsIWidget *aRootWidget); - void FireResizeEvent(); - static void AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell); - virtual void SynthesizeMouseMove(bool aFromScroll) override; PresShell* GetRootPresShell(); @@ -792,8 +791,6 @@ protected: nsTArray mDirtyRoots; nsTArray > mDelayedEvents; - nsRevocableEventPtr > mResizeEvent; - nsCOMPtr mAsyncResizeEventTimer; private: nsIFrame* mCurrentEventFrame; nsCOMPtr mCurrentEventContent; @@ -861,8 +858,7 @@ protected: // have been processed. bool mShouldUnsuppressPainting : 1; - bool mAsyncResizeTimerIsActive : 1; - bool mInResize : 1; + bool mResizeEventPending : 1; bool mImageVisibilityVisited : 1; diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index a81bc2f57cc6..d2337bdaca21 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -859,7 +859,7 @@ CreateVsyncRefreshTimer() return; } - NS_WARNING("Enabling vsync refresh driver\n"); + NS_WARNING("Enabling vsync refresh driver"); if (XRE_IsParentProcess()) { // Make sure all vsync systems are ready. @@ -1330,6 +1330,7 @@ nsRefreshDriver::ObserverCount() const // changes can trigger transitions which fire events when they complete, and // layout changes can affect media queries on child documents, triggering // style changes, etc. + sum += mResizeEventFlushObservers.Length(); sum += mStyleFlushObservers.Length(); sum += mLayoutFlushObservers.Length(); sum += mFrameRequestCallbackDocs.Length(); @@ -1627,6 +1628,24 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime) AutoRestore restoreTickStart(mTickStart); mTickStart = TimeStamp::Now(); + // Resize events should be fired before layout flushes or + // calling animation frame callbacks. + nsAutoTArray observers; + observers.AppendElements(mResizeEventFlushObservers); + for (uint32_t i = observers.Length(); i; --i) { + if (!mPresContext || !mPresContext->GetPresShell()) { + break; + } + // Make sure to not process observers which might have been removed + // during previous iterations. + nsIPresShell* shell = observers[i - 1]; + if (!mResizeEventFlushObservers.Contains(shell)) { + continue; + } + mResizeEventFlushObservers.RemoveElement(shell); + shell->FireResizeEvent(); + } + /* * The timer holds a reference to |this| while calling |Notify|. * However, implementations of |WillRefresh| are permitted to destroy diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h index 3dc86c4031a4..08fa7a355bd3 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -148,6 +148,22 @@ public: bool AddImageRequest(imgIRequest* aRequest); void RemoveImageRequest(imgIRequest* aRequest); + /** + * Add / remove presshells which have pending resize event. + */ + void AddResizeEventFlushObserver(nsIPresShell* aShell) + { + NS_ASSERTION(!mResizeEventFlushObservers.Contains(aShell), + "Double-adding resize event flush observer"); + mResizeEventFlushObservers.AppendElement(aShell); + EnsureTimerStarted(); + } + + void RemoveResizeEventFlushObserver(nsIPresShell* aShell) + { + mResizeEventFlushObservers.RemoveElement(aShell); + } + /** * Add / remove presshells that we should flush style and layout on */ @@ -392,6 +408,7 @@ private: RequestTable mRequests; ImageStartTable mStartTable; + nsAutoTArray mResizeEventFlushObservers; nsAutoTArray mStyleFlushObservers; nsAutoTArray mLayoutFlushObservers; nsAutoTArray mPresShellsToInvalidateIfHidden; diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index facbb47e9c20..73e44137df6c 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -4741,8 +4741,20 @@ ScrollFrameHelper::LayoutScrollbars(nsBoxLayoutState& aState, NS_ASSERTION(!mSupppressScrollbarUpdate, "This should have been suppressed"); + nsIPresShell* presShell = mOuter->PresContext()->PresShell(); + bool hasResizer = HasResizer(); bool scrollbarOnLeft = !IsScrollbarOnRight(); + bool overlayScrollBarsWithZoom = + mIsRoot && LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) && + presShell->IsScrollPositionClampingScrollPortSizeSet(); + + nsSize scrollPortClampingSize = mScrollPort.Size(); + double res = 1.0; + if (overlayScrollBarsWithZoom) { + scrollPortClampingSize = presShell->GetScrollPositionClampingScrollPortSize(); + res = presShell->GetCumulativeResolution(); + } // place the scrollcorner if (mScrollCornerBox || mResizerBox) { @@ -4805,8 +4817,11 @@ ScrollFrameHelper::LayoutScrollbars(nsBoxLayoutState& aState, if (mVScrollbarBox) { NS_PRECONDITION(mVScrollbarBox->IsBoxFrame(), "Must be a box frame!"); vRect = mScrollPort; + if (overlayScrollBarsWithZoom) { + vRect.height = NSToCoordRound(res * scrollPortClampingSize.height); + } vRect.width = aContentArea.width - mScrollPort.width; - vRect.x = scrollbarOnLeft ? aContentArea.x : mScrollPort.XMost(); + vRect.x = scrollbarOnLeft ? aContentArea.x : mScrollPort.x + NSToCoordRound(res * scrollPortClampingSize.width); if (mHasVerticalScrollbar) { nsMargin margin; mVScrollbarBox->GetMargin(margin); @@ -4819,8 +4834,11 @@ ScrollFrameHelper::LayoutScrollbars(nsBoxLayoutState& aState, if (mHScrollbarBox) { NS_PRECONDITION(mHScrollbarBox->IsBoxFrame(), "Must be a box frame!"); hRect = mScrollPort; + if (overlayScrollBarsWithZoom) { + hRect.width = NSToCoordRound(res * scrollPortClampingSize.width); + } hRect.height = aContentArea.height - mScrollPort.height; - hRect.y = true ? mScrollPort.YMost() : aContentArea.y; + hRect.y = mScrollPort.y + NSToCoordRound(res * scrollPortClampingSize.height); if (mHasHorizontalScrollbar) { nsMargin margin; mHScrollbarBox->GetMargin(margin); diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index 1017bc2b965f..e681f48d9f12 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -26,19 +26,22 @@ using namespace mozilla; typedef nsGridContainerFrame::TrackSize TrackSize; -const uint32_t nsGridContainerFrame::kAutoLine = 12345U; const uint32_t nsGridContainerFrame::kTranslatedMaxLine = uint32_t(nsStyleGridLine::kMaxLine - nsStyleGridLine::kMinLine - 1); +const uint32_t nsGridContainerFrame::kAutoLine = kTranslatedMaxLine + 3457U; class nsGridContainerFrame::GridItemCSSOrderIterator { public: enum OrderState { eUnknownOrder, eKnownOrdered, eKnownUnordered }; + enum ChildFilter { eSkipPlaceholders, eIncludeAll }; GridItemCSSOrderIterator(nsIFrame* aGridContainer, nsIFrame::ChildListID aListID, + ChildFilter aFilter = eSkipPlaceholders, OrderState aState = eUnknownOrder) : mChildren(aGridContainer->GetChildList(aListID)) , mArrayIndex(0) + , mSkipPlaceholders(aFilter == eSkipPlaceholders) #ifdef DEBUG , mGridContainer(aGridContainer) , mListID(aListID) @@ -69,6 +72,10 @@ public: // XXX replace this with nsTArray::StableSort when bug 1147091 is fixed. std::stable_sort(mArray->begin(), mArray->end(), IsCSSOrderLessThan); } + + if (mSkipPlaceholders) { + SkipPlaceholders(); + } } nsIFrame* operator*() const @@ -80,6 +87,28 @@ public: return (*mArray)[mArrayIndex]; } + /** + * Skip over placeholder children. + */ + void SkipPlaceholders() + { + if (mEnumerator) { + for (; !mEnumerator->AtEnd(); mEnumerator->Next()) { + nsIFrame* child = mEnumerator->get(); + if (child->GetType() != nsGkAtoms::placeholderFrame) { + return; + } + } + } else { + for (; mArrayIndex < mArray->Length(); ++mArrayIndex) { + nsIFrame* child = (*mArray)[mArrayIndex]; + if (child->GetType() != nsGkAtoms::placeholderFrame) { + return; + } + } + } + } + bool AtEnd() const { MOZ_ASSERT(mEnumerator || mArrayIndex <= mArray->Length()); @@ -100,9 +129,12 @@ public: MOZ_ASSERT(mArrayIndex < mArray->Length(), "iterating past end"); ++mArrayIndex; } + if (mSkipPlaceholders) { + SkipPlaceholders(); + } } - void Reset() + void Reset(ChildFilter aFilter = eSkipPlaceholders) { if (mEnumerator) { mEnumerator.reset(); @@ -110,6 +142,10 @@ public: } else { mArrayIndex = 0; } + mSkipPlaceholders = aFilter == eSkipPlaceholders; + if (mSkipPlaceholders) { + SkipPlaceholders(); + } } bool ItemsAreAlreadyInOrder() const { return mEnumerator.isSome(); } @@ -124,6 +160,8 @@ private: // Used if child list is *not* in ascending 'order'. Maybe> mArray; size_t mArrayIndex; + // Skip placeholder children in the iteration? + bool mSkipPlaceholders; #ifdef DEBUG nsIFrame* mGridContainer; nsIFrame::ChildListID mListID; @@ -1213,13 +1251,19 @@ nsGridContainerFrame::ReflowChildren(GridItemCSSOrderIterator& aIter, nsPresContext* pc = PresContext(); for (; !aIter.AtEnd(); aIter.Next()) { nsIFrame* child = *aIter; - GridArea* area = GetGridAreaForChild(child); - MOZ_ASSERT(area && area->IsDefinite()); - LogicalRect cb = ContainingBlockFor(wm, *area, aColSizes, aRowSizes); - cb += gridOrigin; + const bool isGridItem = child->GetType() != nsGkAtoms::placeholderFrame; + LogicalRect cb(wm); + if (MOZ_LIKELY(isGridItem)) { + GridArea* area = GetGridAreaForChild(child); + MOZ_ASSERT(area && area->IsDefinite()); + cb = ContainingBlockFor(wm, *area, aColSizes, aRowSizes); + cb += gridOrigin; + } else { + cb = aContentArea; + } nsHTMLReflowState childRS(pc, aReflowState, child, cb.Size(wm)); const LogicalMargin margin = childRS.ComputedLogicalMargin(); - if (childRS.ComputedBSize() == NS_AUTOHEIGHT) { + if (childRS.ComputedBSize() == NS_AUTOHEIGHT && MOZ_LIKELY(isGridItem)) { // XXX the start of an align-self:stretch impl. Needs min-/max-bsize // clamping though, and check the prop value is actually 'stretch'! LogicalMargin bp = childRS.ComputedLogicalBorderPadding(); @@ -1234,6 +1278,7 @@ nsGridContainerFrame::ReflowChildren(GridItemCSSOrderIterator& aIter, nsReflowStatus childStatus; ReflowChild(child, pc, childSize, childRS, wm, childPos, containerWidth, 0, childStatus); + childRS.ApplyRelativePositioning(&childPos, containerWidth); FinishReflowChild(child, pc, childSize, &childRS, wm, childPos, containerWidth, 0); ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child); @@ -1330,7 +1375,7 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext, LogicalRect contentArea(wm, bp.IStart(wm), bp.BStart(wm), computedISize, bSize); - normalFlowIter.Reset(); + normalFlowIter.Reset(GridItemCSSOrderIterator::eIncludeAll); ReflowChildren(normalFlowIter, contentArea, colSizes, rowSizes, aDesiredSize, aReflowState, aStatus); @@ -1367,7 +1412,8 @@ nsGridContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, typedef GridItemCSSOrderIterator::OrderState OrderState; OrderState order = mIsNormalFlowInCSSOrder ? OrderState::eKnownOrdered : OrderState::eKnownUnordered; - GridItemCSSOrderIterator iter(this, kPrincipalList, order); + GridItemCSSOrderIterator iter(this, kPrincipalList, + GridItemCSSOrderIterator::eIncludeAll, order); for (; !iter.AtEnd(); iter.Next()) { nsIFrame* child = *iter; BuildDisplayListForChild(aBuilder, child, aDirtyRect, childLists, @@ -1437,8 +1483,7 @@ FrameWantsToBeInAnonymousGridItem(nsIFrame* aFrame) { // Note: This needs to match the logic in // nsCSSFrameConstructor::FrameConstructionItem::NeedsAnonFlexOrGridItem() - return (aFrame->IsFrameOfType(nsIFrame::eLineParticipant) || - nsGkAtoms::placeholderFrame == aFrame->GetType()); + return aFrame->IsFrameOfType(nsIFrame::eLineParticipant); } // Debugging method, to let us assert that our anonymous grid items are diff --git a/layout/reftests/bugs/1133905-1-h-rtl.html b/layout/reftests/bugs/1133905-1-h-rtl.html new file mode 100644 index 000000000000..bc53a95cad51 --- /dev/null +++ b/layout/reftests/bugs/1133905-1-h-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-1-h.html b/layout/reftests/bugs/1133905-1-h.html new file mode 100644 index 000000000000..b7d476e3af6e --- /dev/null +++ b/layout/reftests/bugs/1133905-1-h.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-1-rtl.html b/layout/reftests/bugs/1133905-1-rtl.html new file mode 100644 index 000000000000..d3b3e6437170 --- /dev/null +++ b/layout/reftests/bugs/1133905-1-rtl.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/layout/reftests/bugs/1133905-1-v-rtl.html b/layout/reftests/bugs/1133905-1-v-rtl.html new file mode 100644 index 000000000000..d059105f262c --- /dev/null +++ b/layout/reftests/bugs/1133905-1-v-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-1-v.html b/layout/reftests/bugs/1133905-1-v.html new file mode 100644 index 000000000000..e03fd40bc4f2 --- /dev/null +++ b/layout/reftests/bugs/1133905-1-v.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-1-vh-rtl.html b/layout/reftests/bugs/1133905-1-vh-rtl.html new file mode 100644 index 000000000000..e721caa2a031 --- /dev/null +++ b/layout/reftests/bugs/1133905-1-vh-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-1-vh.html b/layout/reftests/bugs/1133905-1-vh.html new file mode 100644 index 000000000000..1b5faf93d32c --- /dev/null +++ b/layout/reftests/bugs/1133905-1-vh.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-1.html b/layout/reftests/bugs/1133905-1.html new file mode 100644 index 000000000000..d8c80446312d --- /dev/null +++ b/layout/reftests/bugs/1133905-1.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/layout/reftests/bugs/1133905-2-h-rtl.html b/layout/reftests/bugs/1133905-2-h-rtl.html new file mode 100644 index 000000000000..a4c48921a38f --- /dev/null +++ b/layout/reftests/bugs/1133905-2-h-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-2-h.html b/layout/reftests/bugs/1133905-2-h.html new file mode 100644 index 000000000000..8ed9796849be --- /dev/null +++ b/layout/reftests/bugs/1133905-2-h.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-2-rtl.html b/layout/reftests/bugs/1133905-2-rtl.html new file mode 100644 index 000000000000..cf131bc68e45 --- /dev/null +++ b/layout/reftests/bugs/1133905-2-rtl.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/layout/reftests/bugs/1133905-2-v-rtl.html b/layout/reftests/bugs/1133905-2-v-rtl.html new file mode 100644 index 000000000000..ba003666ffa9 --- /dev/null +++ b/layout/reftests/bugs/1133905-2-v-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-2-v.html b/layout/reftests/bugs/1133905-2-v.html new file mode 100644 index 000000000000..8d2daf460ef3 --- /dev/null +++ b/layout/reftests/bugs/1133905-2-v.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-2-vh-rtl.html b/layout/reftests/bugs/1133905-2-vh-rtl.html new file mode 100644 index 000000000000..49601a3e70b9 --- /dev/null +++ b/layout/reftests/bugs/1133905-2-vh-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-2-vh.html b/layout/reftests/bugs/1133905-2-vh.html new file mode 100644 index 000000000000..22ff229eb7e1 --- /dev/null +++ b/layout/reftests/bugs/1133905-2-vh.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-2.html b/layout/reftests/bugs/1133905-2.html new file mode 100644 index 000000000000..7e306fd41b30 --- /dev/null +++ b/layout/reftests/bugs/1133905-2.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/layout/reftests/bugs/1133905-3-h-rtl.html b/layout/reftests/bugs/1133905-3-h-rtl.html new file mode 100644 index 000000000000..ca60b948bb97 --- /dev/null +++ b/layout/reftests/bugs/1133905-3-h-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-3-h.html b/layout/reftests/bugs/1133905-3-h.html new file mode 100644 index 000000000000..82d8583942c6 --- /dev/null +++ b/layout/reftests/bugs/1133905-3-h.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-3-rtl.html b/layout/reftests/bugs/1133905-3-rtl.html new file mode 100644 index 000000000000..8d4976e69f55 --- /dev/null +++ b/layout/reftests/bugs/1133905-3-rtl.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/layout/reftests/bugs/1133905-3-v-rtl.html b/layout/reftests/bugs/1133905-3-v-rtl.html new file mode 100644 index 000000000000..a22480af72cd --- /dev/null +++ b/layout/reftests/bugs/1133905-3-v-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-3-v.html b/layout/reftests/bugs/1133905-3-v.html new file mode 100644 index 000000000000..20f9a012e0dd --- /dev/null +++ b/layout/reftests/bugs/1133905-3-v.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-3-vh-rtl.html b/layout/reftests/bugs/1133905-3-vh-rtl.html new file mode 100644 index 000000000000..5f21eef28951 --- /dev/null +++ b/layout/reftests/bugs/1133905-3-vh-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-3-vh.html b/layout/reftests/bugs/1133905-3-vh.html new file mode 100644 index 000000000000..739b9c3a0279 --- /dev/null +++ b/layout/reftests/bugs/1133905-3-vh.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-3.html b/layout/reftests/bugs/1133905-3.html new file mode 100644 index 000000000000..244f0a505d2a --- /dev/null +++ b/layout/reftests/bugs/1133905-3.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/layout/reftests/bugs/1133905-4-h-rtl.html b/layout/reftests/bugs/1133905-4-h-rtl.html new file mode 100644 index 000000000000..ca2609c98930 --- /dev/null +++ b/layout/reftests/bugs/1133905-4-h-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-4-h.html b/layout/reftests/bugs/1133905-4-h.html new file mode 100644 index 000000000000..1de1436259ed --- /dev/null +++ b/layout/reftests/bugs/1133905-4-h.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-4-rtl.html b/layout/reftests/bugs/1133905-4-rtl.html new file mode 100644 index 000000000000..badae3b37e52 --- /dev/null +++ b/layout/reftests/bugs/1133905-4-rtl.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/layout/reftests/bugs/1133905-4-v-rtl.html b/layout/reftests/bugs/1133905-4-v-rtl.html new file mode 100644 index 000000000000..b3264e4acfe8 --- /dev/null +++ b/layout/reftests/bugs/1133905-4-v-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-4-v.html b/layout/reftests/bugs/1133905-4-v.html new file mode 100644 index 000000000000..00100b7db850 --- /dev/null +++ b/layout/reftests/bugs/1133905-4-v.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-4-vh-rtl.html b/layout/reftests/bugs/1133905-4-vh-rtl.html new file mode 100644 index 000000000000..cdb1ae155513 --- /dev/null +++ b/layout/reftests/bugs/1133905-4-vh-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-4-vh.html b/layout/reftests/bugs/1133905-4-vh.html new file mode 100644 index 000000000000..d27d438ba4c1 --- /dev/null +++ b/layout/reftests/bugs/1133905-4-vh.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-4.html b/layout/reftests/bugs/1133905-4.html new file mode 100644 index 000000000000..f4f13980774e --- /dev/null +++ b/layout/reftests/bugs/1133905-4.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/layout/reftests/bugs/1133905-5-h-rtl.html b/layout/reftests/bugs/1133905-5-h-rtl.html new file mode 100644 index 000000000000..494456d7df6f --- /dev/null +++ b/layout/reftests/bugs/1133905-5-h-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-5-h.html b/layout/reftests/bugs/1133905-5-h.html new file mode 100644 index 000000000000..a5199516d3dc --- /dev/null +++ b/layout/reftests/bugs/1133905-5-h.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-5-rtl.html b/layout/reftests/bugs/1133905-5-rtl.html new file mode 100644 index 000000000000..5125d8d13ac0 --- /dev/null +++ b/layout/reftests/bugs/1133905-5-rtl.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/layout/reftests/bugs/1133905-5-v-rtl.html b/layout/reftests/bugs/1133905-5-v-rtl.html new file mode 100644 index 000000000000..55d45e0a1f9a --- /dev/null +++ b/layout/reftests/bugs/1133905-5-v-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-5-v.html b/layout/reftests/bugs/1133905-5-v.html new file mode 100644 index 000000000000..972cf13cca8b --- /dev/null +++ b/layout/reftests/bugs/1133905-5-v.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-5-vh-rtl.html b/layout/reftests/bugs/1133905-5-vh-rtl.html new file mode 100644 index 000000000000..d73ec750970f --- /dev/null +++ b/layout/reftests/bugs/1133905-5-vh-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-5-vh.html b/layout/reftests/bugs/1133905-5-vh.html new file mode 100644 index 000000000000..94f8b9100ab4 --- /dev/null +++ b/layout/reftests/bugs/1133905-5-vh.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-5.html b/layout/reftests/bugs/1133905-5.html new file mode 100644 index 000000000000..5b8c41983a10 --- /dev/null +++ b/layout/reftests/bugs/1133905-5.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/layout/reftests/bugs/1133905-6-h-rtl.html b/layout/reftests/bugs/1133905-6-h-rtl.html new file mode 100644 index 000000000000..a391dfb54a61 --- /dev/null +++ b/layout/reftests/bugs/1133905-6-h-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-6-h.html b/layout/reftests/bugs/1133905-6-h.html new file mode 100644 index 000000000000..4d097f9e49e3 --- /dev/null +++ b/layout/reftests/bugs/1133905-6-h.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-6-rtl.html b/layout/reftests/bugs/1133905-6-rtl.html new file mode 100644 index 000000000000..60babfcf401d --- /dev/null +++ b/layout/reftests/bugs/1133905-6-rtl.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/layout/reftests/bugs/1133905-6-v-rtl.html b/layout/reftests/bugs/1133905-6-v-rtl.html new file mode 100644 index 000000000000..bc13b040fe28 --- /dev/null +++ b/layout/reftests/bugs/1133905-6-v-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-6-v.html b/layout/reftests/bugs/1133905-6-v.html new file mode 100644 index 000000000000..e8b509f4e5ff --- /dev/null +++ b/layout/reftests/bugs/1133905-6-v.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-6-vh-rtl.html b/layout/reftests/bugs/1133905-6-vh-rtl.html new file mode 100644 index 000000000000..62ac31cb09d2 --- /dev/null +++ b/layout/reftests/bugs/1133905-6-vh-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-6-vh.html b/layout/reftests/bugs/1133905-6-vh.html new file mode 100644 index 000000000000..556026bb6f8e --- /dev/null +++ b/layout/reftests/bugs/1133905-6-vh.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-6.html b/layout/reftests/bugs/1133905-6.html new file mode 100644 index 000000000000..867e9b0828d9 --- /dev/null +++ b/layout/reftests/bugs/1133905-6.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/layout/reftests/bugs/1133905-ref-h-rtl.html b/layout/reftests/bugs/1133905-ref-h-rtl.html new file mode 100644 index 000000000000..ca2609c98930 --- /dev/null +++ b/layout/reftests/bugs/1133905-ref-h-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-ref-h.html b/layout/reftests/bugs/1133905-ref-h.html new file mode 100644 index 000000000000..1de1436259ed --- /dev/null +++ b/layout/reftests/bugs/1133905-ref-h.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-ref-rtl.html b/layout/reftests/bugs/1133905-ref-rtl.html new file mode 100644 index 000000000000..badae3b37e52 --- /dev/null +++ b/layout/reftests/bugs/1133905-ref-rtl.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/layout/reftests/bugs/1133905-ref-v-rtl.html b/layout/reftests/bugs/1133905-ref-v-rtl.html new file mode 100644 index 000000000000..b3264e4acfe8 --- /dev/null +++ b/layout/reftests/bugs/1133905-ref-v-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-ref-v.html b/layout/reftests/bugs/1133905-ref-v.html new file mode 100644 index 000000000000..00100b7db850 --- /dev/null +++ b/layout/reftests/bugs/1133905-ref-v.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-ref-vh-rtl.html b/layout/reftests/bugs/1133905-ref-vh-rtl.html new file mode 100644 index 000000000000..cdb1ae155513 --- /dev/null +++ b/layout/reftests/bugs/1133905-ref-vh-rtl.html @@ -0,0 +1,10 @@ + + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-ref-vh.html b/layout/reftests/bugs/1133905-ref-vh.html new file mode 100644 index 000000000000..d27d438ba4c1 --- /dev/null +++ b/layout/reftests/bugs/1133905-ref-vh.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/layout/reftests/bugs/1133905-ref.html b/layout/reftests/bugs/1133905-ref.html new file mode 100644 index 000000000000..f4f13980774e --- /dev/null +++ b/layout/reftests/bugs/1133905-ref.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index cda141176197..22ddde2e3f90 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1872,6 +1872,54 @@ fuzzy-if(d2d,36,304) HTTP(..) == 1116480-1-fakeitalic-overflow.html 1116480-1-fa == 1128354-1.html 1128354-1-ref.html == 1130231-1-button-padding-rtl.html 1130231-1-button-padding-rtl-ref.html == 1130231-2-button-padding-rtl.html 1130231-2-button-padding-rtl-ref.html +== 1133905-1.html 1133905-ref.html +== 1133905-2.html 1133905-ref.html +== 1133905-3.html 1133905-ref.html +== 1133905-4.html 1133905-ref.html +== 1133905-5.html 1133905-ref.html +== 1133905-6.html 1133905-ref.html +== 1133905-1-v.html 1133905-ref-v.html +== 1133905-2-v.html 1133905-ref-v.html +== 1133905-3-v.html 1133905-ref-v.html +== 1133905-4-v.html 1133905-ref-v.html +fuzzy-if(B2G,61,336) == 1133905-5-v.html 1133905-ref-v.html +fuzzy-if(B2G,61,480) == 1133905-6-v.html 1133905-ref-v.html +== 1133905-1-h.html 1133905-ref-h.html +== 1133905-2-h.html 1133905-ref-h.html +== 1133905-3-h.html 1133905-ref-h.html +== 1133905-4-h.html 1133905-ref-h.html +== 1133905-5-h.html 1133905-ref-h.html +== 1133905-6-h.html 1133905-ref-h.html +== 1133905-1-vh.html 1133905-ref-vh.html +== 1133905-2-vh.html 1133905-ref-vh.html +== 1133905-3-vh.html 1133905-ref-vh.html +== 1133905-4-vh.html 1133905-ref-vh.html +fuzzy-if(B2G,102,720) == 1133905-5-vh.html 1133905-ref-vh.html +fuzzy-if(B2G,101,1138) == 1133905-6-vh.html 1133905-ref-vh.html +== 1133905-1-rtl.html 1133905-ref-rtl.html +== 1133905-2-rtl.html 1133905-ref-rtl.html +== 1133905-3-rtl.html 1133905-ref-rtl.html +== 1133905-4-rtl.html 1133905-ref-rtl.html +== 1133905-5-rtl.html 1133905-ref-rtl.html +== 1133905-6-rtl.html 1133905-ref-rtl.html +== 1133905-1-v-rtl.html 1133905-ref-v-rtl.html +== 1133905-2-v-rtl.html 1133905-ref-v-rtl.html +== 1133905-3-v-rtl.html 1133905-ref-v-rtl.html +== 1133905-4-v-rtl.html 1133905-ref-v-rtl.html +== 1133905-5-v-rtl.html 1133905-ref-v-rtl.html +== 1133905-6-v-rtl.html 1133905-ref-v-rtl.html +== 1133905-1-h-rtl.html 1133905-ref-h-rtl.html +== 1133905-2-h-rtl.html 1133905-ref-h-rtl.html +== 1133905-3-h-rtl.html 1133905-ref-h-rtl.html +== 1133905-4-h-rtl.html 1133905-ref-h-rtl.html +== 1133905-5-h-rtl.html 1133905-ref-h-rtl.html +== 1133905-6-h-rtl.html 1133905-ref-h-rtl.html +== 1133905-1-vh-rtl.html 1133905-ref-vh-rtl.html +== 1133905-2-vh-rtl.html 1133905-ref-vh-rtl.html +== 1133905-3-vh-rtl.html 1133905-ref-vh-rtl.html +== 1133905-4-vh-rtl.html 1133905-ref-vh-rtl.html +fuzzy-if(B2G,102,396) == 1133905-5-vh-rtl.html 1133905-ref-vh-rtl.html +fuzzy-if(B2G,101,672) == 1133905-6-vh-rtl.html 1133905-ref-vh-rtl.html skip-if(B2G||Mulet) == 1150021-1.xul 1150021-1-ref.xul == 1151145-1.html 1151145-1-ref.html == 1151306-1.html 1151306-1-ref.html diff --git a/layout/reftests/css-grid/grid-abspos-items-001-ref.html b/layout/reftests/css-grid/grid-abspos-items-001-ref.html index 2eb05af91527..216f0eae4970 100644 --- a/layout/reftests/css-grid/grid-abspos-items-001-ref.html +++ b/layout/reftests/css-grid/grid-abspos-items-001-ref.html @@ -25,7 +25,7 @@ body,html { color:black; background:white; font-size:16px; padding:0; margin:0; height: 100px; } .zero-size { width:0; height:0; } -.auto-size { width:auto; height:17px; } +.auto-size { width:auto; height:0px; } .a { position: absolute; @@ -88,7 +88,7 @@ span {
a b -c +c d e f @@ -96,7 +96,7 @@ span {
-b +b
@@ -111,9 +111,9 @@ span {
-b -c -d +b +c +d
diff --git a/layout/reftests/css-grid/grid-abspos-items-002-ref.html b/layout/reftests/css-grid/grid-abspos-items-002-ref.html index 7e1f6b4b8360..4c23081cb5c8 100644 --- a/layout/reftests/css-grid/grid-abspos-items-002-ref.html +++ b/layout/reftests/css-grid/grid-abspos-items-002-ref.html @@ -26,7 +26,7 @@ body,html { color:black; background:white; font-size:16px; padding:0; margin:0; height: 100px; } .zero-size { width:0; height:0; } -.auto-size { width:auto; height:17px; } +.auto-size { width:auto; height:0px; } .a { position: absolute; @@ -89,7 +89,7 @@ span {
a b -c +c d e f @@ -97,7 +97,7 @@ span {
-b +b
diff --git a/layout/reftests/css-grid/grid-relpos-items-001-ref.html b/layout/reftests/css-grid/grid-relpos-items-001-ref.html new file mode 100644 index 000000000000..5152107a2e1f --- /dev/null +++ b/layout/reftests/css-grid/grid-relpos-items-001-ref.html @@ -0,0 +1,60 @@ + + + + CSS Test: Testing layout of rel.pos. grid items + + + + + + +
+1 + + +r3 +r4 +
+ + + diff --git a/layout/reftests/css-grid/grid-relpos-items-001.html b/layout/reftests/css-grid/grid-relpos-items-001.html new file mode 100644 index 000000000000..153d56f7ea2b --- /dev/null +++ b/layout/reftests/css-grid/grid-relpos-items-001.html @@ -0,0 +1,62 @@ + + + + CSS Test: Testing layout of rel.pos. grid items + + + + + + + + +
+1 + + +r3 +r4 +
+ + + diff --git a/layout/reftests/css-grid/reftest.list b/layout/reftests/css-grid/reftest.list index 48880be5199c..2810e45b2322 100644 --- a/layout/reftests/css-grid/reftest.list +++ b/layout/reftests/css-grid/reftest.list @@ -23,3 +23,4 @@ pref(layout.css.vertical-text.enabled,true) == rtl-grid-placement-definite-001.h pref(layout.css.vertical-text.enabled,true) == rtl-grid-placement-auto-row-sparse-001.html rtl-grid-placement-auto-row-sparse-001-ref.html pref(layout.css.vertical-text.enabled,true) == vlr-grid-placement-auto-row-sparse-001.html vlr-grid-placement-auto-row-sparse-001-ref.html pref(layout.css.vertical-text.enabled,true) == vrl-grid-placement-auto-row-sparse-001.html vrl-grid-placement-auto-row-sparse-001-ref.html +== grid-relpos-items-001.html grid-relpos-items-001-ref.html diff --git a/layout/reftests/font-face/ex-unit-1-dynamic.html b/layout/reftests/font-face/ex-unit-1-dynamic.html index b8b2b1ca6a1e..93a5e0aa20d4 100644 --- a/layout/reftests/font-face/ex-unit-1-dynamic.html +++ b/layout/reftests/font-face/ex-unit-1-dynamic.html @@ -15,6 +15,6 @@ function run() { 0); } - + diff --git a/layout/style/test/file_animations_async_tests.html b/layout/style/test/file_animations_async_tests.html new file mode 100644 index 000000000000..9d4dfa1feda2 --- /dev/null +++ b/layout/style/test/file_animations_async_tests.html @@ -0,0 +1,77 @@ + + + + + Test for Bug 1086937 + + + + + + +Mozilla Bug 1086937 +
+
+
+ + diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini index c3f994e13218..1ebbb3e9d384 100644 --- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -40,6 +40,7 @@ skip-if = toolkit == 'android' [test_animations_omta_start.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1041017 [test_animations_pausing.html] +support-files = file_animations_pausing.html [test_any_dynamic.html] [test_at_rule_parse_serialize.html] [test_bug73586.html] @@ -267,7 +268,7 @@ support-files = bug732209-css.sjs [test_counter_descriptor_storage.html] [test_position_float_display.html] [test_animations_async_tests.html] -support-files = ../../reftests/fonts/Ahem.ttf file_animations_pausing.html +support-files = ../../reftests/fonts/Ahem.ttf file_animations_async_tests.html [test_setPropertyWithNull.html] [test_attribute_selector_eof_behavior.html] [test_css_loader_crossorigin_data_url.html] diff --git a/layout/style/test/test_animations_async_tests.html b/layout/style/test/test_animations_async_tests.html index f3108c71c404..5d328c7aa740 100644 --- a/layout/style/test/test_animations_async_tests.html +++ b/layout/style/test/test_animations_async_tests.html @@ -6,60 +6,15 @@ - diff --git a/media/gmp-clearkey/0.1/AudioDecoder.cpp b/media/gmp-clearkey/0.1/AudioDecoder.cpp index 58c35c40a78c..3421651340be 100644 --- a/media/gmp-clearkey/0.1/AudioDecoder.cpp +++ b/media/gmp-clearkey/0.1/AudioDecoder.cpp @@ -32,6 +32,8 @@ AudioDecoder::AudioDecoder(GMPAudioHost *aHostAPI) , mNumInputTasks(0) , mHasShutdown(false) { + // We drop the ref in DecodingComplete(). + AddRef(); } AudioDecoder::~AudioDecoder() @@ -84,9 +86,9 @@ AudioDecoder::Decode(GMPAudioSamples* aInput) AutoLock lock(mMutex); mNumInputTasks++; } - mWorkerThread->Post(WrapTask(this, - &AudioDecoder::DecodeTask, - aInput)); + mWorkerThread->Post(WrapTaskRefCounted(this, + &AudioDecoder::DecodeTask, + aInput)); } void @@ -258,8 +260,8 @@ AudioDecoder::Drain() return; } EnsureWorker(); - mWorkerThread->Post(WrapTask(this, - &AudioDecoder::DrainTask)); + mWorkerThread->Post(WrapTaskRefCounted(this, + &AudioDecoder::DrainTask)); } void @@ -270,24 +272,19 @@ AudioDecoder::DecodingComplete() } mHasShutdown = true; - // Worker thread might have dispatched more tasks to the main thread that need this object. - // Append another task to delete |this|. - GetPlatform()->runonmainthread(WrapTask(this, &AudioDecoder::Destroy)); + // Release the reference we added in the constructor. There may be + // WrapRefCounted tasks that also hold references to us, and keep + // us alive a little longer. + Release(); } void -AudioDecoder::Destroy() -{ - delete this; -} - -void -AudioDecoder::MaybeRunOnMainThread(gmp_task_args_base* aTask) +AudioDecoder::MaybeRunOnMainThread(GMPTask* aTask) { class MaybeRunTask : public GMPTask { public: - MaybeRunTask(AudioDecoder* aDecoder, gmp_task_args_base* aTask) + MaybeRunTask(AudioDecoder* aDecoder, GMPTask* aTask) : mDecoder(aDecoder), mTask(aTask) { } @@ -307,8 +304,8 @@ AudioDecoder::MaybeRunOnMainThread(gmp_task_args_base* aTask) } private: - AudioDecoder* mDecoder; - gmp_task_args_base* mTask; + RefPtr mDecoder; + GMPTask* mTask; }; GetPlatform()->runonmainthread(new MaybeRunTask(this, aTask)); diff --git a/media/gmp-clearkey/0.1/AudioDecoder.h b/media/gmp-clearkey/0.1/AudioDecoder.h index d72d652a398b..98915bb7a76b 100644 --- a/media/gmp-clearkey/0.1/AudioDecoder.h +++ b/media/gmp-clearkey/0.1/AudioDecoder.h @@ -21,16 +21,16 @@ #include "gmp-audio-host.h" #include "gmp-task-utils.h" #include "WMFAACDecoder.h" +#include "RefCounted.h" #include "mfobjects.h" class AudioDecoder : public GMPAudioDecoder + , public RefCounted { public: AudioDecoder(GMPAudioHost *aHostAPI); - virtual ~AudioDecoder(); - virtual void InitDecode(const GMPAudioCodec& aCodecSettings, GMPAudioDecoderCallback* aCallback) override; @@ -45,6 +45,7 @@ public: bool HasShutdown() { return mHasShutdown; } private: + virtual ~AudioDecoder(); void EnsureWorker(); @@ -56,8 +57,7 @@ private: HRESULT MFToGMPSample(IMFSample* aSample, GMPAudioSamples* aAudioFrame); - void MaybeRunOnMainThread(gmp_task_args_base* aTask); - void Destroy(); + void MaybeRunOnMainThread(GMPTask* aTask); GMPAudioHost *mHostAPI; // host-owned, invalid at DecodingComplete GMPAudioDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete diff --git a/media/gmp-clearkey/0.1/ClearKeySession.cpp b/media/gmp-clearkey/0.1/ClearKeySession.cpp index d6507a651438..2abbc20309ba 100644 --- a/media/gmp-clearkey/0.1/ClearKeySession.cpp +++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp @@ -23,6 +23,7 @@ #include "gmp-api/gmp-decryption.h" #include "Endian.h" #include +#include using namespace mozilla; diff --git a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp index 1cf9f351a1d0..17cb8ced1ba1 100644 --- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp +++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp @@ -48,7 +48,6 @@ ClearKeySessionManager::ClearKeySessionManager() ClearKeySessionManager::~ClearKeySessionManager() { CK_LOGD("ClearKeySessionManager dtor %p", this); - assert(!mRefCount); } static bool @@ -387,9 +386,9 @@ ClearKeySessionManager::Decrypt(GMPBuffer* aBuffer, return; } - mThread->Post(WrapTask(this, - &ClearKeySessionManager::DoDecrypt, - aBuffer, aMetadata)); + mThread->Post(WrapTaskRefCounted(this, + &ClearKeySessionManager::DoDecrypt, + aBuffer, aMetadata)); } void diff --git a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp index a15f7fed70de..e3891011a372 100644 --- a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp +++ b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp @@ -24,6 +24,7 @@ #include "ClearKeyBase64.h" #include "ArrayUtils.h" #include +#include #include "Endian.h" #include "openaes/oaes_lib.h" @@ -539,3 +540,10 @@ ClearKeyUtils::IsValidSessionId(const char* aBuff, uint32_t aLength) } return true; } + +GMPMutex* GMPCreateMutex() { + GMPMutex* mutex; + auto err = GetPlatform()->createmutex(&mutex); + assert(mutex); + return GMP_FAILED(err) ? nullptr : mutex; +} diff --git a/media/gmp-clearkey/0.1/ClearKeyUtils.h b/media/gmp-clearkey/0.1/ClearKeyUtils.h index 4e359e13efd2..2fd88c48c4b1 100644 --- a/media/gmp-clearkey/0.1/ClearKeyUtils.h +++ b/media/gmp-clearkey/0.1/ClearKeyUtils.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "gmp-decryption.h" #define CLEARKEY_KEY_LEN ((size_t)16) @@ -75,4 +76,25 @@ Contains(const Container& aContainer, const Element& aElement) return aContainer.find(aElement) != aContainer.end(); } +class AutoLock { +public: + explicit AutoLock(GMPMutex* aMutex) + : mMutex(aMutex) + { + assert(aMutex); + if (mMutex) { + mMutex->Acquire(); + } + } + ~AutoLock() { + if (mMutex) { + mMutex->Release(); + } + } +private: + GMPMutex* mMutex; +}; + +GMPMutex* GMPCreateMutex(); + #endif // __ClearKeyUtils_h__ diff --git a/media/gmp-clearkey/0.1/RefCounted.h b/media/gmp-clearkey/0.1/RefCounted.h index e7a5c456febe..6da694295864 100644 --- a/media/gmp-clearkey/0.1/RefCounted.h +++ b/media/gmp-clearkey/0.1/RefCounted.h @@ -18,8 +18,46 @@ #define __RefCount_h__ #include +#include +#include "ClearKeyUtils.h" -// Note: Not thread safe! +#if defined(_MSC_VER) +#include +typedef std::atomic AtomicRefCount; +#else +class AtomicRefCount { +public: + explicit AtomicRefCount(uint32_t aValue) + : mCount(aValue) + , mMutex(GMPCreateMutex()) + { + assert(mMutex); + } + ~AtomicRefCount() + { + if (mMutex) { + mMutex->Destroy(); + } + } + uint32_t operator--() { + AutoLock lock(mMutex); + return --mCount; + } + uint32_t operator++() { + AutoLock lock(mMutex); + return ++mCount; + } + operator uint32_t() { + AutoLock lock(mMutex); + return mCount; + } +private: + uint32_t mCount; + GMPMutex* mMutex; +}; +#endif + +// Note: Thread safe. class RefCounted { public: void AddRef() { @@ -41,8 +79,9 @@ protected: } virtual ~RefCounted() { + assert(!mRefCount); } - uint32_t mRefCount; + AtomicRefCount mRefCount; }; template diff --git a/media/gmp-clearkey/0.1/VideoDecoder.cpp b/media/gmp-clearkey/0.1/VideoDecoder.cpp index 5d86fb320ca8..eee9a584dc72 100644 --- a/media/gmp-clearkey/0.1/VideoDecoder.cpp +++ b/media/gmp-clearkey/0.1/VideoDecoder.cpp @@ -35,6 +35,8 @@ VideoDecoder::VideoDecoder(GMPVideoHost *aHostAPI) , mSentExtraData(false) , mHasShutdown(false) { + // We drop the ref in DecodingComplete(). + AddRef(); } VideoDecoder::~VideoDecoder() @@ -112,9 +114,9 @@ VideoDecoder::Decode(GMPVideoEncodedFrame* aInputFrame, // Note: we don't need the codec specific info on a per-frame basis. // It's mostly useful for WebRTC use cases. - mWorkerThread->Post(WrapTask(this, - &VideoDecoder::DecodeTask, - aInputFrame)); + mWorkerThread->Post(WrapTaskRefCounted(this, + &VideoDecoder::DecodeTask, + aInputFrame)); } class AutoReleaseVideoFrame { @@ -197,12 +199,12 @@ VideoDecoder::DecodeTask(GMPVideoEncodedFrame* aInput) CK_LOGD("VideoDecoder::DecodeTask() output ret=0x%x\n", hr); if (hr == S_OK) { MaybeRunOnMainThread( - WrapTask(this, - &VideoDecoder::ReturnOutput, - CComPtr(output), - mDecoder->GetFrameWidth(), - mDecoder->GetFrameHeight(), - mDecoder->GetStride())); + WrapTaskRefCounted(this, + &VideoDecoder::ReturnOutput, + CComPtr(output), + mDecoder->GetFrameWidth(), + mDecoder->GetFrameHeight(), + mDecoder->GetStride())); } if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { AutoLock lock(mMutex); @@ -232,11 +234,20 @@ VideoDecoder::ReturnOutput(IMFSample* aSample, HRESULT hr; GMPVideoFrame* f = nullptr; - mHostAPI->CreateFrame(kGMPI420VideoFrame, &f); - if (!f) { + auto err = mHostAPI->CreateFrame(kGMPI420VideoFrame, &f); + if (GMP_FAILED(err) || !f) { CK_LOGE("Failed to create i420 frame!\n"); return; } + if (HasShutdown()) { + // Note: GMPVideoHost::CreateFrame() can process messages before returning, + // including a message that calls VideoDecoder::DecodingComplete(), i.e. + // we can shutdown during the call! + CK_LOGD("Shutdown while waiting on GMPVideoHost::CreateFrame()!\n"); + f->Destroy(); + return; + } + auto vf = static_cast(f); hr = SampleToVideoFrame(aSample, aWidth, aHeight, aStride, vf); @@ -291,9 +302,10 @@ VideoDecoder::SampleToVideoFrame(IMFSample* aSample, int32_t halfStride = (stride + 1) / 2; int32_t halfHeight = (aHeight + 1) / 2; - aVideoFrame->CreateEmptyFrame(stride, aHeight, stride, halfStride, halfStride); + auto err = aVideoFrame->CreateEmptyFrame(stride, aHeight, stride, halfStride, halfStride); + ENSURE(GMP_SUCCEEDED(err), E_FAIL); - auto err = aVideoFrame->SetWidth(aWidth); + err = aVideoFrame->SetWidth(aWidth); ENSURE(GMP_SUCCEEDED(err), E_FAIL); err = aVideoFrame->SetHeight(aHeight); ENSURE(GMP_SUCCEEDED(err), E_FAIL); @@ -355,12 +367,12 @@ VideoDecoder::DrainTask() CK_LOGD("VideoDecoder::DrainTask() output ret=0x%x\n", hr); if (hr == S_OK) { MaybeRunOnMainThread( - WrapTask(this, - &VideoDecoder::ReturnOutput, - CComPtr(output), - mDecoder->GetFrameWidth(), - mDecoder->GetFrameHeight(), - mDecoder->GetStride())); + WrapTaskRefCounted(this, + &VideoDecoder::ReturnOutput, + CComPtr(output), + mDecoder->GetFrameWidth(), + mDecoder->GetFrameHeight(), + mDecoder->GetStride())); } } MaybeRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete)); @@ -376,8 +388,8 @@ VideoDecoder::Drain() return; } EnsureWorker(); - mWorkerThread->Post(WrapTask(this, - &VideoDecoder::DrainTask)); + mWorkerThread->Post(WrapTaskRefCounted(this, + &VideoDecoder::DrainTask)); } void @@ -388,24 +400,19 @@ VideoDecoder::DecodingComplete() } mHasShutdown = true; - // Worker thread might have dispatched more tasks to the main thread that need this object. - // Append another task to delete |this|. - GetPlatform()->runonmainthread(WrapTask(this, &VideoDecoder::Destroy)); + // Release the reference we added in the constructor. There may be + // WrapRefCounted tasks that also hold references to us, and keep + // us alive a little longer. + Release(); } void -VideoDecoder::Destroy() -{ - delete this; -} - -void -VideoDecoder::MaybeRunOnMainThread(gmp_task_args_base* aTask) +VideoDecoder::MaybeRunOnMainThread(GMPTask* aTask) { class MaybeRunTask : public GMPTask { public: - MaybeRunTask(VideoDecoder* aDecoder, gmp_task_args_base* aTask) + MaybeRunTask(VideoDecoder* aDecoder, GMPTask* aTask) : mDecoder(aDecoder), mTask(aTask) { } @@ -425,8 +432,8 @@ VideoDecoder::MaybeRunOnMainThread(gmp_task_args_base* aTask) } private: - VideoDecoder* mDecoder; - gmp_task_args_base* mTask; + RefPtr mDecoder; + GMPTask* mTask; }; GetPlatform()->runonmainthread(new MaybeRunTask(this, aTask)); diff --git a/media/gmp-clearkey/0.1/VideoDecoder.h b/media/gmp-clearkey/0.1/VideoDecoder.h index e96dca527758..b2d153513ab1 100644 --- a/media/gmp-clearkey/0.1/VideoDecoder.h +++ b/media/gmp-clearkey/0.1/VideoDecoder.h @@ -25,12 +25,11 @@ #include "mfobjects.h" class VideoDecoder : public GMPVideoDecoder + , public RefCounted { public: VideoDecoder(GMPVideoHost *aHostAPI); - virtual ~VideoDecoder(); - virtual void InitDecode(const GMPVideoCodec& aCodecSettings, const uint8_t* aCodecSpecific, uint32_t aCodecSpecificLength, @@ -53,6 +52,8 @@ public: private: + virtual ~VideoDecoder(); + void EnsureWorker(); void DrainTask(); @@ -70,8 +71,7 @@ private: int32_t aStride, GMPVideoi420Frame* aVideoFrame); - void MaybeRunOnMainThread(gmp_task_args_base* aTask); - void Destroy(); + void MaybeRunOnMainThread(GMPTask* aTask); GMPVideoHost *mHostAPI; // host-owned, invalid at DecodingComplete GMPVideoDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete diff --git a/media/gmp-clearkey/0.1/WMFUtils.h b/media/gmp-clearkey/0.1/WMFUtils.h index 6c938efa0308..517c9718aa66 100644 --- a/media/gmp-clearkey/0.1/WMFUtils.h +++ b/media/gmp-clearkey/0.1/WMFUtils.h @@ -243,21 +243,6 @@ inline uint32_t MicrosecondsToRTPTime(Microseconds us) { return uint32_t(0xffffffff & (us * 90000) / 1000000); } -class AutoLock { -public: - AutoLock(GMPMutex* aMutex) - : mMutex(aMutex) - { - assert(aMutex); - mMutex->Acquire(); - } - ~AutoLock() { - mMutex->Release(); - } -private: - GMPMutex* mMutex; -}; - void dump(const uint8_t* data, uint32_t len, const char* filename); HRESULT diff --git a/media/gmp-clearkey/0.1/gmp-task-utils-generated.h b/media/gmp-clearkey/0.1/gmp-task-utils-generated.h index efcfa8f2eff6..c89965676053 100644 --- a/media/gmp-clearkey/0.1/gmp-task-utils-generated.h +++ b/media/gmp-clearkey/0.1/gmp-task-utils-generated.h @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "RefCounted.h" // 0 arguments -- template class gmp_task_args_nm_0 : public gmp_task_args_base { @@ -1908,3 +1909,30 @@ gmp_task_args_m_14_retRun(); + } + virtual void Destroy() override { + mTask->Destroy(); + gmp_task_args_base::Destroy(); + } +private: + ~RefCountTaskWrapper() {} + + GMPTask* mTask; + RefPtr mRefCounted; +}; + +template +GMPTask* +WrapTaskRefCounted(Type* aType, Method aMethod, Args... args) +{ + GMPTask* t = WrapTask(aType, aMethod, args...); + return new RefCountTaskWrapper(t, aType); +} diff --git a/netwerk/base/nsIBrowserSearchService.idl b/netwerk/base/nsIBrowserSearchService.idl index 201999662b2b..d13f870d1b66 100644 --- a/netwerk/base/nsIBrowserSearchService.idl +++ b/netwerk/base/nsIBrowserSearchService.idl @@ -253,7 +253,7 @@ interface nsIBrowserSearchInitObserver : nsISupports void onInitComplete(in nsresult aStatus); }; -[scriptable, uuid(4a4ce87d-7cb9-4975-a267-345f6a49bb8f)] +[scriptable, uuid(75731859-c7b1-4edf-8d1c-3d4d79a55d1d)] interface nsIBrowserSearchService : nsISupports { /** @@ -281,6 +281,11 @@ interface nsIBrowserSearchService : nsISupports */ readonly attribute bool isInitialized; + /** + * Resets the default engine to its original value. + */ + void resetToOriginalDefaultEngine(); + /** * Adds a new search engine from the file at the supplied URI, optionally * asking the user for confirmation first. If a confirmation dialog is diff --git a/netwerk/base/nsIUploadChannel2.idl b/netwerk/base/nsIUploadChannel2.idl index 196bb0e93e4b..14171a8100d0 100644 --- a/netwerk/base/nsIUploadChannel2.idl +++ b/netwerk/base/nsIUploadChannel2.idl @@ -7,7 +7,7 @@ interface nsIInputStream; -[scriptable, uuid(AD9D3F1C-A8DE-4d0b-9714-1B922297AD65)] +[scriptable, uuid(62e6529a-5cf6-491a-82ef-b3a8273cdd19)] interface nsIUploadChannel2 : nsISupports { /** @@ -45,4 +45,10 @@ interface nsIUploadChannel2 : nsISupports * contians request headers. */ readonly attribute boolean uploadStreamHasHeaders; + + /** + * Clones the upload stream and returns an equivalent stream. + */ + [noscript] + nsIInputStream cloneUploadStream(); }; diff --git a/netwerk/base/security-prefs.js b/netwerk/base/security-prefs.js index b85d1b5fcb1c..d3bd7d008587 100644 --- a/netwerk/base/security-prefs.js +++ b/netwerk/base/security-prefs.js @@ -8,10 +8,10 @@ pref("security.tls.version.fallback-limit", 3); pref("security.tls.insecure_fallback_hosts", ""); pref("security.tls.insecure_fallback_hosts.use_static_list", true); -#if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION) -pref("security.tls.unrestricted_rc4_fallback", false); -#else +#ifdef RELEASE_BUILD pref("security.tls.unrestricted_rc4_fallback", true); +#else +pref("security.tls.unrestricted_rc4_fallback", false); #endif pref("security.ssl.treat_unsafe_negotiation_as_broken", false); diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index b2921fc411d4..1c61d9ef0b6d 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -37,6 +37,7 @@ #include "nsPerformance.h" #include "nsINetworkInterceptController.h" #include "mozIThirdPartyUtil.h" +#include "nsStreamUtils.h" #include @@ -564,6 +565,51 @@ HttpBaseChannel::SetUploadStream(nsIInputStream *stream, return NS_OK; } +static void +EnsureStreamBuffered(nsCOMPtr& aStream) +{ + if (!NS_InputStreamIsBuffered(aStream)) { + nsCOMPtr bufferedStream; + nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), + aStream, + 4096); + NS_ENSURE_SUCCESS_VOID(rv); + aStream.swap(bufferedStream); + } +} + +NS_IMETHODIMP +HttpBaseChannel::CloneUploadStream(nsIInputStream** aClonedStream) +{ + NS_ENSURE_ARG_POINTER(aClonedStream); + *aClonedStream = nullptr; + + if (!mUploadStream) { + return NS_OK; + } + + nsCOMPtr clonedStream; + nsCOMPtr replacementStream; + nsresult rv = NS_CloneInputStream(mUploadStream, getter_AddRefs(clonedStream), + getter_AddRefs(replacementStream)); + NS_ENSURE_SUCCESS(rv, rv); + + if (replacementStream) { + mUploadStream.swap(replacementStream); + + // Ensure that the replacement stream is buffered. + EnsureStreamBuffered(mUploadStream); + } + + // Ensure that the cloned stream is buffered. + EnsureStreamBuffered(clonedStream); + + clonedStream.forget(aClonedStream); + + return NS_OK; +} + + //----------------------------------------------------------------------------- // HttpBaseChannel::nsIUploadChannel2 //----------------------------------------------------------------------------- diff --git a/services/sync/modules/engines/bookmarks.js b/services/sync/modules/engines/bookmarks.js index 1936afc3fcde..b062795c2df2 100644 --- a/services/sync/modules/engines/bookmarks.js +++ b/services/sync/modules/engines/bookmarks.js @@ -247,7 +247,19 @@ BookmarksEngine.prototype = { // Figure out with which key to store the mapping. let key; let id = this._store.idForGUID(guid); - switch (PlacesUtils.bookmarks.getItemType(id)) { + let itemType; + try { + itemType = PlacesUtils.bookmarks.getItemType(id); + } catch (ex) { + this._log.warn("Deleting invalid bookmark record with id", id); + try { + PlacesUtils.bookmarks.removeItem(id); + } catch (ex) { + this._log.warn("Failed to delete invalid bookmark", ex); + } + continue; + } + switch (itemType) { case PlacesUtils.bookmarks.TYPE_BOOKMARK: // Smart bookmarks map to their annotation value. @@ -256,7 +268,7 @@ BookmarksEngine.prototype = { queryId = PlacesUtils.annotations.getItemAnnotation( id, SMART_BOOKMARKS_ANNO); } catch(ex) {} - + if (queryId) key = "q" + queryId; else diff --git a/testing/config/mozharness/android_arm_4_3_config.py b/testing/config/mozharness/android_arm_4_3_config.py index 42b46df7b544..dbe4e73d210e 100644 --- a/testing/config/mozharness/android_arm_4_3_config.py +++ b/testing/config/mozharness/android_arm_4_3_config.py @@ -7,8 +7,7 @@ config = { "mochitest": { "run_filename": "runtestsremote.py", "testsdir": "mochitest", - "options": ["--autorun", "--close-when-done", "--dm_trans=adb", - "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", + "options": ["--dm_trans=adb", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s", "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s", "--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s", @@ -19,8 +18,7 @@ config = { "mochitest-gl": { "run_filename": "runtestsremote.py", "testsdir": "mochitest", - "options": ["--autorun", "--close-when-done", "--dm_trans=adb", - "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", + "options": ["--dm_trans=adb", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s", "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s", "--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s", @@ -32,16 +30,15 @@ config = { "robocop": { "run_filename": "runtestsremote.py", "testsdir": "mochitest", - "options": ["--autorun", "--close-when-done", "--dm_trans=adb", - "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", + "options": ["--dm_trans=adb", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s", "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s", "--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s", "--quiet", "--log-raw=%(raw_log_file)s", "--total-chunks=4", - "--robocop-path=../..", + "--robocop-apk=../../robocop.apk", "--robocop-ids=fennec_ids.txt", - "--robocop=robocop.ini", + "--robocop-ini=robocop.ini", ], }, "reftest": { diff --git a/testing/config/mozharness/android_arm_config.py b/testing/config/mozharness/android_arm_config.py index 45e0e931362b..40ceeb1e1db5 100644 --- a/testing/config/mozharness/android_arm_config.py +++ b/testing/config/mozharness/android_arm_config.py @@ -7,8 +7,7 @@ config = { "mochitest": { "run_filename": "runtestsremote.py", "testsdir": "mochitest", - "options": ["--autorun", "--close-when-done", "--dm_trans=sut", - "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", + "options": ["--dm_trans=sut", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s", "--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s", "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s", @@ -20,8 +19,7 @@ config = { "mochitest-gl": { "run_filename": "runtestsremote.py", "testsdir": "mochitest", - "options": ["--autorun", "--close-when-done", "--dm_trans=sut", - "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", + "options": ["--dm_trans=sut", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s", "--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s", "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s", @@ -34,17 +32,16 @@ config = { "robocop": { "run_filename": "runtestsremote.py", "testsdir": "mochitest", - "options": ["--autorun", "--close-when-done", "--dm_trans=sut", - "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", + "options": ["--dm_trans=sut", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s", "--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s", "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s", "--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s", "--quiet", "--log-raw=%(raw_log_file)s", "--total-chunks=4", - "--robocop-path=../..", + "--robocop-apk=../../robocop.apk", "--robocop-ids=fennec_ids.txt", - "--robocop=robocop.ini", + "--robocop-ini=robocop.ini", ], }, "reftest": { diff --git a/testing/config/mozharness/android_panda_config.py b/testing/config/mozharness/android_panda_config.py index eefbe97757c5..5a21666ad923 100644 --- a/testing/config/mozharness/android_panda_config.py +++ b/testing/config/mozharness/android_panda_config.py @@ -8,7 +8,7 @@ config = { "options": [ "--symbols-path=%(symbols_path)s", "--xre-path=tests/bin", - "--dm_trans=SUT", + "--dm_trans=sut", "--deviceIP=%(device_ip)s", "--localBinDir=../tests/bin", "--apk=%(apk_path)s", @@ -69,12 +69,12 @@ config = { }, "mochitest": { "options": [ + "--dm_trans=sut", "--deviceIP=%(device_ip)s", "--xre-path=../hostutils/xre", "--utility-path=../hostutils/bin", "--certificate-path=certs", "--app=%(app_name)s", - "--console-level=INFO", "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s", "--symbols-path=%(symbols_path)s", @@ -102,6 +102,7 @@ config = { }, "robocop": { "options": [ + "--dm_trans=sut", "--deviceIP=%(device_ip)s", "--xre-path=../hostutils/xre", "--utility-path=../hostutils/bin", @@ -111,7 +112,7 @@ config = { "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s", "--symbols-path=%(symbols_path)s", - "--robocop=mochitest/robocop.ini" + "--robocop-ini=mochitest/robocop.ini" ], "run_filename": "runtestsremote.py", "testsdir": "mochitest" @@ -133,4 +134,4 @@ config = { "testsdir": "xpcshell" } } -} \ No newline at end of file +} diff --git a/testing/config/mozharness/android_x86_config.py b/testing/config/mozharness/android_x86_config.py index 46dad891d50d..4097e96124a2 100644 --- a/testing/config/mozharness/android_x86_config.py +++ b/testing/config/mozharness/android_x86_config.py @@ -6,8 +6,7 @@ config = { "suite_definitions": { "mochitest": { "run_filename": "runtestsremote.py", - "options": ["--autorun", "--close-when-done", "--dm_trans=sut", - "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", + "options": ["--dm_trans=sut", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s", "--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s", "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s", diff --git a/testing/config/mozharness/b2g_desktop_config.py b/testing/config/mozharness/b2g_desktop_config.py index a5184b720c30..7e17ba66e38b 100644 --- a/testing/config/mozharness/b2g_desktop_config.py +++ b/testing/config/mozharness/b2g_desktop_config.py @@ -6,7 +6,6 @@ config = { "suite_definitions": { "mochitest": { "options": [ - "--console-level=INFO", "--total-chunks=%(total_chunks)s", "--this-chunk=%(this_chunk)s", "--profile=%(gaia_profile)s", diff --git a/testing/config/mozharness/b2g_emulator_config.py b/testing/config/mozharness/b2g_emulator_config.py index 4701cb1630e4..d93827c6ef8f 100644 --- a/testing/config/mozharness/b2g_emulator_config.py +++ b/testing/config/mozharness/b2g_emulator_config.py @@ -61,7 +61,6 @@ config = { "options": [ "--adbpath=%(adbpath)s", "--b2gpath=%(b2gpath)s", - "--console-level=INFO", "--emulator=%(emulator)s", "--logdir=%(logcat_dir)s", "--remote-webserver=%(remote_webserver)s", @@ -82,7 +81,6 @@ config = { "options": [ "--adbpath=%(adbpath)s", "--b2gpath=%(b2gpath)s", - "--console-level=INFO", "--emulator=%(emulator)s", "--logdir=%(logcat_dir)s", "--remote-webserver=%(remote_webserver)s", diff --git a/testing/config/mozharness/linux_config.py b/testing/config/mozharness/linux_config.py index 7ebc4cab6ede..b0253001d4cf 100644 --- a/testing/config/mozharness/linux_config.py +++ b/testing/config/mozharness/linux_config.py @@ -30,9 +30,6 @@ config = { "--extra-profile-file=tests/bin/plugins", "--symbols-path=%(symbols_path)s", "--certificate-path=tests/certs", - "--autorun", - "--close-when-done", - "--console-level=INFO", "--setpref=webgl.force-enabled=true", "--quiet", "--log-raw=%(raw_log_file)s", diff --git a/testing/config/mozharness/mac_config.py b/testing/config/mozharness/mac_config.py index 6d1b2cbdea0c..9811d93b9312 100644 --- a/testing/config/mozharness/mac_config.py +++ b/testing/config/mozharness/mac_config.py @@ -30,9 +30,6 @@ config = { "--extra-profile-file=tests/bin/plugins", "--symbols-path=%(symbols_path)s", "--certificate-path=tests/certs", - "--autorun", - "--close-when-done", - "--console-level=INFO", "--quiet", "--log-raw=%(raw_log_file)s" ], diff --git a/testing/config/mozharness/taskcluster_linux_config.py b/testing/config/mozharness/taskcluster_linux_config.py index 0860181b4c22..753a9c301b37 100644 --- a/testing/config/mozharness/taskcluster_linux_config.py +++ b/testing/config/mozharness/taskcluster_linux_config.py @@ -10,8 +10,7 @@ config = { "mochitest_options": [ "--appname=%(binary_path)s", "--utility-path=tests/bin", "--extra-profile-file=tests/bin/plugins", "--symbols-path=%(symbols_path)s", - "--certificate-path=tests/certs", "--autorun", "--close-when-done", - "--console-level=INFO", "--setpref=webgl.force-enabled=true", + "--certificate-path=tests/certs", "--setpref=webgl.force-enabled=true", "--quiet", "--log-raw=%(raw_log_file)s" ], "webapprt_options": [ diff --git a/testing/config/mozharness/windows_config.py b/testing/config/mozharness/windows_config.py index 6d1b2cbdea0c..9811d93b9312 100644 --- a/testing/config/mozharness/windows_config.py +++ b/testing/config/mozharness/windows_config.py @@ -30,9 +30,6 @@ config = { "--extra-profile-file=tests/bin/plugins", "--symbols-path=%(symbols_path)s", "--certificate-path=tests/certs", - "--autorun", - "--close-when-done", - "--console-level=INFO", "--quiet", "--log-raw=%(raw_log_file)s" ], diff --git a/testing/mochitest/mach_commands.py b/testing/mochitest/mach_commands.py index e770530dffe2..e75c09acbf05 100644 --- a/testing/mochitest/mach_commands.py +++ b/testing/mochitest/mach_commands.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals -import argparse +from argparse import Namespace import logging import mozpack.path as mozpath import os @@ -24,8 +24,8 @@ from mach.decorators import ( Command, ) +here = os.path.abspath(os.path.dirname(__file__)) -from mozlog import structured ADB_NOT_FOUND = ''' The %s command requires the adb binary to be on your path. @@ -117,18 +117,7 @@ class MochitestRunner(MozbuildObject): 'mochitest') self.bin_dir = os.path.join(self.topobjdir, 'dist', 'bin') - def run_b2g_test( - self, - test_paths=None, - b2g_home=None, - xre_path=None, - total_chunks=None, - this_chunk=None, - no_window=None, - repeat=0, - run_until_failure=False, - chrome=False, - **kwargs): + def run_b2g_test(self, test_paths=None, **kwargs): """Runs a b2g mochitest. test_paths is an enumerable of paths to tests. It can be a relative path @@ -157,13 +146,11 @@ class MochitestRunner(MozbuildObject): ('.py', 'r', imp.PY_SOURCE)) import mochitest - from mochitest_options import B2GOptions - parser = B2GOptions() - options = parser.parse_args([]) + options = Namespace(**kwargs) if test_path: - if chrome: + if options.chrome: test_root_file = mozpath.join( self.mochitest_dir, 'chrome', @@ -180,125 +167,33 @@ class MochitestRunner(MozbuildObject): return 1 options.testPath = test_path - for k, v in kwargs.iteritems(): - setattr(options, k, v) - options.noWindow = no_window - options.totalChunks = total_chunks - options.thisChunk = this_chunk - options.repeat = repeat - options.runUntilFailure = run_until_failure - - options.symbolsPath = os.path.join( - self.distdir, - 'crashreporter-symbols') - - options.consoleLevel = 'INFO' - if conditions.is_b2g_desktop(self): - options.desktop = True - options.app = self.get_binary_path() - if not options.app.endswith('-bin'): - options.app = '%s-bin' % options.app - if not os.path.isfile(options.app): - options.app = options.app[:-len('-bin')] - - return mochitest.run_desktop_mochitests(parser, options) + if options.desktop: + return mochitest.run_desktop_mochitests(options) try: which.which('adb') except which.WhichError: # TODO Find adb automatically if it isn't on the path - print(ADB_NOT_FOUND % ('mochitest-remote', b2g_home)) + print(ADB_NOT_FOUND % ('mochitest-remote', options.b2gPath)) return 1 - options.b2gPath = b2g_home - options.logdir = self.mochitest_dir - options.httpdPath = self.mochitest_dir - options.xrePath = xre_path - options.chrome = chrome - return mochitest.run_remote_mochitests(parser, options) + return mochitest.run_remote_mochitests(options) - def run_desktop_test( - self, - context, - suite=None, - test_paths=None, - debugger=None, - debugger_args=None, - slowscript=False, - screenshot_on_fail=False, - shuffle=False, - closure_behaviour='auto', - rerun_failures=False, - no_autorun=False, - repeat=0, - run_until_failure=False, - slow=False, - chunk_by_dir=0, - chunk_by_runtime=False, - total_chunks=None, - this_chunk=None, - extraPrefs=[], - jsdebugger=False, - debug_on_failure=False, - start_at=None, - end_at=None, - e10s=False, - enable_cpow_warnings=False, - strict_content_sandbox=False, - nested_oop=False, - dmd=False, - dump_output_directory=None, - dump_about_memory_after_test=False, - dump_dmd_after_test=False, - install_extension=None, - quiet=False, - environment=[], - app_override=None, - bisectChunk=None, - runByDir=False, - useTestMediaDevices=False, - timeout=None, - max_timeouts=None, - **kwargs): + def run_desktop_test(self, context, suite=None, test_paths=None, **kwargs): """Runs a mochitest. - test_paths are path to tests. They can be a relative path from the - top source directory, an absolute filename, or a directory containing - test files. - suite is the type of mochitest to run. It can be one of ('plain', 'chrome', 'browser', 'metro', 'a11y', 'jetpack-package', 'jetpack-addon'). - debugger is a program name or path to a binary (presumably a debugger) - to run the test in. e.g. 'gdb' - - debugger_args are the arguments passed to the debugger. - - slowscript is true if the user has requested the SIGSEGV mechanism of - invoking the slow script dialog. - - shuffle is whether test order should be shuffled (defaults to false). - - closure_behaviour denotes whether to keep the browser open after tests - complete. + test_paths are path to tests. They can be a relative path from the + top source directory, an absolute filename, or a directory containing + test files. """ - if rerun_failures and test_paths: - print('Cannot specify both --rerun-failures and a test path.') - return 1 - # Make absolute paths relative before calling os.chdir() below. if test_paths: test_paths = [self._wrap_path_argument( p).relpath() if os.path.isabs(p) else p for p in test_paths] - failure_file_path = os.path.join( - self.statedir, - 'mochitest_failures.json') - - if rerun_failures and not os.path.exists(failure_file_path): - print('No failure file present. Did you run mochitests before?') - return 1 - # runtests.py is ambiguous, so we load the file/module manually. if 'mochitest' not in sys.modules: import imp @@ -321,13 +216,10 @@ class MochitestRunner(MozbuildObject): for handler in remove_handlers: logging.getLogger().removeHandler(handler) - opts = mochitest.MochitestOptions() - options = opts.parse_args([]) + options = Namespace(**kwargs) - options.subsuite = '' flavor = suite - # Need to set the suite options before verifyOptions below. if suite == 'plain': # Don't need additional options for plain. flavor = 'mochitest' @@ -338,6 +230,7 @@ class MochitestRunner(MozbuildObject): flavor = 'browser-chrome' elif suite == 'devtools': options.browserChrome = True + options.subsuite = 'devtools' elif suite == 'jetpack-package': options.jetpackPackage = True elif suite == 'jetpack-addon': @@ -349,70 +242,16 @@ class MochitestRunner(MozbuildObject): options.a11y = True elif suite == 'webapprt-content': options.webapprtContent = True - options.app = self.get_webapp_runtime_path() + if not options.app or options.app == self.get_binary_path(): + options.app = self.get_webapp_runtime_path() elif suite == 'webapprt-chrome': options.webapprtChrome = True - options.app = self.get_webapp_runtime_path() options.browserArgs.append("-test-mode") + if not options.app or options.app == self.get_binary_path(): + options.app = self.get_webapp_runtime_path() else: raise Exception('None or unrecognized mochitest suite type.') - if dmd: - options.dmdPath = self.bin_dir - - options.autorun = not no_autorun - options.closeWhenDone = closure_behaviour != 'open' - options.slowscript = slowscript - options.screenshotOnFail = screenshot_on_fail - options.shuffle = shuffle - options.consoleLevel = 'INFO' - options.repeat = repeat - options.runUntilFailure = run_until_failure - options.runSlower = slow - options.testingModulesDir = os.path.join(self.tests_dir, 'modules') - options.extraProfileFiles.append(os.path.join(self.distdir, 'plugins')) - options.symbolsPath = os.path.join( - self.distdir, - 'crashreporter-symbols') - options.chunkByDir = chunk_by_dir - options.chunkByRuntime = chunk_by_runtime - options.totalChunks = total_chunks - options.thisChunk = this_chunk - options.jsdebugger = jsdebugger - options.debugOnFailure = debug_on_failure - options.startAt = start_at - options.endAt = end_at - options.e10s = e10s - options.enableCPOWWarnings = enable_cpow_warnings - options.strictContentSandbox = strict_content_sandbox - options.nested_oop = nested_oop - options.dumpAboutMemoryAfterTest = dump_about_memory_after_test - options.dumpDMDAfterTest = dump_dmd_after_test - options.dumpOutputDirectory = dump_output_directory - options.quiet = quiet - options.environment = environment - options.extraPrefs = extraPrefs - options.bisectChunk = bisectChunk - options.runByDir = runByDir - options.useTestMediaDevices = useTestMediaDevices - if timeout: - options.timeout = int(timeout) - if max_timeouts: - options.maxTimeouts = int(max_timeouts) - - options.failureFile = failure_file_path - if install_extension is not None: - options.extensionsToInstall = [ - os.path.join( - self.topsrcdir, - install_extension)] - - for k, v in kwargs.iteritems(): - setattr(options, k, v) - - if suite == 'devtools': - options.subsuite = 'devtools' - if test_paths: resolver = self._spawn(TestResolver) @@ -430,368 +269,65 @@ class MochitestRunner(MozbuildObject): manifest = TestManifest() manifest.tests.extend(tests) - if len( - tests) == 1 and closure_behaviour == 'auto' and suite == 'plain': + # XXX why is this such a special case? + if len(tests) == 1 and options.closeWhenDone and suite == 'plain': options.closeWhenDone = False options.manifestFile = manifest - if rerun_failures: - options.testManifest = failure_file_path - - if debugger: - options.debugger = debugger - - if debugger_args: - if options.debugger is None: - print("--debugger-args passed, but no debugger specified.") - return 1 - options.debuggerArgs = debugger_args - - if app_override: - if app_override == "dist": - options.app = self.get_binary_path(where='staged-package') - elif app_override: - options.app = app_override - if options.gmp_path is None: - # Need to fix the location of gmp_fake which might not be - # shipped in the binary - bin_path = self.get_binary_path() - gmp_modules = ( - ('gmp-fake', '1.0'), - ('gmp-clearkey', '0.1'), - ('gmp-fakeopenh264', '1.0') - ) - options.gmp_path = os.pathsep.join( - os.path.join(os.path.dirname(bin_path), *p) - for p in gmp_modules) - - logger_options = { - key: value for key, - value in vars(options).iteritems() if key.startswith('log')} - runner = mochitest.Mochitest(logger_options) - options = opts.verifyOptions(options, runner) - - if options is None: - raise Exception('mochitest option validator failed.') - # We need this to enable colorization of output. self.log_manager.enable_unstructured() - - result = runner.runTests(options) - + result = mochitest.run_test_harness(options) self.log_manager.disable_unstructured() - if runner.message_logger.errors: - result = 1 - runner.message_logger.logger.warning("The following tests failed:") - for error in runner.message_logger.errors: - runner.message_logger.logger.log_raw(error) - - runner.message_logger.finish() - return result - def run_android_test(self, args): + def run_android_test(self, test_path, **kwargs): self.tests_dir = os.path.join(self.topobjdir, '_tests') self.mochitest_dir = os.path.join(self.tests_dir, 'testing', 'mochitest') import imp path = os.path.join(self.mochitest_dir, 'runtestsremote.py') with open(path, 'r') as fh: imp.load_module('runtestsremote', fh, path, - ('.py', 'r', imp.PY_SOURCE)) + ('.py', 'r', imp.PY_SOURCE)) import runtestsremote - sys.exit(runtestsremote.main(args)) + options = Namespace(**kwargs) + if test_path: + options.testPath = test_path -def add_mochitest_general_args(parser): - parser.add_argument( - '--debugger', - '-d', - metavar='DEBUGGER', - help='Debugger binary to run test in. Program name or path.') - - parser.add_argument( - '--debugger-args', - metavar='DEBUGGER_ARGS', - help='Arguments to pass to the debugger.') - - # Bug 933807 introduced JS_DISABLE_SLOW_SCRIPT_SIGNALS to avoid clever - # segfaults induced by the slow-script-detecting logic for Ion/Odin JITted - # code. If we don't pass this, the user will need to periodically type - # "continue" to (safely) resume execution. There are ways to implement - # automatic resuming; see the bug. - parser.add_argument( - '--slowscript', - action='store_true', - help='Do not set the JS_DISABLE_SLOW_SCRIPT_SIGNALS env variable; when not set, recoverable but misleading SIGSEGV instances may occur in Ion/Odin JIT code') - - parser.add_argument( - '--screenshot-on-fail', - action='store_true', - help='Take screenshots on all test failures. Set $MOZ_UPLOAD_DIR to a directory for storing the screenshots.') - - parser.add_argument( - '--shuffle', action='store_true', - help='Shuffle execution order.') - - parser.add_argument( - '--keep-open', - action='store_const', - dest='closure_behaviour', - const='open', - default='auto', - help='Always keep the browser open after tests complete.') - - parser.add_argument( - '--auto-close', - action='store_const', - dest='closure_behaviour', - const='close', - default='auto', - help='Always close the browser after tests complete.') - - parser.add_argument( - '--rerun-failures', - action='store_true', - help='Run only the tests that failed during the last test run.') - - parser.add_argument( - '--no-autorun', - action='store_true', - help='Do not starting running tests automatically.') - - parser.add_argument( - '--repeat', type=int, default=0, - help='Repeat the test the given number of times.') - - parser.add_argument( - "--run-until-failure", - action='store_true', - help='Run tests repeatedly and stops on the first time a test fails. ' - 'Default cap is 30 runs, which can be overwritten ' - 'with the --repeat parameter.') - - parser.add_argument( - '--slow', action='store_true', - help='Delay execution between tests.') - - parser.add_argument( - '--end-at', - type=str, - help='Stop running the test sequence at this test.') - - parser.add_argument( - '--start-at', - type=str, - help='Start running the test sequence at this test.') - - parser.add_argument( - '--chunk-by-dir', - type=int, - help='Group tests together in chunks by this many top directories.') - - parser.add_argument( - '--chunk-by-runtime', - action='store_true', - help="Group tests such that each chunk has roughly the same runtime.") - - parser.add_argument( - '--total-chunks', - type=int, - help='Total number of chunks to split tests into.') - - parser.add_argument( - '--this-chunk', - type=int, - help='If running tests by chunks, the number of the chunk to run.') - - parser.add_argument( - '--debug-on-failure', - action='store_true', - help='Breaks execution and enters the JS debugger on a test failure. ' - 'Should be used together with --jsdebugger.') - - parser.add_argument( - '--setpref', default=[], action='append', - metavar='PREF=VALUE', dest='extraPrefs', - help='defines an extra user preference') - - parser.add_argument( - '--jsdebugger', - action='store_true', - help='Start the browser JS debugger before running the test. Implies --no-autorun.') - - parser.add_argument( - '--e10s', - action='store_true', - help='Run tests with electrolysis preferences and test filtering enabled.') - - parser.add_argument( - '--enable-cpow-warnings', - action='store_true', - help='Run tests with unsafe CPOW usage warnings enabled.') - - parser.add_argument( - '--strict-content-sandbox', - action='store_true', - help='Run tests with a more strict content sandbox (Windows only).') - - parser.add_argument( - '--nested_oop', - action='store_true', - help='Run tests with nested oop preferences and test filtering enabled.') - - parser.add_argument( - '--dmd', action='store_true', - help='Run tests with DMD active.') - - parser.add_argument( - '--dump-about-memory-after-test', - action='store_true', - help='Dump an about:memory log after every test.') - - parser.add_argument( - '--dump-dmd-after-test', action='store_true', - help='Dump a DMD log after every test.') - - parser.add_argument( - '--dump-output-directory', - action='store', - help='Specifies the directory in which to place dumped memory reports.') - - parser.add_argument( - 'test_paths', - default=None, - nargs='*', - metavar='TEST', - help='Test to run. Can be specified as a single file, a ' - 'directory, or omitted. If omitted, the entire test suite is ' - 'executed.') - - parser.add_argument( - '--install-extension', - help='Install given extension before running selected tests. ' - 'Parameter is a path to xpi file.') - - parser.add_argument( - '--quiet', - default=False, - action='store_true', - help='Do not print test log lines unless a failure occurs.') - - parser.add_argument( - '--setenv', - default=[], - action='append', - metavar='NAME=VALUE', - dest='environment', - help="Sets the given variable in the application's environment") - - parser.add_argument( - '--run-by-dir', - default=False, - action='store_true', - dest='runByDir', - help='Run each directory in a single browser instance with a fresh profile.') - - parser.add_argument( - '--bisect-chunk', - type=str, - dest='bisectChunk', - help='Specify the failing test name to find the previous tests that may be causing the failure.') - - parser.add_argument( - '--use-test-media-devices', - default=False, - action='store_true', - dest='useTestMediaDevices', - help='Use test media device drivers for media testing.') - - parser.add_argument( - '--app-override', - default=None, - action='store', - help="Override the default binary used to run tests with the path you provide, e.g. " - " --app-override /usr/bin/firefox . " - "If you have run ./mach package beforehand, you can specify 'dist' to " - "run tests against the distribution bundle's binary.") - - parser.add_argument( - '--timeout', - default=None, - help='The per-test timeout time in seconds (default: 60 seconds)') - - parser.add_argument( - '--max-timeouts', default=None, - help='The maximum number of timeouts permitted before halting testing') - - parser.add_argument( - "--tag", - dest='test_tags', action='append', - help="Filter out tests that don't have the given tag. Can be used " - "multiple times in which case the test must contain at least one " - "of the given tags.") - - parser.add_argument( - "--subsuite", - default=None, - help="Subsuite of tests to run. Unlike tags, subsuites also remove " - "tests from the default set. Only one can be specified at once.") + sys.exit(runtestsremote.run_test_harness(options)) - return parser +# parser -def add_mochitest_b2g_args(parser): - parser.add_argument( - '--busybox', - default=None, - help='Path to busybox binary to install on device') - - parser.add_argument( - '--logdir', default=None, - help='directory to store log files') - - parser.add_argument( - '--profile', default=None, - help='for desktop testing, the path to the \ - gaia profile to use') - - parser.add_argument( - '--gecko-path', default=None, - help='the path to a gecko distribution that should \ - be installed on the emulator prior to test') - - parser.add_argument( - '--no-window', - action='store_true', - default=False, - help='Pass --no-window to the emulator') - - parser.add_argument( - '--sdcard', default="10MB", - help='Define size of sdcard: 1MB, 50MB...etc') - - parser.add_argument( - '--marionette', - default=None, - help='host:port to use when connecting to Marionette') - - return parser +def TestPathArg(func): + test_paths = CommandArgument('test_paths', nargs='*', metavar='TEST', default=None, + help='Test to run. Can be a single test file or a directory of tests to ' + '(run recursively). If omitted, the entire suite is run.') + return test_paths(func) def setup_argument_parser(): - parser = argparse.ArgumentParser() + build_obj = MozbuildObject.from_environment(cwd=here) - general_args = parser.add_argument_group('Mochitest Arguments', - 'Arguments that apply to all versions of mochitest.') - general_args = add_mochitest_general_args(general_args) + build_path = os.path.join(build_obj.topobjdir, 'build') + if build_path not in sys.path: + sys.path.append(build_path) - b2g_args = parser.add_argument_group('B2G Arguments', 'Arguments specific \ - to running mochitest on B2G devices and emulator') - b2g_args = add_mochitest_b2g_args(b2g_args) + mochitest_dir = os.path.join(build_obj.topobjdir, '_tests', 'testing', 'mochitest') - structured.commandline.add_logging_group(parser) - return parser + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + + import imp + path = os.path.join(build_obj.topobjdir, mochitest_dir, 'runtests.py') + with open(path, 'r') as fh: + imp.load_module('mochitest', fh, path, + ('.py', 'r', imp.PY_SOURCE)) + + from mochitest_options import MochitestArgumentParser + + return MochitestArgumentParser() # condition filters @@ -808,6 +344,7 @@ def is_platform_in(*platforms): ' or '.join(platforms)) return is_platform_supported + def verify_host_bin(): # validate MOZ_HOST_BIN environment variables for Android tests MOZ_HOST_BIN = os.environ.get('MOZ_HOST_BIN') @@ -822,13 +359,14 @@ def verify_host_bin(): return 1 return 0 + @CommandProvider class MachCommands(MachCommandBase): def __init__(self, context): MachCommandBase.__init__(self, context) - for attr in ('b2g_home', 'xre_path', 'device_name', 'target_out'): + for attr in ('device_name', 'target_out'): setattr(self, attr, getattr(context, attr, None)) @Command( @@ -837,6 +375,7 @@ class MachCommands(MachCommandBase): conditions=[is_platform_in('firefox', 'mulet', 'b2g', 'b2g_desktop', 'android')], description='Run a plain mochitest (integration test, plain web page).', parser=setup_argument_parser) + @TestPathArg def run_mochitest_plain(self, test_paths, **kwargs): if is_platform_in('firefox', 'mulet')(self): return self.run_mochitest(test_paths, 'plain', **kwargs) @@ -853,13 +392,15 @@ class MachCommands(MachCommandBase): conditions=[is_platform_in('firefox', 'emulator', 'android')], description='Run a chrome mochitest (integration test with some XUL).', parser=setup_argument_parser) + @TestPathArg def run_mochitest_chrome(self, test_paths, **kwargs): + kwargs['chrome'] = True if conditions.is_firefox(self): return self.run_mochitest(test_paths, 'chrome', **kwargs) elif conditions.is_b2g(self) and conditions.is_emulator(self): - return self.run_mochitest_remote(test_paths, chrome=True, **kwargs) + return self.run_mochitest_remote(test_paths, **kwargs) elif conditions.is_android(self): - return self.run_mochitest_android(test_paths, chrome=True, **kwargs) + return self.run_mochitest_android(test_paths, **kwargs) @Command( 'mochitest-browser', @@ -867,6 +408,7 @@ class MachCommands(MachCommandBase): conditions=[conditions.is_firefox], description='Run a mochitest with browser chrome (integration test with a standard browser).', parser=setup_argument_parser) + @TestPathArg def run_mochitest_browser(self, test_paths, **kwargs): return self.run_mochitest(test_paths, 'browser', **kwargs) @@ -876,6 +418,7 @@ class MachCommands(MachCommandBase): conditions=[conditions.is_firefox], description='Run a devtools mochitest with browser chrome (integration test with a standard browser with the devtools frame).', parser=setup_argument_parser) + @TestPathArg def run_mochitest_devtools(self, test_paths, **kwargs): return self.run_mochitest(test_paths, 'devtools', **kwargs) @@ -883,6 +426,7 @@ class MachCommands(MachCommandBase): conditions=[conditions.is_firefox], description='Run a jetpack package test.', parser=setup_argument_parser) + @TestPathArg def run_mochitest_jetpack_package(self, test_paths, **kwargs): return self.run_mochitest(test_paths, 'jetpack-package', **kwargs) @@ -890,6 +434,7 @@ class MachCommands(MachCommandBase): conditions=[conditions.is_firefox], description='Run a jetpack addon test.', parser=setup_argument_parser) + @TestPathArg def run_mochitest_jetpack_addon(self, test_paths, **kwargs): return self.run_mochitest(test_paths, 'jetpack-addon', **kwargs) @@ -899,6 +444,7 @@ class MachCommands(MachCommandBase): conditions=[conditions.is_firefox], description='Run a mochitest with metro browser chrome (tests for Windows touch interface).', parser=setup_argument_parser) + @TestPathArg def run_mochitest_metro(self, test_paths, **kwargs): return self.run_mochitest(test_paths, 'metro', **kwargs) @@ -906,6 +452,7 @@ class MachCommands(MachCommandBase): conditions=[conditions.is_firefox], description='Run an a11y mochitest (accessibility tests).', parser=setup_argument_parser) + @TestPathArg def run_mochitest_a11y(self, test_paths, **kwargs): return self.run_mochitest(test_paths, 'a11y', **kwargs) @@ -915,6 +462,7 @@ class MachCommands(MachCommandBase): conditions=[conditions.is_firefox], description='Run a webapprt chrome mochitest (Web App Runtime with the browser chrome).', parser=setup_argument_parser) + @TestPathArg def run_mochitest_webapprt_chrome(self, test_paths, **kwargs): return self.run_mochitest(test_paths, 'webapprt-chrome', **kwargs) @@ -924,6 +472,7 @@ class MachCommands(MachCommandBase): conditions=[conditions.is_firefox], description='Run a webapprt content mochitest (Content rendering of the Web App Runtime).', parser=setup_argument_parser) + @TestPathArg def run_mochitest_webapprt_content(self, test_paths, **kwargs): return self.run_mochitest(test_paths, 'webapprt-content', **kwargs) @@ -933,6 +482,7 @@ class MachCommands(MachCommandBase): parser=setup_argument_parser) @CommandArgument('-f', '--flavor', choices=FLAVORS.keys(), help='Only run tests of this flavor.') + @TestPathArg def run_mochitest_general(self, test_paths, flavor=None, test_objects=None, **kwargs): self._preruntest() @@ -1019,12 +569,6 @@ class MachCommands(MachCommandBase): from mozbuild.controller.building import BuildDriver - if self.device_name.startswith('emulator'): - emulator = 'arm' - if 'x86' in self.device_name: - emulator = 'x86' - kwargs['emulator'] = emulator - self._ensure_state_subdir_exists('.') driver = self._spawn(BuildDriver) @@ -1032,8 +576,6 @@ class MachCommands(MachCommandBase): mochitest = self._spawn(MochitestRunner) return mochitest.run_b2g_test( - b2g_home=self.b2g_home, - xre_path=self.xre_path, test_paths=test_paths, **kwargs) @@ -1060,39 +602,28 @@ class MachCommands(MachCommandBase): mochitest = self._spawn(MochitestRunner) return mochitest.run_b2g_test(test_paths=test_paths, **kwargs) - def run_mochitest_android(self, test_paths, chrome=False, **kwargs): + def run_mochitest_android(self, test_paths, **kwargs): host_ret = verify_host_bin() if host_ret != 0: return host_ret - args = [ - '--xre-path=' + os.environ.get('MOZ_HOST_BIN'), - '--dm_trans=adb', - '--deviceIP=', - '--console-level=INFO', - '--app=' + self.substs['ANDROID_PACKAGE_NAME'], - '--log-mach=-', - '--autorun', - '--close-when-done', - '--testing-modules-dir=' + os.path.join(self.topobjdir, '_tests', 'modules'), - ] + test_path = None if test_paths: if len(test_paths) > 1: print('Warning: Only the first test path will be used.') test_path = self._wrap_path_argument(test_paths[0]).relpath() - args.append('--test-path=%s' % test_path) - if chrome: - args.append('--chrome') mochitest = self._spawn(MochitestRunner) - return mochitest.run_android_test(args) + return mochitest.run_android_test(test_path, **kwargs) + @CommandProvider class AndroidCommands(MachCommandBase): @Command('robocop', category='testing', conditions=[conditions.is_android], - description='Run a Robocop test.') + description='Run a Robocop test.', + parser=setup_argument_parser) @CommandArgument( 'test_path', default=None, @@ -1100,37 +631,17 @@ class AndroidCommands(MachCommandBase): metavar='TEST', help='Test to run. Can be specified as a Robocop test name (like "testLoad"), ' 'or omitted. If omitted, the entire test suite is executed.') - def run_robocop(self, test_path): + def run_robocop(self, test_path, **kwargs): host_ret = verify_host_bin() if host_ret != 0: return host_ret - args = [ - '--xre-path=' + os.environ.get('MOZ_HOST_BIN'), - '--dm_trans=adb', - '--deviceIP=', - '--console-level=INFO', - '--app=' + - self.substs['ANDROID_PACKAGE_NAME'], - '--robocop-apk=' + - os.path.join( - self.topobjdir, - 'build', - 'mobile', - 'robocop', - 'robocop-debug.apk'), - '--robocop-ini=' + - os.path.join( - self.topobjdir, - '_tests', - 'testing', - 'mochitest', - 'robocop.ini'), - '--log-mach=-', - ] - - if test_path: - args.append('--test-path=%s' % test_path) + if not kwargs.get('robocopIni'): + kwargs['robocopIni'] = os.path.join(self.topobjdir, '_tests', 'testing', + 'mochitest', 'robocop.ini') + if not kwargs.get('robocopApk'): + kwargs['robocopApk'] = os.path.join(self.topobjdir, 'build', 'mobile', + 'robocop', 'robocop-debug.apk') mochitest = self._spawn(MochitestRunner) - return mochitest.run_android_test(args) + return mochitest.run_android_test(test_path, **kwargs) diff --git a/testing/mochitest/mochitest_options.py b/testing/mochitest/mochitest_options.py index b2318fcd0dd5..b384e448af1d 100644 --- a/testing/mochitest/mochitest_options.py +++ b/testing/mochitest/mochitest_options.py @@ -2,333 +2,379 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -from argparse import ArgumentParser +from abc import ABCMeta, abstractmethod, abstractproperty +from argparse import ArgumentParser, SUPPRESS from urlparse import urlparse -import logging import os import tempfile +from droid import DroidADB, DroidSUT +from mozlog import structured from mozprofile import DEFAULT_PORTS import mozinfo import moznetwork -from automation import Automation here = os.path.abspath(os.path.dirname(__file__)) try: - from mozbuild.base import MozbuildObject + from mozbuild.base import ( + MozbuildObject, + MachCommandConditions as conditions, + ) build_obj = MozbuildObject.from_environment(cwd=here) except ImportError: build_obj = None + conditions = None -__all__ = ["MochitestOptions", "B2GOptions"] VMWARE_RECORDING_HELPER_BASENAME = "vmwarerecordinghelper" -class MochitestOptions(ArgumentParser): - """Usage instructions for runtests.py. - All arguments are optional. - If --chrome is specified, chrome tests will be run instead of web content tests. - If --browser-chrome is specified, browser-chrome tests will be run instead of web content tests. - See for details on the logging levels. - """ +class ArgumentContainer(): + __metaclass__ = ABCMeta + + @abstractproperty + def args(self): + pass + + @abstractproperty + def defaults(self): + pass + + @abstractmethod + def validate(self, parser, args, context): + pass + + def get_full_path(self, path, cwd): + """Get an absolute path relative to cwd.""" + return os.path.normpath(os.path.join(cwd, os.path.expanduser(path))) + + +class MochitestArguments(ArgumentContainer): + """General mochitest arguments.""" LOG_LEVELS = ("DEBUG", "INFO", "WARNING", "ERROR", "FATAL") LEVEL_STRING = ", ".join(LOG_LEVELS) - mochitest_options = [ - [["--close-when-done"], - {"action": "store_true", + + args = [ + [["--keep-open"], + {"action": "store_false", "dest": "closeWhenDone", - "default": False, - "help": "close the application when tests are done running", + "default": True, + "help": "Always keep the browser open after tests complete.", }], [["--appname"], {"dest": "app", "default": None, - "help": "absolute path to application, overriding default", + "help": "Override the default binary used to run tests with the path provided, e.g " + "/usr/bin/firefox. If you have run ./mach package beforehand, you can " + "specify 'dist' to run tests against the distribution bundle's binary.", + "suppress": True, }], [["--utility-path"], {"dest": "utilityPath", "default": build_obj.bindir if build_obj is not None else None, - "help": "absolute path to directory containing utility programs (xpcshell, ssltunnel, certutil)", + "help": "absolute path to directory containing utility programs " + "(xpcshell, ssltunnel, certutil)", + "suppress": True, }], [["--certificate-path"], {"dest": "certPath", + "default": None, "help": "absolute path to directory containing certificate store to use testing profile", - "default": os.path.join(build_obj.topsrcdir, 'build', 'pgo', 'certs') if build_obj is not None else None, + "suppress": True, }], - [["--autorun"], - {"action": "store_true", + [["--no-autorun"], + {"action": "store_false", "dest": "autorun", - "help": "start running tests when the application starts", - "default": False, + "default": True, + "help": "Do not start running tests automatically.", }], [["--timeout"], {"type": int, - "dest": "timeout", - "help": "per-test timeout in seconds", "default": None, + "help": "The per-test timeout in seconds (default: 60 seconds).", + }], + [["--max-timeouts"], + {"type": int, + "dest": "maxTimeouts", + "default": None, + "help": "The maximum number of timeouts permitted before halting testing.", }], [["--total-chunks"], {"type": int, "dest": "totalChunks", - "help": "how many chunks to split the tests up into", + "help": "Total number of chunks to split tests into.", "default": None, }], [["--this-chunk"], {"type": int, "dest": "thisChunk", - "help": "which chunk to run", + "help": "If running tests by chunks, the chunk number to run.", "default": None, }], [["--chunk-by-runtime"], {"action": "store_true", "dest": "chunkByRuntime", - "help": "group tests such that each chunk has roughly the same runtime", + "help": "Group tests such that each chunk has roughly the same runtime.", "default": False, }], [["--chunk-by-dir"], {"type": int, "dest": "chunkByDir", - "help": "group tests together in the same chunk that are in the same top chunkByDir directories", + "help": "Group tests together in the same chunk that are in the same top " + "chunkByDir directories.", "default": 0, }], [["--run-by-dir"], {"action": "store_true", "dest": "runByDir", - "help": "Run each directory in a single browser instance with a fresh profile", + "help": "Run each directory in a single browser instance with a fresh profile.", "default": False, }], [["--shuffle"], - {"dest": "shuffle", - "action": "store_true", - "help": "randomize test order", + {"action": "store_true", + "help": "Shuffle execution order of tests.", "default": False, }], [["--console-level"], {"dest": "consoleLevel", "choices": LOG_LEVELS, - "metavar": "LEVEL", - "help": "one of %s to determine the level of console " - "logging" % LEVEL_STRING, - "default": None, + "default": "INFO", + "help": "One of %s to determine the level of console logging." % LEVEL_STRING, + "suppress": True, }], [["--chrome"], {"action": "store_true", - "dest": "chrome", - "help": "run chrome Mochitests", "default": False, + "help": "Run chrome mochitests.", + "suppress": True, }], [["--ipcplugins"], {"action": "store_true", "dest": "ipcplugins", - "help": "run ipcplugins Mochitests", + "help": "Run ipcplugins mochitests.", "default": False, + "suppress": True, }], [["--test-path"], {"dest": "testPath", - "help": "start in the given directory's tests", "default": "", + "help": "Run the given test or recursively run the given directory of tests.", + # if running from mach, a test_paths arg is exposed instead + "suppress": build_obj is not None, }], [["--bisect-chunk"], {"dest": "bisectChunk", - "help": "Specify the failing test name to find the previous tests that may be causing the failure.", "default": None, + "help": "Specify the failing test name to find the previous tests that may be " + "causing the failure.", }], [["--start-at"], {"dest": "startAt", - "help": "skip over tests until reaching the given test", "default": "", + "help": "Start running the test sequence at this test.", }], [["--end-at"], {"dest": "endAt", - "help": "don't run any tests after the given one", "default": "", + "help": "Stop running the test sequence at this test.", }], [["--browser-chrome"], {"action": "store_true", "dest": "browserChrome", - "help": "run browser chrome Mochitests", "default": False, + "help": "run browser chrome Mochitests", + "suppress": True, }], [["--subsuite"], - {"dest": "subsuite", - "help": "subsuite of tests to run", - "default": None, + {"default": None, + "help": "Subsuite of tests to run. Unlike tags, subsuites also remove tests from " + "the default set. Only one can be specified at once.", }], [["--jetpack-package"], {"action": "store_true", "dest": "jetpackPackage", - "help": "run jetpack package tests", + "help": "Run jetpack package tests.", "default": False, + "suppress": True, }], [["--jetpack-addon"], {"action": "store_true", "dest": "jetpackAddon", - "help": "run jetpack addon tests", + "help": "Run jetpack addon tests.", "default": False, + "suppress": True, }], [["--webapprt-content"], {"action": "store_true", "dest": "webapprtContent", - "help": "run WebappRT content tests", + "help": "Run WebappRT content tests.", "default": False, + "suppress": True, }], [["--webapprt-chrome"], {"action": "store_true", "dest": "webapprtChrome", - "help": "run WebappRT chrome tests", + "help": "Run WebappRT chrome tests.", "default": False, + "suppress": True, }], [["--a11y"], {"action": "store_true", - "dest": "a11y", - "help": "run accessibility Mochitests", + "help": "Run accessibility Mochitests.", "default": False, + "suppress": True, }], [["--setenv"], {"action": "append", "dest": "environment", "metavar": "NAME=VALUE", - "help": "sets the given variable in the application's " - "environment", "default": [], + "help": "Sets the given variable in the application's environment.", }], [["--exclude-extension"], {"action": "append", "dest": "extensionsToExclude", - "help": "excludes the given extension from being installed " - "in the test profile", "default": [], + "help": "Excludes the given extension from being installed in the test profile.", + "suppress": True, }], [["--browser-arg"], {"action": "append", "dest": "browserArgs", - "metavar": "ARG", - "help": "provides an argument to the test application", "default": [], + "help": "Provides an argument to the test application (e.g Firefox).", + "suppress": True, }], [["--leak-threshold"], {"type": int, "dest": "defaultLeakThreshold", - "metavar": "THRESHOLD", - "help": "fail if the number of bytes leaked in default " - "processes through refcounted objects (or bytes " - "in classes with MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) " - "is greater than the given number", "default": 0, + "help": "Fail if the number of bytes leaked in default processes through " + "refcounted objects (or bytes in classes with MOZ_COUNT_CTOR and " + "MOZ_COUNT_DTOR) is greater than the given number.", + "suppress": True, }], [["--fatal-assertions"], {"action": "store_true", "dest": "fatalAssertions", - "help": "abort testing whenever an assertion is hit " - "(requires a debug build to be effective)", "default": False, + "help": "Abort testing whenever an assertion is hit (requires a debug build to " + "be effective).", + "suppress": True, }], [["--extra-profile-file"], {"action": "append", "dest": "extraProfileFiles", - "help": "copy specified files/dirs to testing profile", "default": [], + "help": "Copy specified files/dirs to testing profile. Can be specified more " + "than once.", + "suppress": True, }], [["--install-extension"], {"action": "append", "dest": "extensionsToInstall", - "help": "install the specified extension in the testing profile." - "The extension file's name should be .xpi where is" - "the extension's id as indicated in its install.rdf." - "An optional path can be specified too.", "default": [], + "help": "Install the specified extension in the testing profile. Can be a path " + "to a .xpi file.", }], [["--profile-path"], {"dest": "profilePath", - "help": "Directory where the profile will be stored." - "This directory will be deleted after the tests are finished", "default": None, + "help": "Directory where the profile will be stored. This directory will be " + "deleted after the tests are finished.", + "suppress": True, }], [["--testing-modules-dir"], {"dest": "testingModulesDir", - "help": "Directory where testing-only JS modules are located.", "default": None, + "help": "Directory where testing-only JS modules are located.", + "suppress": True, }], [["--use-vmware-recording"], {"action": "store_true", "dest": "vmwareRecording", - "help": "enables recording while the application is running " - "inside a VMware Workstation 7.0 or later VM", "default": False, + "help": "Enables recording while the application is running inside a VMware " + "Workstation 7.0 or later VM.", + "suppress": True, }], [["--repeat"], {"type": int, - "dest": "repeat", - "metavar": "REPEAT", - "help": "repeats the test or set of tests the given number of times, ie: repeat: 1 will run the test twice.", "default": 0, + "help": "Repeat the tests the given number of times.", }], [["--run-until-failure"], {"action": "store_true", "dest": "runUntilFailure", - "help": "Run tests repeatedly and stops on the first time a test fails. " - "Default cap is 30 runs, which can be overwritten with the --repeat parameter.", "default": False, + "help": "Run tests repeatedly but stop the first time a test fails. Default cap " + "is 30 runs, which can be overridden with the --repeat parameter.", }], [["--manifest"], {"dest": "manifestFile", - "help": ".ini format of tests to run.", "default": None, + "help": "Path to a manifestparser (.ini formatted) manifest of tests to run.", + "suppress": True, }], [["--testrun-manifest-file"], {"dest": "testRunManifestFile", - "help": "Overrides the default filename of the tests.json manifest file that is created from the manifest and used by the test runners to run the tests. Only useful when running multiple test runs simulatenously on the same machine.", "default": 'tests.json', + "help": "Overrides the default filename of the tests.json manifest file that is " + "generated by the harness and used by SimpleTest. Only useful when running " + "multiple test runs simulatenously on the same machine.", + "suppress": True, }], [["--failure-file"], {"dest": "failureFile", - "help": "Filename of the output file where we can store a .json list of failures to be run in the future with --run-only-tests.", "default": None, + "help": "Filename of the output file where we can store a .json list of failures " + "to be run in the future with --run-only-tests.", + "suppress": True, }], [["--run-slower"], {"action": "store_true", "dest": "runSlower", - "help": "Delay execution between test files.", "default": False, + "help": "Delay execution between tests.", }], [["--metro-immersive"], {"action": "store_true", "dest": "immersiveMode", - "help": "launches tests in immersive browser", "default": False, + "help": "Launches tests in an immersive browser.", + "suppress": True, }], [["--httpd-path"], {"dest": "httpdPath", "default": None, - "help": "path to the httpd.js file", + "help": "Path to the httpd.js file.", + "suppress": True, }], [["--setpref"], {"action": "append", + "metavar": "PREF=VALUE", "default": [], "dest": "extraPrefs", - "metavar": "PREF=VALUE", - "help": "defines an extra user preference", + "help": "Defines an extra user preference.", }], [["--jsdebugger"], {"action": "store_true", "default": False, - "dest": "jsdebugger", - "help": "open the browser debugger", + "help": "Start the browser JS debugger before running the test. Implies --no-autorun.", }], [["--debug-on-failure"], {"action": "store_true", "default": False, "dest": "debugOnFailure", - "help": "breaks execution and enters the JS debugger on a test failure. Should be used together with --jsdebugger." + "help": "Breaks execution and enters the JS debugger on a test failure. Should " + "be used together with --jsdebugger." }], [["--e10s"], {"action": "store_true", "default": False, - "dest": "e10s", "help": "Run tests with electrolysis preferences and test filtering enabled.", }], [["--strict-content-sandbox"], @@ -336,17 +382,23 @@ class MochitestOptions(ArgumentParser): "default": False, "dest": "strictContentSandbox", "help": "Run tests with a more strict content sandbox (Windows only).", + "suppress": not mozinfo.isWin, }], [["--nested_oop"], {"action": "store_true", "default": False, - "dest": "nested_oop", "help": "Run tests with nested_oop preferences and test filtering enabled.", }], + [["--dmd"], + {"action": "store_true", + "default": False, + "help": "Run tests with DMD active.", + }], [["--dmd-path"], {"default": None, "dest": "dmdPath", "help": "Specifies the path to the directory containing the shared library for DMD.", + "suppress": True, }], [["--dump-output-directory"], {"default": None, @@ -357,40 +409,41 @@ class MochitestOptions(ArgumentParser): {"action": "store_true", "default": False, "dest": "dumpAboutMemoryAfterTest", - "help": "Produce an about:memory dump after each test in the directory specified " - "by --dump-output-directory." + "help": "Dump an about:memory log after each test in the directory specified " + "by --dump-output-directory.", }], [["--dump-dmd-after-test"], {"action": "store_true", "default": False, "dest": "dumpDMDAfterTest", - "help": "Produce a DMD dump after each test in the directory specified " - "by --dump-output-directory." + "help": "Dump a DMD log after each test in the directory specified " + "by --dump-output-directory.", }], [["--slowscript"], {"action": "store_true", "default": False, - "dest": "slowscript", "help": "Do not set the JS_DISABLE_SLOW_SCRIPT_SIGNALS env variable; " - "when not set, recoverable but misleading SIGSEGV instances " - "may occur in Ion/Odin JIT code." + "when not set, recoverable but misleading SIGSEGV instances " + "may occur in Ion/Odin JIT code.", }], [["--screenshot-on-fail"], {"action": "store_true", "default": False, "dest": "screenshotOnFail", - "help": "Take screenshots on all test failures. Set $MOZ_UPLOAD_DIR to a directory for storing the screenshots." + "help": "Take screenshots on all test failures. Set $MOZ_UPLOAD_DIR to a directory " + "for storing the screenshots." }], [["--quiet"], {"action": "store_true", - "default": False, "dest": "quiet", - "help": "Do not print test log lines unless a failure occurs." + "default": False, + "help": "Do not print test log lines unless a failure occurs.", }], [["--pidfile"], {"dest": "pidFile", - "help": "name of the pidfile to generate", "default": "", + "help": "Name of the pidfile to generate.", + "suppress": True, }], [["--use-test-media-devices"], {"action": "store_true", @@ -400,93 +453,121 @@ class MochitestOptions(ArgumentParser): }], [["--gmp-path"], {"default": None, - "dest": "gmp_path", "help": "Path to fake GMP plugin. Will be deduced from the binary if not passed.", + "suppress": True, }], [["--xre-path"], {"dest": "xrePath", "default": None, # individual scripts will set a sane default - "help": "absolute path to directory containing XRE (probably xulrunner)", + "help": "Absolute path to directory containing XRE (probably xulrunner).", + "suppress": True, }], [["--symbols-path"], {"dest": "symbolsPath", "default": None, - "help": "absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols", + "help": "Absolute path to directory containing breakpad symbols, or the URL of a " + "zip file containing symbols", + "suppress": True, }], [["--debugger"], - {"dest": "debugger", - "help": "use the given debugger to launch the application", + {"default": None, + "help": "Debugger binary to run tests in. Program name or path.", }], [["--debugger-args"], {"dest": "debuggerArgs", - "help": "pass the given args to the debugger _before_ the application on the command line", + "default": None, + "help": "Arguments to pass to the debugger.", }], [["--debugger-interactive"], {"action": "store_true", "dest": "debuggerInteractive", - "help": "prevents the test harness from redirecting stdout and stderr for interactive debuggers", - }], - [["--max-timeouts"], - {"type": int, - "dest": "maxTimeouts", - "help": "maximum number of timeouts permitted before halting testing", "default": None, + "help": "Prevents the test harness from redirecting stdout and stderr for " + "interactive debuggers.", + "suppress": True, }], [["--tag"], {"action": "append", "dest": "test_tags", "default": None, - "help": "filter out tests that don't have the given tag. Can be " - "used multiple times in which case the test must contain " - "at least one of the given tags.", + "help": "Filter out tests that don't have the given tag. Can be used multiple " + "times in which case the test must contain at least one of the given tags.", }], [["--enable-cpow-warnings"], {"action": "store_true", "dest": "enableCPOWWarnings", - "help": "enable logging of unsafe CPOW usage, which is disabled by default for tests", + "help": "Enable logging of unsafe CPOW usage, which is disabled by default for tests", + "suppress": True, }], ] - def __init__(self, **kwargs): - ArgumentParser.__init__(self, usage=self.__doc__, **kwargs) - for option, value in self.mochitest_options: - # Allocate new lists so references to original don't get mutated. - # allowing multiple uses within a single process. - if "default" in value and isinstance(value["default"], list): - value["default"] = [] - self.add_argument(*option, **value) + defaults = { + # Bug 1065098 - The geckomediaplugin process fails to produce a leak + # log for some reason. + 'ignoreMissingLeaks': ["geckomediaplugin"], - def verifyOptions(self, options, mochitest): - """ verify correct options and cleanup paths """ + # Set server information on the args object + 'webServer': '127.0.0.1', + 'httpPort': DEFAULT_PORTS['http'], + 'sslPort': DEFAULT_PORTS['https'], + 'webSocketPort': '9988', + # The default websocket port is incorrect in mozprofile; it is + # set to the SSL proxy setting. See: + # see https://bugzilla.mozilla.org/show_bug.cgi?id=916517 + # args.webSocketPort = DEFAULT_PORTS['ws'] + } + + def validate(self, parser, options, context): + """Validate generic options.""" # for test manifest parsing. mozinfo.update({"strictContentSandbox": options.strictContentSandbox}) # for test manifest parsing. mozinfo.update({"nested_oop": options.nested_oop}) - if options.app is None: - if build_obj is not None: - options.app = build_obj.get_binary_path() - else: - self.error( - "could not find the application path, --appname must be specified") + # b2g and android don't use 'app' the same way, so skip validation + if parser.app not in ('b2g', 'android'): + if options.app is None: + if build_obj: + options.app = build_obj.get_binary_path() + else: + parser.error( + "could not find the application path, --appname must be specified") + elif options.app == "dist" and build_obj: + options.app = build_obj.get_binary_path(where='staged-package') + + options.app = self.get_full_path(options.app, parser.oldcwd) + if not os.path.exists(options.app): + parser.error("Error: Path {} doesn't exist. Are you executing " + "$objdir/_tests/testing/mochitest/runtests.py?".format( + options.app)) + + if options.gmp_path is None and options.app and build_obj: + # Need to fix the location of gmp_fake which might not be shipped in the binary + gmp_modules = ( + ('gmp-fake', '1.0'), + ('gmp-clearkey', '0.1'), + ('gmp-fakeopenh264', '1.0') + ) + options.gmp_path = os.pathsep.join( + os.path.join(build_obj.bindir, *p) for p in gmp_modules) if options.totalChunks is not None and options.thisChunk is None: - self.error( + parser.error( "thisChunk must be specified when totalChunks is specified") if options.totalChunks: if not 1 <= options.thisChunk <= options.totalChunks: - self.error("thisChunk must be between 1 and totalChunks") + parser.error("thisChunk must be between 1 and totalChunks") if options.chunkByDir and options.chunkByRuntime: - self.error( + parser.error( "can only use one of --chunk-by-dir or --chunk-by-runtime") if options.xrePath is None: # default xrePath to the app path if not provided # but only if an app path was explicitly provided - if options.app != self.get_default('app'): + if options.app != parser.get_default('app'): options.xrePath = os.path.dirname(options.app) if mozinfo.isMac: options.xrePath = os.path.join( @@ -497,58 +578,49 @@ class MochitestOptions(ArgumentParser): # otherwise default to dist/bin options.xrePath = build_obj.bindir else: - self.error( + parser.error( "could not find xre directory, --xre-path must be specified") # allow relative paths - options.xrePath = mochitest.getFullPath(options.xrePath) + options.xrePath = self.get_full_path(options.xrePath, parser.oldcwd) if options.profilePath: - options.profilePath = mochitest.getFullPath(options.profilePath) - options.app = mochitest.getFullPath(options.app) - if options.dmdPath is not None: - options.dmdPath = mochitest.getFullPath(options.dmdPath) + options.profilePath = self.get_full_path(options.profilePath, parser.oldcwd) - if not os.path.exists(options.app): - msg = """\ - Error: Path %(app)s doesn't exist. - Are you executing $objdir/_tests/testing/mochitest/runtests.py?""" - self.error(msg % {"app": options.app}) - return None + if options.dmdPath: + options.dmdPath = self.get_full_path(options.dmdPath, parser.oldcwd) + + if options.dmd and not options.dmdPath: + if build_obj: + options.dmdPath = build_obj.bin_dir + else: + parser.error( + "could not find dmd libraries, specify them with --dmd-path") if options.utilityPath: - options.utilityPath = mochitest.getFullPath(options.utilityPath) + options.utilityPath = self.get_full_path(options.utilityPath, parser.oldcwd) if options.certPath: - options.certPath = mochitest.getFullPath(options.certPath) + options.certPath = self.get_full_path(options.certPath, parser.oldcwd) + elif build_obj: + options.certPath = os.path.join(build_obj.topsrcdir, 'build', 'pgo', 'certs') - if options.symbolsPath and len( - urlparse( - options.symbolsPath).scheme) < 2: - options.symbolsPath = mochitest.getFullPath(options.symbolsPath) - - # Set server information on the options object - options.webServer = '127.0.0.1' - options.httpPort = DEFAULT_PORTS['http'] - options.sslPort = DEFAULT_PORTS['https'] - # options.webSocketPort = DEFAULT_PORTS['ws'] - # <- http://hg.mozilla.org/mozilla-central/file/b871dfb2186f/build/automation.py.in#l30 - options.webSocketPort = str(9988) - # The default websocket port is incorrect in mozprofile; it is - # set to the SSL proxy setting. See: - # see https://bugzilla.mozilla.org/show_bug.cgi?id=916517 + if options.symbolsPath and len(urlparse(options.symbolsPath).scheme) < 2: + options.symbolsPath = self.get_full_path(options.symbolsPath, parser.oldcwd) + elif not options.symbolsPath and build_obj: + options.symbolsPath = os.path.join(build_obj.distdir, 'crashreporter-symbols') if options.vmwareRecording: if not mozinfo.isWin: - self.error( + parser.error( "use-vmware-recording is only supported on Windows.") - mochitest.vmwareHelperPath = os.path.join( + options.vmwareHelperPath = os.path.join( options.utilityPath, VMWARE_RECORDING_HELPER_BASENAME + ".dll") - if not os.path.exists(mochitest.vmwareHelperPath): - self.error("%s not found, cannot automate VMware recording." % - mochitest.vmwareHelperPath) + if not os.path.exists(options.vmwareHelperPath): + parser.error("%s not found, cannot automate VMware recording." % + options.vmwareHelperPath) if options.webapprtContent and options.webapprtChrome: - self.error( + parser.error( "Only one of --webapprt-content and --webapprt-chrome may be given.") if options.jsdebugger: @@ -560,20 +632,31 @@ class MochitestOptions(ArgumentParser): options.autorun = False if options.debugOnFailure and not options.jsdebugger: - self.error( - "--debug-on-failure should be used together with --jsdebugger.") + parser.error( + "--debug-on-failure requires --jsdebugger.") + + if options.debuggerArgs and not options.debugger: + parser.error( + "--debugger-args requires --debugger.") - # Try to guess the testing modules directory. - # This somewhat grotesque hack allows the buildbot machines to find the - # modules directory without having to configure the buildbot hosts. This - # code should never be executed in local runs because the build system - # should always set the flag that populates this variable. If buildbot ever - # passes this argument, this code can be deleted. if options.testingModulesDir is None: - possible = os.path.join(here, os.path.pardir, 'modules') + if build_obj: + options.testingModulesDir = os.path.join( + build_obj.topobjdir, '_tests', 'modules') + else: + # Try to guess the testing modules directory. + # This somewhat grotesque hack allows the buildbot machines to find the + # modules directory without having to configure the buildbot hosts. This + # code should never be executed in local runs because the build system + # should always set the flag that populates this variable. If buildbot ever + # passes this argument, this code can be deleted. + possible = os.path.join(here, os.path.pardir, 'modules') - if os.path.isdir(possible): - options.testingModulesDir = possible + if os.path.isdir(possible): + options.testingModulesDir = possible + + if build_obj: + options.extraProfileFiles.append(os.path.join(build_obj.distdir, 'plugins')) # Even if buildbot is updated, we still want this, as the path we pass in # to the app must be absolute and have proper slashes. @@ -586,8 +669,8 @@ class MochitestOptions(ArgumentParser): options.testingModulesDir) if not os.path.isdir(options.testingModulesDir): - self.error('--testing-modules-dir not a directory: %s' % - options.testingModulesDir) + parser.error('--testing-modules-dir not a directory: %s' % + options.testingModulesDir) options.testingModulesDir = options.testingModulesDir.replace( '\\', @@ -597,12 +680,12 @@ class MochitestOptions(ArgumentParser): if options.immersiveMode: if not mozinfo.isWin: - self.error("immersive is only supported on Windows 8 and up.") - mochitest.immersiveHelperPath = os.path.join( + parser.error("immersive is only supported on Windows 8 and up.") + options.immersiveHelperPath = os.path.join( options.utilityPath, "metrotestharness.exe") - if not os.path.exists(mochitest.immersiveHelperPath): - self.error("%s not found, cannot launch immersive tests." % - mochitest.immersiveHelperPath) + if not os.path.exists(options.immersiveHelperPath): + parser.error("%s not found, cannot launch immersive tests." % + options.immersiveHelperPath) if options.runUntilFailure: if not options.repeat: @@ -613,16 +696,16 @@ class MochitestOptions(ArgumentParser): if options.dumpAboutMemoryAfterTest or options.dumpDMDAfterTest: if not os.path.isdir(options.dumpOutputDirectory): - self.error('--dump-output-directory not a directory: %s' % - options.dumpOutputDirectory) + parser.error('--dump-output-directory not a directory: %s' % + options.dumpOutputDirectory) if options.useTestMediaDevices: if not mozinfo.isLinux: - self.error( + parser.error( '--use-test-media-devices is only supported on Linux currently') for f in ['/usr/bin/gst-launch-0.10', '/usr/bin/pactl']: if not os.path.isfile(f): - self.error( + parser.error( 'Missing binary %s required for ' '--use-test-media-devices' % f) @@ -638,10 +721,6 @@ class MochitestOptions(ArgumentParser): "geckomediaplugin": 20000, } - # Bug 1065098 - The geckomediaplugin process fails to produce a leak - # log for some reason. - options.ignoreMissingLeaks = ["geckomediaplugin"] - # Bug 1091917 - We exit early in tab processes on Windows, so we don't # get leak logs yet. if mozinfo.isWin: @@ -654,151 +733,191 @@ class MochitestOptions(ArgumentParser): return options -class B2GOptions(MochitestOptions): - b2g_options = [ +class B2GArguments(ArgumentContainer): + """B2G specific arguments.""" + + args = [ [["--b2gpath"], {"dest": "b2gPath", - "help": "path to B2G repo or qemu dir", "default": None, + "help": "Path to B2G repo or QEMU directory.", + "suppress": True, }], [["--desktop"], {"action": "store_true", - "dest": "desktop", - "help": "Run the tests on a B2G desktop build", "default": False, + "help": "Run the tests on a B2G desktop build.", + "suppress": True, }], [["--marionette"], - {"dest": "marionette", + {"default": None, "help": "host:port to use when connecting to Marionette", - "default": None, }], [["--emulator"], - {"dest": "emulator", - "help": "Architecture of emulator to use: x86 or arm", - "default": None, + {"default": None, + "help": "Architecture of emulator to use, x86 or arm", + "suppress": True, }], [["--wifi"], - {"dest": "wifi", + {"default": False, "help": "Devine wifi configuration for on device mochitest", - "default": False, + "suppress": True, }], [["--sdcard"], - {"dest": "sdcard", + {"default": "10MB", "help": "Define size of sdcard: 1MB, 50MB...etc", - "default": "10MB", }], [["--no-window"], {"action": "store_true", "dest": "noWindow", - "help": "Pass --no-window to the emulator", "default": False, + "help": "Pass --no-window to the emulator", }], [["--adbpath"], {"dest": "adbPath", - "help": "path to adb", "default": "adb", + "help": "Path to adb binary.", + "suppress": True, }], [["--deviceIP"], {"dest": "deviceIP", - "help": "ip address of remote device to test", "default": None, + "help": "IP address of remote device to test.", + "suppress": True, }], [["--devicePort"], - {"dest": "devicePort", + {"default": 20701, "help": "port of remote device to test", - "default": 20701, + "suppress": True, }], [["--remote-logfile"], {"dest": "remoteLogFile", - "help": "Name of log file on the device relative to the device root. \ - PLEASE ONLY USE A FILENAME.", "default": None, + "help": "Name of log file on the device relative to the device root. " + "PLEASE ONLY USE A FILENAME.", + "suppress": True, }], [["--remote-webserver"], {"dest": "remoteWebServer", - "help": "ip address where the remote web server is hosted at", "default": None, + "help": "IP address where the remote web server is hosted.", + "suppress": True, }], [["--http-port"], {"dest": "httpPort", - "help": "ip address where the remote web server is hosted at", "default": DEFAULT_PORTS['http'], + "help": "Port used for http on the remote web server.", + "suppress": True, }], [["--ssl-port"], {"dest": "sslPort", - "help": "ip address where the remote web server is hosted at", "default": DEFAULT_PORTS['https'], + "help": "Port used for https on the remote web server.", + "suppress": True, }], [["--gecko-path"], {"dest": "geckoPath", - "help": "the path to a gecko distribution that should \ - be installed on the emulator prior to test", "default": None, + "help": "The path to a gecko distribution that should be installed on the emulator " + "prior to test.", + "suppress": True, }], [["--profile"], {"dest": "profile", - "help": "for desktop testing, the path to the \ - gaia profile to use", "default": None, + "help": "For desktop testing, the path to the gaia profile to use.", }], [["--logdir"], {"dest": "logdir", - "help": "directory to store log files", "default": None, + "help": "Directory to store log files.", }], [['--busybox'], {"dest": 'busybox', - "help": "Path to busybox binary to install on device", "default": None, + "help": "Path to busybox binary to install on device.", }], [['--profile-data-dir'], {"dest": 'profile_data_dir', - "help": "Path to a directory containing preference and other \ - data to be installed into the profile", "default": os.path.join(here, 'profile_data'), + "help": "Path to a directory containing preference and other data to be installed " + "into the profile.", + "suppress": True, }], ] - def __init__(self): - MochitestOptions.__init__(self) - - for option in self.b2g_options: - self.add_argument(*option[0], **option[1]) - - defaults = {} - defaults["logFile"] = "mochitest.log" - defaults["autorun"] = True - defaults["closeWhenDone"] = True - defaults["extensionsToExclude"] = ["specialpowers"] + defaults = { + 'logFile': 'mochitest.log', + 'extensionsToExclude': ['specialpowers'], # See dependencies of bug 1038943. - defaults["defaultLeakThreshold"] = 5536 - self.set_defaults(**defaults) + 'defaultLeakThreshold': 5536, + } + + def validate(self, parser, options, context): + """Validate b2g options.""" + + if options.desktop and not options.app: + if not (build_obj and conditions.is_b2g_desktop(build_obj)): + parser.error( + "--desktop specified, but no b2g desktop build detected! Either " + "build for b2g desktop, or point --appname to a b2g desktop binary.") + elif build_obj and conditions.is_b2g_desktop(build_obj): + options.desktop = True + if not options.app: + options.app = build_obj.get_binary_path() + if not options.app.endswith('-bin'): + options.app = '%s-bin' % options.app + if not os.path.isfile(options.app): + options.app = options.app[:-len('-bin')] - def verifyRemoteOptions(self, options): if options.remoteWebServer is None: if os.name != "nt": options.remoteWebServer = moznetwork.get_ip() else: - self.error( + parser.error( "You must specify a --remote-webserver=") options.webServer = options.remoteWebServer + if not options.b2gPath and hasattr(context, 'b2g_home'): + options.b2gPath = context.b2g_home + + if hasattr(context, 'device_name') and not options.emulator: + if context.device_name.startswith('emulator'): + options.emulator = 'x86' if 'x86' in context.device_name else 'arm' + if options.geckoPath and not options.emulator: - self.error( + parser.error( "You must specify --emulator if you specify --gecko-path") if options.logdir and not options.emulator: - self.error("You must specify --emulator if you specify --logdir") + parser.error("You must specify --emulator if you specify --logdir") + elif not options.logdir and options.emulator and build_obj: + options.logdir = os.path.join( + build_obj.topobjdir, '_tests', 'testing', 'mochitest') + + if hasattr(context, 'xre_path'): + options.xrePath = context.xre_path if not os.path.isdir(options.xrePath): - self.error("--xre-path '%s' is not a directory" % options.xrePath) + parser.error("--xre-path '%s' is not a directory" % options.xrePath) + xpcshell = os.path.join(options.xrePath, 'xpcshell') if not os.access(xpcshell, os.F_OK): - self.error('xpcshell not found at %s' % xpcshell) + parser.error('xpcshell not found at %s' % xpcshell) + if self.elf_arm(xpcshell): - self.error('--xre-path points to an ARM version of xpcshell; it ' - 'should instead point to a version that can run on ' - 'your desktop') + parser.error('--xre-path points to an ARM version of xpcshell; it ' + 'should instead point to a version that can run on ' + 'your desktop') + + if not options.httpdPath and build_obj: + options.httpdPath = os.path.join( + build_obj.topobjdir, '_tests', 'testing', 'mochitest') + + # Bug 1071866 - B2G Mochitests do not always produce a leak log. + options.ignoreMissingLeaks.append("default") + # Bug 1070068 - Leak logging does not work for tab processes on B2G. + options.ignoreMissingLeaks.append("tab") if options.pidFile != "": f = open(options.pidFile, 'w') @@ -807,34 +926,15 @@ class B2GOptions(MochitestOptions): return options - def verifyOptions(self, options, mochitest): - # since we are reusing verifyOptions, it will exit if App is not found - temp = options.app - options.app = __file__ - tempPort = options.httpPort - tempSSL = options.sslPort - tempIP = options.webServer - options = MochitestOptions.verifyOptions(self, options, mochitest) - options.webServer = tempIP - options.app = temp - options.sslPort = tempSSL - options.httpPort = tempPort - - # Bug 1071866 - B2G Mochitests do not always produce a leak log. - options.ignoreMissingLeaks.append("default") - - # Bug 1070068 - Leak logging does not work for tab processes on B2G. - options.ignoreMissingLeaks.append("tab") - - return options - def elf_arm(self, filename): data = open(filename, 'rb').read(20) return data[:4] == "\x7fELF" and ord(data[18]) == 40 # EM_ARM -class RemoteOptions(MochitestOptions): - remote_options = [ +class AndroidArguments(ArgumentContainer): + """Android specific arguments.""" + + args = [ [["--remote-app-path"], {"dest": "remoteAppPath", "help": "Path to remote executable relative to device root using \ @@ -853,10 +953,10 @@ class RemoteOptions(MochitestOptions): "default": None, }], [["--dm_trans"], - {"dest": "dm_trans", - "default": "sut", - "help": "the transport to use to communicate with device: \ - [adb|sut]; default=sut", + {"choices": ["adb", "sut"], + "default": "adb", + "help": "The transport to use for communication with the device [default: adb].", + "suppress": True, }], [["--devicePort"], {"dest": "devicePort", @@ -869,6 +969,7 @@ class RemoteOptions(MochitestOptions): "default": "fennec", "help": "The executable's name of remote product to test - either \ fennec or firefox, defaults to fennec", + "suppress": True, }], [["--remote-logfile"], {"dest": "remoteLogFile", @@ -885,35 +986,24 @@ class RemoteOptions(MochitestOptions): {"dest": "httpPort", "default": DEFAULT_PORTS['http'], "help": "http port of the remote web server", + "suppress": True, }], [["--ssl-port"], {"dest": "sslPort", "default": DEFAULT_PORTS['https'], "help": "ssl port of the remote web server", + "suppress": True, }], [["--robocop-ini"], {"dest": "robocopIni", "default": "", "help": "name of the .ini file containing the list of tests to run", }], - [["--robocop"], - {"dest": "robocop", - "default": "", - "help": "name of the .ini file containing the list of tests to run. \ - [DEPRECATED- please use --robocop-ini", - }], [["--robocop-apk"], {"dest": "robocopApk", "default": "", "help": "name of the Robocop APK to use for ADB test running", }], - [["--robocop-path"], - {"dest": "robocopPath", - "default": "", - "help": "Path to the folder where robocop.apk is located at. \ - Primarily used for ADB test running. \ - [DEPRECATED- please use --robocop-apk]", - }], [["--robocop-ids"], {"dest": "robocopIds", "default": "", @@ -925,114 +1015,114 @@ class RemoteOptions(MochitestOptions): "default": None, "help": "remote directory to use as test root \ (eg. /mnt/sdcard/tests or /data/local/tests)", + "suppress": True, }], ] - def __init__(self, automation, **kwargs): - self._automation = automation or Automation() - MochitestOptions.__init__(self) + defaults = { + 'dm': None, + 'logFile': 'mochitest.log', + 'utilityPath': None, + } - for option in self.remote_options: - self.add_argument(*option[0], **option[1]) + def validate(self, parser, options, context): + """Validate android options.""" - defaults = {} - defaults["logFile"] = "mochitest.log" - defaults["autorun"] = True - defaults["closeWhenDone"] = True - defaults["utilityPath"] = None - self.set_defaults(**defaults) + if build_obj: + options.log_mach = '-' - def verifyRemoteOptions(self, options, automation): - options_logger = logging.getLogger('MochitestRemote') + if options.dm_trans == "adb": + if options.deviceIP: + options.dm = DroidADB( + options.deviceIP, + options.devicePort, + deviceRoot=options.remoteTestRoot) + elif options.deviceSerial: + options.dm = DroidADB( + None, + None, + deviceSerial=options.deviceSerial, + deviceRoot=options.remoteTestRoot) + else: + options.dm = DroidADB(deviceRoot=options.remoteTestRoot) + elif options.dm_trans == 'sut': + if options.deviceIP is None: + parser.error( + "If --dm_trans = sut, you must provide a device IP") + + options.dm = DroidSUT( + options.deviceIP, + options.devicePort, + deviceRoot=options.remoteTestRoot) if not options.remoteTestRoot: - options.remoteTestRoot = automation._devicemanager.deviceRoot + options.remoteTestRoot = options.dm.deviceRoot if options.remoteWebServer is None: if os.name != "nt": options.remoteWebServer = moznetwork.get_ip() else: - options_logger.error( + parser.error( "you must specify a --remote-webserver=") - return None options.webServer = options.remoteWebServer - if (options.dm_trans == 'sut' and options.deviceIP is None): - options_logger.error( - "If --dm_trans = sut, you must provide a device IP") - return None - - if (options.remoteLogFile is None): + if options.remoteLogFile is None: options.remoteLogFile = options.remoteTestRoot + \ '/logs/mochitest.log' - if (options.remoteLogFile.count('/') < 1): + if options.remoteLogFile.count('/') < 1: options.remoteLogFile = options.remoteTestRoot + \ '/' + options.remoteLogFile - if (options.remoteAppPath and options.app): - options_logger.error( + if options.remoteAppPath and options.app: + parser.error( "You cannot specify both the remoteAppPath and the app setting") - return None - elif (options.remoteAppPath): + elif options.remoteAppPath: options.app = options.remoteTestRoot + "/" + options.remoteAppPath - elif (options.app is None): - # Neither remoteAppPath nor app are set -- error - options_logger.error("You must specify either appPath or app") - return None + elif options.app is None: + if build_obj: + options.app = build_obj.substs['ANDROID_PACKAGE_NAME'] + else: + # Neither remoteAppPath nor app are set -- error + parser.error("You must specify either appPath or app") + + if build_obj and 'MOZ_HOST_BIN' in os.environ: + options.xrePath = os.environ['MOZ_HOST_BIN'] # Only reset the xrePath if it wasn't provided - if (options.xrePath is None): + if options.xrePath is None: options.xrePath = options.utilityPath - if (options.pidFile != ""): + if options.pidFile != "": f = open(options.pidFile, 'w') f.write("%s" % os.getpid()) f.close() - # Robocop specific deprecated options. - if options.robocop: - if options.robocopIni: - options_logger.error( - "can not use deprecated --robocop and replacement --robocop-ini together") - return None - options.robocopIni = options.robocop - del options.robocop - - if options.robocopPath: - if options.robocopApk: - options_logger.error( - "can not use deprecated --robocop-path and replacement --robocop-apk together") - return None - options.robocopApk = os.path.join( - options.robocopPath, - 'robocop.apk') - del options.robocopPath - # Robocop specific options if options.robocopIni != "": if not os.path.exists(options.robocopIni): - options_logger.error( + parser.error( "Unable to find specified robocop .ini manifest '%s'" % options.robocopIni) - return None options.robocopIni = os.path.abspath(options.robocopIni) + if not options.robocopApk and build_obj: + options.robocopApk = os.path.join(build_obj.topobjdir, 'build', 'mobile', + 'robocop', 'robocop-debug.apk') + if options.robocopApk != "": if not os.path.exists(options.robocopApk): - options_logger.error( + parser.error( "Unable to find robocop APK '%s'" % options.robocopApk) - return None options.robocopApk = os.path.abspath(options.robocopApk) if options.robocopIds != "": if not os.path.exists(options.robocopIds): - options_logger.error( + parser.error( "Unable to find specified robocop IDs file '%s'" % options.robocopIds) - return None options.robocopIds = os.path.abspath(options.robocopIds) # allow us to keep original application around for cleanup while @@ -1040,20 +1130,84 @@ class RemoteOptions(MochitestOptions): options.remoteappname = options.app return options - def verifyOptions(self, options, mochitest): - # since we are reusing verifyOptions, it will exit if App is not found - temp = options.app - options.app = __file__ - tempPort = options.httpPort - tempSSL = options.sslPort - tempIP = options.webServer - # We are going to override this option later anyway, just pretend - # like it's not set for verification purposes. - options.dumpOutputDirectory = None - options = MochitestOptions.verifyOptions(self, options, mochitest) - options.webServer = tempIP - options.app = temp - options.sslPort = tempSSL - options.httpPort = tempPort - return options +container_map = { + 'generic': [MochitestArguments], + 'b2g': [MochitestArguments, B2GArguments], + 'android': [MochitestArguments, AndroidArguments], +} + + +class MochitestArgumentParser(ArgumentParser): + """ + Usage instructions for runtests.py. + + All arguments are optional. + If --chrome is specified, chrome tests will be run instead of web content tests. + If --browser-chrome is specified, browser-chrome tests will be run instead of web content tests. + See for details on the logging levels. + """ + + _containers = None + context = {} + + def __init__(self, app=None, **kwargs): + ArgumentParser.__init__(self, usage=self.__doc__, conflict_handler='resolve', **kwargs) + + self.oldcwd = os.getcwd() + self.app = app + if not self.app and build_obj: + if conditions.is_android(build_obj): + self.app = 'android' + elif conditions.is_b2g(build_obj): + self.app = 'b2g' + if not self.app: + # platform can't be determined and app wasn't specified explicitly, + # so just use generic arguments and hope for the best + self.app = 'generic' + + if self.app not in container_map: + self.error("Unrecognized app '{}'! Must be one of: {}".format( + self.app, ', '.join(container_map.keys()))) + + defaults = {} + for container in self.containers: + defaults.update(container.defaults) + group = self.add_argument_group(container.__class__.__name__, container.__doc__) + + for cli, kwargs in container.args: + # Allocate new lists so references to original don't get mutated. + # allowing multiple uses within a single process. + if "default" in kwargs and isinstance(kwargs['default'], list): + kwargs["default"] = [] + + if 'suppress' in kwargs: + if kwargs['suppress']: + kwargs['help'] = SUPPRESS + del kwargs['suppress'] + + group.add_argument(*cli, **kwargs) + + self.set_defaults(**defaults) + structured.commandline.add_logging_group(self) + + @property + def containers(self): + if self._containers: + return self._containers + + containers = container_map[self.app] + self._containers = [c() for c in containers] + return self._containers + + def validate(self, args): + for container in self.containers: + args = container.validate(self, args, self.context) + return args + + def parse_args(self, *args, **kwargs): + return self.validate(ArgumentParser.parse_args(self, *args, **kwargs)) + + def parse_known_args(self, *args, **kwargs): + args, remainder = ArgumentParser.parse_known_args(self, *args, **kwargs) + return (self.validate(args), remainder) diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index 92e6a780a28e..75640b875559 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -53,13 +53,21 @@ from manifestparser.filters import ( subsuite, tags, ) -from mochitest_options import MochitestOptions +from mochitest_options import MochitestArgumentParser from mozprofile import Profile, Preferences from mozprofile.permissions import ServerLocations from urllib import quote_plus as encodeURIComponent from mozlog.structured.formatters import TbplFormatter from mozlog.structured import commandline +here = os.path.abspath(os.path.dirname(__file__)) + +try: + from mozbuild.base import MozbuildObject + build_obj = MozbuildObject.from_environment(cwd=here) +except ImportError: + build_obj = None + ########################### # Option for NSPR logging # @@ -2595,29 +2603,34 @@ class Mochitest(MochitestUtilsMixin): return dirlist -def main(): +def run_test_harness(options): + logger_options = { + key: value for key, value in vars(options).iteritems() if key.startswith('log')} + runner = Mochitest(logger_options) + result = runner.runTests(options) + + # don't dump failures if running from automation as treeherder already displays them + if build_obj: + if runner.message_logger.errors: + result = 1 + runner.message_logger.logger.warning("The following tests failed:") + for error in runner.message_logger.errors: + runner.message_logger.logger.log_raw(error) + + runner.message_logger.finish() + + return result + + +def cli(args=sys.argv[1:]): # parse command line options - parser = MochitestOptions() - commandline.add_logging_group(parser) - options = parser.parse_args() + parser = MochitestArgumentParser(app='generic') + options = parser.parse_args(args) if options is None: # parsing error sys.exit(1) - logger_options = { - key: value for key, - value in vars(options).iteritems() if key.startswith('log')} - mochitest = Mochitest(logger_options) - options = parser.verifyOptions(options, mochitest) - options.utilityPath = mochitest.getFullPath(options.utilityPath) - options.certPath = mochitest.getFullPath(options.certPath) - if options.symbolsPath and len(urlparse(options.symbolsPath).scheme) < 2: - options.symbolsPath = mochitest.getFullPath(options.symbolsPath) - - return_code = mochitest.runTests(options) - mochitest.message_logger.finish() - - sys.exit(return_code) + return run_test_harness(options) if __name__ == "__main__": - main() + sys.exit(cli()) diff --git a/testing/mochitest/runtestsb2g.py b/testing/mochitest/runtestsb2g.py index eacffc52956e..856e073c01f3 100644 --- a/testing/mochitest/runtestsb2g.py +++ b/testing/mochitest/runtestsb2g.py @@ -17,7 +17,7 @@ sys.path.insert(0, here) from automationutils import processLeakLog from runtests import Mochitest from runtests import MochitestUtilsMixin -from mochitest_options import B2GOptions, MochitestOptions +from mochitest_options import MochitestArgumentParser from marionette import Marionette from mozprofile import Profile, Preferences from mozlog import structured @@ -417,7 +417,7 @@ class B2GDesktopMochitest(B2GMochitest, Mochitest): return self.build_profile(options) -def run_remote_mochitests(parser, options): +def run_remote_mochitests(options): # create our Marionette instance marionette_args = { 'adb_path': options.adbPath, @@ -434,7 +434,6 @@ def run_remote_mochitests(parser, options): marionette_args['host'] = host marionette_args['port'] = int(port) - options = parser.verifyRemoteOptions(options) if (options is None): print "ERROR: Invalid options specified, use --help for a list of valid options" sys.exit(1) @@ -446,7 +445,6 @@ def run_remote_mochitests(parser, options): options.xrePath, remote_log_file=options.remoteLogFile) - options = parser.verifyOptions(options, mochitest) if (options is None): sys.exit(1) @@ -469,7 +467,7 @@ def run_remote_mochitests(parser, options): sys.exit(retVal) -def run_desktop_mochitests(parser, options): +def run_desktop_mochitests(options): # create our Marionette instance marionette_args = {} if options.marionette: @@ -486,7 +484,6 @@ def run_desktop_mochitests(parser, options): marionette_args, options, options.profile_data_dir) - options = MochitestOptions.verifyOptions(parser, options, mochitest) if options is None: sys.exit(1) @@ -502,14 +499,13 @@ def run_desktop_mochitests(parser, options): def main(): - parser = B2GOptions() - structured.commandline.add_logging_group(parser) + parser = MochitestArgumentParser(app='b2g') options = parser.parse_args() if options.desktop: - run_desktop_mochitests(parser, options) + run_desktop_mochitests(options) else: - run_remote_mochitests(parser, options) + run_remote_mochitests(options) if __name__ == "__main__": main() diff --git a/testing/mochitest/runtestsremote.py b/testing/mochitest/runtestsremote.py index 08e84bd40a21..3c3ab549e1ab 100644 --- a/testing/mochitest/runtestsremote.py +++ b/testing/mochitest/runtestsremote.py @@ -18,13 +18,11 @@ sys.path.insert( from automation import Automation from remoteautomation import RemoteAutomation, fennecLogcatFilters from runtests import Mochitest, MessageLogger -from mochitest_options import RemoteOptions -from mozlog import structured +from mochitest_options import MochitestArgumentParser from manifestparser import TestManifest from manifestparser.filters import chunk_by_slice import devicemanager -import droid import mozinfo SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__))) @@ -461,40 +459,16 @@ class MochiRemote(Mochitest): return self._automation.runApp(*args, **kwargs) -def main(args): +def run_test_harness(options): message_logger = MessageLogger(logger=None) process_args = {'messageLogger': message_logger} auto = RemoteAutomation(None, "fennec", processArgs=process_args) - parser = RemoteOptions(auto) - structured.commandline.add_logging_group(parser) - options = parser.parse_args(args) - - if (options.dm_trans == "adb"): - if (options.deviceIP): - dm = droid.DroidADB( - options.deviceIP, - options.devicePort, - deviceRoot=options.remoteTestRoot) - elif (options.deviceSerial): - dm = droid.DroidADB( - None, - None, - deviceSerial=options.deviceSerial, - deviceRoot=options.remoteTestRoot) - else: - dm = droid.DroidADB(deviceRoot=options.remoteTestRoot) - else: - dm = droid.DroidSUT( - options.deviceIP, - options.devicePort, - deviceRoot=options.remoteTestRoot) - auto.setDeviceManager(dm) - options = parser.verifyRemoteOptions(options, auto) - if options is None: raise ValueError("Invalid options specified, use --help for a list of valid options") + dm = options.dm + auto.setDeviceManager(dm) mochitest = MochiRemote(auto, dm, options) log = mochitest.log @@ -508,10 +482,6 @@ def main(args): auto.setProduct(options.remoteProductName) auto.setAppName(options.remoteappname) - options = parser.verifyOptions(options, mochitest) - if (options is None): - return 1 - logParent = os.path.dirname(options.remoteLogFile) dm.mkDir(logParent) auto.setRemoteLog(options.remoteLogFile) @@ -738,5 +708,12 @@ def main(args): return retVal +def main(args=sys.argv[1:]): + parser = MochitestArgumentParser(app='android') + options = parser.parse_args(args) + + return run_test_harness(options) + + if __name__ == "__main__": - sys.exit(main(sys.argv[1:])) + sys.exit(main()) diff --git a/testing/modules/Assert.jsm b/testing/modules/Assert.jsm index e9631fa57dbe..d226b660b609 100644 --- a/testing/modules/Assert.jsm +++ b/testing/modules/Assert.jsm @@ -274,7 +274,7 @@ proto.deepEqual = function deepEqual(actual, expected, message) { * (string) Short explanation of the expected result */ proto.notDeepEqual = function notDeepEqual(actual, expected, message) { - this.report(_deepEqual(actual, expected), actual, expected, message, "notDeepEqual"); + this.report(ObjectUtils.deepEqual(actual, expected), actual, expected, message, "notDeepEqual"); }; /** diff --git a/testing/mozharness/mozharness.json b/testing/mozharness/mozharness.json index 22b117a39f7e..0565e208cf16 100644 --- a/testing/mozharness/mozharness.json +++ b/testing/mozharness/mozharness.json @@ -1,4 +1,4 @@ { "repo": "https://hg.mozilla.org/build/mozharness", - "revision": "c9633e9c344a" + "revision": "565ea3ec1d4d" } diff --git a/testing/profiles/prefs_general.js b/testing/profiles/prefs_general.js index a4a8762daebb..b0f3d50f8e48 100644 --- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -154,9 +154,6 @@ user_pref("layout.css.object-fit-and-position.enabled", true); // Enable CSS Ruby for testing user_pref("layout.css.ruby.enabled", true); -// Enable CSS Font Loading API for testing -user_pref("layout.css.font-loading-api.enabled", true); - // Enable unicode-range for testing user_pref("layout.css.unicode-range.enabled", true); diff --git a/testing/testsuite-targets.mk b/testing/testsuite-targets.mk index 52b7eb1adcec..d0a5320363e5 100644 --- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -29,16 +29,16 @@ endif RUN_MOCHITEST_B2G_DESKTOP = \ rm -f ./$@.log && \ - $(PYTHON) _tests/testing/mochitest/runtestsb2g.py --autorun --close-when-done \ - --console-level=INFO --log-tbpl=./$@.log \ + $(PYTHON) _tests/testing/mochitest/runtestsb2g.py \ + --log-tbpl=./$@.log \ --desktop --profile ${GAIA_PROFILE_DIR} \ --failure-file=$(abspath _tests/testing/mochitest/makefailures.json) \ $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS) RUN_MOCHITEST = \ rm -f ./$@.log && \ - $(PYTHON) _tests/testing/mochitest/runtests.py --autorun --close-when-done \ - --console-level=INFO --log-tbpl=./$@.log \ + $(PYTHON) _tests/testing/mochitest/runtests.py \ + --log-tbpl=./$@.log \ --failure-file=$(abspath _tests/testing/mochitest/makefailures.json) \ --testing-modules-dir=$(abspath _tests/modules) \ --extra-profile-file=$(DIST)/plugins \ @@ -46,8 +46,8 @@ RUN_MOCHITEST = \ RERUN_MOCHITEST = \ rm -f ./$@.log && \ - $(PYTHON) _tests/testing/mochitest/runtests.py --autorun --close-when-done \ - --console-level=INFO --log-tbpl=./$@.log \ + $(PYTHON) _tests/testing/mochitest/runtests.py \ + --log-tbpl=./$@.log \ --run-only-tests=makefailures.json \ --testing-modules-dir=$(abspath _tests/modules) \ --extra-profile-file=$(DIST)/plugins \ @@ -55,8 +55,8 @@ RERUN_MOCHITEST = \ RUN_MOCHITEST_REMOTE = \ rm -f ./$@.log && \ - $(PYTHON) _tests/testing/mochitest/runtestsremote.py --autorun --close-when-done \ - --console-level=INFO --log-tbpl=./$@.log $(DM_FLAGS) --dm_trans=$(DM_TRANS) \ + $(PYTHON) _tests/testing/mochitest/runtestsremote.py \ + --log-tbpl=./$@.log $(DM_FLAGS) --dm_trans=$(DM_TRANS) \ --app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \ --testing-modules-dir=$(abspath _tests/modules) \ $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS) @@ -67,7 +67,7 @@ RUN_MOCHITEST_ROBOCOP = \ --robocop-apk=$(DEPTH)/build/mobile/robocop/robocop-debug.apk \ --robocop-ids=$(DEPTH)/mobile/android/base/fennec_ids.txt \ --robocop-ini=_tests/testing/mochitest/robocop.ini \ - --console-level=INFO --log-tbpl=./$@.log $(DM_FLAGS) --dm_trans=$(DM_TRANS) \ + --log-tbpl=./$@.log $(DM_FLAGS) --dm_trans=$(DM_TRANS) \ --app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \ $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS) diff --git a/testing/web-platform/meta/IndexedDB/idbobjectstore_createIndex6-event_order.htm.ini b/testing/web-platform/meta/IndexedDB/idbobjectstore_createIndex6-event_order.htm.ini index 1c1cdc698d81..e48d35c05331 100644 --- a/testing/web-platform/meta/IndexedDB/idbobjectstore_createIndex6-event_order.htm.ini +++ b/testing/web-platform/meta/IndexedDB/idbobjectstore_createIndex6-event_order.htm.ini @@ -1,6 +1,5 @@ [idbobjectstore_createIndex6-event_order.htm] type: testharness - expected: ERROR [IDBObjectStore.createIndex() - event order when unique constraint is triggered] expected: FAIL diff --git a/testing/web-platform/meta/IndexedDB/idbobjectstore_createIndex7-event_order.htm.ini b/testing/web-platform/meta/IndexedDB/idbobjectstore_createIndex7-event_order.htm.ini index 32941b896c11..ed7f7fabb82c 100644 --- a/testing/web-platform/meta/IndexedDB/idbobjectstore_createIndex7-event_order.htm.ini +++ b/testing/web-platform/meta/IndexedDB/idbobjectstore_createIndex7-event_order.htm.ini @@ -1,6 +1,5 @@ [idbobjectstore_createIndex7-event_order.htm] type: testharness - expected: ERROR [IDBObjectStore.createIndex() - Event ordering for ConstraintError on request] expected: FAIL diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 349b3b074c07..28d3c7011aff 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -8983,10 +8983,6 @@ "path": "IndexedDB/index_sort_order.htm", "url": "/IndexedDB/index_sort_order.htm" }, - { - "path": "IndexedDB/interfaces.htm", - "url": "/IndexedDB/interfaces.htm" - }, { "path": "IndexedDB/interfaces.html", "url": "/IndexedDB/interfaces.html" @@ -10759,6 +10755,10 @@ "path": "dom/nodes/Text-constructor.html", "url": "/dom/nodes/Text-constructor.html" }, + { + "path": "dom/nodes/Text-splitText.html", + "url": "/dom/nodes/Text-splitText.html" + }, { "path": "dom/nodes/attributes.html", "url": "/dom/nodes/attributes.html" @@ -11803,6 +11803,10 @@ "path": "html/dom/documents/dom-tree-accessors/document.title-08.html", "url": "/html/dom/documents/dom-tree-accessors/document.title-08.html" }, + { + "path": "html/dom/documents/dom-tree-accessors/document.title-09.html", + "url": "/html/dom/documents/dom-tree-accessors/document.title-09.html" + }, { "path": "html/dom/documents/dom-tree-accessors/nameditem-01.html", "url": "/html/dom/documents/dom-tree-accessors/nameditem-01.html" @@ -16896,8 +16900,8 @@ "url": "/web-animations/animation-node/idlharness.html" }, { - "path": "web-animations/animation-timeline/animation-timeline.html", - "url": "/web-animations/animation-timeline/animation-timeline.html" + "path": "web-animations/animation-timeline/document-timeline.html", + "url": "/web-animations/animation-timeline/document-timeline.html" }, { "path": "web-animations/animation-timeline/idlharness.html", @@ -24821,7 +24825,7 @@ } ] }, - "rev": "20aef05e164be1ccbbd8f66192f01e778b5e5c18", + "rev": "40a9c4e9e4f99a738cd1a7602066c5e84d1b90b5", "url_base": "/", "version": 2 } \ No newline at end of file diff --git a/testing/web-platform/meta/encoding/textdecoder-labels.html.ini b/testing/web-platform/meta/encoding/textdecoder-labels.html.ini index 8a551a01bdbf..2a5c3af495e0 100644 --- a/testing/web-platform/meta/encoding/textdecoder-labels.html.ini +++ b/testing/web-platform/meta/encoding/textdecoder-labels.html.ini @@ -3,3 +3,51 @@ [name=big5 label=big5-hkscs] expected: FAIL + ["big5-hkscs" => "big5"] + expected: FAIL + + [" big5-hkscs" => "big5"] + expected: FAIL + + ["big5-hkscs " => "big5"] + expected: FAIL + + [" big5-hkscs " => "big5"] + expected: FAIL + + ["\\tbig5-hkscs" => "big5"] + expected: FAIL + + ["big5-hkscs\\t" => "big5"] + expected: FAIL + + ["\\tbig5-hkscs\\t" => "big5"] + expected: FAIL + + ["\\nbig5-hkscs" => "big5"] + expected: FAIL + + ["big5-hkscs\\n" => "big5"] + expected: FAIL + + ["\\nbig5-hkscs\\n" => "big5"] + expected: FAIL + + ["\\fbig5-hkscs" => "big5"] + expected: FAIL + + ["big5-hkscs\\f" => "big5"] + expected: FAIL + + ["\\fbig5-hkscs\\f" => "big5"] + expected: FAIL + + ["\\rbig5-hkscs" => "big5"] + expected: FAIL + + ["big5-hkscs\\r" => "big5"] + expected: FAIL + + ["\\rbig5-hkscs\\r" => "big5"] + expected: FAIL + diff --git a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-09.html.ini b/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-09.html.ini new file mode 100644 index 000000000000..617f8d14bacc --- /dev/null +++ b/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-09.html.ini @@ -0,0 +1,11 @@ +[document.title-09.html] + type: testharness + [No title element in SVG document] + expected: FAIL + + [Title element in SVG document] + expected: FAIL + + [Title element not child of SVG root] + expected: FAIL + diff --git a/testing/web-platform/meta/html/dom/reflection-embedded.html.ini b/testing/web-platform/meta/html/dom/reflection-embedded.html.ini index cb428a65fc77..1a233fdb9d70 100644 --- a/testing/web-platform/meta/html/dom/reflection-embedded.html.ini +++ b/testing/web-platform/meta/html/dom/reflection-embedded.html.ini @@ -249,18 +249,6 @@ [img.lowsrc: IDL set to object "test-valueOf" followed by IDL get] expected: FAIL - [img.hspace: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [img.hspace: setAttribute() to object "3" followed by IDL get] - expected: FAIL - - [img.vspace: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [img.vspace: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [iframe.tabIndex: setAttribute() to -2147483648 followed by IDL get] expected: FAIL @@ -495,18 +483,6 @@ [object.code: IDL set to object "test-valueOf" followed by IDL get] expected: FAIL - [object.hspace: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [object.hspace: setAttribute() to object "3" followed by IDL get] - expected: FAIL - - [object.vspace: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [object.vspace: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [param.tabIndex: setAttribute() to -2147483648 followed by IDL get] expected: FAIL @@ -675,18 +651,6 @@ [video.mediaGroup: IDL set to object "test-valueOf" followed by IDL get] expected: FAIL - [video.width: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [video.width: setAttribute() to object "3" followed by IDL get] - expected: FAIL - - [video.height: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [video.height: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [audio.tabIndex: setAttribute() to -2147483648 followed by IDL get] expected: FAIL @@ -1062,18 +1026,6 @@ [canvas.tabIndex: IDL set to -2147483648 followed by IDL get] expected: FAIL - [canvas.width: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [canvas.width: setAttribute() to object "3" followed by IDL get] - expected: FAIL - - [canvas.height: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [canvas.height: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [map.tabIndex: setAttribute() to -2147483648 followed by IDL get] expected: FAIL diff --git a/testing/web-platform/meta/html/dom/reflection-forms.html.ini b/testing/web-platform/meta/html/dom/reflection-forms.html.ini index f50425fbab91..69c83cd68fce 100644 --- a/testing/web-platform/meta/html/dom/reflection-forms.html.ini +++ b/testing/web-platform/meta/html/dom/reflection-forms.html.ini @@ -612,18 +612,6 @@ [input.inputMode: IDL set to "URL" followed by IDL get] expected: FAIL - [input.maxLength: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [input.maxLength: setAttribute() to object "3" followed by IDL get] - expected: FAIL - - [input.size: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [input.size: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [input.type: setAttribute() to "datetime" followed by IDL get] expected: FAIL @@ -708,12 +696,6 @@ [select.tabIndex: IDL set to -2147483648 followed by IDL get] expected: FAIL - [select.size: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [select.size: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [datalist.tabIndex: setAttribute() to -2147483648 followed by IDL get] expected: FAIL @@ -762,12 +744,6 @@ [textarea.tabIndex: IDL set to -2147483648 followed by IDL get] expected: FAIL - [textarea.cols: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [textarea.cols: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [textarea.dirName: typeof IDL attribute] expected: FAIL @@ -1491,18 +1467,6 @@ [textarea.inputMode: IDL set to "URL" followed by IDL get] expected: FAIL - [textarea.maxLength: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [textarea.maxLength: setAttribute() to object "3" followed by IDL get] - expected: FAIL - - [textarea.rows: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [textarea.rows: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [keygen.tabIndex: setAttribute() to -2147483648 followed by IDL get] expected: FAIL diff --git a/testing/web-platform/meta/html/dom/reflection-grouping.html.ini b/testing/web-platform/meta/html/dom/reflection-grouping.html.ini index 33093e70c565..a6e4ca405ec0 100644 --- a/testing/web-platform/meta/html/dom/reflection-grouping.html.ini +++ b/testing/web-platform/meta/html/dom/reflection-grouping.html.ini @@ -48,9 +48,6 @@ [pre.width: setAttribute() to "-1" followed by IDL get] expected: FAIL - [pre.width: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - [pre.width: IDL set to -36 followed by IDL get] expected: FAIL @@ -87,9 +84,6 @@ [ol.start: setAttribute() to -2147483648 followed by IDL get] expected: FAIL - [ol.start: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - [ol.start: IDL set to -2147483648 followed by IDL get] expected: FAIL @@ -120,9 +114,6 @@ [li.value: setAttribute() to -2147483648 followed by IDL get] expected: FAIL - [li.value: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - [li.value: IDL set to -2147483648 followed by IDL get] expected: FAIL diff --git a/testing/web-platform/meta/html/dom/reflection-obsolete.html.ini b/testing/web-platform/meta/html/dom/reflection-obsolete.html.ini index 9f7fe5efb09e..bf5272106d49 100644 --- a/testing/web-platform/meta/html/dom/reflection-obsolete.html.ini +++ b/testing/web-platform/meta/html/dom/reflection-obsolete.html.ini @@ -12,18 +12,6 @@ [applet.tabIndex: IDL set to -2147483648 followed by IDL get] expected: FAIL - [applet.hspace: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [applet.hspace: setAttribute() to object "3" followed by IDL get] - expected: FAIL - - [applet.vspace: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [applet.vspace: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [marquee.tabIndex: setAttribute() to -2147483648 followed by IDL get] expected: FAIL @@ -714,9 +702,6 @@ [marquee.hspace: setAttribute() to object "2" followed by IDL get] expected: FAIL - [marquee.hspace: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - [marquee.hspace: setAttribute() to object "3" followed by IDL get] expected: FAIL @@ -900,9 +885,6 @@ [marquee.scrollAmount: setAttribute() to object "2" followed by IDL get] expected: FAIL - [marquee.scrollAmount: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - [marquee.scrollAmount: setAttribute() to object "3" followed by IDL get] expected: FAIL @@ -1086,9 +1068,6 @@ [marquee.scrollDelay: setAttribute() to object "2" followed by IDL get] expected: FAIL - [marquee.scrollDelay: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - [marquee.scrollDelay: setAttribute() to object "3" followed by IDL get] expected: FAIL @@ -1380,9 +1359,6 @@ [marquee.vspace: setAttribute() to object "2" followed by IDL get] expected: FAIL - [marquee.vspace: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - [marquee.vspace: setAttribute() to object "3" followed by IDL get] expected: FAIL diff --git a/testing/web-platform/meta/html/dom/reflection-tabular.html.ini b/testing/web-platform/meta/html/dom/reflection-tabular.html.ini index 8275b90f18f0..9eb83f56d4a6 100644 --- a/testing/web-platform/meta/html/dom/reflection-tabular.html.ini +++ b/testing/web-platform/meta/html/dom/reflection-tabular.html.ini @@ -147,12 +147,6 @@ [colgroup.span: setAttribute() to 2147483647 followed by IDL get] expected: FAIL - [colgroup.span: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [colgroup.span: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [colgroup.span: IDL set to 0 must throw INDEX_SIZE_ERR] expected: FAIL @@ -174,12 +168,6 @@ [col.span: setAttribute() to 2147483647 followed by IDL get] expected: FAIL - [col.span: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [col.span: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [col.span: IDL set to 0 must throw INDEX_SIZE_ERR] expected: FAIL @@ -249,24 +237,12 @@ [td.colSpan: setAttribute() to 2147483647 followed by IDL get] expected: FAIL - [td.colSpan: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [td.colSpan: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [td.colSpan: IDL set to 2147483647 followed by IDL get] expected: FAIL [td.rowSpan: setAttribute() to 2147483647 followed by IDL get] expected: FAIL - [td.rowSpan: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [td.rowSpan: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [td.rowSpan: IDL set to 2147483647 followed by IDL get] expected: FAIL @@ -285,24 +261,12 @@ [th.colSpan: setAttribute() to 2147483647 followed by IDL get] expected: FAIL - [th.colSpan: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [th.colSpan: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [th.colSpan: IDL set to 2147483647 followed by IDL get] expected: FAIL [th.rowSpan: setAttribute() to 2147483647 followed by IDL get] expected: FAIL - [th.rowSpan: setAttribute() to object "3" followed by getAttribute()] - expected: FAIL - - [th.rowSpan: setAttribute() to object "3" followed by IDL get] - expected: FAIL - [th.rowSpan: IDL set to 2147483647 followed by IDL get] expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/interfaces.html.ini b/testing/web-platform/meta/html/semantics/interfaces.html.ini index 447409cbe546..f2f77f91bee0 100644 --- a/testing/web-platform/meta/html/semantics/interfaces.html.ini +++ b/testing/web-platform/meta/html/semantics/interfaces.html.ini @@ -36,3 +36,39 @@ [Interfaces for summary] expected: FAIL + [Interfaces for BASEFONT] + expected: FAIL + + [Interfaces for IMAGE] + expected: FAIL + + [Interfaces for KEYGEN] + expected: FAIL + + [Interfaces for MARQUEE] + expected: FAIL + + [Interfaces for TD] + expected: FAIL + + [Interfaces for TH] + expected: FAIL + + [Interfaces for DETAILS] + expected: FAIL + + [Interfaces for COMMAND] + expected: FAIL + + [Interfaces for RB] + expected: FAIL + + [Interfaces for BDI] + expected: FAIL + + [Interfaces for DIALOG] + expected: FAIL + + [Interfaces for SUMMARY] + expected: FAIL + diff --git a/testing/web-platform/meta/media-source/URL-createObjectURL-revoke.html.ini b/testing/web-platform/meta/media-source/URL-createObjectURL-revoke.html.ini index 2efe4976b69d..4ef367336b13 100644 --- a/testing/web-platform/meta/media-source/URL-createObjectURL-revoke.html.ini +++ b/testing/web-platform/meta/media-source/URL-createObjectURL-revoke.html.ini @@ -1,4 +1,3 @@ [URL-createObjectURL-revoke.html] type: testharness prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false] - diff --git a/testing/web-platform/meta/mozilla-sync b/testing/web-platform/meta/mozilla-sync index af73402b0de1..a3063f835a9f 100644 --- a/testing/web-platform/meta/mozilla-sync +++ b/testing/web-platform/meta/mozilla-sync @@ -1 +1 @@ -821cd0c3a55889f05c04ed9b6c12c810cd1a6c0f \ No newline at end of file +eb7ac376c982330bc85c58e68fffc55aabef604c \ No newline at end of file diff --git a/testing/web-platform/meta/service-workers/cache-storage/window/cache-add.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/window/cache-add.https.html.ini index bf41585c09e1..1e6b582ca873 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/window/cache-add.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/window/cache-add.https.html.ini @@ -2,11 +2,9 @@ type: testharness [Cache.add called with non-HTTP/HTTPS URL] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158265 - expected: FAIL [Cache.add called with Request object] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 - expected: FAIL [Cache.add called with Request object with a used body] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 @@ -14,9 +12,7 @@ [Cache.add called twice with the same Request object] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 - expected: FAIL [Cache.addAll called with the same Request object specified twice] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158263 - expected: FAIL diff --git a/testing/web-platform/meta/service-workers/cache-storage/window/cache-delete.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/window/cache-delete.https.html.ini index f06f2f745e0f..d5a20135c2ec 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/window/cache-delete.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/window/cache-delete.https.html.ini @@ -2,7 +2,6 @@ type: testharness [Cache.delete called with a Request object] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 - expected: FAIL [Cache.delete with a Request object containing used body] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 diff --git a/testing/web-platform/meta/service-workers/cache-storage/window/cache-match.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/window/cache-match.https.html.ini index 6bafd0970b15..2e28cd3d7dee 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/window/cache-match.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/window/cache-match.https.html.ini @@ -2,13 +2,14 @@ type: testharness [Cache.matchAll with responses containing "Vary" header] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158292 - expected: FAIL + [Cache.match with responses containing "Vary" header] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158292 - expected: FAIL + [Cache.matchAll with "ignoreVary" parameter] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158292 - expected: FAIL + [Cache.match with Request containing non-empty body] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 expected: FAIL + diff --git a/testing/web-platform/meta/service-workers/cache-storage/window/cache-put.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/window/cache-put.https.html.ini index 53d3a17ede39..653135c64a8a 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/window/cache-put.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/window/cache-put.https.html.ini @@ -6,4 +6,4 @@ [Cache.put with a used response body] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158301 - expected: FAIL + diff --git a/testing/web-platform/meta/service-workers/cache-storage/window/cache-storage-match.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/window/cache-storage-match.https.html.ini index 06b1cfa7c992..b8d08b15d60b 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/window/cache-storage-match.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/window/cache-storage-match.https.html.ini @@ -2,4 +2,4 @@ type: testharness [CacheStorageMatch with no caches available but name provided] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158314 - expected: FAIL + diff --git a/testing/web-platform/meta/service-workers/cache-storage/window/cache-storage.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/window/cache-storage.https.html.ini index c2599cf9bc74..11d28f491f64 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/window/cache-storage.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/window/cache-storage.https.html.ini @@ -2,4 +2,4 @@ type: testharness [CacheStorage.open with existing cache] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158306 - expected: FAIL + diff --git a/testing/web-platform/meta/service-workers/cache-storage/window/sandboxed-iframes.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/window/sandboxed-iframes.https.html.ini index d2e6b2ab7a46..7f8edd628afa 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/window/sandboxed-iframes.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/window/sandboxed-iframes.https.html.ini @@ -1,4 +1,3 @@ [sandboxed-iframes.https.html] type: testharness bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158324 - expected: ERROR diff --git a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-add.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-add.https.html.ini index bf41585c09e1..1e6b582ca873 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-add.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-add.https.html.ini @@ -2,11 +2,9 @@ type: testharness [Cache.add called with non-HTTP/HTTPS URL] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158265 - expected: FAIL [Cache.add called with Request object] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 - expected: FAIL [Cache.add called with Request object with a used body] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 @@ -14,9 +12,7 @@ [Cache.add called twice with the same Request object] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 - expected: FAIL [Cache.addAll called with the same Request object specified twice] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158263 - expected: FAIL diff --git a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-delete.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-delete.https.html.ini index f06f2f745e0f..d5a20135c2ec 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-delete.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-delete.https.html.ini @@ -2,7 +2,6 @@ type: testharness [Cache.delete called with a Request object] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 - expected: FAIL [Cache.delete with a Request object containing used body] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 diff --git a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-match.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-match.https.html.ini index 6bafd0970b15..2e28cd3d7dee 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-match.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-match.https.html.ini @@ -2,13 +2,14 @@ type: testharness [Cache.matchAll with responses containing "Vary" header] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158292 - expected: FAIL + [Cache.match with responses containing "Vary" header] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158292 - expected: FAIL + [Cache.matchAll with "ignoreVary" parameter] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158292 - expected: FAIL + [Cache.match with Request containing non-empty body] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158262 expected: FAIL + diff --git a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-put.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-put.https.html.ini index 53d3a17ede39..653135c64a8a 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-put.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-put.https.html.ini @@ -6,4 +6,4 @@ [Cache.put with a used response body] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158301 - expected: FAIL + diff --git a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-storage-match.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-storage-match.https.html.ini index 06b1cfa7c992..b8d08b15d60b 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-storage-match.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-storage-match.https.html.ini @@ -2,4 +2,4 @@ type: testharness [CacheStorageMatch with no caches available but name provided] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158314 - expected: FAIL + diff --git a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-storage.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-storage.https.html.ini index c2599cf9bc74..11d28f491f64 100644 --- a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-storage.https.html.ini +++ b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-storage.https.html.ini @@ -2,4 +2,4 @@ type: testharness [CacheStorage.open with existing cache] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1158306 - expected: FAIL + diff --git a/testing/web-platform/meta/web-animations/animation-timeline/animation-timeline.html.ini b/testing/web-platform/meta/web-animations/animation-timeline/animation-timeline.html.ini deleted file mode 100644 index 5dcd4b12618a..000000000000 --- a/testing/web-platform/meta/web-animations/animation-timeline/animation-timeline.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[animation-timeline.html] - type: testharness - prefs: [dom.animations-api.core.enabled:true] diff --git a/testing/web-platform/meta/web-animations/animation-timeline/idlharness.html.ini b/testing/web-platform/meta/web-animations/animation-timeline/idlharness.html.ini index fa02d2cbe197..0a21d75b8645 100644 --- a/testing/web-platform/meta/web-animations/animation-timeline/idlharness.html.ini +++ b/testing/web-platform/meta/web-animations/animation-timeline/idlharness.html.ini @@ -4,6 +4,3 @@ [AnimationTimeline must be primary interface of document.timeline] expected: FAIL - [Stringification of document.timeline] - expected: FAIL - diff --git a/testing/web-platform/meta/workers/Worker_terminate_event_queue.htm.ini b/testing/web-platform/meta/workers/Worker_terminate_event_queue.htm.ini deleted file mode 100644 index 0f55fdcc75ed..000000000000 --- a/testing/web-platform/meta/workers/Worker_terminate_event_queue.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[Worker_terminate_event_queue.htm] - type: testharness - [ AbstractWorker terminate(): clear event queue ] - expected: FAIL - diff --git a/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/incoming-message.html.ini b/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/incoming-message.html.ini index aa08cb3adbea..2ac22957f95c 100644 --- a/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/incoming-message.html.ini +++ b/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/incoming-message.html.ini @@ -1,6 +1,5 @@ [incoming-message.html] type: testharness - expected: ERROR [close() and incoming message] expected: FAIL diff --git a/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/setInterval.html.ini b/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/setInterval.html.ini index 3a9fffd6d0af..d861d4edc276 100644 --- a/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/setInterval.html.ini +++ b/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/setInterval.html.ini @@ -1,3 +1,5 @@ [setInterval.html] type: testharness - expected: ERROR + [close() and setInterval] + expected: FAIL + diff --git a/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/setTimeout.html.ini b/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/setTimeout.html.ini new file mode 100644 index 000000000000..fc1d81fcb646 --- /dev/null +++ b/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/setTimeout.html.ini @@ -0,0 +1,5 @@ +[setTimeout.html] + type: testharness + [close() and setTimeout] + expected: FAIL + diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_createIndex6-event_order.htm b/testing/web-platform/tests/IndexedDB/idbobjectstore_createIndex6-event_order.htm index 20b4890382da..10c04b6a8a73 100644 --- a/testing/web-platform/tests/IndexedDB/idbobjectstore_createIndex6-event_order.htm +++ b/testing/web-platform/tests/IndexedDB/idbobjectstore_createIndex6-event_order.htm @@ -11,6 +11,9 @@ - - - - - -
diff --git a/testing/web-platform/tests/battery-status/battery-interface.js b/testing/web-platform/tests/battery-status/battery-interface.js index 7542dc965fbc..7aac89381297 100644 --- a/testing/web-platform/tests/battery-status/battery-interface.js +++ b/testing/web-platform/tests/battery-status/battery-interface.js @@ -144,13 +144,6 @@ assert_equals(battery.onchargingchange, null, desc); }, 'onchargingchange: treat array as null'); - test(function() { - var desc = 'onchargingchange did not treat noncallable host object as null'; - battery.onchargingchange = function() {}; - battery.onchargingchange = Node; - assert_equals(battery.onchargingchange, null, desc); - }, 'onchargingchange: treat non-callable host object as null'); - // attribute EventHandler onchargingtimechange; test(function() { @@ -214,13 +207,6 @@ assert_equals(battery.onchargingtimechange, null, desc); }, 'onchargingtimechange: treat array as null'); - test(function() { - var desc = 'onchargingtimechange did not treat noncallable host object as null'; - battery.onchargingtimechange = function() {}; - battery.onchargingtimechange = Node; - assert_equals(battery.onchargingtimechange, null, desc); - }, 'onchargingtimechange: treat non-callable host object as null'); - // attribute EventHandler ondischargingtimechange; test(function() { @@ -284,13 +270,6 @@ assert_equals(battery.ondischargingtimechange, null, desc); }, 'ondischargingtimechange: treat array as null'); - test(function() { - var desc = 'ondischargingtimechange did not treat noncallable host object as null'; - battery.ondischargingtimechange = function() {}; - battery.ondischargingtimechange = Node; - assert_equals(battery.ondischargingtimechange, null, desc); - }, 'ondischargingtimechange: treat non-callable host object as null'); - // attribute EventHandler onlevelchange; test(function() { @@ -354,13 +333,6 @@ assert_equals(battery.onlevelchange, null, desc); }, 'onlevelchange: treat array as null'); - test(function() { - var desc = 'onlevelchange did not treat noncallable host object as null'; - battery.onlevelchange = function() {}; - battery.onlevelchange = Node; - assert_equals(battery.onlevelchange, null, desc); - }, 'onlevelchange: treat non-callable host object as null'); - done(); }, function () {}); diff --git a/testing/web-platform/tests/dom/nodes/CharacterData-appendData.html b/testing/web-platform/tests/dom/nodes/CharacterData-appendData.html index 464a119a03df..d754218bfb1f 100644 --- a/testing/web-platform/tests/dom/nodes/CharacterData-appendData.html +++ b/testing/web-platform/tests/dom/nodes/CharacterData-appendData.html @@ -24,6 +24,14 @@ function testNode(create, type) { assert_equals(node.data, "test") }, type + ".appendData('')") + test(function() { + var node = create() + assert_equals(node.data, "test") + node.appendData(", append more 資料,測試資料"); + assert_equals(node.data, "test, append more 資料,測試資料"); + assert_equals(node.length, 25); + }, type + ".appendData(non-ASCII)") + test(function() { var node = create() assert_equals(node.data, "test") diff --git a/testing/web-platform/tests/dom/nodes/CharacterData-deleteData.html b/testing/web-platform/tests/dom/nodes/CharacterData-deleteData.html index 25654bafedc6..ef31a79dcf48 100644 --- a/testing/web-platform/tests/dom/nodes/CharacterData-deleteData.html +++ b/testing/web-platform/tests/dom/nodes/CharacterData-deleteData.html @@ -7,22 +7,79 @@
diff --git a/testing/web-platform/tests/dom/nodes/CharacterData-insertData.html b/testing/web-platform/tests/dom/nodes/CharacterData-insertData.html index a1f1b390c0ed..983e791dca2d 100644 --- a/testing/web-platform/tests/dom/nodes/CharacterData-insertData.html +++ b/testing/web-platform/tests/dom/nodes/CharacterData-insertData.html @@ -7,22 +7,74 @@
diff --git a/testing/web-platform/tests/dom/nodes/Text-splitText.html b/testing/web-platform/tests/dom/nodes/Text-splitText.html new file mode 100644 index 000000000000..aec1cee52ed5 --- /dev/null +++ b/testing/web-platform/tests/dom/nodes/Text-splitText.html @@ -0,0 +1,53 @@ + + +Text.splitText() + + + +
+ diff --git a/testing/web-platform/tests/encoding/api-invalid-label.html b/testing/web-platform/tests/encoding/api-invalid-label.html index 04f939a789d8..2f2fd6eb0c68 100644 --- a/testing/web-platform/tests/encoding/api-invalid-label.html +++ b/testing/web-platform/tests/encoding/api-invalid-label.html @@ -2,13 +2,32 @@ Encoding API: invalid label + diff --git a/testing/web-platform/tests/encoding/resources/text-plain-charset.py b/testing/web-platform/tests/encoding/resources/text-plain-charset.py new file mode 100644 index 000000000000..a1c07e70189b --- /dev/null +++ b/testing/web-platform/tests/encoding/resources/text-plain-charset.py @@ -0,0 +1,3 @@ +def main(request, response): + response.headers.set("Content-Type", "text/plain;charset=" + request.GET.first("label")) + response.content = "hello encoding" diff --git a/testing/web-platform/tests/encoding/single-byte-decoder.html b/testing/web-platform/tests/encoding/single-byte-decoder.html index 62e34cfb3069..eb463e1add8e 100644 --- a/testing/web-platform/tests/encoding/single-byte-decoder.html +++ b/testing/web-platform/tests/encoding/single-byte-decoder.html @@ -86,7 +86,7 @@ async_test(function(t) { var frame = document.createElement("iframe"), name = compatibility_names[encoding.name] || encoding.name; - frame.src = "resources/single-byte-raw.py?label=" + label + frame.src = "resources/text-plain-charset.py?label=" + label frame.onload = t.step_func_done(function() { assert_equals(frame.contentDocument.characterSet, name) assert_equals(frame.contentDocument.inputEncoding, name) diff --git a/testing/web-platform/tests/encoding/textdecoder-labels.html b/testing/web-platform/tests/encoding/textdecoder-labels.html index 5a56ff9cb6db..1f3202c963eb 100644 --- a/testing/web-platform/tests/encoding/textdecoder-labels.html +++ b/testing/web-platform/tests/encoding/textdecoder-labels.html @@ -4,22 +4,33 @@ diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-09.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-09.html new file mode 100644 index 000000000000..731d78cc14f3 --- /dev/null +++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/document.title-09.html @@ -0,0 +1,60 @@ + + + + +
+ diff --git a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-06.html b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-06.html index 1ff039915e1b..843acbd8dad1 100644 --- a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-06.html +++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-06.html @@ -63,9 +63,7 @@ test(function() { assert_equals(img2.id, "test4"); assert_false("test4" in document, '"test4" in document should be false'); - var collection = document.test4; - assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection"); - assert_array_equals(collection, [img1, img2]); + assert_equals(document.test4, undefined); }, "If there are two imgs, nothing should be returned. (id)"); test(function() { diff --git a/testing/web-platform/tests/html/dom/reflection.js b/testing/web-platform/tests/html/dom/reflection.js index 4010d7e30480..7bd41d516225 100644 --- a/testing/web-platform/tests/html/dom/reflection.js +++ b/testing/web-platform/tests/html/dom/reflection.js @@ -373,7 +373,7 @@ ReflectionTests.typeMap = { {toString:function() {return 2;}, valueOf: null}, {valueOf:function() {return 3;}}], "domExpected": function(val) { - var parsed = ReflectionTests.parseNonneg(val + ""); + var parsed = ReflectionTests.parseNonneg(String(val)); if (parsed === false || parsed > maxInt || parsed < minInt) { return null; } @@ -409,7 +409,7 @@ ReflectionTests.typeMap = { {toString:function() {return 2;}, valueOf: null}, {valueOf:function() {return 3;}}], "domExpected": function(val) { - var parsed = ReflectionTests.parseNonneg(val + ""); + var parsed = ReflectionTests.parseNonneg(String(val)); // Note maxInt, not maxUnsigned. if (parsed === false || parsed < 0 || parsed > maxInt) { return null; @@ -450,7 +450,7 @@ ReflectionTests.typeMap = { {toString:function() {return 2;}, valueOf: null}, {valueOf:function() {return 3;}}], "domExpected": function(val) { - var parsed = ReflectionTests.parseNonneg(val + ""); + var parsed = ReflectionTests.parseNonneg(String(val)); // Note maxInt, not maxUnsigned. if (parsed === false || parsed < 1 || parsed > maxInt) { return null; @@ -696,7 +696,7 @@ ReflectionTests.doReflects = function(data, idlName, idlObj, domName, domObj) { } try { domObj.setAttribute(domName, domTests[i]); - ReflectionHarness.test(domObj.getAttribute(domName), domTests[i] + "", "setAttribute() to " + ReflectionHarness.stringRep(domTests[i]) + " followed by getAttribute()"); + ReflectionHarness.test(domObj.getAttribute(domName), String(domTests[i]), "setAttribute() to " + ReflectionHarness.stringRep(domTests[i]) + " followed by getAttribute()"); ReflectionHarness.test(idlObj[idlName], domExpected[i], "setAttribute() to " + ReflectionHarness.stringRep(domTests[i]) + " followed by IDL get"); if (ReflectionHarness.catchUnexpectedExceptions) { ReflectionHarness.success(); diff --git a/testing/web-platform/tests/html/semantics/interfaces.html b/testing/web-platform/tests/html/semantics/interfaces.html index d3b2a23e27cd..c08fea82e523 100644 --- a/testing/web-platform/tests/html/semantics/interfaces.html +++ b/testing/web-platform/tests/html/semantics/interfaces.html @@ -9,22 +9,25 @@
diff --git a/testing/web-platform/tests/html/semantics/interfaces.js b/testing/web-platform/tests/html/semantics/interfaces.js index 5f5d2d908fab..3609e36bf483 100644 --- a/testing/web-platform/tests/html/semantics/interfaces.js +++ b/testing/web-platform/tests/html/semantics/interfaces.js @@ -134,5 +134,6 @@ var elements = [ ["dialog", "Dialog"], ["figcaption", ""], ["summary", ""], - ["track", "Track"] + ["track", "Track"], + ["foo", "Unknown"] ]; diff --git a/testing/web-platform/tests/media-source/mediasource-config-changes.js b/testing/web-platform/tests/media-source/mediasource-config-changes.js index 586e25f2b305..2ccc1e1a05d1 100644 --- a/testing/web-platform/tests/media-source/mediasource-config-changes.js +++ b/testing/web-platform/tests/media-source/mediasource-config-changes.js @@ -97,5 +97,5 @@ function mediaSourceConfigChangeTest(directory, idA, idB, description) }); }); }); - }, description, { timeout: 10000 } ); + }, description); }; diff --git a/testing/web-platform/tests/media-source/mediasource-duration.html b/testing/web-platform/tests/media-source/mediasource-duration.html index ca6b25de73fc..c0a33f3421c8 100644 --- a/testing/web-platform/tests/media-source/mediasource-duration.html +++ b/testing/web-platform/tests/media-source/mediasource-duration.html @@ -211,7 +211,7 @@ test.done(); }); }); - }, 'Test setting same duration multiple times does not fire duplicate durationchange', {timeout: 2500}); + }, 'Test setting same duration multiple times does not fire duplicate durationchange'); diff --git a/testing/web-platform/tests/media-source/mediasource-getvideoplaybackquality.html b/testing/web-platform/tests/media-source/mediasource-getvideoplaybackquality.html index d813d631f955..1c71968fc159 100644 --- a/testing/web-platform/tests/media-source/mediasource-getvideoplaybackquality.html +++ b/testing/web-platform/tests/media-source/mediasource-getvideoplaybackquality.html @@ -65,7 +65,7 @@ assert_greater_than(timeUpdateCount, 2, "timeUpdateCount"); test.done(); }); - }, "Test HTMLVideoElement.getVideoPlaybackQuality() with MediaSource API", {timeout: 5000}); + }, "Test HTMLVideoElement.getVideoPlaybackQuality() with MediaSource API"); diff --git a/testing/web-platform/tests/media-source/mediasource-play.html b/testing/web-platform/tests/media-source/mediasource-play.html index 73153462d770..928aa72f3317 100644 --- a/testing/web-platform/tests/media-source/mediasource-play.html +++ b/testing/web-platform/tests/media-source/mediasource-play.html @@ -38,7 +38,7 @@ mediaSource.endOfStream(); mediaElement.play(); }); - }, "Test normal playback case with MediaSource API", {timeout: 5000}); + }, "Test normal playback case with MediaSource API"); diff --git a/testing/web-platform/tests/media-source/mediasource-seek-during-pending-seek.html b/testing/web-platform/tests/media-source/mediasource-seek-during-pending-seek.html index b12903289dcc..4b415a6df2b5 100644 --- a/testing/web-platform/tests/media-source/mediasource-seek-during-pending-seek.html +++ b/testing/web-platform/tests/media-source/mediasource-seek-during-pending-seek.html @@ -63,7 +63,7 @@ test.done(); }); - }, 'Test seeking to a new location before transitioning beyond HAVE_METADATA.', {timeout: 10000} ); + }, 'Test seeking to a new location before transitioning beyond HAVE_METADATA.'); mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData) { @@ -136,7 +136,7 @@ assert_greater_than(mediaElement.readyState, mediaElement.HAVE_CURRENT_DATA, 'Greater than HAVE_CURRENT_DATA'); test.done(); }); - }, 'Test seeking to a new location during a pending seek.', {timeout: 10000} ); + }, 'Test seeking to a new location during a pending seek.'); diff --git a/testing/web-platform/tests/media-source/mediasource-util.js b/testing/web-platform/tests/media-source/mediasource-util.js index de1560e17ba0..0271fc5caee7 100644 --- a/testing/web-platform/tests/media-source/mediasource-util.js +++ b/testing/web-platform/tests/media-source/mediasource-util.js @@ -1,6 +1,4 @@ (function(window) { - setup({ timeout: 12000 }); - var SEGMENT_INFO_LIST = [ { url: 'mp4/test.mp4', diff --git a/testing/web-platform/tests/resources/idlharness.js b/testing/web-platform/tests/resources/idlharness.js index 170b5835392d..b8a87c669900 100644 --- a/testing/web-platform/tests/resources/idlharness.js +++ b/testing/web-platform/tests/resources/idlharness.js @@ -555,8 +555,8 @@ function IdlInterface(obj, is_callback) { this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); }); if (this.has_extended_attribute("Unforgeable")) { this.members - .filter(m => !m["static"] && (m.type == "attribute" || m.type == "operation")) - .forEach(m => m.isUnforgeable = true); + .filter(function(m) { return !m["static"] && (m.type == "attribute" || m.type == "operation"); }) + .forEach(function(m) { return m.isUnforgeable = true; }); } /** diff --git a/testing/web-platform/tests/resources/testharness.js b/testing/web-platform/tests/resources/testharness.js index 67b0f406c08f..981938cbc23d 100644 --- a/testing/web-platform/tests/resources/testharness.js +++ b/testing/web-platform/tests/resources/testharness.js @@ -368,8 +368,20 @@ policies and contribution forms [3]. self.addEventListener("message", function(event) { if (event.data.type && event.data.type === "connect") { - this_obj._add_message_port(event.ports[0]); - event.ports[0].start(); + if (event.ports && event.ports[0]) { + // If a MessageChannel was passed, then use it to + // send results back to the main window. This + // allows the tests to work even if the browser + // does not fully support MessageEvent.source in + // ServiceWorkers yet. + this_obj._add_message_port(event.ports[0]); + event.ports[0].start(); + } else { + // If there is no MessageChannel, then attempt to + // use the MessageEvent.source to send results + // back to the main window. + this_obj._add_message_port(event.source); + } } }); @@ -1476,15 +1488,24 @@ policies and contribution forms [3]. var message_port; if (is_service_worker(worker)) { - // The ServiceWorker's implicit MessagePort is currently not - // reliably accessible from the ServiceWorkerGlobalScope due to - // Blink setting MessageEvent.source to null for messages sent via - // ServiceWorker.postMessage(). Until that's resolved, create an - // explicit MessageChannel and pass one end to the worker. - var message_channel = new MessageChannel(); - message_port = message_channel.port1; - message_port.start(); - worker.postMessage({type: "connect"}, [message_channel.port2]); + if (window.MessageChannel) { + // The ServiceWorker's implicit MessagePort is currently not + // reliably accessible from the ServiceWorkerGlobalScope due to + // Blink setting MessageEvent.source to null for messages sent + // via ServiceWorker.postMessage(). Until that's resolved, + // create an explicit MessageChannel and pass one end to the + // worker. + var message_channel = new MessageChannel(); + message_port = message_channel.port1; + message_port.start(); + worker.postMessage({type: "connect"}, [message_channel.port2]); + } else { + // If MessageChannel is not available, then try the + // ServiceWorker.postMessage() approach using MessageEvent.source + // on the other end. + message_port = navigator.serviceWorker; + worker.postMessage({type: "connect"}); + } } else if (is_shared_worker(worker)) { message_port = worker.port; } else { diff --git a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-add.js b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-add.js index 3e827382bd14..49b8db4cd710 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-add.js +++ b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-add.js @@ -22,12 +22,12 @@ cache_test(function(cache) { cache_test(function(cache) { return assert_promise_rejects( cache.add('javascript://this-is-not-http-mmkay'), - 'NetworkError', - 'Cache.add should throw a NetworkError for non-HTTP/HTTPS URLs.'); + new TypeError(), + 'Cache.add should throw a TypeError for non-HTTP/HTTPS URLs.'); }, 'Cache.add called with non-HTTP/HTTPS URL'); cache_test(function(cache) { - var request = new Request('../resources/simple.txt', {method: 'POST', body: 'Hello'}); + var request = new Request('../resources/simple.txt'); return cache.add(request) .then(function(result) { assert_equals(result, undefined, @@ -36,28 +36,18 @@ cache_test(function(cache) { }, 'Cache.add called with Request object'); cache_test(function(cache) { - var request = new Request('../resources/simple.txt', {method: 'POST', body: 'Hello'}); - return request.text() - .then(function() { - assert_false(request.bodyUsed); - }) - .then(function() { - return cache.add(request); - }); - }, 'Cache.add called with Request object with a used body'); - -cache_test(function(cache) { - var request = new Request('../resources/simple.txt', {method: 'POST', body: 'Hello'}); + var request = new Request('../resources/simple.txt'); return cache.add(request) .then(function(result) { assert_equals(result, undefined, 'Cache.add should resolve with undefined on success.'); }) .then(function() { - return assert_promise_rejects( - cache.add(request), - new TypeError(), - 'Cache.add should throw TypeError if same request is added twice.'); + return cache.add(request); + }) + .then(function(result) { + assert_equals(result, undefined, + 'Cache.add should resolve with undefined on success.'); }); }, 'Cache.add called twice with the same Request object'); @@ -137,8 +127,8 @@ cache_test(function(cache) { var request = new Request('../resources/simple.txt'); return assert_promise_rejects( cache.addAll([request, request]), - new TypeError(), - 'Cache.addAll should throw TypeError if the same request is added ' + + 'InvalidStateError', + 'Cache.addAll should throw InvalidStateError if the same request is added ' + 'twice.'); }, 'Cache.addAll called with the same Request object specified twice'); diff --git a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-delete.js b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-delete.js index 36ca8473164c..75a474c2b2c9 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-delete.js +++ b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-delete.js @@ -43,8 +43,8 @@ cache_test(function(cache) { }, 'Cache.delete called with a string URL'); cache_test(function(cache) { - var request = new Request(test_url, { method: 'POST', body: 'Abc' }); - return cache.put(request.clone(), new_test_response()) + var request = new Request(test_url); + return cache.put(request, new_test_response()) .then(function() { return cache.delete(request); }) @@ -52,33 +52,9 @@ cache_test(function(cache) { assert_true(result, 'Cache.delete should resolve with "true" if an entry ' + 'was successfully deleted.'); - assert_false(request.bodyUsed, - 'Cache.delete should not consume request body.'); }); }, 'Cache.delete called with a Request object'); -cache_test(function(cache) { - var request = new Request(test_url, { method: 'POST', body: 'Abc' }); - return cache.put(request.clone(), new_test_response()) - .then(function() { - return request.text(); - }) - .then(function() { - assert_true(request.bodyUsed, - '[https://fetch.spec.whatwg.org/#body-mixin] ' + - 'Request.bodyUsed should be true after text() method ' + - 'resolves.'); - }) - .then(function() { - return cache.delete(request); - }) - .then(function(result) { - assert_true(result, - 'Cache.delete should resolve with "true" if an entry ' + - 'was successfully deleted.'); - }); - }, 'Cache.delete with a Request object containing used body'); - cache_test(function(cache) { return cache.delete(test_url) .then(function(result) { diff --git a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-match.js b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-match.js index 4a027b6f1653..8d3e5ee68599 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-match.js +++ b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-match.js @@ -103,14 +103,6 @@ var vary_entries = [ request: new Request('http://example.com/c'), response: new Response('', {headers: {'Vary': 'Cookies'}}) - }, - - { - name: 'vary_wildcard', - request: new Request('http://example.com/c', - {headers: {'Cookies': 'x', 'X-Key': '1'}}), - response: new Response('', - {headers: {'Vary': '*'}}) } ]; @@ -181,32 +173,6 @@ prepopulated_cache_test(simple_entries, function(cache, entries) { }); }, 'Cache.match with new Request'); -cache_test(function(cache) { - var request = new Request('https://example.com/foo', { - method: 'POST', - body: 'Hello world!' - }); - var response = new Response('Booyah!', { - status: 200, - headers: {'Content-Type': 'text/plain'} - }); - - return cache.put(request.clone(), response.clone()) - .then(function() { - assert_false( - request.bodyUsed, - '[https://fetch.spec.whatwg.org/#concept-body-used-flag] ' + - 'Request.bodyUsed flag should be initially false.'); - }) - .then(function() { - return cache.match(request); - }) - .then(function(result) { - assert_false(request.bodyUsed, - 'Cache.match should not consume Request body.'); - }); - }, 'Cache.match with Request containing non-empty body'); - prepopulated_cache_test(simple_entries, function(cache, entries) { return cache.matchAll(entries.a.request, {ignoreSearch: true}) @@ -337,7 +303,6 @@ prepopulated_cache_test(vary_entries, function(cache, entries) { assert_array_equivalent( result, [ - entries.vary_wildcard.response, entries.vary_cookie_absent.response ], 'Cache.matchAll should exclude matches if a vary header is ' + @@ -354,7 +319,6 @@ prepopulated_cache_test(vary_entries, function(cache, entries) { assert_array_equivalent( result, [ - entries.vary_wildcard.response ], 'Cache.matchAll should exclude matches if a vary header is ' + 'missing in the cached request, but is present in the query ' + @@ -381,7 +345,6 @@ prepopulated_cache_test(vary_entries, function(cache, entries) { assert_object_in_array( result, [ - entries.vary_wildcard.response, entries.vary_cookie_absent.response ], 'Cache.match should honor "Vary" header.'); @@ -398,7 +361,6 @@ prepopulated_cache_test(vary_entries, function(cache, entries) { entries.vary_cookie_is_cookie.response, entries.vary_cookie_is_good.response, entries.vary_cookie_absent.response, - entries.vary_wildcard.response ], 'Cache.matchAll should honor "ignoreVary" parameter.'); }); @@ -464,6 +426,15 @@ cache_test(function(cache) { }); }, 'Cache.match invoked multiple times for the same Request/Response'); +prepopulated_cache_test(simple_entries, function(cache, entries) { + var request = new Request(entries.a.request, { method: 'POST' }); + return cache.match(request) + .then(function(result) { + assert_equals(result, undefined, + 'Cache.match should not find a match'); + }); + }, 'Cache.match with POST Request'); + // Helpers --- // Run |test_function| with a Cache object as its only parameter. Prior to the diff --git a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-put.js b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-put.js index ec1617f04e9a..eeedf80eb4b5 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-put.js +++ b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-put.js @@ -67,29 +67,6 @@ cache_test(function(cache) { }); }, 'Cache.put with Response without a body'); -cache_test(function(cache) { - var request = new Request(test_url, { - method: 'POST', - body: 'Hello' - }); - var response = new Response(test_body); - assert_false(request.bodyUsed, - '[https://fetch.spec.whatwg.org/#dom-body-bodyused] ' + - 'Request.bodyUsed should be initially false.'); - return cache.put(request, response.clone()) - .then(function() { - assert_true(request.bodyUsed, - 'Cache.put should consume Request body.'); - }) - .then(function() { - return cache.match(request); - }) - .then(function(result) { - assert_object_equals(result, response, - 'Cache.put should store response body.'); - }); - }, 'Cache.put with Request containing a body'); - cache_test(function(cache) { var request = new Request(test_url); var response = new Response(test_body); @@ -294,18 +271,11 @@ cache_test(function(cache) { cache_test(function(cache) { var request = new Request(test_url, {method: 'POST', body: test_body}); - assert_false(request.bodyUsed, - '[https://fetch.spec.whatwg.org/#dom-body-bodyused] ' + - 'Request.bodyUsed should be initially false.'); - var copy = new Request(request); - assert_true(request.bodyUsed, - '[https://fetch.spec.whatwg.org/#dom-request] ' + - 'Request constructor should set input\'s used flag.'); return assert_promise_rejects( cache.put(request, new Response(test_body)), new TypeError(), - 'Cache.put should throw a TypeError for a request with used body.'); - }, 'Cache.put with a used request body'); + 'Cache.put should throw a TypeError for a POST request.'); + }, 'Cache.put with a POST request'); cache_test(function(cache) { var response = new Response(test_body); @@ -313,12 +283,35 @@ cache_test(function(cache) { '[https://fetch.spec.whatwg.org/#dom-body-bodyused] ' + 'Response.bodyUsed should be initially false.'); return response.text().then(function() { - assert_false( + assert_true( response.bodyUsed, '[https://fetch.spec.whatwg.org/#concept-body-consume-body] ' + - 'The text() method should not set "body passed" flag.'); - return cache.put(new Request(test_url), response); - }); + 'The text() method should set "body used" flag.'); + return assert_promise_rejects( + cache.put(new Request(test_url), response), + new TypeError, + '[https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#cache-put] ' + + 'Cache put should reject with TypeError when Response ' + + 'body is already used.'); + }); }, 'Cache.put with a used response body'); +cache_test(function(cache) { + return assert_promise_rejects( + cache.put(new Request(test_url), + new Response(test_body, { headers: { VARY: '*' }})), + new TypeError(), + 'Cache.put should reject VARY:* Responses with a TypeError.'); + }, 'Cache.put with a VARY:* Response'); + +cache_test(function(cache) { + return assert_promise_rejects( + cache.put(new Request(test_url), + new Response(test_body, + { headers: { VARY: 'Accept-Language,*' }})), + new TypeError(), + 'Cache.put should reject Responses with an embedded VARY:* with a ' + + 'TypeError.'); + }, 'Cache.put with an embedded VARY:* Response'); + done(); diff --git a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage-match.js b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage-match.js index 06b2888e891c..0de85146a6a7 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage-match.js +++ b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage-match.js @@ -111,8 +111,11 @@ promise_test(function(test) { return self.caches.match(transaction.request, {cacheName: 'foo'}); }) .then(function(response) { - assert_equals(response, undefined, - 'The response should not be found.'); + assert_unreached('The match with bad cache name should reject.'); + }) + .catch(function(err) { + assert_equals(err.name, 'NotFoundError', + 'The match should reject with NotFoundError.'); return self.caches.has('foo'); }) .then(function(has_foo) { diff --git a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage.js b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage.js index cb8e0483b221..a8d4e7e61de4 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage.js +++ b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage.js @@ -106,6 +106,7 @@ promise_test(function(t) { promise_test(function(t) { var cache_name = 'cache-storage/open'; + var url = '../resources/simple.txt'; var cache; return self.caches.delete(cache_name) .then(function() { @@ -115,21 +116,26 @@ promise_test(function(t) { cache = result; }) .then(function() { - return self.caches.open(cache_name); - }) - .then(function(result) { - assert_equals(result, cache, - 'CacheStorage.open should return the named Cache ' + - 'object if it exists.'); + return cache.add('../resources/simple.txt'); }) .then(function() { return self.caches.open(cache_name); }) .then(function(result) { - assert_equals(result, cache, - 'CacheStorage.open should return the same ' + - 'instance of an existing Cache object.'); - }); + assert_true(result instanceof Cache, + 'CacheStorage.open should return a Cache object'); + assert_not_equals(result, cache, + 'CacheStorage.open should return a new Cache ' + + 'object each time its called.'); + return Promise.all([cache.keys(), result.keys()]); + }) + .then(function(results) { + var expected_urls = results[0].map(function(r) { return r.url }); + var actual_urls = results[1].map(function(r) { return r.url }); + assert_array_equals(actual_urls, expected_urls, + 'CacheStorage.open should return a new Cache ' + + 'object for the same backing store.'); + }) }, 'CacheStorage.open with existing cache'); promise_test(function(t) { diff --git a/testing/web-platform/tests/service-workers/cache-storage/window/sandboxed-iframes.https.html b/testing/web-platform/tests/service-workers/cache-storage/window/sandboxed-iframes.https.html index aa8613ec77db..de70db4e1371 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/window/sandboxed-iframes.https.html +++ b/testing/web-platform/tests/service-workers/cache-storage/window/sandboxed-iframes.https.html @@ -23,7 +23,7 @@ function wait_for_message(id) { self.addEventListener('message', function listener(e) { if (e.data.id === id) { resolve(e.data); - self.removeEventListener(listener); + self.removeEventListener('message', listener); } }); }); diff --git a/testing/web-platform/tests/web-animations/animation-timeline/animation-timeline.html b/testing/web-platform/tests/web-animations/animation-timeline/document-timeline.html similarity index 98% rename from testing/web-platform/tests/web-animations/animation-timeline/animation-timeline.html rename to testing/web-platform/tests/web-animations/animation-timeline/document-timeline.html index edb0c84553ed..e7ab1687a148 100644 --- a/testing/web-platform/tests/web-animations/animation-timeline/animation-timeline.html +++ b/testing/web-platform/tests/web-animations/animation-timeline/document-timeline.html @@ -1,6 +1,6 @@ -Web Animations API: AnimationTimeline tests +Web Animations API: DocumentTimeline tests
diff --git a/testing/web-platform/tests/web-animations/animation-timeline/idlharness.html b/testing/web-platform/tests/web-animations/animation-timeline/idlharness.html index 440ebb4e0e70..9a489086a171 100644 --- a/testing/web-platform/tests/web-animations/animation-timeline/idlharness.html +++ b/testing/web-platform/tests/web-animations/animation-timeline/idlharness.html @@ -1,15 +1,18 @@ -Web Animations API: AnimationTimeline tests +Web Animations API: DocumentTimeline tests
-
diff --git a/testing/web-platform/tests/workers/interfaces/WorkerGlobalScope/close/incoming-message.html b/testing/web-platform/tests/workers/interfaces/WorkerGlobalScope/close/incoming-message.html index 767693da02e3..d65695632bc1 100644 --- a/testing/web-platform/tests/workers/interfaces/WorkerGlobalScope/close/incoming-message.html +++ b/testing/web-platform/tests/workers/interfaces/WorkerGlobalScope/close/incoming-message.html @@ -12,23 +12,15 @@ close();
- - diff --git a/testing/web-platform/tests/workers/support/WorkerTerminate.js b/testing/web-platform/tests/workers/support/WorkerTerminate.js index 03e028746da6..7c99e7ec5605 100644 --- a/testing/web-platform/tests/workers/support/WorkerTerminate.js +++ b/testing/web-platform/tests/workers/support/WorkerTerminate.js @@ -2,9 +2,9 @@ onmessage = function(evt) { for (var i=0; true; i++) { - if (i%1000 == 1) + if (i%1000 == 0) { postMessage(i); } } -} \ No newline at end of file +} diff --git a/toolkit/components/search/nsSearchService.js b/toolkit/components/search/nsSearchService.js index 94e964417dbc..21059430d6de 100644 --- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -3304,6 +3304,10 @@ SearchService.prototype = { return this.getEngineByName(defaultEngine); }, + resetToOriginalDefaultEngine: function SRCH_SVC__resetToOriginalDefaultEngine() { + this.defaultEngine = this._originalDefaultEngine; + }, + _buildCache: function SRCH_SVC__buildCache() { if (!getBoolPref(BROWSER_SEARCH_PREF + "cache.enabled", true)) return; diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 34dee8d44f09..1d3e4ac001fd 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -4052,178 +4052,103 @@ "description": "Deleted or to-be-reused innerwindow which has had mutation event listeners." }, "CHARSET_OVERRIDE_SITUATION": { - "expires_in_version": "default", + "expires_in_version": "never", "kind": "enumerated", "n_values": 8, "description": "Labeling status of top-level page when overriding charset (0: unlabeled file URL without detection, 1: unlabeled non-TLD-guessed non-file URL without detection, 2: unlabeled file URL with detection, 3: unlabeled non-file URL with detection, 4: labeled, 5: already overridden, 6: bug, 7: unlabeled with TLD guessing)" }, "CHARSET_OVERRIDE_USED": { - "expires_in_version": "default", + "expires_in_version": "never", "kind": "flag", "description": "Whether the character encoding menu was used to override an encoding in this session." }, - "DECODER_INSTANTIATED_HZ": { - "expires_in_version": "default", - "kind": "flag", - "description": "Whether the decoder for HZ has been instantiated in this session." - }, - "DECODER_INSTANTIATED_ISO2022CN": { - "expires_in_version": "40", - "kind": "flag", - "description": "Whether the decoder for ISO-2022-CN has been instantiated in this session." - }, - "DECODER_INSTANTIATED_ISO2022KR": { - "expires_in_version": "40", - "kind": "flag", - "description": "Whether the decoder for ISO-2022-KR has been instantiated in this session." - }, "DECODER_INSTANTIATED_ISO2022JP": { - "expires_in_version": "default", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for ISO-2022-JP has been instantiated in this session." }, - "DECODER_INSTANTIATED_JOHAB": { - "expires_in_version": "32", - "kind": "flag", - "description": "Whether the decoder for JOHAB has been instantiated in this session. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "DECODER_INSTANTIATED_T61": { - "expires_in_version": "32", - "kind": "flag", - "description": "Whether the decoder for T.61 has been instantiated in this session. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "DECODER_INSTANTIATED_IBM850": { - "expires_in_version": "38", - "kind": "flag", - "description": "Whether the decoder for IBM850 has been instantiated in this session." - }, - "DECODER_INSTANTIATED_IBM852": { - "expires_in_version": "38", - "kind": "flag", - "description": "Whether the decoder for IBM852 has been instantiated in this session." - }, - "DECODER_INSTANTIATED_IBM855": { - "expires_in_version": "38", - "kind": "flag", - "description": "Whether the decoder for IBM855 has been instantiated in this session." - }, - "DECODER_INSTANTIATED_IBM857": { - "expires_in_version": "38", - "kind": "flag", - "description": "Whether the decoder for IBM857 has been instantiated in this session." - }, - "DECODER_INSTANTIATED_IBM862": { - "expires_in_version": "38", - "kind": "flag", - "description": "Whether the decoder for IBM862 has been instantiated in this session." - }, "DECODER_INSTANTIATED_IBM866": { "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for IBM866 has been instantiated in this session." }, "DECODER_INSTANTIATED_MACGREEK": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACGREEK has been instantiated in this session." }, "DECODER_INSTANTIATED_MACICELANDIC": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACICELANDIC has been instantiated in this session." }, "DECODER_INSTANTIATED_MACCE": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACCE has been instantiated in this session." }, "DECODER_INSTANTIATED_MACHEBREW": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACHEBREW has been instantiated in this session." }, "DECODER_INSTANTIATED_MACARABIC": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACARABIC has been instantiated in this session." }, "DECODER_INSTANTIATED_MACFARSI": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACFARSI has been instantiated in this session." }, "DECODER_INSTANTIATED_MACCROATIAN": { - "expires_in_version": "default", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACCROATIAN has been instantiated in this session." }, "DECODER_INSTANTIATED_MACCYRILLIC": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACCYRILLIC has been instantiated in this session." }, "DECODER_INSTANTIATED_MACROMANIAN": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACROMANIAN has been instantiated in this session." }, "DECODER_INSTANTIATED_MACTURKISH": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACTURKISH has been instantiated in this session." }, "DECODER_INSTANTIATED_MACDEVANAGARI": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACDEVANAGARI has been instantiated in this session." }, "DECODER_INSTANTIATED_MACGUJARATI": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACGUJARATI has been instantiated in this session." }, "DECODER_INSTANTIATED_MACGURMUKHI": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for MACGURMUKHI has been instantiated in this session." }, - "DECODER_INSTANTIATED_ISOIR111": { - "expires_in_version": "38", - "kind": "flag", - "description": "Whether the decoder for ISOIR111 has been instantiated in this session." - }, - "DECODER_INSTANTIATED_ARMSCII8": { - "expires_in_version": "default", - "kind": "flag", - "description": "Whether the decoder for ARMSCII8 has been instantiated in this session." - }, - "DECODER_INSTANTIATED_VISCII": { - "expires_in_version": "40", - "kind": "flag", - "description": "Whether the decoder for VISCII has been instantiated in this session." - }, - "DECODER_INSTANTIATED_VIETTCVN5712": { - "expires_in_version": "40", - "kind": "flag", - "description": "Whether the decoder for VIETTCVN5712 has been instantiated in this session." - }, - "DECODER_INSTANTIATED_VIETVPS": { - "expires_in_version": "40", - "kind": "flag", - "description": "Whether the decoder for VIETVPS has been instantiated in this session." - }, "DECODER_INSTANTIATED_KOI8R": { - "expires_in_version": "default", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for KOI8R has been instantiated in this session." }, "DECODER_INSTANTIATED_KOI8U": { - "expires_in_version": "40", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for KOI8U has been instantiated in this session." }, "DECODER_INSTANTIATED_ISO_8859_5": { - "expires_in_version": "default", + "expires_in_version": "never", "kind": "flag", "description": "Whether the decoder for ISO-8859-5 has been instantiated in this session." }, diff --git a/toolkit/content/tests/chrome/chrome.ini b/toolkit/content/tests/chrome/chrome.ini index 4de31c8d0747..51da9f11c98d 100644 --- a/toolkit/content/tests/chrome/chrome.ini +++ b/toolkit/content/tests/chrome/chrome.ini @@ -85,6 +85,7 @@ skip-if = os == "win" # Intermittent failures, bug 919016 [test_bug570192.xul] [test_bug585946.xul] [test_bug624329.xul] +skip-if = (os == 'mac' && os_version == '10.10') # Unexpectedly perma-passes on OSX 10.10 [test_bug792324.xul] [test_bug1048178.xul] skip-if = toolkit == "cocoa" diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index 5b5f3155c653..2a7fa5f328e4 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -466,6 +466,14 @@ public: virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint, nsIObserver* aObserver) override { return SynthesizeNativeMouseEvent(aPoint, NSMouseMoved, 0, aObserver); } + virtual nsresult SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint, + uint32_t aNativeMessage, + double aDeltaX, + double aDeltaY, + double aDeltaZ, + uint32_t aModifierFlags, + uint32_t aAdditionalFlags, + nsIObserver* aObserver) override; // Mac specific methods diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 98da068af409..b4ec2eac8d8e 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -91,6 +91,7 @@ #include "InputData.h" #include "VibrancyManager.h" #include "nsNativeThemeCocoa.h" +#include "nsIDOMWindowUtils.h" using namespace mozilla; using namespace mozilla::layers; @@ -1185,6 +1186,42 @@ nsresult nsChildView::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint, NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +nsresult nsChildView::SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint, + uint32_t aNativeMessage, + double aDeltaX, + double aDeltaY, + double aDeltaZ, + uint32_t aModifierFlags, + uint32_t aAdditionalFlags, + nsIObserver* aObserver) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + AutoObserverNotifier notifier(aObserver, "mousescrollevent"); + + NSPoint pt = + nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor()); + + // Move the mouse cursor to the requested position and reconnect it to the mouse. + CGWarpMouseCursorPosition(NSPointToCGPoint(pt)); + CGAssociateMouseAndMouseCursorPosition(true); + + // Mostly copied from http://stackoverflow.com/a/6130349 + CGScrollEventUnit units = + (aAdditionalFlags & nsIDOMWindowUtils::MOUSESCROLL_SCROLL_LINES) + ? kCGScrollEventUnitLine : kCGScrollEventUnitPixel; + CGEventRef cgEvent = CGEventCreateScrollWheelEvent(NULL, units, 3, aDeltaY, aDeltaX, aDeltaZ); + if (!cgEvent) { + return NS_ERROR_FAILURE; + } + + CGEventPost(kCGHIDEventTap, cgEvent); + CFRelease(cgEvent); + return NS_OK; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + // First argument has to be an NSMenu representing the application's top-level // menu bar. The returned item is *not* retained. static NSMenuItem* NativeMenuItemWithLocation(NSMenu* menubar, NSString* locationString) diff --git a/xpcom/glue/pldhash.cpp b/xpcom/glue/pldhash.cpp index dea48fed2eb0..64982c7bb531 100644 --- a/xpcom/glue/pldhash.cpp +++ b/xpcom/glue/pldhash.cpp @@ -266,6 +266,41 @@ PL_DHashTableInit(PLDHashTable* aTable, const PLDHashTableOps* aOps, aTable->Init(aOps, aEntrySize, aLength); } +PLDHashTable& PLDHashTable::operator=(PLDHashTable&& aOther) +{ + if (this == &aOther) { + return *this; + } + + // Destruct |this|. + Finish(); + + // Move pieces over. + mOps = Move(aOther.mOps); + mHashShift = Move(aOther.mHashShift); + mEntrySize = Move(aOther.mEntrySize); + mEntryCount = Move(aOther.mEntryCount); + mRemovedCount = Move(aOther.mRemovedCount); + mGeneration = Move(aOther.mGeneration); + mEntryStore = Move(aOther.mEntryStore); +#ifdef PL_DHASHMETER + mStats = Move(aOther.mStats); +#endif +#ifdef DEBUG + // Atomic<> doesn't have an |operator=(Atomic<>&&)|. + mRecursionLevel = uint32_t(aOther.mRecursionLevel); +#endif + + // Clear up |aOther| so its destruction will be a no-op. + aOther.mOps = nullptr; + aOther.mEntryStore = nullptr; +#ifdef DEBUG + aOther.mRecursionLevel = 0; +#endif + + return *this; +} + /* * Double hashing needs the second hash code to be relatively prime to table * size, so we simply make hash2 odd. @@ -305,7 +340,10 @@ PLDHashTable::EntryIsFree(PLDHashEntryHdr* aEntry) MOZ_ALWAYS_INLINE void PLDHashTable::Finish() { - MOZ_ASSERT(IsInitialized()); + if (!IsInitialized()) { + MOZ_ASSERT(!mEntryStore); + return; + } INCREMENT_RECURSION_LEVEL(this); diff --git a/xpcom/glue/pldhash.h b/xpcom/glue/pldhash.h index 326a9cc25cb1..8588ec06f26f 100644 --- a/xpcom/glue/pldhash.h +++ b/xpcom/glue/pldhash.h @@ -223,32 +223,18 @@ public: #endif {} - PLDHashTable(PLDHashTable&& aOther) { *this = mozilla::Move(aOther); } - - PLDHashTable& operator=(PLDHashTable&& aOther) - { - using mozilla::Move; - - mOps = Move(aOther.mOps); - mHashShift = Move(aOther.mHashShift); - mEntrySize = Move(aOther.mEntrySize); - mEntryCount = Move(aOther.mEntryCount); - mRemovedCount = Move(aOther.mRemovedCount); - mGeneration = Move(aOther.mGeneration); - mEntryStore = Move(aOther.mEntryStore); - -#ifdef PL_DHASHMETER - mStats = Move(aOther.mStats); -#endif - + PLDHashTable(PLDHashTable&& aOther) + : mOps(nullptr) + , mEntryStore(nullptr) #ifdef DEBUG - // Atomic<> doesn't have an |operator=(Atomic<>&&)|. - mRecursionLevel = uint32_t(aOther.mRecursionLevel); + , mRecursionLevel(0) #endif - - return *this; + { + *this = mozilla::Move(aOther); } + PLDHashTable& operator=(PLDHashTable&& aOther); + bool IsInitialized() const { return !!mOps; } // These should be used rarely. diff --git a/xpcom/tests/TestPLDHash.cpp b/xpcom/tests/TestPLDHash.cpp index cc511b663fdd..9ba7db413f5f 100644 --- a/xpcom/tests/TestPLDHash.cpp +++ b/xpcom/tests/TestPLDHash.cpp @@ -112,21 +112,73 @@ static bool test_pldhash_lazy_storage() return true; } -// See bug 931062, we skip this test on Android due to OOM. -#ifndef MOZ_WIDGET_ANDROID -// We insert the integers 0.., so this is has function is (a) as simple as +// We insert the integers 0.., so this hash function is (a) as simple as // possible, and (b) collision-free. Both of which are good, because we want // this test to be as fast as possible. static PLDHashNumber -hash(PLDHashTable *table, const void *key) +trivial_hash(PLDHashTable *table, const void *key) { return (PLDHashNumber)(size_t)key; } +static bool test_pldhash_move_semantics() +{ + static const PLDHashTableOps ops = { + trivial_hash, + PL_DHashMatchEntryStub, + PL_DHashMoveEntryStub, + PL_DHashClearEntryStub, + nullptr + }; + + PLDHashTable t1, t2; + PL_DHashTableInit(&t1, &ops, sizeof(PLDHashEntryStub)); + PL_DHashTableAdd(&t1, (const void*)88); + PL_DHashTableInit(&t2, &ops, sizeof(PLDHashEntryStub)); + PL_DHashTableAdd(&t2, (const void*)99); + + t1 = mozilla::Move(t1); // self-move + + t1 = mozilla::Move(t2); // inited overwritten with inited + + PL_DHashTableFinish(&t1); + PL_DHashTableFinish(&t2); + + PLDHashTable t3, t4; + PL_DHashTableInit(&t3, &ops, sizeof(PLDHashEntryStub)); + PL_DHashTableAdd(&t3, (const void*)88); + + t3 = mozilla::Move(t4); // inited overwritten with uninited + + PL_DHashTableFinish(&t3); + PL_DHashTableFinish(&t4); + + PLDHashTable t5, t6; + PL_DHashTableInit(&t6, &ops, sizeof(PLDHashEntryStub)); + PL_DHashTableAdd(&t6, (const void*)88); + + t5 = mozilla::Move(t6); // uninited overwritten with inited + + PL_DHashTableFinish(&t5); + PL_DHashTableFinish(&t6); + + PLDHashTable t7; + PLDHashTable t8(mozilla::Move(t7)); // new table constructed with uninited + + PLDHashTable t9; + PL_DHashTableInit(&t9, &ops, sizeof(PLDHashEntryStub)); + PL_DHashTableAdd(&t9, (const void*)88); + PLDHashTable t10(mozilla::Move(t9)); // new table constructed with inited + + return true; +} + +// See bug 931062, we skip this test on Android due to OOM. +#ifndef MOZ_WIDGET_ANDROID static bool test_pldhash_grow_to_max_capacity() { static const PLDHashTableOps ops = { - hash, + trivial_hash, PL_DHashMatchEntryStub, PL_DHashMoveEntryStub, PL_DHashClearEntryStub, @@ -174,6 +226,7 @@ static const struct Test { } tests[] = { DECL_TEST(test_pldhash_Init_capacity_ok), DECL_TEST(test_pldhash_lazy_storage), + DECL_TEST(test_pldhash_move_semantics), // See bug 931062, we skip this test on Android due to OOM. #ifndef MOZ_WIDGET_ANDROID DECL_TEST(test_pldhash_grow_to_max_capacity),