mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 22:55:23 +00:00
Merge mozilla-central to b2g-inbound
This commit is contained in:
commit
b187a55721
2
CLOBBER
2
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.
|
||||
|
@ -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,6 +188,11 @@ var FullScreen = {
|
||||
document.removeEventListener("popupshown", this._setPopupOpen, false);
|
||||
document.removeEventListener("popuphidden", this._setPopupOpen, false);
|
||||
|
||||
this.cleanupDomFullscreen();
|
||||
}
|
||||
},
|
||||
|
||||
cleanupDomFullscreen: function () {
|
||||
this.cancelWarning();
|
||||
gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
|
||||
gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
|
||||
@ -192,7 +202,6 @@ var FullScreen = {
|
||||
|
||||
window.messageManager
|
||||
.broadcastAsyncMessage("DOMFullscreen:Cleanup");
|
||||
}
|
||||
},
|
||||
|
||||
getMouseTargetRect: function()
|
||||
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -477,3 +477,4 @@ support-files =
|
||||
[browser_bug1124271_readerModePinnedTab.js]
|
||||
support-files =
|
||||
readerModeArticle.html
|
||||
[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);
|
||||
}
|
||||
}
|
||||
});
|
@ -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") {
|
||||
|
@ -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)"],
|
||||
|
@ -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.")
|
||||
});
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -301,10 +301,16 @@ let gSyncPane = {
|
||||
// If the account is verified the next promise in the chain will
|
||||
// fetch profile data.
|
||||
return data.verified;
|
||||
}).then(shouldGetProfile => {
|
||||
if (shouldGetProfile) {
|
||||
}).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) {
|
||||
// Make sure the image is available before displaying it,
|
||||
|
@ -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]);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<nsIURI> aURI)
|
||||
: mURI(aURI)
|
||||
{
|
||||
}
|
||||
@ -57,56 +58,55 @@ URL::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHa
|
||||
URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
URL& aBase, ErrorResult& aRv)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
return Constructor(aUrl, aBase.GetURI(), aRv);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<URL>
|
||||
URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
const Optional<nsAString>& aBase, ErrorResult& aRv)
|
||||
{
|
||||
if (aBase.WasPassed()) {
|
||||
return Constructor(aUrl, aBase.Value(), aRv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> 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> url = new URL(uri);
|
||||
return url.forget();
|
||||
return Constructor(aUrl, nullptr, aRv);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<URL>
|
||||
URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
const nsAString& aBase, ErrorResult& aRv)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
return Constructor(aUrl, aBase, aRv);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<URL>
|
||||
URL::Constructor(const nsAString& aUrl, const nsAString& aBase,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIURI> 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>
|
||||
URL::Constructor(const nsAString& aUrl, nsIURI* aBase, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIURI> 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> url = new URL(uri);
|
||||
nsRefPtr<URL> url = new URL(uri.forget());
|
||||
return url.forget();
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(URL)
|
||||
|
||||
explicit URL(nsIURI* aURI);
|
||||
explicit URL(already_AddRefed<nsIURI> aURI);
|
||||
|
||||
// WebIDL methods
|
||||
bool
|
||||
@ -49,8 +49,16 @@ public:
|
||||
Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
URL& aBase, ErrorResult& aRv);
|
||||
static already_AddRefed<URL>
|
||||
Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
const Optional<nsAString>& aBase, ErrorResult& aRv);
|
||||
// Versions of Constructor that we can share with workers and other code.
|
||||
static already_AddRefed<URL>
|
||||
Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
const nsAString& aBase, ErrorResult& aRv);
|
||||
static already_AddRefed<URL>
|
||||
Constructor(const nsAString& aUrl, const nsAString& aBase, ErrorResult& aRv);
|
||||
static already_AddRefed<URL>
|
||||
Constructor(const nsAString& aUrl, nsIURI* aBase, ErrorResult& aRv);
|
||||
|
||||
static void CreateObjectURL(const GlobalObject& aGlobal,
|
||||
File& aBlob,
|
||||
|
@ -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<AsyncEventDispatcher> 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<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
|
||||
this, NS_LITERAL_STRING("MozExitedDomFullscreen"), true, true);
|
||||
asyncDispatcher->PostDOMEvent();
|
||||
SetWindowFullScreen(this, false);
|
||||
}
|
||||
}
|
||||
|
@ -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"))) {
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
||||
window.requestAnimationFrame(function() {
|
||||
is(resizeHandlerRan, true, "Resize handler should have run");
|
||||
|
||||
win.removeEventListener("resize", handleResize, false);
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -146,7 +146,7 @@ ErrorResult::ThrowErrorWithMessage(va_list ap, const dom::ErrNum errorNumber,
|
||||
MOZ_ASSERT(argCount <= 10);
|
||||
argCount = std::min<uint16_t>(argCount, 10);
|
||||
while (argCount--) {
|
||||
message->mArgs.AppendElement(*va_arg(ap, nsString*));
|
||||
message->mArgs.AppendElement(*va_arg(ap, const nsAString*));
|
||||
}
|
||||
mMessage = message;
|
||||
#ifdef DEBUG
|
||||
|
@ -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).
|
||||
|
24
dom/cache/CacheStorage.cpp
vendored
24
dom/cache/CacheStorage.cpp
vendored
@ -314,6 +314,30 @@ CacheStorage::PrefEnabled(JSContext* aCx, JSObject* aObj)
|
||||
return Cache::PrefEnabled(aCx, aObj);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<CacheStorage>
|
||||
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<Namespace>(aNamespace);
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
return CreateOnMainThread(ns, global, aPrincipal, aRv);
|
||||
}
|
||||
|
||||
nsISupports*
|
||||
CacheStorage::GetParentObject() const
|
||||
{
|
||||
|
7
dom/cache/CacheStorage.h
vendored
7
dom/cache/CacheStorage.h
vendored
@ -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<Promise> Delete(const nsAString& aKey, ErrorResult& aRv);
|
||||
already_AddRefed<Promise> Keys(ErrorResult& aRv);
|
||||
|
||||
// chrome-only webidl interface methods
|
||||
static already_AddRefed<CacheStorage>
|
||||
Constructor(const GlobalObject& aGlobal, CacheStorageNamespace aNamespace,
|
||||
nsIPrincipal* aPrincipal, ErrorResult& aRv);
|
||||
|
||||
// binding methods
|
||||
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
|
4
dom/cache/moz.build
vendored
4
dom/cache/moz.build
vendored
@ -94,3 +94,7 @@ FINAL_LIBRARY = 'xul'
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'test/mochitest/mochitest.ini',
|
||||
]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += [
|
||||
'test/mochitest/chrome.ini',
|
||||
]
|
||||
|
1
dom/cache/test/mochitest/chrome.ini
vendored
Normal file
1
dom/cache/test/mochitest/chrome.ini
vendored
Normal file
@ -0,0 +1 @@
|
||||
[test_chrome_constructor.html]
|
43
dom/cache/test/mochitest/test_chrome_constructor.html
vendored
Normal file
43
dom/cache/test/mochitest/test_chrome_constructor.html
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Validate Interfaces Exposed to Workers</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
var Cu = Components.utils;
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// attach to a different origin's CacheStorage
|
||||
var url = 'http://example.com/';
|
||||
var uri = Services.io.newURI(url, null, null);
|
||||
var principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
||||
var storage = new CacheStorage('content', principal);
|
||||
|
||||
// verify we can use the other origin's CacheStorage as normal
|
||||
var req = new Request('http://example.com/index.html');
|
||||
var res = new Response('hello world');
|
||||
var cache;
|
||||
storage.open('foo').then(function(c) {
|
||||
cache = c;
|
||||
ok(cache, 'storage should create cache');
|
||||
return cache.put(req, res.clone());
|
||||
}).then(function() {
|
||||
return cache.match(req);
|
||||
}).then(function(foundResponse) {
|
||||
return Promise.all([res.text(), foundResponse.text()]);
|
||||
}).then(function(results) {
|
||||
is(results[0], results[1], 'cache should contain response');
|
||||
return storage.delete('foo');
|
||||
}).then(function(deleted) {
|
||||
ok(deleted, 'storage should delete cache');
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -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]
|
||||
|
84
dom/canvas/test/webgl-mochitest/test_uninit_data.html
Normal file
84
dom/canvas/test/webgl-mochitest/test_uninit_data.html
Normal file
@ -0,0 +1,84 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='content-type' content='text/html; charset=utf-8'/>
|
||||
|
||||
<title>Test contents of uninitialized buffers</title>
|
||||
|
||||
<script src='/tests/SimpleTest/SimpleTest.js'></script>
|
||||
<link rel='stylesheet' href='/tests/SimpleTest/test.css'>
|
||||
<script src='webgl-util.js'></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function TestFB(gl) {
|
||||
var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
|
||||
ok(status == gl.FRAMEBUFFER_COMPLETE, 'FB should be complete.');
|
||||
|
||||
var pixel = new Uint8Array(4);
|
||||
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
|
||||
|
||||
ok(!pixel[0], 'R channel should be 0, was ' + pixel[0] + '.');
|
||||
ok(!pixel[1], 'G channel should be 0, was ' + pixel[1] + '.');
|
||||
ok(!pixel[2], 'B channel should be 0, was ' + pixel[2] + '.');
|
||||
ok(!pixel[3], 'A channel should be 0, was ' + pixel[3] + '.');
|
||||
}
|
||||
|
||||
function Test(contextAttribs) {
|
||||
ok(true, '===============================');
|
||||
ok(true, 'Testing ' + JSON.stringify(contextAttribs));
|
||||
|
||||
var c = document.createElement('canvas');
|
||||
var gl = c.getContext('webgl', contextAttribs);
|
||||
if (!gl) {
|
||||
todo(false, 'WebGL is unavailable.');
|
||||
return;
|
||||
}
|
||||
|
||||
var rb = gl.createRenderbuffer();
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
|
||||
|
||||
var tex = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, tex);
|
||||
|
||||
var err = gl.getError();
|
||||
ok(!err, 'Error should be 0x0, was 0x' + err.toString(16));
|
||||
if (err)
|
||||
return;
|
||||
|
||||
var fb = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
|
||||
|
||||
ok(true, 'Backed with RB');
|
||||
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
|
||||
gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 1, 1);
|
||||
TestFB(gl);
|
||||
|
||||
ok(true, 'Backed with texture');
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
TestFB(gl);
|
||||
|
||||
err = gl.getError();
|
||||
ok(!err, 'Error should be 0x0, was 0x' + err.toString(16));
|
||||
}
|
||||
|
||||
// Give ourselves a scope to return early from:
|
||||
(function() {
|
||||
// We test multiple configurations because we've had bugs regarding faking RGBX on
|
||||
// ANGLE: With alpha:false, uninitialized buffers were being filled with (0,0,0,1)
|
||||
// instead of (0,0,0,0).
|
||||
Test({alpha: false, antialias: false});
|
||||
Test({alpha: true, antialias: false});
|
||||
Test({alpha: false, antialias: true});
|
||||
Test({alpha: true, antialias: true});
|
||||
|
||||
ok(true, 'Test complete.');
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1528,6 +1528,9 @@ EventStateManager::BeginTrackingDragGesture(nsPresContext* aPresContext,
|
||||
getter_AddRefs(mGestureDownContent));
|
||||
|
||||
mGestureDownFrameOwner = inDownFrame->GetContent();
|
||||
if (!mGestureDownFrameOwner) {
|
||||
mGestureDownFrameOwner = mGestureDownContent;
|
||||
}
|
||||
}
|
||||
mGestureModifiers = inDownEvent->modifiers;
|
||||
mGestureDownButtons = inDownEvent->buttons;
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<MediaDecoderStateMachine> 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<MediaDecoderStateMachine> 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()
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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<ResolveValueType> mResolveValue;
|
||||
Maybe<RejectValueType> 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<ThenValueBase> mThenValue;
|
||||
ResolveValueType mResolveValue;
|
||||
ResolveOrRejectValue mValue;
|
||||
};
|
||||
|
||||
class RejectRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
RejectRunnable(ThenValueBase* aThenValue, RejectValueType aRejectValue)
|
||||
: mThenValue(aThenValue)
|
||||
, mRejectValue(aRejectValue) {}
|
||||
explicit ThenValueBase(AbstractThread* aResponseTarget, const char* aCallSite)
|
||||
: mResponseTarget(aResponseTarget), mCallSite(aCallSite) {}
|
||||
|
||||
~RejectRunnable()
|
||||
void Dispatch(MediaPromise *aPromise)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mThenValue || mThenValue->IsDisconnected());
|
||||
aPromise->mMutex.AssertCurrentThreadOwns();
|
||||
MOZ_ASSERT(!aPromise->IsPending());
|
||||
|
||||
nsRefPtr<nsRunnable> runnable =
|
||||
static_cast<nsRunnable*>(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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP Run()
|
||||
virtual void Disconnect() override
|
||||
{
|
||||
PROMISE_LOG("RejectRunnable::Run() [this=%p]", this);
|
||||
mThenValue->DoReject(mRejectValue);
|
||||
mThenValue = nullptr;
|
||||
return NS_OK;
|
||||
MOZ_ASSERT(ThenValueBase::mResponseTarget->IsCurrentThreadIn());
|
||||
MOZ_DIAGNOSTIC_ASSERT(!Consumer::mComplete);
|
||||
Consumer::mDisconnected = true;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<ThenValueBase> mThenValue;
|
||||
RejectValueType mRejectValue;
|
||||
};
|
||||
|
||||
explicit ThenValueBase(const char* aCallSite) : mCallSite(aCallSite) {}
|
||||
|
||||
virtual void Dispatch(MediaPromise *aPromise) = 0;
|
||||
|
||||
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<AbstractThread> mResponseTarget; // May be released on any thread.
|
||||
const char* mCallSite;
|
||||
};
|
||||
|
||||
@ -207,51 +242,20 @@ protected:
|
||||
}
|
||||
|
||||
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
|
||||
class ThenValue : public ThenValueBase
|
||||
class MethodThenValue : public ThenValueBase
|
||||
{
|
||||
public:
|
||||
ThenValue(AbstractThread* aResponseTarget, ThisType* aThisVal,
|
||||
MethodThenValue(AbstractThread* aResponseTarget, ThisType* aThisVal,
|
||||
ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
|
||||
const char* aCallSite)
|
||||
: ThenValueBase(aCallSite)
|
||||
, mResponseTarget(aResponseTarget)
|
||||
: 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<nsRunnable> runnable =
|
||||
resolved ? static_cast<nsRunnable*>(new (typename ThenValueBase::ResolveRunnable)(this, aPromise->mResolveValue.ref()))
|
||||
: static_cast<nsRunnable*>(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<AbstractThread> mResponseTarget; // May be released on any thread.
|
||||
nsRefPtr<ThisType> 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<typename ResolveFunction, typename RejectFunction>
|
||||
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<ResolveFunction> mResolveFunction; // Only accessed and deleted on dispatch thread.
|
||||
Maybe<RejectFunction> 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<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
|
||||
already_AddRefed<Consumer> 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<ThenValueBase> thenValue = new ThenValue<ThisType, ResolveMethodType, RejectMethodType>(
|
||||
nsRefPtr<ThenValueBase> thenValue = new MethodThenValue<ThisType, ResolveMethodType, RejectMethodType>(
|
||||
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<typename ResolveFunction, typename RejectFunction>
|
||||
already_AddRefed<Consumer> RefableThen(AbstractThread* aResponseThread, const char* aCallSite,
|
||||
ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
|
||||
{
|
||||
nsRefPtr<ThenValueBase> thenValue = new FunctionThenValue<ResolveFunction, RejectFunction>(aResponseThread,
|
||||
Move(aResolveFunction), Move(aRejectFunction), aCallSite);
|
||||
ThenInternal(aResponseThread, thenValue, aCallSite);
|
||||
return thenValue.forget();
|
||||
}
|
||||
|
||||
@ -332,6 +382,15 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
template<typename ThisType, typename ResolveFunction, typename RejectFunction>
|
||||
void Then(AbstractThread* aResponseThread, const char* aCallSite,
|
||||
ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
|
||||
{
|
||||
nsRefPtr<Consumer> c =
|
||||
RefableThen(aResponseThread, aCallSite, Move(aResolveFunction), Move(aRejectFunction));
|
||||
return;
|
||||
}
|
||||
|
||||
void ChainTo(already_AddRefed<Private> 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(), "<chained promise>");
|
||||
if (mValue.IsResolve()) {
|
||||
aOther->Resolve(mValue.ResolveValue(), "<chained promise>");
|
||||
} else {
|
||||
aOther->Reject(mRejectValue.ref(), "<chained promise>");
|
||||
aOther->Reject(mValue.RejectValue(), "<chained promise>");
|
||||
}
|
||||
}
|
||||
|
||||
@ -383,8 +442,7 @@ protected:
|
||||
|
||||
const char* mCreationSite; // For logging
|
||||
Mutex mMutex;
|
||||
Maybe<ResolveValueType> mResolveValue;
|
||||
Maybe<RejectValueType> mRejectValue;
|
||||
ResolveOrRejectValue mValue;
|
||||
nsTArray<nsRefPtr<ThenValueBase>> mThenValues;
|
||||
nsTArray<nsRefPtr<Private>> 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();
|
||||
}
|
||||
};
|
||||
|
@ -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<SharedDecoderProxy> proxy(new SharedDecoderProxy(this, aCallback));
|
||||
@ -147,12 +150,20 @@ void
|
||||
SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy)
|
||||
{
|
||||
if (aProxy && mActiveProxy == aProxy) {
|
||||
mWaitForInternalDrain = true;
|
||||
mActiveProxy->Drain();
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
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);
|
||||
if (mWaitForInternalDrain) {
|
||||
mWaitForInternalDrain = false;
|
||||
mon.NotifyAll();
|
||||
} else {
|
||||
mActiveCallback->DrainComplete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
mActiveCallback->DrainComplete();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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() {
|
||||
]]>
|
||||
</script>
|
||||
<!-- chrome://mochitests/content/chrome/dom/tests/mochitest/chrome/test_MozEnteredDomFullscreen_event.xul -->
|
||||
<browser type="content" id="browser" width="400" height="400" src="http://mochi.test:8888/tests/dom/tests/mochitest/general/file_MozEnteredDomFullscreen.html"/>
|
||||
<browser type="content" id="browser" width="400" height="400" src="http://mochi.test:8888/tests/dom/tests/mochitest/general/file_MozDomFullscreen.html"/>
|
||||
|
||||
</window>
|
@ -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]
|
||||
|
@ -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()
|
@ -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
|
||||
|
@ -13,32 +13,32 @@
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::cache::Cache::PrefEnabled"]
|
||||
interface Cache {
|
||||
[Throws]
|
||||
Promise<Response> match(RequestInfo request, optional CacheQueryOptions options);
|
||||
[Throws]
|
||||
Promise<sequence<Response>> matchAll(optional RequestInfo request, optional CacheQueryOptions options);
|
||||
[Throws]
|
||||
Promise<void> add(RequestInfo request);
|
||||
[Throws]
|
||||
Promise<void> addAll(sequence<RequestInfo> requests);
|
||||
[Throws]
|
||||
Promise<void> put(RequestInfo request, Response response);
|
||||
[Throws]
|
||||
Promise<boolean> delete(RequestInfo request, optional CacheQueryOptions options);
|
||||
[Throws]
|
||||
Promise<sequence<Request>> keys(optional RequestInfo request, optional CacheQueryOptions options);
|
||||
[NewObject]
|
||||
Promise<Response> match(RequestInfo request, optional CacheQueryOptions options);
|
||||
[NewObject]
|
||||
Promise<sequence<Response>> matchAll(optional RequestInfo request, optional CacheQueryOptions options);
|
||||
[NewObject]
|
||||
Promise<void> add(RequestInfo request);
|
||||
[NewObject]
|
||||
Promise<void> addAll(sequence<RequestInfo> requests);
|
||||
[NewObject]
|
||||
Promise<void> put(RequestInfo request, Response response);
|
||||
[NewObject]
|
||||
Promise<boolean> delete(RequestInfo request, optional CacheQueryOptions options);
|
||||
[NewObject]
|
||||
Promise<sequence<Request>> 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;
|
||||
};
|
||||
|
@ -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<Response> match(RequestInfo request, optional CacheQueryOptions options);
|
||||
[Throws]
|
||||
Promise<boolean> has(DOMString cacheName);
|
||||
[Throws]
|
||||
Promise<Cache> open(DOMString cacheName);
|
||||
[Throws]
|
||||
Promise<boolean> delete(DOMString cacheName);
|
||||
[Throws]
|
||||
Promise<sequence<DOMString>> keys();
|
||||
[NewObject]
|
||||
Promise<Response> match(RequestInfo request, optional CacheQueryOptions options);
|
||||
[NewObject]
|
||||
Promise<boolean> has(DOMString cacheName);
|
||||
[NewObject]
|
||||
Promise<Cache> open(DOMString cacheName);
|
||||
[NewObject]
|
||||
Promise<boolean> delete(DOMString cacheName);
|
||||
[NewObject]
|
||||
Promise<sequence<DOMString>> keys();
|
||||
};
|
||||
|
||||
// chrome-only, gecko specific extension
|
||||
enum CacheStorageNamespace {
|
||||
"content", "chrome"
|
||||
};
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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 {
|
||||
};
|
||||
|
@ -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<nsIInputStream> mUploadStream;
|
||||
public:
|
||||
FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
||||
@ -2451,6 +2453,13 @@ public:
|
||||
|
||||
mContentPolicyType = loadInfo->GetContentPolicyType();
|
||||
|
||||
nsCOMPtr<nsIUploadChannel2> 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<FetchEventInit> init(aCx);
|
||||
|
@ -34,7 +34,7 @@ class URLProxy final
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URLProxy)
|
||||
|
||||
explicit URLProxy(mozilla::dom::URL* aURL)
|
||||
explicit URLProxy(already_AddRefed<mozilla::dom::URL> 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<URLProxy> mBaseProxy;
|
||||
mozilla::ErrorResult& mRv;
|
||||
|
||||
@ -236,13 +236,17 @@ private:
|
||||
|
||||
public:
|
||||
ConstructorRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
const nsAString& aURL, const nsAString& aBase,
|
||||
const nsAString& aURL, const Optional<nsAString>& 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<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
mRv.Throw(rv);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> 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<mozilla::dom::URL> 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<nsIURI> 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<ConstructorRunnable> runnable =
|
||||
new ConstructorRunnable(workerPrivate, aUrl, aBase.GetURLProxy(), aRv);
|
||||
|
||||
if (!runnable->Dispatch(cx)) {
|
||||
JS_ReportPendingException(cx);
|
||||
}
|
||||
return FinishConstructor(cx, workerPrivate, runnable, aRv);
|
||||
}
|
||||
|
||||
nsRefPtr<URLProxy> proxy = runnable->GetURLProxy();
|
||||
if (!proxy) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
// static
|
||||
already_AddRefed<URL>
|
||||
URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
const Optional<nsAString>& aBase, ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = aGlobal.Context();
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
|
||||
|
||||
nsRefPtr<URL> url = new URL(workerPrivate, proxy);
|
||||
return url.forget();
|
||||
nsRefPtr<ConstructorRunnable> 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<nsAString> base;
|
||||
base = &aBase;
|
||||
nsRefPtr<ConstructorRunnable> 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>
|
||||
URL::FinishConstructor(JSContext* aCx, WorkerPrivate* aPrivate,
|
||||
ConstructorRunnable* aRunnable, ErrorResult& aRv)
|
||||
{
|
||||
if (!aRunnable->Dispatch(aCx)) {
|
||||
JS_ReportPendingException(aCx);
|
||||
}
|
||||
|
||||
nsRefPtr<URLProxy> proxy = runnable->GetURLProxy();
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<URLProxy> proxy = aRunnable->GetURLProxy();
|
||||
if (!proxy) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<URL> url = new URL(workerPrivate, proxy);
|
||||
nsRefPtr<URL> url = new URL(aPrivate, proxy);
|
||||
return url.forget();
|
||||
}
|
||||
|
||||
|
@ -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<URL>
|
||||
Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
const Optional<nsAString>& aBase, ErrorResult& aRv);
|
||||
static already_AddRefed<URL>
|
||||
Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
const nsAString& aBase, ErrorResult& aRv);
|
||||
|
||||
@ -123,6 +127,10 @@ private:
|
||||
return mURLProxy;
|
||||
}
|
||||
|
||||
static already_AddRefed<URL>
|
||||
FinishConstructor(JSContext* aCx, WorkerPrivate* aPrivate,
|
||||
ConstructorRunnable* aRunnable, ErrorResult& aRv);
|
||||
|
||||
void CreateSearchParamsIfNeeded();
|
||||
|
||||
void SetSearchInternal(const nsAString& aSearch);
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
@ -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!
|
||||
|
@ -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);
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -1,2 +1 @@
|
||||
// Not useful.
|
||||
dump("worker_updatefoundevent2.js loaded\n");
|
||||
|
72
gfx/layers/apz/test/apz_test_native_event_utils.js
Normal file
72
gfx/layers/apz/test/apz_test_native_event_utils.js
Normal file
@ -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);
|
||||
}
|
@ -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'
|
||||
|
109
gfx/layers/apz/test/test_wheel_scroll.html
Normal file
109
gfx/layers/apz/test/test_wheel_scroll.html
Normal file
@ -0,0 +1,109 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1013412
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1013412</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
#content {
|
||||
height: 800px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#scroller {
|
||||
height: 2000px;
|
||||
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
|
||||
}
|
||||
|
||||
#scrollbox {
|
||||
margin-top: 200px;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
border-radius: 250px;
|
||||
box-shadow: inset 0 0 0 60px #555;
|
||||
background: #777;
|
||||
}
|
||||
|
||||
#circle {
|
||||
position: relative;
|
||||
left: 240px;
|
||||
top: 20px;
|
||||
border: 10px solid white;
|
||||
border-radius: 10px;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
transform-origin: 10px 230px;
|
||||
will-change: transform;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1161206">Mozilla Bug 1161206</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<p>Scrolling the page should be async, but scrolling over the dark circle should not scroll the page and instead rotate the white ball.</p>
|
||||
<div id="scroller">
|
||||
<div id="scrollbox">
|
||||
<div id="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
var rotation = 0;
|
||||
var rotationAdjusted = false;
|
||||
|
||||
var incrementForMode = function (mode) {
|
||||
switch (mode) {
|
||||
case WheelEvent.DOM_DELTA_PIXEL: return 1;
|
||||
case WheelEvent.DOM_DELTA_LINE: return 15;
|
||||
case WheelEvent.DOM_DELTA_PAGE: return 400;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
document.getElementById("scrollbox").addEventListener("wheel", function (e) {
|
||||
rotation += e.deltaY * incrementForMode(e.deltaMode) * 0.2;
|
||||
document.getElementById("circle").style.transform = "rotate(" + rotation + "deg)";
|
||||
rotationAdjusted = true;
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
function* runTest() {
|
||||
var content = document.getElementById('content');
|
||||
for (i = 0; i < 300; i++) { // enough iterations that we would scroll to the bottom of 'content'
|
||||
yield synthesizeNativeWheelAndWaitForEvent(content, 100, 150, 0, -5, continueTest);
|
||||
}
|
||||
var scrollbox = document.getElementById('scrollbox');
|
||||
is(content.scrollTop > 0, true, "We should have scrolled down somewhat");
|
||||
is(content.scrollTop < content.scrollTopMax, true, "We should not have scrolled to the bottom of the scrollframe");
|
||||
is(rotationAdjusted, true, "The rotation should have been adjusted");
|
||||
}
|
||||
|
||||
var gTestContinuation = null;
|
||||
function continueTest() {
|
||||
if (!gTestContinuation) {
|
||||
gTestContinuation = runTest();
|
||||
}
|
||||
var ret = gTestContinuation.next();
|
||||
if (ret.done) {
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
is(ret.value, true, "Wheel event successfully synthesized");
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(continueTest, window);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -2344,7 +2344,7 @@ gfxPlatform::UsesOffMainThreadCompositing()
|
||||
already_AddRefed<mozilla::gfx::VsyncSource>
|
||||
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<mozilla::gfx::VsyncSource> softwareVsync = new SoftwareVsyncSource();
|
||||
return softwareVsync.forget();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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<SurfaceCacheImpl> 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<bool> 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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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; }
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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,18 +666,36 @@ 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() &&
|
||||
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());
|
||||
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() ||
|
||||
(AwaitingSyncReply() && !ShouldDeferMessage(aMsg)) ||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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<T*>
|
||||
{
|
||||
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<js::gc::Cell*>(vp));
|
||||
}
|
||||
static void relocate(T** vp) {}
|
||||
};
|
||||
|
||||
@ -657,10 +664,10 @@ struct GCMethods<JSObject*>
|
||||
return v != nullptr && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(v));
|
||||
}
|
||||
static void postBarrier(JSObject** vp) {
|
||||
JS::HeapCellPostBarrier(reinterpret_cast<js::gc::Cell**>(vp));
|
||||
JS::HeapObjectPostBarrier(vp);
|
||||
}
|
||||
static void relocate(JSObject** vp) {
|
||||
JS::HeapCellRelocate(reinterpret_cast<js::gc::Cell**>(vp));
|
||||
JS::HeapObjectRelocate(vp);
|
||||
}
|
||||
};
|
||||
|
||||
@ -672,10 +679,10 @@ struct GCMethods<JSFunction*>
|
||||
return v != nullptr && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(v));
|
||||
}
|
||||
static void postBarrier(JSFunction** vp) {
|
||||
JS::HeapCellPostBarrier(reinterpret_cast<js::gc::Cell**>(vp));
|
||||
JS::HeapObjectPostBarrier(reinterpret_cast<JSObject**>(vp));
|
||||
}
|
||||
static void relocate(JSFunction** vp) {
|
||||
JS::HeapCellRelocate(reinterpret_cast<js::gc::Cell**>(vp));
|
||||
JS::HeapObjectRelocate(reinterpret_cast<JSObject**>(vp));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -75,3 +75,33 @@ template void js::PreBarrierFunctor<JS::Value>::operator()<JSObject>(JSObject*);
|
||||
template void js::PreBarrierFunctor<JS::Value>::operator()<JSString>(JSString*);
|
||||
template void js::PreBarrierFunctor<jsid>::operator()<JS::Symbol>(JS::Symbol*);
|
||||
template void js::PreBarrierFunctor<jsid>::operator()<JSString>(JSString*);
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::HeapObjectPostBarrier(JSObject** objp)
|
||||
{
|
||||
MOZ_ASSERT(objp);
|
||||
MOZ_ASSERT(*objp);
|
||||
js::InternalGCMethods<JSObject*>::postBarrierRelocate(objp);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::HeapObjectRelocate(JSObject** objp)
|
||||
{
|
||||
MOZ_ASSERT(objp);
|
||||
MOZ_ASSERT(*objp);
|
||||
js::InternalGCMethods<JSObject*>::postBarrierRemove(objp);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::HeapValuePostBarrier(JS::Value* valuep)
|
||||
{
|
||||
MOZ_ASSERT(valuep);
|
||||
js::InternalGCMethods<JS::Value>::postBarrierRelocate(valuep);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::HeapValueRelocate(JS::Value* valuep)
|
||||
{
|
||||
MOZ_ASSERT(valuep);
|
||||
js::InternalGCMethods<JS::Value>::postBarrierRemove(valuep);
|
||||
}
|
||||
|
@ -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<js::gc::Cell*>(valuep->toGCThing())->runtimeFromMainThread();
|
||||
runtime->gc.storeBuffer.removeRelocatableValueFromAnyThread(valuep);
|
||||
}
|
||||
|
||||
template struct StoreBuffer::MonoTypeBuffer<StoreBuffer::ValueEdge>;
|
||||
template struct StoreBuffer::MonoTypeBuffer<StoreBuffer::CellPtrEdge>;
|
||||
template struct StoreBuffer::MonoTypeBuffer<StoreBuffer::SlotsEdge>;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -138,10 +138,10 @@ typedef struct CapturingContentInfo {
|
||||
mozilla::StaticRefPtr<nsIContent> 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.
|
||||
*/
|
||||
|
@ -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<PresShell*>(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<nsRunnableMethod<PresShell> > 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<nsIPresShell> 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<nsIPresShell> 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
|
||||
|
@ -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<nsIFrame*> mDirtyRoots;
|
||||
|
||||
nsTArray<nsAutoPtr<DelayedEvent> > mDelayedEvents;
|
||||
nsRevocableEventPtr<nsRunnableMethod<PresShell> > mResizeEvent;
|
||||
nsCOMPtr<nsITimer> mAsyncResizeEventTimer;
|
||||
private:
|
||||
nsIFrame* mCurrentEventFrame;
|
||||
nsCOMPtr<nsIContent> mCurrentEventContent;
|
||||
@ -861,8 +858,7 @@ protected:
|
||||
// have been processed.
|
||||
bool mShouldUnsuppressPainting : 1;
|
||||
|
||||
bool mAsyncResizeTimerIsActive : 1;
|
||||
bool mInResize : 1;
|
||||
bool mResizeEventPending : 1;
|
||||
|
||||
bool mImageVisibilityVisited : 1;
|
||||
|
||||
|
@ -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<TimeStamp> restoreTickStart(mTickStart);
|
||||
mTickStart = TimeStamp::Now();
|
||||
|
||||
// Resize events should be fired before layout flushes or
|
||||
// calling animation frame callbacks.
|
||||
nsAutoTArray<nsIPresShell*, 16> 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
|
||||
|
@ -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<nsIPresShell*, 16> mResizeEventFlushObservers;
|
||||
nsAutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
|
||||
nsAutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;
|
||||
nsAutoTArray<nsIPresShell*, 16> mPresShellsToInvalidateIfHidden;
|
||||
|
@ -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);
|
||||
|
@ -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<nsTArray<nsIFrame*>> 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;
|
||||
const bool isGridItem = child->GetType() != nsGkAtoms::placeholderFrame;
|
||||
LogicalRect cb(wm);
|
||||
if (MOZ_LIKELY(isGridItem)) {
|
||||
GridArea* area = GetGridAreaForChild(child);
|
||||
MOZ_ASSERT(area && area->IsDefinite());
|
||||
LogicalRect cb = ContainingBlockFor(wm, *area, aColSizes, aRowSizes);
|
||||
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
|
||||
|
10
layout/reftests/bugs/1133905-1-h-rtl.html
Normal file
10
layout/reftests/bugs/1133905-1-h-rtl.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=325">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="width: 9000px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
9
layout/reftests/bugs/1133905-1-h.html
Normal file
9
layout/reftests/bugs/1133905-1-h.html
Normal file
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=325">
|
||||
</head>
|
||||
<body>
|
||||
<div style="width: 9000px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user