Merge m-c to f-t

This commit is contained in:
Phil Ringnalda 2013-11-24 17:52:09 -08:00
commit 098b0d1deb
184 changed files with 2221 additions and 792 deletions

View File

@ -28,9 +28,10 @@ function duplicate(delta, msg, cb) {
var start = gBrowser.sessionHistory.index;
duplicateTabIn(gBrowser.selectedTab, "tab", delta);
let tab = gBrowser.selectedTab;
gBrowser.selectedBrowser.addEventListener("pageshow", function () {
gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee, false);
tab.addEventListener("SSTabRestored", function tabRestoredListener() {
tab.removeEventListener("SSTabRestored", tabRestoredListener, false);
is(gBrowser.sessionHistory.index, start + delta, msg);
executeSoon(cb);
}, false);

View File

@ -22,10 +22,6 @@ EXTRA_DSO_LDOPTS += \
$(MOZ_COMPONENT_LIBS) \
$(NULL)
ifdef JS_SHARED_LIBRARY
EXTRA_DSO_LDOPTS += $(MOZ_JS_LIBS)
endif
LOCAL_INCLUDES += -I$(srcdir)/../migration/src
# Mac: Need to link with CoreFoundation for Mac Migrators (PList reading code)

View File

@ -3,8 +3,3 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
LOCAL_INCLUDES = -I$(srcdir)/../build
EXTRA_DSO_LDOPTS = \
$(XPCOM_GLUE_LDOPTS) \
$(NSPR_LIBS) \
$(NULL)

View File

@ -2624,6 +2624,14 @@ let SessionStoreInternal = {
// be ignored and don't override any tab data set by restoreHistory().
TabState.flush(tab.linkedBrowser);
// Ensure the index is in bounds.
let activeIndex = (tabData.index || tabData.entries.length) - 1;
activeIndex = Math.min(activeIndex, tabData.entries.length - 1);
activeIndex = Math.max(activeIndex, 0);
// Save the index in case we updated it above.
tabData.index = activeIndex + 1;
// keep the data around to prevent dataloss in case
// a tab gets closed before it's been properly restored
browser.__SS_data = tabData;
@ -2638,20 +2646,10 @@ let SessionStoreInternal = {
pageStyle: tabData.pageStyle || null
});
if (tabData.entries.length == 0) {
// make sure to blank out this tab's content
// (just purging the tab's history won't be enough)
browser.loadURIWithFlags("about:blank",
Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
null, null, null);
continue;
}
browser.stop(); // in case about:blank isn't done yet
// wall-paper fix for bug 439675: make sure that the URL to be loaded
// is always visible in the address bar
let activeIndex = (tabData.index || tabData.entries.length) - 1;
let activePageData = tabData.entries[activeIndex] || null;
let uri = activePageData ? activePageData.url || null : null;
browser.userTypedValue = uri;
@ -2800,15 +2798,13 @@ let SessionStoreInternal = {
// Remove the history listener, since we no longer need it once we start restoring
this._removeSHistoryListener(aTab);
let activeIndex = (tabData.index || tabData.entries.length) - 1;
if (activeIndex >= tabData.entries.length)
activeIndex = tabData.entries.length - 1;
let activeIndex = tabData.index - 1;
// Reset currentURI. This creates a new session history entry with a new
// doc identifier, so we need to explicitly save and restore the old doc
// identifier (corresponding to the SHEntry at activeIndex) below.
browser.webNavigation.setCurrentURI(Utils.makeURI("about:blank"));
// Attach data that will be restored on "load" event, after tab is restored.
if (activeIndex > -1) {
if (tabData.entries.length) {
// restore those aspects of the currently active documents which are not
// preserved in the plain history entries (mainly scroll state and text data)
browser.__SS_restore_data = tabData.entries[activeIndex] || {};
@ -2826,6 +2822,14 @@ let SessionStoreInternal = {
// ignore page load errors
didStartLoad = false;
}
} else {
browser.__SS_restore_data = {};
browser.__SS_restore_pageStyle = "";
browser.__SS_restore_tab = aTab;
browser.loadURIWithFlags("about:blank",
Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
null, null, null);
didStartLoad = true;
}
// Handle userTypedValue. Setting userTypedValue seems to update gURLbar
@ -2850,8 +2854,8 @@ let SessionStoreInternal = {
// channel (via the progress listener), so reset the tab ourselves. We will
// also send SSTabRestored since this tab has technically been restored.
if (!didStartLoad) {
this._sendTabRestoredNotification(aTab);
this._resetTabRestoringState(aTab);
this._sendTabRestoredNotification(aTab);
}
return didStartLoad;
@ -2935,12 +2939,19 @@ let SessionStoreInternal = {
PageStyle.restore(aBrowser.docShell, frameList, aBrowser.__SS_restore_pageStyle);
TextAndScrollData.restore(frameList);
// notify the tabbrowser that this document has been completely restored
this._sendTabRestoredNotification(aBrowser.__SS_restore_tab);
let tab = aBrowser.__SS_restore_tab;
// Drop all the state associated with restoring the tab. We're
// done with that now.
delete aBrowser.__SS_data;
delete aBrowser.__SS_restore_data;
delete aBrowser.__SS_restore_pageStyle;
delete aBrowser.__SS_restore_tab;
// Notify the tabbrowser that this document has been completely
// restored. Do so after restoration is completely finished and
// all state for it has been dropped.
this._sendTabRestoredNotification(tab);
},
/**

View File

@ -119,7 +119,7 @@ function test() {
// verify tab: (A), in undo list
let tab_A_restored = test(function() ss.undoCloseTab(aWin, 0));
ok(tab_A_restored, "a tab is in undo list");
whenBrowserLoaded(tab_A_restored.linkedBrowser, function() {
whenTabRestored(tab_A_restored, function() {
is(testURL, tab_A_restored.linkedBrowser.currentURI.spec,
"it's the same tab that we expect");
aWin.gBrowser.removeTab(tab_A_restored);
@ -137,14 +137,14 @@ function test() {
let tab_B = aWin.gBrowser.addTab(testURL2);
ss.setTabState(tab_B, JSON.stringify(state1));
whenBrowserLoaded(tab_B.linkedBrowser, function() {
whenTabRestored(tab_B, function() {
// populate tab: (B) with different form data
for (let item in fieldList)
setFormValue(tab_B, item, fieldList[item]);
// duplicate tab: (B)
let tab_C = aWin.gBrowser.duplicateTab(tab_B);
whenBrowserLoaded(tab_C.linkedBrowser, function() {
whenTabRestored(tab_C, function() {
// verify the correctness of the duplicated tab
is(ss.getTabValue(tab_C, key1), value1,
"tab successfully duplicated - correct state");

View File

@ -11,15 +11,13 @@ function test() {
"browser/components/sessionstore/test/browser_339445_sample.html";
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
let doc = tab.linkedBrowser.contentDocument;
is(doc.getElementById("storageTestItem").textContent, "PENDING",
"sessionStorage value has been set");
let tab2 = gBrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab2, function() {
let doc2 = tab2.linkedBrowser.contentDocument;
is(doc2.getElementById("storageTestItem").textContent, "SUCCESS",
"sessionStorage value has been duplicated");
@ -29,6 +27,6 @@ function test() {
gBrowser.removeTab(tab);
finish();
}, true);
}, true);
});
});
}

View File

@ -88,14 +88,12 @@ function test() {
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_346337_sample.html";
let tab = tabbrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
for (let xpath in fieldList)
setFormValue(tab, xpath, fieldList[xpath]);
let tab2 = tabbrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab2, function() {
for (let xpath in fieldList)
ok(compareFormValue(tab2, xpath, fieldList[xpath]),
"The value for \"" + xpath + "\" was correctly restored");
@ -105,8 +103,7 @@ function test() {
tabbrowser.removeTab(tab);
tab = undoCloseTab();
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab, function() {
for (let xpath in fieldList)
if (fieldList[xpath])
ok(!compareFormValue(tab, xpath, fieldList[xpath]),
@ -120,7 +117,7 @@ function test() {
tabbrowser.addTab();
tabbrowser.removeTab(tab);
finish();
}, true);
}, true);
}, true);
});
});
});
}

View File

@ -71,8 +71,7 @@ function test() {
// create a new tab
let testURL = "about:";
tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
// make sure that the next closed tab will increase getClosedTabCount
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
@ -87,15 +86,14 @@ function test() {
tab = test(function() ss.undoCloseTab(window, 0));
ok(tab, "undoCloseTab doesn't throw")
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
is(this.currentURI.spec, testURL, "correct tab was reopened");
whenTabRestored(tab, function() {
is(tab.linkedBrowser.currentURI.spec, testURL, "correct tab was reopened");
// clean up
if (gPrefService.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
gBrowser.removeTab(tab);
finish();
}, true);
}, true);
});
});
}

View File

@ -14,15 +14,12 @@ function test() {
// restore a blank tab
let tab = gBrowser.addTab("about:");
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
let history = tab.linkedBrowser.webNavigation.sessionHistory;
ok(history.count >= 1, "the new tab does have at least one history entry");
ss.setTabState(tab, JSON.stringify({ entries: [] }));
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab, function() {
ok(history.count == 0, "the tab was restored without any history whatsoever");
gBrowser.removeTab(tab);
@ -33,6 +30,6 @@ function test() {
if (gPrefService.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
finish();
}, true);
}, true);
});
});
}

View File

@ -13,8 +13,7 @@ function test() {
// create a new tab
let tab = gBrowser.addTab(testURL);
ss.setTabValue(tab, key, value);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
// get the tab's state
let state = ss.getTabState(tab);
ok(state, "get the tab's state");
@ -30,7 +29,7 @@ function test() {
// clean up
gBrowser.removeTab(tab);
}, true);
});
//////////////////////////////////
// setTabState and duplicateTab //
@ -44,31 +43,30 @@ function test() {
let tab2 = gBrowser.addTab();
// set the tab's state
ss.setTabState(tab2, JSON.stringify(state));
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab2, function() {
// verify the correctness of the restored tab
ok(ss.getTabValue(tab2, key2) == value2 && this.currentURI.spec == testURL,
ok(ss.getTabValue(tab2, key2) == value2 && tab2.linkedBrowser.currentURI.spec == testURL,
"the tab's state was correctly restored");
// add text data
let textbox = this.contentDocument.getElementById("textbox");
let textbox = tab2.linkedBrowser.contentDocument.getElementById("textbox");
textbox.value = value3;
// duplicate the tab
let duplicateTab = ss.duplicateTab(window, tab2);
gBrowser.removeTab(tab2);
duplicateTab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenTabRestored(duplicateTab, function() {
// verify the correctness of the duplicated tab
ok(ss.getTabValue(duplicateTab, key2) == value2 && this.currentURI.spec == testURL,
ok(ss.getTabValue(duplicateTab, key2) == value2 &&
duplicateTab.linkedBrowser.currentURI.spec == testURL,
"correctly duplicated the tab's state");
let textbox = this.contentDocument.getElementById("textbox");
let textbox = duplicateTab.linkedBrowser.contentDocument.getElementById("textbox");
is(textbox.value, value3, "also duplicated text data");
// clean up
gBrowser.removeTab(duplicateTab);
finish();
}, true);
}, true);
});
});
}

View File

@ -28,8 +28,7 @@ function test() {
function testOpenCloseWindow(aIsPrivate, aTest, aCallback) {
whenNewWindowLoaded({ private: aIsPrivate }, function(win) {
win.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
win.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
whenBrowserLoaded(win.gBrowser.selectedBrowser, function() {
executeSoon(function() {
// Mark the window with some unique data to be restored later on.
ss.setWindowValue(win, aTest.key, aTest.value);
@ -37,7 +36,7 @@ function test() {
win.close();
aCallback();
});
}, true);
});
win.gBrowser.selectedBrowser.loadURI(aTest.url);
});
}

View File

@ -27,9 +27,7 @@ function test() {
newWin.gBrowser.loadURI(testURL, null, null);
newWin.gBrowser.addEventListener("load", function (aEvent) {
newWin.gBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(newWin.gBrowser.selectedBrowser, function() {
// get the sessionstore state for the window
let state = ss.getWindowState(newWin);
@ -67,7 +65,7 @@ function test() {
cs.removeAll();
newWin.close();
finish();
}, true);
});
}, false);
}

View File

@ -10,17 +10,14 @@ function test() {
"browser/components/sessionstore/test/browser_447951_sample.html#";
let tab = gBrowser.addTab();
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
let tabState = { entries: [] };
let max_entries = gPrefService.getIntPref("browser.sessionhistory.max_entries");
for (let i = 0; i < max_entries; i++)
tabState.entries.push({ url: baseURL + i });
ss.setTabState(tab, JSON.stringify(tabState));
tab.addEventListener("SSTabRestored", function(aEvent) {
tab.removeEventListener("SSTabRestored", arguments.callee, false);
whenTabRestored(tab, function() {
tabState = JSON.parse(ss.getTabState(tab));
is(tabState.entries.length, max_entries, "session history filled to the limit");
is(tabState.entries[0].url, baseURL + 0, "... but not more");
@ -32,8 +29,15 @@ function test() {
0, 0, 0, 0, false, false, false, false, 0, null);
doc.querySelector("a").dispatchEvent(event);
executeSoon(function() {
function check() {
tabState = JSON.parse(ss.getTabState(tab));
if (tabState.entries[tabState.entries.length - 1].url != baseURL + "end") {
// It may take a few passes through the event loop before we
// get the right URL.
executeSoon(check);
return;
}
is(tab.linkedBrowser.currentURI.spec, baseURL + "end",
"the new anchor was loaded");
is(tabState.entries[tabState.entries.length - 1].url, baseURL + "end",
@ -44,7 +48,9 @@ function test() {
// clean up
gBrowser.removeTab(tab);
finish();
});
}, false);
}, true);
}
check();
});
});
}

View File

@ -18,8 +18,7 @@ function test() {
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_454908_sample.html";
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
let doc = tab.linkedBrowser.contentDocument;
for (let id in fieldValues)
doc.getElementById(id).value = fieldValues[id];
@ -27,8 +26,7 @@ function test() {
gBrowser.removeTab(tab);
tab = undoCloseTab();
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab, function() {
let doc = tab.linkedBrowser.contentDocument;
for (let id in fieldValues) {
let node = doc.getElementById(id);
@ -47,6 +45,6 @@ function test() {
gBrowser.addTab();
gBrowser.removeTab(tab);
finish();
}, true);
}, true);
});
});
}

View File

@ -13,13 +13,11 @@ function test() {
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_456342_sample.xhtml";
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
let expectedValue = "try to save me";
// Since bug 537289 we only save non-default values, so we need to set each
// form field's value after load.
let formEls = aEvent.originalTarget.forms[0].elements;
let formEls = tab.linkedBrowser.contentDocument.forms[0].elements;
for (let i = 0; i < formEls.length; i++)
formEls[i].value = expectedValue;
@ -49,5 +47,5 @@ function test() {
if (gPrefService.prefHasUserValue("browser.sessionstore.privacy_level"))
gPrefService.clearUserPref("browser.sessionstore.privacy_level");
finish();
}, true);
});
}

View File

@ -31,14 +31,8 @@ function test() {
typeText(doc.getElementsByName("1|#out2")[0], Math.random());
typeText(doc.defaultView.frames[0].frames[1].document.getElementById("in1"), new Date());
frameCount = 0;
let tab2 = gBrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
// wait for all frames to load completely
if (frameCount++ < 5)
return;
tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab2, function() {
let doc = tab2.linkedBrowser.contentDocument;
let win = tab2.linkedBrowser.contentWindow;
isnot(doc.getElementById("out1").value,
@ -59,6 +53,6 @@ function test() {
gBrowser.removeTab(tab);
finish();
}, true);
});
}, true);
}

View File

@ -13,8 +13,7 @@ function test() {
// set a unique value on a new, blank tab
let tab1 = gBrowser.addTab();
tab1.linkedBrowser.addEventListener("load", function() {
tab1.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab1.linkedBrowser, function() {
ss.setTabValue(tab1, uniqueName, uniqueValue1);
// duplicate the tab with that value
@ -26,14 +25,13 @@ function test() {
// overwrite the tab with the value which should remove it
ss.setTabState(tab1, JSON.stringify({ entries: [] }));
tab1.linkedBrowser.addEventListener("load", function() {
tab1.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab1, function() {
is(ss.getTabValue(tab1, uniqueName), "", "tab value was cleared");
// clean up
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab1);
finish();
}, true);
}, true);
});
});
}

View File

@ -18,15 +18,13 @@ function test() {
"browser/components/sessionstore/test/browser_466937_sample.html";
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
let doc = tab.linkedBrowser.contentDocument;
doc.getElementById("reverse_thief").value = "/home/user/secret2";
doc.getElementById("bystander").value = testPath;
let tab2 = gBrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab2, function() {
doc = tab2.linkedBrowser.contentDocument;
is(doc.getElementById("thief").value, "",
"file path wasn't set to text field value");
@ -40,6 +38,6 @@ function test() {
gBrowser.removeTab(tab);
finish();
}, true);
}, true);
});
});
}

View File

@ -13,14 +13,12 @@ function test() {
let tab = gBrowser.addTab("about:sessionrestore");
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.addEventListener("load", function(aEvent) {
browser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(browser, function() {
let doc = browser.contentDocument;
// click on the "Start New Session" button after about:sessionrestore is loaded
doc.getElementById("errorCancel").click();
browser.addEventListener("load", function(aEvent) {
browser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(browser, function() {
let doc = browser.contentDocument;
is(doc.URL, "about:blank", "loaded page is about:blank");
@ -31,14 +29,12 @@ function test() {
gPrefService.setCharPref("browser.startup.homepage", homepage);
gPrefService.setIntPref("browser.startup.page", 1);
gBrowser.loadURI("about:sessionrestore");
browser.addEventListener("load", function(aEvent) {
browser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(browser, function() {
let doc = browser.contentDocument;
// click on the "Start New Session" button after about:sessionrestore is loaded
doc.getElementById("errorCancel").click();
browser.addEventListener("load", function(aEvent) {
browser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(browser, function() {
let doc = browser.contentDocument;
is(doc.URL, homepage, "loaded page is the homepage");
@ -48,8 +44,8 @@ function test() {
gPrefService.clearUserPref("browser.startup.page");
gPrefService.clearUserPref("browser.startup.homepage");
finish();
}, true);
}, true);
}, true);
}, true);
});
});
});
});
}

View File

@ -12,15 +12,13 @@ function test() {
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_485482_sample.html";
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
let doc = tab.linkedBrowser.contentDocument;
doc.querySelector("input[type=text]").value = uniqueValue;
doc.querySelector("input[type=checkbox]").checked = true;
let tab2 = gBrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab2, function() {
doc = tab2.linkedBrowser.contentDocument;
is(doc.querySelector("input[type=text]").value, uniqueValue,
"generated XPath expression was valid");
@ -31,6 +29,6 @@ function test() {
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab);
finish();
}, true);
}, true);
});
});
}

View File

@ -10,8 +10,7 @@ function test() {
let uniqueValue = Math.random() + "\u2028Second line\u2029Second paragraph\u2027";
let tab = gBrowser.addTab();
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
ss.setTabValue(tab, "bug485563", uniqueValue);
let tabState = JSON.parse(ss.getTabState(tab));
is(tabState.extData["bug485563"], uniqueValue,
@ -23,5 +22,5 @@ function test() {
gBrowser.removeTab(tab);
finish();
}, true);
});
}

View File

@ -14,9 +14,7 @@ function test() {
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.addEventListener("load", function() {
browser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(browser, function() {
let tabState = JSON.parse(ss.getTabState(tab));
is(tabState.entries[0].referrer, REFERRER1,
"Referrer retrieved via getTabState matches referrer set via loadURI.");
@ -24,29 +22,20 @@ function test() {
tabState.entries[0].referrer = REFERRER2;
ss.setTabState(tab, JSON.stringify(tabState));
tab.addEventListener("SSTabRestored", function(e) {
tab.removeEventListener("SSTabRestored", arguments.callee, true);
whenTabRestored(tab, function(e) {
is(window.content.document.referrer, REFERRER2, "document.referrer matches referrer set via setTabState.");
gBrowser.removeTab(tab);
// Call stopPropagation on the event so we won't fire the
// tabbrowser's SSTabRestored listeners.
e.stopPropagation();
let newTab = ss.undoCloseTab(window, 0);
newTab.addEventListener("SSTabRestored", function(e) {
newTab.removeEventListener("SSTabRestored", arguments.callee, true);
whenTabRestored(newTab, function() {
is(window.content.document.referrer, REFERRER2, "document.referrer is still correct after closing and reopening the tab.");
gBrowser.removeTab(newTab);
// Call stopPropagation on the event so we won't fire the
// tabbrowser's SSTabRestored listeners.
e.stopPropagation();
finish();
}, true);
}, true);
},true);
});
});
});
let referrerURI = Services.io.newURI(REFERRER1, null, null);
browser.loadURI("http://example.org", referrerURI, null);

View File

@ -72,16 +72,12 @@ function test() {
// http://example.com. We need to load the blank window first, otherwise the
// docshell gets confused and doesn't have a current history entry.
let tab = gBrowser.addTab("about:blank");
let tabBrowser = tab.linkedBrowser;
let browser = tab.linkedBrowser;
tabBrowser.addEventListener("load", function(aEvent) {
tabBrowser.removeEventListener("load", arguments.callee, true);
tabBrowser.loadURI("http://example.com", null, null);
tabBrowser.addEventListener("load", function(aEvent) {
tabBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(browser, function() {
browser.loadURI("http://example.com", null, null);
whenBrowserLoaded(browser, function() {
// After these push/replaceState calls, the window should have three
// history entries:
// testURL (state object: null) <-- oldest
@ -102,13 +98,12 @@ function test() {
ss.setTabState(tab2, state, true);
// Run checkState() once the tab finishes loading its restored state.
tab2.linkedBrowser.addEventListener("load", function() {
tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab2, function() {
SimpleTest.executeSoon(function() {
checkState(tab2);
});
}, true);
});
}, true);
}, true);
});
});
}

View File

@ -47,9 +47,7 @@ function test() {
// create and select a first tab
let tab = gBrowser.addTab(TEST_URL);
tab.linkedBrowser.addEventListener("load", function loadListener(e) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
// step1: the above has triggered some saveStateDelayed(), sleep until
// it's done, and get the initial sessionstore.js mtime
setTimeout(function step1(e) {
@ -72,5 +70,5 @@ function test() {
finish();
}, 3500); // end of sleep after tab selection and scrolling
}, 3500); // end of sleep after initial saveStateDelayed()
}, true);
});
}

View File

@ -198,24 +198,20 @@ function test() {
event.initEvent("input", true, false);
gURLBar.dispatchEvent(event);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
executeSoon(function () {
is(browser.userTypedValue, "example.org",
"userTypedValue was set when changing gURLBar.value");
is(browser.userTypedClear, 0,
"userTypedClear was not changed when changing gURLBar.value");
executeSoon(function () {
is(browser.userTypedValue, "example.org",
"userTypedValue was set when changing gURLBar.value");
is(browser.userTypedClear, 0,
"userTypedClear was not changed when changing gURLBar.value");
// Now make sure ss gets these values too
let newState = JSON.parse(ss.getBrowserState());
is(newState.windows[0].tabs[0].userTypedValue, "example.org",
"sessionstore got correct userTypedValue");
is(newState.windows[0].tabs[0].userTypedClear, 0,
"sessionstore got correct userTypedClear");
runNextTest();
});
}, true);
// Now make sure ss gets these values too
let newState = JSON.parse(ss.getBrowserState());
is(newState.windows[0].tabs[0].userTypedValue, "example.org",
"sessionstore got correct userTypedValue");
is(newState.windows[0].tabs[0].userTypedClear, 0,
"sessionstore got correct userTypedClear");
runNextTest();
});
});
}

View File

@ -5,12 +5,10 @@
function test() {
let tab1 = gBrowser.addTab("about:rights");
let tab2 = gBrowser.addTab("about:mozilla");
tab1.linkedBrowser.addEventListener("load", mainPart, true);
whenBrowserLoaded(tab1.linkedBrowser, mainPart);
waitForExplicitFinish();
function mainPart() {
tab1.linkedBrowser.removeEventListener("load", mainPart, true);
// Tell the session storer that the tab is pinned
let newTabState = '{"entries":[{"url":"about:rights"}],"pinned":true,"userTypedValue":"Hello World!"}';
ss.setTabState(tab1, newTabState);

View File

@ -4,9 +4,7 @@ function test() {
var tab1 = gBrowser.addTab("data:text/plain;charset=utf-8,foo");
gBrowser.pinTab(tab1);
tab1.linkedBrowser.addEventListener("load", function () {
tab1.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab1.linkedBrowser, function() {
var tab2 = gBrowser.addTab();
gBrowser.pinTab(tab2);
@ -19,5 +17,5 @@ function test() {
gBrowser.removeTab(tab1);
gBrowser.removeTab(tab2);
finish();
}, true);
});
}

View File

@ -163,9 +163,7 @@ function onStateRestored(aSubject, aTopic, aData) {
newWin.addEventListener("load", function(aEvent) {
newWin.removeEventListener("load", arguments.callee, false);
newWin.gBrowser.selectedBrowser.addEventListener("load", function() {
newWin.gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(newWin.gBrowser.selectedBrowser, function() {
// pin this tab
if (shouldPinTab)
newWin.gBrowser.pinTab(newWin.gBrowser.selectedTab);
@ -197,7 +195,7 @@ function onStateRestored(aSubject, aTopic, aData) {
else {
newWin.BrowserTryToCloseWindow();
}
}, true);
});
}, false);
}

View File

@ -82,12 +82,10 @@ function testTabRestoreData(aFormData, aExpectedValues, aCallback) {
let tab = gBrowser.addTab(testURL);
let tabState = { entries: [{ url: testURL, formdata: aFormData}] };
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
ss.setTabState(tab, JSON.stringify(tabState));
tab.addEventListener("SSTabRestored", function(aEvent) {
tab.removeEventListener("SSTabRestored", arguments.callee);
whenTabRestored(tab, function() {
let doc = tab.linkedBrowser.contentDocument;
let select = doc.getElementById("select_id");
let value = select.options[select.selectedIndex].value;
@ -98,9 +96,7 @@ function testTabRestoreData(aFormData, aExpectedValues, aCallback) {
// clean up
gBrowser.removeTab(tab);
// Call stopPropagation on the event so we won't fire the
// tabbrowser's SSTabRestored listeners.
aEvent.stopPropagation();
aCallback();
});
@ -127,5 +123,5 @@ function testTabRestoreData(aFormData, aExpectedValues, aCallback) {
is(value, aExpectedValues[0],
"Collection has been saved correctly");
});
}, true);
});
}

View File

@ -178,9 +178,6 @@ function testOnWindow(aIsPrivate, aCallback) {
}
function waitForTabLoad(aWin, aURL, aCallback) {
aWin.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
aWin.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
executeSoon(aCallback);
}, true);
whenBrowserLoaded(aWin.gBrowser.selectedBrowser, aCallback);
aWin.gBrowser.selectedBrowser.loadURI(aURL);
}

View File

@ -52,7 +52,7 @@ function runTests() {
// Let the pending tab load.
gBrowser.selectedTab = tab;
yield whenBrowserLoaded(tab.linkedBrowser);
yield whenTabRestored(tab);
// Ensure no 'image' or 'pending' attributes are stored.
let {attributes} = JSON.parse(ss.getTabState(tab));

View File

@ -14,8 +14,7 @@ function test() {
let testURL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_form_restore_events_sample.html";
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenBrowserLoaded(tab.linkedBrowser, function() {
let doc = tab.linkedBrowser.contentDocument;
// text fields
@ -45,8 +44,7 @@ function test() {
let tab2 = gBrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
whenTabRestored(tab2, function() {
let doc = tab2.linkedBrowser.contentDocument;
let inputFired = doc.getElementById("inputFired").textContent.trim().split();
let changeFired = doc.getElementById("changeFired").textContent.trim().split();
@ -62,6 +60,6 @@ function test() {
gBrowser.removeTab(tab);
finish();
}, true);
}, true);
});
});
}

View File

@ -137,5 +137,3 @@ add_task(function browsing() {
gBrowser.removeTab(tab2);
gBrowser.removeTab(tab1);
});

View File

@ -311,6 +311,15 @@ function whenWindowLoaded(aWindow, aCallback = next) {
}, false);
}
function whenTabRestored(aTab, aCallback = next) {
aTab.addEventListener("SSTabRestored", function onRestored(aEvent) {
aTab.removeEventListener("SSTabRestored", onRestored, true);
executeSoon(function executeWhenTabRestored() {
aCallback();
});
}, true);
}
var gUniqueCounter = 0;
function r() {
return Date.now() + "-" + (++gUniqueCounter);
@ -349,7 +358,7 @@ let gProgressListener = {
function gProgressListener_onStateChange(aBrowser, aWebProgress, aRequest,
aStateFlags, aStatus) {
if ((!this._checkRestoreState ||
aBrowser.__SS_restoreState == TAB_STATE_RESTORING) &&
(aBrowser.__SS_restoreState && aBrowser.__SS_restoreState == TAB_STATE_RESTORING)) &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
@ -364,12 +373,12 @@ let gProgressListener = {
for (let win in BrowserWindowIterator()) {
for (let i = 0; i < win.gBrowser.tabs.length; i++) {
let browser = win.gBrowser.tabs[i].linkedBrowser;
if (browser.__SS_restoreState == TAB_STATE_RESTORING)
if (!browser.__SS_restoreState)
wasRestored++;
else if (browser.__SS_restoreState == TAB_STATE_RESTORING)
isRestoring++;
else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
needsRestore++;
else
wasRestored++;
}
}
return [needsRestore, isRestoring, wasRestored];

View File

@ -218,22 +218,6 @@
fun:_ZN7mozilla3gfx5ScaleEPhiiiS1_iiiNS0_13SurfaceFormatE
...
}
{
Bug 812421 (all 32-bit systems)
Memcheck:Leak
fun:malloc
fun:_ZN3JSC19ExecutableAllocator10createPoolEj
fun:_ZN3JSC19ExecutableAllocator11poolForSizeEj
...
}
{
Bug 812421 (all 64-bit systems)
Memcheck:Leak
fun:malloc
fun:_ZN3JSC19ExecutableAllocator10createPoolEm
fun:_ZN3JSC19ExecutableAllocator11poolForSizeEm
...
}
{
Bug 812422
Memcheck:Leak

View File

@ -16,6 +16,7 @@
#include "nsJSPrincipals.h"
#include "nsCOMPtr.h"
#include "nsPrincipal.h"
#include "nsIContentSecurityPolicy.h"
class nsIURI;
@ -53,6 +54,7 @@ public:
virtual ~nsNullPrincipal();
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
};
#endif // nsNullPrincipal_h__

View File

@ -149,8 +149,11 @@ nsNullPrincipal::GetHashValue(uint32_t *aResult)
NS_IMETHODIMP
nsNullPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
{
// We don't actually do security policy caching. And it's not like anyone
// can set a security policy for us anyway.
// Leftover from old security model, a "security policy" is a set of
// rules for property access that can override the SOP. Policies are
// associated with origins and since nsNullPinricipals never get the
// same origin twice, it's not possible to specify a "security
// policy" for it. Hence, we do not cache the security policy.
*aSecurityPolicy = nullptr;
return NS_OK;
}
@ -158,8 +161,11 @@ nsNullPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
NS_IMETHODIMP
nsNullPrincipal::SetSecurityPolicy(void* aSecurityPolicy)
{
// We don't actually do security policy caching. And it's not like anyone
// can set a security policy for us anyway.
// Leftover from old security model, a "security policy" is a set of
// rules for property access that can override the SOP. Policies are
// associated with origins and since nsNullPinricipals never get the
// same origin twice, it's not possible to specify a "security
// policy" for it. Hence, we do not cache the security policy.
return NS_OK;
}
@ -172,16 +178,20 @@ nsNullPrincipal::GetURI(nsIURI** aURI)
NS_IMETHODIMP
nsNullPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
{
// CSP on a null principal makes no sense
*aCsp = nullptr;
NS_IF_ADDREF(*aCsp = mCSP);
return NS_OK;
}
NS_IMETHODIMP
nsNullPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
{
// CSP on a null principal makes no sense
return NS_ERROR_NOT_AVAILABLE;
// If CSP was already set, it should not be destroyed! Instead, it should
// get set anew when a new principal is created.
if (mCSP)
return NS_ERROR_ALREADY_INITIALIZED;
mCSP = aCsp;
return NS_OK;
}
NS_IMETHODIMP

View File

@ -131,7 +131,7 @@ CPPSRCS += $(CPP_UNIT_TESTS)
CPP_UNIT_TEST_BINS := $(CPP_UNIT_TESTS:.cpp=$(BIN_SUFFIX))
SIMPLE_PROGRAMS += $(CPP_UNIT_TEST_BINS)
INCLUDES += -I$(DIST)/include/testing
LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) $(MOZ_JS_LIBS) $(if $(JS_SHARED_LIBRARY),,$(MOZ_ZLIB_LIBS))
LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) $(if $(JS_SHARED_LIBRARY),,$(MOZ_ZLIB_LIBS))
ifndef MOZ_PROFILE_GENERATE
libs:: $(CPP_UNIT_TEST_BINS) $(call mkdir_deps,$(DIST)/cppunittests)
@ -1709,3 +1709,7 @@ include $(topsrcdir)/config/makefiles/autotargets.mk
ifneq ($(NULL),$(AUTO_DEPS))
default all libs tools export:: $(AUTO_DEPS)
endif
export:: $(GENERATED_FILES)
GARBAGE += $(GENERATED_FILES)

View File

@ -51,9 +51,9 @@ function ContentSecurityPolicy() {
this._request = "";
this._requestOrigin = "";
this._requestPrincipal = "";
this._weakRequestPrincipal = { get : function() { return null; } };
this._referrer = "";
this._docRequest = null;
this._weakDocRequest = { get : function() { return null; } };
CSPdebug("CSP object initialized, no policies to enforce yet");
this._cache = { };
@ -296,7 +296,7 @@ ContentSecurityPolicy.prototype = {
return;
// Save the docRequest for fetching a policy-uri
this._docRequest = aChannel;
this._weakDocRequest = Cu.getWeakReference(aChannel);
// save the document URI (minus <fragment>) and referrer for reporting
let uri = aChannel.URI.cloneIgnoringRef();
@ -307,8 +307,9 @@ ContentSecurityPolicy.prototype = {
this._requestOrigin = uri;
//store a reference to the principal, that can later be used in shouldLoad
this._requestPrincipal = Components.classes["@mozilla.org/scriptsecuritymanager;1"].
getService(Components.interfaces.nsIScriptSecurityManager).getChannelPrincipal(aChannel);
this._weakRequestPrincipal = Cu.getWeakReference(Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager)
.getChannelPrincipal(aChannel));
if (aChannel.referrer) {
let referrer = aChannel.referrer.cloneIgnoringRef();
@ -358,13 +359,13 @@ ContentSecurityPolicy.prototype = {
newpolicy = CSPRep.fromStringSpecCompliant(aPolicy,
selfURI,
aReportOnly,
this._docRequest,
this._weakDocRequest.get(),
this);
} else {
newpolicy = CSPRep.fromString(aPolicy,
selfURI,
aReportOnly,
this._docRequest,
this._weakDocRequest.get(),
this);
}
@ -481,8 +482,8 @@ ContentSecurityPolicy.prototype = {
// we need to set an nsIChannelEventSink on the channel object
// so we can tell it to not follow redirects when posting the reports
chan.notificationCallbacks = new CSPReportRedirectSink(policy);
if (this._docRequest) {
chan.loadGroup = this._docRequest.loadGroup;
if (this._weakDocRequest.get()) {
chan.loadGroup = this._weakDocRequest.get().loadGroup;
}
chan.QueryInterface(Ci.nsIUploadChannel)
@ -501,7 +502,7 @@ ContentSecurityPolicy.prototype = {
.getService(Ci.nsIContentPolicy);
if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_CSP_REPORT,
chan.URI, this._requestOrigin,
null, null, null, this._requestPrincipal)
null, null, null, this._weakRequestPrincipal.get())
!= Ci.nsIContentPolicy.ACCEPT) {
continue; // skip unauthorized URIs
}

View File

@ -30,6 +30,7 @@
#include "mozilla/dom/Element.h"
#include "xpcpublic.h"
#include "nsObserverService.h"
#include "nsFocusManager.h"
using namespace mozilla::dom;
@ -210,6 +211,16 @@ MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS,
EnumerateAll(MarkUserData, &nsCCUncollectableMarker::sGeneration);
}
}
if (doc) {
nsPIDOMWindow* inner = doc->GetInnerWindow();
if (inner) {
inner->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
}
nsPIDOMWindow* outer = doc->GetWindow();
if (outer) {
outer->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
}
}
}
void MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS,
@ -336,6 +347,8 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
++sGeneration;
}
nsFocusManager::MarkUncollectableForCCGeneration(sGeneration);
nsresult rv;
// Iterate all toplevel windows

View File

@ -2463,8 +2463,11 @@ nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
NS_ENSURE_SUCCESS(rv, rv);
}
nsresult rv = InitCSP(aChannel);
NS_ENSURE_SUCCESS(rv, rv);
// If this is not a data document, set CSP.
if (!mLoadedAsData) {
nsresult rv = InitCSP(aChannel);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}

View File

@ -0,0 +1,27 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- this should be allowed (no CSP)-->
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img_good&type=img/png"> </img>
<script type="text/javascript">
var req = new XMLHttpRequest();
req.onload = function() {
//this should be allowed (no CSP)
try {
var img = document.createElement("img");
img.src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img2_good&type=img/png";
document.body.appendChild(img);
} catch(e) {
console.log("yo: "+e);
}
};
req.open("get", "file_CSP_bug941404_xhr.html", true);
req.responseType = "document";
req.send();
</script>
</body>
</html>

View File

@ -0,0 +1,5 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
</body>
</html>

View File

@ -0,0 +1 @@
Content-Security-Policy: default-src 'none' 'unsafe-inline' 'unsafe-eval'

View File

@ -0,0 +1,15 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox="allow-same-origin" -->
<!-- Content-Security-Policy: default-src 'self' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img_bad&type=img/png"> </img>
<!-- these should load ok -->
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img_good&type=img/png" />
<script src='/tests/content/base/test/csp/file_CSP.sjs?testid=scripta_bad&type=text/javascript'></script>
</body>
</html>

View File

@ -0,0 +1 @@
Content-Security-Policy: default-src 'self'

View File

@ -0,0 +1,14 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox -->
<!-- Content-Security-Policy: default-src 'self' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img2_bad&type=img/png"> </img>
<!-- these should load ok -->
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img2a_good&type=img/png" />
</body>
</html>

View File

@ -0,0 +1 @@
Content-Security-Policy: default-src 'self'

View File

@ -0,0 +1,12 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox -->
<!-- Content-Security-Policy: default-src 'none' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img3_bad&type=img/png"> </img>
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img3a_bad&type=img/png" />
</body>
</html>

View File

@ -0,0 +1 @@
Content-Security-Policy: default-src 'none'

View File

@ -0,0 +1,12 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox -->
<!-- Content-Security-Policy: default-src 'none' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img4_bad&type=img/png"> </img>
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img4a_bad&type=img/png" />
</body>
</html>

View File

@ -0,0 +1 @@
Content-Security-Policy: default-src 'none'

View File

@ -0,0 +1,26 @@
<!DOCTYPE HTML>
<html>
<head> <meta charset="utf-8"> </head>
<script type="text/javascript">
function ok(result, desc) {
window.parent.postMessage({ok: result, desc: desc}, "*");
}
function doStuff() {
ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
}
</script>
<script src='file_iframe_sandbox_pass.js'></script>
<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
I am sandboxed but with only inline "allow-scripts"
<!-- sandbox="allow-scripts" -->
<!-- Content-Security-Policy: default-src 'none' 'unsafe-inline'-->
<!-- these should be stopped by CSP -->
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img5_bad&type=img/png" />
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img5a_bad&type=img/png"> </img>
<script src='/tests/content/base/test/csp/file_CSP.sjs?testid=script5_bad&type=text/javascript'></script>
<script src='http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=script5a_bad&type=text/javascript'></script>
</body>
</html>

View File

@ -0,0 +1 @@
Content-Security-Policy: default-src 'none' 'unsafe-inline';

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<script type="text/javascript">
function ok(result, desc) {
window.parent.postMessage({ok: result, desc: desc}, "*");
}
function doStuff() {
ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
document.getElementById('a_form').submit();
// trigger the javascript: url test
sendMouseEvent({type:'click'}, 'a_link');
}
</script>
<script src='file_iframe_sandbox_pass.js'></script>
<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
I am sandboxed but with "allow-scripts"
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img6_bad&type=img/png"> </img>
<script src='http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=script6_bad&type=text/javascript'></script>
<form method="get" action="file_iframe_sandbox_form_fail.html" id="a_form">
First name: <input type="text" name="firstname">
Last name: <input type="text" name="lastname">
<input type="submit" onclick="doSubmit()" id="a_button">
</form>
<a href = 'javascript:ok(true, "documents sandboxed with allow-scripts should be able to run script from javascript: URLs");' id='a_link'>click me</a>
</body>
</html>

View File

@ -0,0 +1 @@
Content-Security-Policy: default-src 'self' 'unsafe-inline';

View File

@ -68,6 +68,18 @@ support-files =
file_bug836922_npolicies.html^headers^
file_bug836922_npolicies_ro_violation.sjs
file_bug836922_npolicies_violation.sjs
file_bug886164.html
file_bug886164.html^headers^
file_bug886164_2.html
file_bug886164_2.html^headers^
file_bug886164_3.html
file_bug886164_3.html^headers^
file_bug886164_4.html
file_bug886164_4.html^headers^
file_bug886164_5.html
file_bug886164_5.html^headers^
file_bug886164_6.html
file_bug886164_6.html^headers^
file_csp_bug768029.html
file_csp_bug768029.sjs
file_csp_bug773891.html
@ -87,6 +99,9 @@ support-files =
file_policyuri_regression_from_multipolicy_policy
file_nonce_source.html
file_nonce_source.html^headers^
file_CSP_bug941404.html
file_CSP_bug941404_xhr.html
file_CSP_bug941404_xhr.html^headers^
[test_CSP.html]
[test_CSP_bug663567.html]
@ -101,8 +116,10 @@ support-files =
[test_CSP_inlinestyle.html]
[test_bothCSPheaders.html]
[test_bug836922_npolicies.html]
[test_bug886164.html]
[test_csp_redirects.html]
[test_CSP_bug910139.html]
[test_CSP_bug909029.html]
[test_policyuri_regression_from_multipolicy.html]
[test_nonce_source.html]
[test_CSP_bug941404.html]

View File

@ -0,0 +1,115 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Bug 941404 - Data documents should not set CSP</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
<script class="testbody" type="text/javascript">
var path = "/tests/content/base/test/csp/";
// These are test results: -1 means it hasn't run,
// true/false is the pass/fail result.
window.tests = {
img_good: -1,
img2_good: -1,
};
//csp related
// This is used to watch the blocked data bounce off CSP and allowed data
// get sent out to the wire.
function examiner() {
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
SpecialPowers.addObserver(this, "http-on-modify-request", false);
}
examiner.prototype = {
observe: function(subject, topic, data) {
// subject should be an nsURI, and should be either allowed or blocked.
if (!SpecialPowers.can_QI(subject))
return;
var testpat = new RegExp("testid=([a-z0-9_]+)");
//_good things better be allowed!
//_bad things better be stopped!
if (topic === "http-on-modify-request") {
//these things were allowed by CSP
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
if (!testpat.test(asciiSpec)) return;
var testid = testpat.exec(asciiSpec)[1];
window.testResult(testid,
/_good/.test(testid),
asciiSpec + " allowed by csp");
}
if(topic === "csp-on-violate-policy") {
//these were blocked... record that they were blocked
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
if (!testpat.test(asciiSpec)) return;
var testid = testpat.exec(asciiSpec)[1];
window.testResult(testid,
/_bad/.test(testid),
asciiSpec + " blocked by \"" + data + "\"");
}
},
// must eventually call this to remove the listener,
// or mochitests might get borked.
remove: function() {
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
SpecialPowers.removeObserver(this, "http-on-modify-request");
}
}
window.examiner = new examiner();
window.testResult = function(testname, result, msg) {
//test already complete.... forget it... remember the first result.
if (window.tests[testname] != -1)
return;
window.tests[testname] = result;
is(result, true, testname + ' test: ' + msg);
// if any test is incomplete, keep waiting
for (var v in window.tests)
if(tests[v] == -1) {
console.log(v + " is not complete");
return;
}
// ... otherwise, finish
window.examiner.remove();
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv(
{'set':[["security.csp.speccompliant", true]]},
function() {
// save this for last so that our listeners are registered.
// ... this loads the testbed of good and bad requests.
document.getElementById('cspframe').src = 'file_CSP_bug941404.html';
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,183 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Bug 886164 - Enforce CSP in sandboxed iframe</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<iframe style="width:200px;height:200px;" id='cspframe' sandbox="allow-same-origin"></iframe>
<iframe style="width:200px;height:200px;" id='cspframe2' sandbox></iframe>
<iframe style="width:200px;height:200px;" id='cspframe3' sandbox="allow-same-origin"></iframe>
<iframe style="width:200px;height:200px;" id='cspframe4' sandbox></iframe>
<iframe style="width:200px;height:200px;" id='cspframe5' sandbox="allow-scripts"></iframe>
<iframe style="width:200px;height:200px;" id='cspframe6' sandbox="allow-same-origin allow-scripts"></iframe>
<script class="testbody" type="text/javascript">
var path = "/tests/content/base/test/csp/";
// These are test results: -1 means it hasn't run,
// true/false is the pass/fail result.
window.tests = {
// sandbox allow-same-origin; 'self'
img_good: -1, // same origin
img_bad: -1, //example.com
// sandbox; 'self'
img2_bad: -1, //example.com
img2a_good: -1, // same origin & is image
// sandbox allow-same-origin; 'none'
img3_bad: -1,
img3a_bad: -1,
// sandbox; 'none'
img4_bad: -1,
img4a_bad: -1,
// sandbox allow-scripts; 'none' 'unsafe-inline'
img5_bad: -1,
img5a_bad: -1,
script5_bad: -1,
script5a_bad: -1,
// sandbox allow-same-origin allow-scripts; 'self' 'unsafe-inline'
img6_bad: -1,
script6_bad: -1,
};
// a postMessage handler that is used by sandboxed iframes without
// 'allow-same-origin' to communicate pass/fail back to this main page.
// it expects to be called with an object like {ok: true/false, desc:
// <description of the test> which it then forwards to ok()
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
ok_wrapper(event.data.ok, event.data.desc);
}
var cspTestsDone = false;
var iframeSandboxTestsDone = false;
// iframe related
var completedTests = 0;
var passedTests = 0;
function ok_wrapper(result, desc) {
ok(result, desc);
completedTests++;
if (result) {
passedTests++;
}
if (completedTests === 5) {
iframeSandboxTestsDone = true;
if (cspTestsDone) {
SimpleTest.finish();
}
}
}
//csp related
// This is used to watch the blocked data bounce off CSP and allowed data
// get sent out to the wire.
function examiner() {
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
SpecialPowers.addObserver(this, "http-on-modify-request", false);
}
examiner.prototype = {
observe: function(subject, topic, data) {
// subject should be an nsURI, and should be either allowed or blocked.
if (!SpecialPowers.can_QI(subject))
return;
var testpat = new RegExp("testid=([a-z0-9_]+)");
//_good things better be allowed!
//_bad things better be stopped!
if (topic === "http-on-modify-request") {
//these things were allowed by CSP
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
if (!testpat.test(asciiSpec)) return;
var testid = testpat.exec(asciiSpec)[1];
window.testResult(testid,
/_good/.test(testid),
asciiSpec + " allowed by csp");
}
if(topic === "csp-on-violate-policy") {
//these were blocked... record that they were blocked
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
if (!testpat.test(asciiSpec)) return;
var testid = testpat.exec(asciiSpec)[1];
window.testResult(testid,
/_bad/.test(testid),
asciiSpec + " blocked by \"" + data + "\"");
}
},
// must eventually call this to remove the listener,
// or mochitests might get borked.
remove: function() {
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
SpecialPowers.removeObserver(this, "http-on-modify-request");
}
}
window.examiner = new examiner();
window.testResult = function(testname, result, msg) {
//test already complete.... forget it... remember the first result.
if (window.tests[testname] != -1)
return;
window.tests[testname] = result;
ok(result, testname + ' test: ' + msg);
// if any test is incomplete, keep waiting
for (var v in window.tests)
if(tests[v] == -1)
return;
// ... otherwise, finish
window.examiner.remove();
cspTestsDone = true;
if (iframeSandboxTestsDone) {
SimpleTest.finish();
}
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv(
{'set':[["security.csp.speccompliant", true]]},
function() {
// save this for last so that our listeners are registered.
// ... this loads the testbed of good and bad requests.
document.getElementById('cspframe').src = 'file_bug886164.html';
document.getElementById('cspframe2').src = 'file_bug886164_2.html';
document.getElementById('cspframe3').src = 'file_bug886164_3.html';
document.getElementById('cspframe4').src = 'file_bug886164_4.html';
document.getElementById('cspframe5').src = 'file_bug886164_5.html';
document.getElementById('cspframe6').src = 'file_bug886164_6.html';
});
</script>
</pre>
</body>
</html>

View File

@ -12,10 +12,11 @@
<body>
<iframe style="width:200px;height:200px;" id='testframe'></iframe>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv(
{'set':[["security.csp.speccompliant", true]]},
function() {
SimpleTest.waitForExplicitFinish();
function () {
var testframe = document.getElementById('testframe');
testframe.src = 'file_policyuri_regression_from_multipolicy.html';
testframe.addEventListener('load', function checkInlineScriptExecuted () {

View File

@ -1173,6 +1173,10 @@ WebGLContext::ForceClearFramebufferWithDefaultValues(GLbitfield mask, const bool
bool
WebGLContext::PresentScreenBuffer()
{
if (IsContextLost()) {
return false;
}
if (!mShouldPresent) {
return false;
}

View File

@ -371,7 +371,7 @@ public:
return GetTexParameter(target, pname);
}
JS::Value GetUniform(JSContext* cx, WebGLProgram *prog,
WebGLUniformLocation *location, ErrorResult& rv);
WebGLUniformLocation *location);
already_AddRefed<WebGLUniformLocation>
GetUniformLocation(WebGLProgram *prog, const nsAString& name);
void Hint(GLenum target, GLenum mode);
@ -425,7 +425,7 @@ public:
nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(elt);
rv = SurfaceFromElementResultToImageSurface(res, getter_AddRefs(isurf),
&srcFormat);
if (rv.Failed())
if (rv.Failed() || !isurf)
return;
uint32_t byteLength = isurf->Stride() * isurf->Height();
@ -464,7 +464,7 @@ public:
nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(elt);
rv = SurfaceFromElementResultToImageSurface(res, getter_AddRefs(isurf),
&srcFormat);
if (rv.Failed())
if (rv.Failed() || !isurf)
return;
uint32_t byteLength = isurf->Stride() * isurf->Height();

View File

@ -1807,7 +1807,7 @@ WebGLContext::GetTexParameter(GLenum target, GLenum pname)
JS::Value
WebGLContext::GetUniform(JSContext* cx, WebGLProgram *prog,
WebGLUniformLocation *location, ErrorResult& rv)
WebGLUniformLocation *location)
{
if (IsContextLost())
return JS::NullValue();
@ -1876,20 +1876,20 @@ WebGLContext::GetUniform(JSContext* cx, WebGLProgram *prog,
}
if (index == uniforms) {
rv.Throw(NS_ERROR_FAILURE); // XXX GL error? shouldn't happen.
GenerateWarning("getUniform: internal error: hit an OpenGL driver bug");
return JS::NullValue();
}
GLenum baseType;
GLint unitSize;
if (!BaseTypeAndSizeFromUniformType(uniformType, &baseType, &unitSize)) {
rv.Throw(NS_ERROR_FAILURE);
GenerateWarning("getUniform: internal error: unknown uniform type 0x%x", uniformType);
return JS::NullValue();
}
// this should never happen
if (unitSize > 16) {
rv.Throw(NS_ERROR_FAILURE);
GenerateWarning("getUniform: internal error: unexpected uniform unit size %d", unitSize);
return JS::NullValue();
}
@ -1901,7 +1901,7 @@ WebGLContext::GetUniform(JSContext* cx, WebGLProgram *prog,
} else {
JSObject* obj = Float32Array::Create(cx, this, unitSize, fv);
if (!obj) {
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
ErrorOutOfMemory("getUniform: out of memory");
}
return JS::ObjectOrNullValue(obj);
}
@ -1913,7 +1913,7 @@ WebGLContext::GetUniform(JSContext* cx, WebGLProgram *prog,
} else {
JSObject* obj = Int32Array::Create(cx, this, unitSize, iv);
if (!obj) {
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
ErrorOutOfMemory("getUniform: out of memory");
}
return JS::ObjectOrNullValue(obj);
}
@ -1928,7 +1928,7 @@ WebGLContext::GetUniform(JSContext* cx, WebGLProgram *prog,
uv[k] = JS::BooleanValue(iv[k] ? true : false);
JSObject* obj = JS_NewArrayObject(cx, unitSize, uv);
if (!obj) {
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
ErrorOutOfMemory("getUniform: out of memory");
}
return JS::ObjectOrNullValue(obj);
}
@ -2639,11 +2639,14 @@ nsresult
WebGLContext::SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
gfxImageSurface **imageOut, WebGLTexelFormat *format)
{
*imageOut = nullptr;
*format = WebGLTexelFormat::None;
if (!res.mSurface)
return NS_ERROR_FAILURE;
return NS_OK;
if (res.mSurface->GetType() != gfxSurfaceTypeImage) {
// SurfaceFromElement lied!
return NS_ERROR_FAILURE;
return NS_OK;
}
// We disallow loading cross-domain images and videos that have not been validated

View File

@ -37,7 +37,7 @@ class WebGLImageConverter
* texels with typed pointers and this value will tell us by how much we need
* to increment these pointers to advance to the next texel.
*/
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
static size_t NumElementsPerTexelForFormat() {
switch (Format) {
case WebGLTexelFormat::R8:
@ -73,9 +73,9 @@ class WebGLImageConverter
* to return immediately in these cases to allow the compiler to avoid generating
* useless code.
*/
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) SrcFormat,
MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) DstFormat,
MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelPremultiplicationOp) PremultiplicationOp>
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) SrcFormat,
MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) DstFormat,
MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelPremultiplicationOp) PremultiplicationOp>
void run()
{
// check for never-called cases. We early-return to allow the compiler
@ -146,9 +146,9 @@ class WebGLImageConverter
typename DataTypeForFormat<DstFormat>::Type
DstType;
const MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) IntermediateSrcFormat
const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) IntermediateSrcFormat
= IntermediateFormat<SrcFormat>::Value;
const MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) IntermediateDstFormat
const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) IntermediateDstFormat
= IntermediateFormat<DstFormat>::Value;
typedef
typename DataTypeForFormat<IntermediateSrcFormat>::Type
@ -213,8 +213,8 @@ class WebGLImageConverter
return;
}
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) SrcFormat,
MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) DstFormat>
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) SrcFormat,
MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) DstFormat>
void run(WebGLTexelPremultiplicationOp premultiplicationOp)
{
#define WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(PremultiplicationOp) \
@ -232,7 +232,7 @@ class WebGLImageConverter
#undef WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP
}
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) SrcFormat>
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) SrcFormat>
void run(WebGLTexelFormat dstFormat,
WebGLTexelPremultiplicationOp premultiplicationOp)
{

View File

@ -46,14 +46,7 @@ MOZ_END_ENUM_CLASS(WebGLTexelPremultiplicationOp)
namespace WebGLTexelConversions {
// remove this as soon as B2G and Windows use newer compilers
#ifdef MOZ_HAVE_CXX11_STRONG_ENUMS
#define MOZ_ENUM_CLASS_INTEGER_TYPE(X) X
#else
#define MOZ_ENUM_CLASS_INTEGER_TYPE(X) X::Enum
#endif
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
struct IsFloatFormat
{
static const bool Value =
@ -64,7 +57,7 @@ struct IsFloatFormat
Format == WebGLTexelFormat::A32F;
};
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
struct Is16bppFormat
{
static const bool Value =
@ -73,7 +66,7 @@ struct Is16bppFormat
Format == WebGLTexelFormat::RGB565;
};
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format,
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format,
bool IsFloat = IsFloatFormat<Format>::Value,
bool Is16bpp = Is16bppFormat<Format>::Value>
struct DataTypeForFormat
@ -81,22 +74,22 @@ struct DataTypeForFormat
typedef uint8_t Type;
};
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
struct DataTypeForFormat<Format, true, false>
{
typedef float Type;
};
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
struct DataTypeForFormat<Format, false, true>
{
typedef uint16_t Type;
};
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
struct IntermediateFormat
{
static const MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Value
static const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Value
= IsFloatFormat<Format>::Value
? WebGLTexelFormat::RGBA32F
: WebGLTexelFormat::RGBA8;
@ -172,7 +165,7 @@ MOZ_ALWAYS_INLINE bool HasColor(WebGLTexelFormat format) {
//----------------------------------------------------------------------
// Pixel unpacking routines.
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format, typename SrcType, typename DstType>
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format, typename SrcType, typename DstType>
MOZ_ALWAYS_INLINE void
unpack(const SrcType* __restrict src,
DstType* __restrict dst)
@ -332,8 +325,8 @@ unpack<WebGLTexelFormat::A32F, float, float>(const float* __restrict src, float*
// Pixel packing routines.
//
template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format,
MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelPremultiplicationOp) PremultiplicationOp,
template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format,
MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelPremultiplicationOp) PremultiplicationOp,
typename SrcType,
typename DstType>
MOZ_ALWAYS_INLINE void

View File

@ -96,6 +96,8 @@ MOZ_END_ENUM_CLASS(WebGLImageDataStatus)
* textures from DOM elements. See gfxImageSurface::Format().
*/
MOZ_BEGIN_ENUM_CLASS(WebGLTexelFormat, int)
// returned by SurfaceFromElementResultToImageSurface to indicate absence of image data
None,
// dummy error code returned by GetWebGLTexelFormat in error cases,
// after assertion failure (so this never happens in debug builds)
BadFormat,

View File

@ -117,7 +117,6 @@ UNIFIED_SOURCES += [
'DOMMediaStream.cpp',
'EncodedBufferCache.cpp',
'FileBlockCache.cpp',
'Latency.cpp',
'MediaCache.cpp',
'MediaDecoder.cpp',
'MediaDecoderReader.cpp',
@ -144,8 +143,10 @@ UNIFIED_SOURCES += [
]
# DecoderTraits.cpp needs to be built separately because of Mac OS X headers.
# Latency.cpp needs to be built separately because it forces NSPR logging.
SOURCES += [
'DecoderTraits.cpp',
'Latency.cpp',
]
FAIL_ON_WARNINGS = True

View File

@ -51,7 +51,6 @@ UNIFIED_SOURCES += [
'LoadContext.cpp',
'nsAboutRedirector.cpp',
'nsDefaultURIFixup.cpp',
'nsDocShell.cpp',
'nsDocShellEditorData.cpp',
'nsDocShellEnumerator.cpp',
'nsDocShellLoadInfo.cpp',
@ -62,6 +61,11 @@ UNIFIED_SOURCES += [
'SerializedLoadContext.cpp',
]
# nsDocShell.cpp cannot be built in unified mode because it forces NSPR logging.
SOURCES += [
'nsDocShell.cpp',
]
FAIL_ON_WARNINGS = True
MSVC_ENABLE_PGO = True

View File

@ -82,7 +82,6 @@ UNIFIED_SOURCES += [
'nsFocusManager.cpp',
'nsGlobalWindowCommands.cpp',
'nsHistory.cpp',
'nsJSEnvironment.cpp',
'nsJSTimeoutHandler.cpp',
'nsJSUtils.cpp',
'nsLocation.cpp',
@ -107,6 +106,8 @@ SOURCES += [
'nsDOMWindowUtils.cpp',
# This file has a #error "Never include windows.h in this file!"
'nsGlobalWindow.cpp',
# This file forces NSPR logging.
'nsJSEnvironment.cpp',
# nsPluginArray.cpp includes npapi.h indirectly, and that includes a lot of system headers
'nsPluginArray.cpp',
]

View File

@ -3436,6 +3436,43 @@ nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindow* aWindow)
mFocusedWindow = aWindow;
}
void
nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration)
{
if (!sInstance) {
return;
}
if (sInstance->mActiveWindow) {
sInstance->mActiveWindow->
MarkUncollectableForCCGeneration(aGeneration);
}
if (sInstance->mFocusedWindow) {
sInstance->mFocusedWindow->
MarkUncollectableForCCGeneration(aGeneration);
}
if (sInstance->mWindowBeingLowered) {
sInstance->mWindowBeingLowered->
MarkUncollectableForCCGeneration(aGeneration);
}
if (sInstance->mFocusedContent) {
sInstance->mFocusedContent->OwnerDoc()->
MarkUncollectableForCCGeneration(aGeneration);
}
if (sInstance->mFirstBlurEvent) {
sInstance->mFirstBlurEvent->OwnerDoc()->
MarkUncollectableForCCGeneration(aGeneration);
}
if (sInstance->mFirstFocusEvent) {
sInstance->mFirstFocusEvent->OwnerDoc()->
MarkUncollectableForCCGeneration(aGeneration);
}
if (sInstance->mMouseDownEventHandlingDocument) {
sInstance->mMouseDownEventHandlingDocument->
MarkUncollectableForCCGeneration(aGeneration);
}
}
nsresult
NS_NewFocusManager(nsIFocusManager** aResult)
{

View File

@ -121,6 +121,7 @@ public:
static bool sMouseFocusesFormControl;
static void MarkUncollectableForCCGeneration(uint32_t aGeneration);
protected:
nsFocusManager();

View File

@ -550,7 +550,8 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
mIsActive(false), mIsBackground(false),
mInnerWindow(nullptr), mOuterWindow(aOuterWindow),
// Make sure no actual window ends up with mWindowID == 0
mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false)
mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false),
mMarkedCCGeneration(0)
{}
nsPIDOMWindow::~nsPIDOMWindow() {}
@ -1619,7 +1620,7 @@ MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* a
}
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow)
if (tmp->IsBlackForCC()) {
if (tmp->IsBlackForCC(false)) {
if (tmp->mCachedXBLPrototypeHandlers) {
tmp->mCachedXBLPrototypeHandlers->Enumerate(MarkXBLHandlers, nullptr);
}
@ -1632,11 +1633,11 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindow)
return tmp->IsBlackForCC();
return tmp->IsBlackForCC(true);
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindow)
return tmp->IsBlackForCC();
return tmp->IsBlackForCC(false);
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
inline void
@ -1659,10 +1660,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindow, tmp->mRefCnt.get())
}
if (!cb.WantAllTraces() && tmp->IsBlackForCC()) {
return NS_SUCCESS_INTERRUPTED_TRAVERSE;
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
@ -1791,12 +1788,16 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
bool
nsGlobalWindow::IsBlackForCC()
nsGlobalWindow::IsBlackForCC(bool aTracingNeeded)
{
return
(mDoc &&
nsCCUncollectableMarker::InGeneration(mDoc->GetMarkedCCGeneration())) ||
(nsCCUncollectableMarker::sGeneration && IsBlack());
if (!nsCCUncollectableMarker::sGeneration) {
return false;
}
return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
IsBlack()) &&
(!aTracingNeeded ||
HasNothingToTrace(static_cast<nsIDOMEventTarget*>(this)));
}
void

View File

@ -355,7 +355,7 @@ public:
void PoisonOuterWindowProxy(JSObject *aObject);
virtual void OnFinalize(JSObject* aObject);
virtual bool IsBlackForCC();
virtual bool IsBlackForCC(bool aTracingNeeded = true);
// nsIScriptObjectPrincipal
virtual nsIPrincipal* GetPrincipal();

View File

@ -27,8 +27,8 @@ NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
#define NS_ISCRIPTGLOBALOBJECT_IID \
{ 0xa6c0bfae, 0x8be4, 0x4747, \
{ 0xaf, 0x1a, 0xe3, 0xf0, 0x3f, 0xb6, 0x0e, 0xb8 } }
{ 0x30c64680, 0x909a, 0x4435, \
{ 0x90, 0x3b, 0x29, 0x3e, 0xb5, 0x5d, 0xc7, 0xa0 } }
/**
* The global object which keeps a script context for each supported script
@ -80,7 +80,7 @@ public:
return NS_OK;
}
virtual bool IsBlackForCC() { return false; }
virtual bool IsBlackForCC(bool aTracingNeeded = true) { return false; }
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptGlobalObject,

View File

@ -60,8 +60,8 @@ enum UIStateChangeType
};
#define NS_PIDOMWINDOW_IID \
{ 0x4f4eadf9, 0xe795, 0x48e5, \
{ 0x89, 0x4b, 0x04, 0x40, 0xb2, 0x5d, 0xa6, 0xfa } }
{ 0xf26953de, 0xa799, 0x4a92, \
{ 0x87, 0x49, 0x7c, 0x37, 0xe5, 0x90, 0x3f, 0x37 } }
class nsPIDOMWindow : public nsIDOMWindowInternal
{
@ -653,6 +653,15 @@ public:
// WebIDL-ish APIs
nsPerformance* GetPerformance();
void MarkUncollectableForCCGeneration(uint32_t aGeneration)
{
mMarkedCCGeneration = aGeneration;
}
uint32_t GetMarkedCCGeneration()
{
return mMarkedCCGeneration;
}
protected:
// The nsPIDOMWindow constructor. The aOuterWindow argument should
// be null if and only if the created window itself is an outer
@ -736,6 +745,8 @@ protected:
// This is only used by the inner window. Set to true once we've sent
// the (chrome|content)-document-global-created notification.
bool mHasNotifiedGlobalCreated;
uint32_t mMarkedCCGeneration;
};

View File

@ -156,6 +156,8 @@ public:
*/
bool IsBlackAndDoesNotNeedTracing(nsISupports* aThis);
bool HasNothingToTrace(nsISupports* aThis);
// Only meant to be called by code that preserves a wrapper.
void SetPreservingWrapper(bool aPreserve)
{

View File

@ -36,17 +36,20 @@ SearchGray(void* aGCThing, const char* aName, void* aClosure)
}
}
inline bool
nsWrapperCache::HasNothingToTrace(nsISupports* aThis)
{
nsXPCOMCycleCollectionParticipant* participant = nullptr;
CallQueryInterface(aThis, &participant);
bool hasGrayObjects = false;
participant->Trace(aThis, TraceCallbackFunc(SearchGray), &hasGrayObjects);
return !hasGrayObjects;
}
inline bool
nsWrapperCache::IsBlackAndDoesNotNeedTracing(nsISupports* aThis)
{
if (IsBlack()) {
nsXPCOMCycleCollectionParticipant* participant = nullptr;
CallQueryInterface(aThis, &participant);
bool hasGrayObjects = false;
participant->Trace(aThis, TraceCallbackFunc(SearchGray), &hasGrayObjects);
return !hasGrayObjects;
}
return false;
return IsBlack() && HasNothingToTrace(aThis);
}
inline void

View File

@ -2147,7 +2147,7 @@ ThreadsafeCheckIsChrome(JSContext* aCx, JSObject* aObj)
{
using mozilla::dom::workers::GetWorkerPrivateFromContext;
return NS_IsMainThread() ? xpc::AccessCheck::isChrome(aObj):
GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
GetWorkerPrivateFromContext(aCx)->UsesSystemPrincipal();
}
void

View File

@ -4,16 +4,8 @@
include $(topsrcdir)/config/rules.mk
EncodingUtils.$(OBJ_SUFFIX): labelsencodings.properties.h
FallbackEncoding.$(OBJ_SUFFIX): localesfallbacks.properties.h
PROPS2ARRAYS = $(topsrcdir)/intl/locale/src/props2arrays.py
labelsencodings.properties.h: $(PROPS2ARRAYS) labelsencodings.properties
$(PYTHON) $^ $@
localesfallbacks.properties.h: $(PROPS2ARRAYS) localesfallbacks.properties
$(PYTHON) $^ $@
GARBAGE += \
labelsencodings.properties.h \
localesfallbacks.properties.h \
$(NULL)

View File

@ -30,3 +30,7 @@ LOCAL_INCLUDES += [
'/intl/locale/src',
]
GENERATED_FILES += [
'labelsencodings.properties.h',
'localesfallbacks.properties.h',
]

View File

@ -33,7 +33,7 @@ EXPORTS.mozilla.dom.file += [
'LockedFile.h',
]
SOURCES += [
UNIFIED_SOURCES += [
'ArchiveEvent.cpp',
'ArchiveReader.cpp',
'ArchiveRequest.cpp',

View File

@ -278,7 +278,7 @@ IDBRequest::CaptureCaller()
const char* filename = nullptr;
uint32_t lineNo = 0;
if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) {
MOZ_CRASH("Failed to get caller.");
NS_WARNING("Failed to get caller.");
return;
}

View File

@ -112,3 +112,4 @@ support-files =
[test_webapp_clearBrowserData_inproc_inproc.html]
[test_webapp_clearBrowserData_inproc_oop.html]
[test_webapp_clearBrowserData_oop_inproc.html]
[test_bug937006.html]

View File

@ -0,0 +1,37 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
<html>
<head>
<title>Bug 937006 - "Hit MOZ_CRASH(Failed to get caller.)" using setTimeout on IndexedDB call</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body onload="runTest();">
<script type="text/javascript;version=1.7">
function runTest() {
if (!SpecialPowers.isMainProcess()) {
window.runTest = function() {
todo(false, "Figure out this test for child processes!");
SimpleTest.finish();
return;
}
}
// doing this IDBRequest should not be able to retrieve the filename and
// line number.
//setTimeout(indexedDB.deleteDatabase.bind(indexedDB), 0, 'x');
setTimeout(function() {
ok(true, "Still alive");
SimpleTest.finish();
}, 10);
}
</script>
</body>
</html>

View File

@ -23,6 +23,8 @@
namespace mozilla {
namespace dom {
using namespace workers;
NS_IMPL_ISUPPORTS0(PromiseNativeHandler)
// PromiseTask
@ -54,36 +56,65 @@ private:
nsRefPtr<Promise> mPromise;
};
// This class processes the promise's callbacks with promise's result.
class PromiseResolverTask MOZ_FINAL : public nsRunnable
class WorkerPromiseTask MOZ_FINAL : public WorkerRunnable
{
public:
PromiseResolverTask(Promise* aPromise,
JS::Handle<JS::Value> aValue,
Promise::PromiseState aState)
WorkerPromiseTask(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
: WorkerRunnable(aWorkerPrivate, WorkerThread,
UnchangedBusyCount, SkipWhenClearing)
, mPromise(aPromise)
{
MOZ_ASSERT(aPromise);
MOZ_COUNT_CTOR(WorkerPromiseTask);
}
~WorkerPromiseTask()
{
MOZ_COUNT_DTOR(WorkerPromiseTask);
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
mPromise->mTaskPending = false;
mPromise->RunTask();
return true;
}
private:
nsRefPtr<Promise> mPromise;
};
class PromiseResolverMixin
{
public:
PromiseResolverMixin(Promise* aPromise,
JS::Handle<JS::Value> aValue,
Promise::PromiseState aState)
: mPromise(aPromise)
, mValue(aValue)
, mState(aState)
{
MOZ_ASSERT(aPromise);
MOZ_ASSERT(mState != Promise::Pending);
MOZ_COUNT_CTOR(PromiseResolverTask);
MOZ_COUNT_CTOR(PromiseResolverMixin);
JSContext* cx = nsContentUtils::GetSafeJSContext();
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
/* It's safe to use unsafeGet() here: the unsafeness comes from the
* possibility of updating the value of mJSObject without triggering the
* barriers. However if the value will always be marked, post barriers
* unnecessary. */
JS_AddNamedValueRootRT(JS_GetRuntime(cx), mValue.unsafeGet(),
"PromiseResolverTask.mValue");
"PromiseResolverMixin.mValue");
}
~PromiseResolverTask()
virtual ~PromiseResolverMixin()
{
MOZ_COUNT_DTOR(PromiseResolverTask);
NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
MOZ_COUNT_DTOR(PromiseResolverMixin);
JSContext* cx = nsContentUtils::GetSafeJSContext();
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
/* It's safe to use unsafeGet() here: the unsafeness comes from the
* possibility of updating the value of mJSObject without triggering the
@ -92,18 +123,66 @@ public:
JS_RemoveValueRootRT(JS_GetRuntime(cx), mValue.unsafeGet());
}
NS_IMETHOD Run()
protected:
void
RunInternal()
{
NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
mPromise->RunResolveTask(
JS::Handle<JS::Value>::fromMarkedLocation(mValue.address()),
mState, Promise::SyncTask);
return NS_OK;
}
private:
nsRefPtr<Promise> mPromise;
JS::Heap<JS::Value> mValue;
Promise::PromiseState mState;
NS_DECL_OWNINGTHREAD;
};
// This class processes the promise's callbacks with promise's result.
class PromiseResolverTask MOZ_FINAL : public nsRunnable,
public PromiseResolverMixin
{
public:
PromiseResolverTask(Promise* aPromise,
JS::Handle<JS::Value> aValue,
Promise::PromiseState aState)
: PromiseResolverMixin(aPromise, aValue, aState)
{}
~PromiseResolverTask()
{}
NS_IMETHOD Run()
{
RunInternal();
return NS_OK;
}
};
class WorkerPromiseResolverTask MOZ_FINAL : public WorkerRunnable,
public PromiseResolverMixin
{
public:
WorkerPromiseResolverTask(WorkerPrivate* aWorkerPrivate,
Promise* aPromise,
JS::Handle<JS::Value> aValue,
Promise::PromiseState aState)
: WorkerRunnable(aWorkerPrivate, WorkerThread,
UnchangedBusyCount, SkipWhenClearing),
PromiseResolverMixin(aPromise, aValue, aState)
{}
~WorkerPromiseResolverTask()
{}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
RunInternal();
return true;
}
};
// Promise
@ -166,27 +245,23 @@ Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
return PromiseBinding::Wrap(aCx, aScope, this);
}
/* static */ bool
Promise::PrefEnabled()
{
return Preferences::GetBool("dom.promise.enabled", false);
}
/* static */ bool
Promise::EnabledForScope(JSContext* aCx, JSObject* /* unused */)
{
if (NS_IsMainThread()) {
// No direct return so the chrome/certified app checks happen below.
if (Preferences::GetBool("dom.promise.enabled", false)) {
return true;
}
} else {
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
return workerPrivate->PromiseEnabled() || workerPrivate->UsesSystemPrincipal();
}
// Enable if the pref is enabled or if we're chrome or if we're a
// certified app.
if (PrefEnabled()) {
return true;
}
// Note that we have no concept of a certified app in workers.
// XXXbz well, why not?
if (!NS_IsMainThread()) {
return workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
}
// FIXME(nsm): Remove these checks once promises are enabled by default.
nsIPrincipal* prin = nsContentUtils::GetSubjectPrincipal();
return nsContentUtils::IsSystemPrincipal(prin) ||
prin->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
@ -278,10 +353,15 @@ Promise::Constructor(const GlobalObject& aGlobal,
PromiseInit& aInit, ErrorResult& aRv)
{
JSContext* cx = aGlobal.GetContext();
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
if (!window) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
nsCOMPtr<nsPIDOMWindow> window;
// On workers, let the window be null.
if (MOZ_LIKELY(NS_IsMainThread())) {
window = do_QueryInterface(aGlobal.GetAsSupports());
if (!window) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
}
nsRefPtr<Promise> promise = new Promise(window);
@ -322,10 +402,13 @@ Promise::Constructor(const GlobalObject& aGlobal,
Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
const Optional<JS::Handle<JS::Value>>& aValue, ErrorResult& aRv)
{
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
if (!window) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
nsCOMPtr<nsPIDOMWindow> window;
if (MOZ_LIKELY(NS_IsMainThread())) {
window = do_QueryInterface(aGlobal.GetAsSupports());
if (!window) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
}
nsRefPtr<Promise> promise = new Promise(window);
@ -339,10 +422,13 @@ Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
Promise::Reject(const GlobalObject& aGlobal, JSContext* aCx,
const Optional<JS::Handle<JS::Value>>& aValue, ErrorResult& aRv)
{
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
if (!window) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
nsCOMPtr<nsPIDOMWindow> window;
if (MOZ_LIKELY(NS_IsMainThread())) {
window = do_QueryInterface(aGlobal.GetAsSupports());
if (!window) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
}
nsRefPtr<Promise> promise = new Promise(window);
@ -413,8 +499,15 @@ Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
// callbacks with promise's result. If promise's state is rejected, queue a
// task to process our reject callbacks with promise's result.
if (mState != Pending && !mTaskPending) {
nsRefPtr<PromiseTask> task = new PromiseTask(this);
NS_DispatchToCurrentThread(task);
if (MOZ_LIKELY(NS_IsMainThread())) {
nsRefPtr<PromiseTask> task = new PromiseTask(this);
NS_DispatchToCurrentThread(task);
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
nsRefPtr<WorkerPromiseTask> task = new WorkerPromiseTask(worker, this);
worker->Dispatch(task);
}
mTaskPending = true;
}
}
@ -430,7 +523,7 @@ Promise::RunTask()
mResolveCallbacks.Clear();
mRejectCallbacks.Clear();
JSContext* cx = nsContentUtils::GetSafeJSContext();
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
JSAutoRequest ar(cx);
JS::Rooted<JS::Value> value(cx, mResult);
@ -453,16 +546,27 @@ Promise::MaybeReportRejected()
MOZ_ASSERT(mResult.isObject(), "How did we get a JSErrorReport?");
nsCOMPtr<nsPIDOMWindow> win =
do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(&mResult.toObject()));
// Remains null in case of worker.
nsCOMPtr<nsPIDOMWindow> win;
bool isChromeError = false;
if (MOZ_LIKELY(NS_IsMainThread())) {
win =
do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(&mResult.toObject()));
nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(&mResult.toObject());
isChromeError = nsContentUtils::IsSystemPrincipal(principal);
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
isChromeError = worker->IsChromeWorker();
}
nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(&mResult.toObject());
// Now post an event to do the real reporting async
NS_DispatchToCurrentThread(
NS_DispatchToMainThread(
new AsyncErrorReporter(JS_GetObjectRuntime(&mResult.toObject()),
report,
nullptr,
nsContentUtils::IsSystemPrincipal(principal),
isChromeError,
win));
}
@ -539,9 +643,17 @@ Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
// If the synchronous flag is unset, queue a task to process our
// accept callbacks with value.
if (aAsynchronous == AsyncTask) {
nsRefPtr<PromiseResolverTask> task =
new PromiseResolverTask(this, aValue, aState);
NS_DispatchToCurrentThread(task);
if (MOZ_LIKELY(NS_IsMainThread())) {
nsRefPtr<PromiseResolverTask> task =
new PromiseResolverTask(this, aValue, aState);
NS_DispatchToCurrentThread(task);
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
nsRefPtr<WorkerPromiseResolverTask> task =
new WorkerPromiseResolverTask(worker, this, aValue, aState);
worker->Dispatch(task);
}
return;
}

View File

@ -29,10 +29,13 @@ class Promise MOZ_FINAL : public nsISupports,
public nsWrapperCache
{
friend class NativePromiseCallback;
friend class PromiseTask;
friend class PromiseResolverMixin;
friend class PromiseResolverTask;
friend class ResolvePromiseCallback;
friend class PromiseTask;
friend class RejectPromiseCallback;
friend class ResolvePromiseCallback;
friend class WorkerPromiseResolverTask;
friend class WorkerPromiseTask;
friend class WrapperPromiseCallback;
public:
@ -42,7 +45,6 @@ public:
Promise(nsPIDOMWindow* aWindow);
~Promise();
static bool PrefEnabled();
static bool EnabledForScope(JSContext* aCx, JSObject* /* unused */);
void MaybeResolve(JSContext* aCx,

View File

@ -75,7 +75,13 @@ void
ResolvePromiseCallback::Call(JS::Handle<JS::Value> aValue)
{
// Run resolver's algorithm with value and the synchronous flag set.
AutoJSContext cx;
JSContext *cx = nsContentUtils::GetDefaultJSContextForThread();
Maybe<AutoCxPusher> pusher;
if (NS_IsMainThread()) {
pusher.construct(cx);
}
Maybe<JSAutoCompartment> ac;
EnterCompartment(ac, cx, aValue);
@ -110,7 +116,13 @@ void
RejectPromiseCallback::Call(JS::Handle<JS::Value> aValue)
{
// Run resolver's algorithm with value and the synchronous flag set.
AutoJSContext cx;
JSContext *cx = nsContentUtils::GetDefaultJSContextForThread();
Maybe<AutoCxPusher> pusher;
if (NS_IsMainThread()) {
pusher.construct(cx);
}
Maybe<JSAutoCompartment> ac;
EnterCompartment(ac, cx, aValue);
@ -146,7 +158,17 @@ WrapperPromiseCallback::~WrapperPromiseCallback()
void
WrapperPromiseCallback::Call(JS::Handle<JS::Value> aValue)
{
AutoJSContext cx;
// AutoCxPusher and co. interact with xpconnect, which crashes on
// workers. On workers we'll get the right context from
// GetDefaultJSContextForThread(), and since there is only one context, we
// don't need to push or pop it from the stack.
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
Maybe<AutoCxPusher> pusher;
if (NS_IsMainThread()) {
pusher.construct(cx);
}
Maybe<JSAutoCompartment> ac;
EnterCompartment(ac, cx, aValue);

View File

@ -37,7 +37,7 @@ public:
};
// WrapperPromiseCallback execs a JS Callback with a value, and then the return
// value is sent to the aNextPromise->resolveFunction() or to
// value is sent to the aNextPromise->ResolveFunction() or to
// aNextPromise->RejectFunction() if the JS Callback throws.
class WrapperPromiseCallback MOZ_FINAL : public PromiseCallback
{

View File

@ -63,6 +63,15 @@ function promiseReject() {
});
}
function promiseRejectNoHandler() {
// This test only checks that the code that reports unhandled errors in the
// Promises implementation does not crash or leak.
var promise = new Promise(function(res, rej) {
noSuchMethod();
});
runTest();
}
function promiseRejectNoArg() {
var promise = new Promise(function(resolve, reject) {
reject();
@ -474,7 +483,8 @@ var tests = [ promiseResolve, promiseReject,
promiseThenNoArg,
promiseThenUndefinedResolveFunction,
promiseThenNullResolveFunction,
promiseCatchNoArg
promiseCatchNoArg,
promiseRejectNoHandler,
];
function runTest() {

View File

@ -621,7 +621,6 @@ interface WebGLRenderingContext {
any getTexParameter(GLenum target, GLenum pname);
[Throws]
any getUniform(WebGLProgram? program, WebGLUniformLocation? location);
[NewObject]

View File

@ -6,6 +6,7 @@
#include "WorkerPrivate.h"
#include "ChromeWorkerScope.h"
#include "File.h"
#include "RuntimeService.h"
#include "jsapi.h"
#include "js/OldDebugAPI.h"
@ -19,6 +20,7 @@
#include "mozilla/dom/ImageDataBinding.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/PromiseBinding.h"
#include "mozilla/dom/TextDecoderBinding.h"
#include "mozilla/dom/TextEncoderBinding.h"
#include "mozilla/dom/XMLHttpRequestBinding.h"
@ -61,6 +63,8 @@ WorkerPrivate::RegisterBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
!ImageDataBinding::GetConstructorObject(aCx, aGlobal) ||
!MessageEventBinding::GetConstructorObject(aCx, aGlobal) ||
!MessagePortBinding::GetConstructorObject(aCx, aGlobal) ||
(PromiseEnabled() &&
!PromiseBinding::GetConstructorObject(aCx, aGlobal)) ||
!TextDecoderBinding::GetConstructorObject(aCx, aGlobal) ||
!TextEncoderBinding::GetConstructorObject(aCx, aGlobal) ||
!XMLHttpRequestBinding_workers::GetConstructorObject(aCx, aGlobal) ||
@ -77,4 +81,4 @@ WorkerPrivate::RegisterBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
}
return true;
}
}

View File

@ -176,25 +176,10 @@ static_assert(NS_ARRAY_LENGTH(gStringChars) == ID_COUNT,
#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
#define DUMP_CONTROLLED_BY_PREF 1
#define PREF_DOM_WINDOW_DUMP_ENABLED "browser.dom.window.dump.enabled"
// Protected by RuntimeService::mMutex.
// Initialized by DumpPrefChanged via RuntimeService::Init().
bool gWorkersDumpEnabled;
static int
DumpPrefChanged(const char* aPrefName, void* aClosure)
{
MOZ_ASSERT(NS_IsMainThread());
bool enabled = Preferences::GetBool(PREF_DOM_WINDOW_DUMP_ENABLED, false);
Mutex* mutex = static_cast<Mutex*>(aClosure);
MutexAutoLock lock(*mutex);
gWorkersDumpEnabled = enabled;
return 0;
}
#endif
#define PREF_PROMISE_ENABLED "dom.promise.enabled"
class LiteralRebindingCString : public nsDependentCString
{
public:
@ -1159,6 +1144,7 @@ END_WORKERS_NAMESPACE
// This is only touched on the main thread. Initialized in Init() below.
JSSettings RuntimeService::sDefaultJSSettings;
bool RuntimeService::sDefaultPreferences[WORKERPREF_COUNT] = { false };
RuntimeService::RuntimeService()
: mMutex("RuntimeService::mMutex"), mObserved(false),
@ -1569,6 +1555,11 @@ RuntimeService::Init()
WORKER_DEFAULT_ALLOCATION_THRESHOLD);
}
// If dump is not controlled by pref, it's set to true.
#ifndef DUMP_CONTROLLED_BY_PREF
sDefaultPreferences[WORKERPREF_DUMP] = true;
#endif
mIdleThreadTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
NS_ENSURE_STATE(mIdleThreadTimer);
@ -1624,10 +1615,14 @@ RuntimeService::Init()
#endif
#if DUMP_CONTROLLED_BY_PREF
NS_FAILED(Preferences::RegisterCallbackAndCall(
DumpPrefChanged,
WorkerPrefChanged,
PREF_DOM_WINDOW_DUMP_ENABLED,
&mMutex)) ||
reinterpret_cast<void *>(WORKERPREF_DUMP))) ||
#endif
NS_FAILED(Preferences::RegisterCallbackAndCall(
WorkerPrefChanged,
PREF_PROMISE_ENABLED,
reinterpret_cast<void *>(WORKERPREF_PROMISE))) ||
NS_FAILED(Preferences::RegisterCallback(LoadJSContextOptions,
PREF_JS_OPTIONS_PREFIX,
nullptr)) ||
@ -1790,10 +1785,13 @@ RuntimeService::Cleanup()
NS_FAILED(Preferences::UnregisterCallback(LoadJSContextOptions,
PREF_WORKERS_OPTIONS_PREFIX,
nullptr)) ||
NS_FAILED(Preferences::UnregisterCallback(WorkerPrefChanged,
PREF_PROMISE_ENABLED,
reinterpret_cast<void *>(WORKERPREF_PROMISE))) ||
#if DUMP_CONTROLLED_BY_PREF
NS_FAILED(Preferences::UnregisterCallback(DumpPrefChanged,
NS_FAILED(Preferences::UnregisterCallback(WorkerPrefChanged,
PREF_DOM_WINDOW_DUMP_ENABLED,
&mMutex)) ||
reinterpret_cast<void *>(WORKERPREF_DUMP))) ||
#endif
#ifdef JS_GC_ZEAL
NS_FAILED(Preferences::UnregisterCallback(
@ -2171,6 +2169,12 @@ RuntimeService::UpdateAllWorkerJSContextOptions()
sDefaultJSSettings.chrome.options);
}
void
RuntimeService::UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue)
{
BROADCAST_ALL_WORKERS(UpdatePreference, aPref, aValue);
}
void
RuntimeService::UpdateAllWorkerMemoryParameter(JSGCParamKey aKey,
uint32_t aValue)
@ -2230,16 +2234,33 @@ RuntimeService::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK;
}
bool
RuntimeService::WorkersDumpEnabled()
/* static */ int
RuntimeService::WorkerPrefChanged(const char* aPrefName, void* aClosure)
{
#if DUMP_CONTROLLED_BY_PREF
MutexAutoLock lock(mMutex);
// In optimized builds we check a pref that controls if we should
// enable output from dump() or not, in debug builds it's always
// enabled.
return gWorkersDumpEnabled;
#else
return true;
AssertIsOnMainThread();
uintptr_t tmp = reinterpret_cast<uintptr_t>(aClosure);
MOZ_ASSERT(tmp < WORKERPREF_COUNT);
WorkerPreference key = static_cast<WorkerPreference>(tmp);
if (key == WORKERPREF_PROMISE) {
sDefaultPreferences[WORKERPREF_PROMISE] =
Preferences::GetBool(PREF_PROMISE_ENABLED, false);
#ifdef DUMP_CONTROLLED_BY_PREF
} else if (key == WORKERPREF_DUMP) {
key = WORKERPREF_DUMP;
sDefaultPreferences[WORKERPREF_DUMP] =
Preferences::GetBool(PREF_DOM_WINDOW_DUMP_ENABLED, false);
#endif
}
// This function should never be registered as a callback for a preference it
// does not handle.
MOZ_ASSERT(key != WORKERPREF_COUNT);
RuntimeService* rts = RuntimeService::GetService();
if (rts) {
rts->UpdateAllWorkerPreference(key, sDefaultPreferences[key]);
}
return 0;
}

View File

@ -101,6 +101,7 @@ class RuntimeService MOZ_FINAL : public nsIObserver
nsCString mSystemCharset;
static JSSettings sDefaultJSSettings;
static bool sDefaultPreferences[WORKERPREF_COUNT];
public:
struct NavigatorStrings
@ -181,6 +182,13 @@ public:
aSettings = sDefaultJSSettings;
}
static void
GetDefaultPreferences(bool aPreferences[WORKERPREF_COUNT])
{
AssertIsOnMainThread();
memcpy(aPreferences, sDefaultPreferences, WORKERPREF_COUNT * sizeof(bool));
}
static void
SetDefaultJSContextOptions(const JS::ContextOptions& aContentOptions,
const JS::ContextOptions& aChromeOptions)
@ -193,6 +201,9 @@ public:
void
UpdateAllWorkerJSContextOptions();
void
UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue);
static void
SetDefaultJSGCSettings(JSGCParamKey aKey, uint32_t aValue)
{
@ -241,9 +252,6 @@ public:
void
GarbageCollectAllWorkers(bool aShrinking);
bool
WorkersDumpEnabled();
private:
RuntimeService();
~RuntimeService();
@ -281,6 +289,9 @@ private:
static void
ShutdownIdleThreads(nsITimer* aTimer, void* aClosure);
static int
WorkerPrefChanged(const char* aPrefName, void* aClosure);
};
END_WORKERS_NAMESPACE

View File

@ -1465,6 +1465,29 @@ public:
}
};
class UpdatePreferenceRunnable : public WorkerControlRunnable
{
WorkerPreference mPref;
bool mValue;
public:
UpdatePreferenceRunnable(WorkerPrivate* aWorkerPrivate,
WorkerPreference aPref,
bool aValue)
: WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
mPref(aPref),
mValue(aValue)
{
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
aWorkerPrivate->UpdatePreferenceInternal(aCx, mPref, mValue);
return true;
}
};
class UpdateJSWorkerMemoryParameterRunnable : public WorkerControlRunnable
{
uint32_t mValue;
@ -2778,6 +2801,21 @@ WorkerPrivateParent<Derived>::UpdateJSContextOptions(JSContext* aCx,
}
}
template <class Derived>
void
WorkerPrivateParent<Derived>::UpdatePreference(JSContext* aCx, WorkerPreference aPref, bool aValue)
{
AssertIsOnParentThread();
MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
nsRefPtr<UpdatePreferenceRunnable> runnable =
new UpdatePreferenceRunnable(ParentAsWorkerPrivate(), aPref, aValue);
if (!runnable->Dispatch(aCx)) {
NS_WARNING("Failed to update worker preferences!");
JS_ClearPendingException(aCx);
}
}
template <class Derived>
void
WorkerPrivateParent<Derived>::UpdateJSWorkerMemoryParameter(JSContext* aCx,
@ -3284,6 +3322,15 @@ WorkerPrivate::WorkerPrivate(JSContext* aCx,
{
MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid());
MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty());
if (aParent) {
aParent->AssertIsOnWorkerThread();
aParent->GetAllPreferences(mPreferences);
}
else {
AssertIsOnMainThread();
RuntimeService::GetDefaultPreferences(mPreferences);
}
}
WorkerPrivate::~WorkerPrivate()
@ -5083,6 +5130,19 @@ WorkerPrivate::UpdateJSContextOptionsInternal(JSContext* aCx,
}
}
void
WorkerPrivate::UpdatePreferenceInternal(JSContext* aCx, WorkerPreference aPref, bool aValue)
{
AssertIsOnWorkerThread();
MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
mPreferences[aPref] = aValue;
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
mChildWorkers[index]->UpdatePreference(aCx, aPref, aValue);
}
}
void
WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx,
JSGCParamKey aKey,

View File

@ -476,6 +476,9 @@ public:
UpdateJSContextOptions(JSContext* aCx, const JS::ContextOptions& aChromeOptions,
const JS::ContextOptions& aContentOptions);
void
UpdatePreference(JSContext* aCx, WorkerPreference aPref, bool aValue);
void
UpdateJSWorkerMemoryParameter(JSContext* aCx, JSGCParamKey key,
uint32_t value);
@ -815,6 +818,8 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
nsCOMPtr<nsIThread> mThread;
#endif
bool mPreferences[WORKERPREF_COUNT];
protected:
~WorkerPrivate();
@ -977,6 +982,9 @@ public:
UpdateJSContextOptionsInternal(JSContext* aCx, const JS::ContextOptions& aContentOptions,
const JS::ContextOptions& aChromeOptions);
void
UpdatePreferenceInternal(JSContext* aCx, WorkerPreference aPref, bool aValue);
void
UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, uint32_t aValue);
@ -1069,6 +1077,20 @@ public:
bool
RegisterBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
bool
DumpEnabled() const
{
AssertIsOnWorkerThread();
return mPreferences[WORKERPREF_DUMP];
}
bool
PromiseEnabled() const
{
AssertIsOnWorkerThread();
return mPreferences[WORKERPREF_PROMISE];
}
private:
WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent,
const nsAString& aScriptURL, bool aIsChromeWorker,
@ -1146,6 +1168,13 @@ private:
bool aToMessagePort,
uint64_t aMessagePortSerial,
ErrorResult& aRv);
void
GetAllPreferences(bool aPreferences[WORKERPREF_COUNT]) const
{
AssertIsOnWorkerThread();
memcpy(aPreferences, mPreferences, WORKERPREF_COUNT * sizeof(bool));
}
};
// This class is only used to trick the DOM bindings. We never create

View File

@ -234,10 +234,7 @@ WorkerGlobalScope::Dump(const Optional<nsAString>& aString) const
return;
}
RuntimeService* runtimeService = RuntimeService::GetService();
MOZ_ASSERT(runtimeService);
if (!runtimeService->WorkersDumpEnabled()) {
if (!mWorkerPrivate->DumpEnabled()) {
return;
}

View File

@ -162,6 +162,13 @@ struct JSSettings
}
};
enum WorkerPreference
{
WORKERPREF_DUMP = 0, // browser.dom.window.dump.enabled
WORKERPREF_PROMISE, // dom.promise.enabled
WORKERPREF_COUNT
};
// All of these are implemented in RuntimeService.cpp
bool
ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,

View File

@ -27,6 +27,7 @@ support-files =
multi_sharedWorker_sharedWorker.js
navigator_worker.js
newError_worker.js
promise_worker.js
recursion_worker.js
recursiveOnerror_worker.js
relativeLoad_import.js
@ -84,6 +85,7 @@ support-files =
[test_multi_sharedWorker_lifetimes.html]
[test_navigator.html]
[test_newError.html]
[test_promise.html]
[test_recursion.html]
[test_recursiveOnerror.html]
[test_relativeLoad.html]

View File

@ -0,0 +1,395 @@
function ok(a, msg) {
dump("OK: " + !!a + " => " + a + " " + msg + "\n");
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
}
function is(a, b, msg) {
dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
}
function isnot(a, b, msg) {
dump("ISNOT: " + (a!==b) + " => " + a + " | " + b + " " + msg + "\n");
postMessage({type: 'status', status: a !== b, msg: a + " !== " + b + ": " + msg });
}
function promiseResolve() {
ok(Promise, "Promise object should exist");
var promise = new Promise(function(resolve, reject) {
ok(resolve, "Promise.resolve exists");
ok(reject, "Promise.reject exists");
resolve(42);
}).then(function(what) {
ok(true, "Then - resolveCb has been called");
is(what, 42, "ResolveCb received 42");
runTest();
}, function() {
ok(false, "Then - rejectCb has been called");
runTest();
});
}
function promiseReject() {
var promise = new Promise(function(resolve, reject) {
reject(42);
}).then(function(what) {
ok(false, "Then - resolveCb has been called");
runTest();
}, function(what) {
ok(true, "Then - rejectCb has been called");
is(what, 42, "RejectCb received 42");
runTest();
});
}
function promiseException() {
var promise = new Promise(function(resolve, reject) {
throw 42;
}).then(function(what) {
ok(false, "Then - resolveCb has been called");
runTest();
}, function(what) {
ok(true, "Then - rejectCb has been called");
is(what, 42, "RejectCb received 42");
runTest();
});
}
function promiseAsync() {
var global = "foo";
var f = new Promise(function(r1, r2) {
is(global, "foo", "Global should be foo");
r1(42);
is(global, "foo", "Global should still be foo");
setTimeout(function() {
is(global, "bar", "Global should still be bar!");
runTest();
}, 0);
}).then(function() {
global = "bar";
});
is(global, "foo", "Global should still be foo (2)");
}
function promiseDoubleThen() {
var steps = 0;
var promise = new Promise(function(r1, r2) {
r1(42);
});
promise.then(function(what) {
ok(true, "Then.resolve has been called");
is(what, 42, "Value == 42");
steps++;
}, function(what) {
ok(false, "Then.reject has been called");
});
promise.then(function(what) {
ok(true, "Then.resolve has been called");
is(steps, 1, "Then.resolve - step == 1");
is(what, 42, "Value == 42");
runTest();
}, function(what) {
ok(false, "Then.reject has been called");
});
}
function promiseThenException() {
var promise = new Promise(function(resolve, reject) {
resolve(42);
});
promise.then(function(what) {
ok(true, "Then.resolve has been called");
throw "booh";
}).catch(function(e) {
ok(true, "Catch has been called!");
runTest();
});
}
function promiseThenCatchThen() {
var promise = new Promise(function(resolve, reject) {
resolve(42);
});
var promise2 = promise.then(function(what) {
ok(true, "Then.resolve has been called");
is(what, 42, "Value == 42");
return what + 1;
}, function(what) {
ok(false, "Then.reject has been called");
});
isnot(promise, promise2, "These 2 promise objs are different");
promise2.then(function(what) {
ok(true, "Then.resolve has been called");
is(what, 43, "Value == 43");
return what + 1;
}, function(what) {
ok(false, "Then.reject has been called");
}).catch(function() {
ok(false, "Catch has been called");
}).then(function(what) {
ok(true, "Then.resolve has been called");
is(what, 44, "Value == 44");
runTest();
}, function(what) {
ok(false, "Then.reject has been called");
});
}
function promiseRejectThenCatchThen() {
var promise = new Promise(function(resolve, reject) {
reject(42);
});
var promise2 = promise.then(function(what) {
ok(false, "Then.resolve has been called");
}, function(what) {
ok(true, "Then.reject has been called");
is(what, 42, "Value == 42");
return what + 1;
});
isnot(promise, promise2, "These 2 promise objs are different");
promise2.then(function(what) {
ok(true, "Then.resolve has been called");
is(what, 43, "Value == 43");
return what+1;
}).catch(function(what) {
ok(false, "Catch has been called");
}).then(function(what) {
ok(true, "Then.resolve has been called");
is(what, 44, "Value == 44");
runTest();
});
}
function promiseRejectThenCatchThen2() {
var promise = new Promise(function(resolve, reject) {
reject(42);
});
promise.then(function(what) {
ok(true, "Then.resolve has been called");
is(what, 42, "Value == 42");
return what+1;
}).catch(function(what) {
is(what, 42, "Value == 42");
ok(true, "Catch has been called");
return what+1;
}).then(function(what) {
ok(true, "Then.resolve has been called");
is(what, 43, "Value == 43");
runTest();
});
}
function promiseRejectThenCatchExceptionThen() {
var promise = new Promise(function(resolve, reject) {
reject(42);
});
promise.then(function(what) {
ok(false, "Then.resolve has been called");
}, function(what) {
ok(true, "Then.reject has been called");
is(what, 42, "Value == 42");
throw(what + 1);
}).catch(function(what) {
ok(true, "Catch has been called");
is(what, 43, "Value == 43");
return what + 1;
}).then(function(what) {
ok(true, "Then.resolve has been called");
is(what, 44, "Value == 44");
runTest();
});
}
function promiseThenCatchOrderingResolve() {
var global = 0;
var f = new Promise(function(r1, r2) {
r1(42);
});
f.then(function() {
f.then(function() {
global++;
});
f.catch(function() {
global++;
});
f.then(function() {
global++;
});
setTimeout(function() {
is(global, 2, "Many steps... should return 2");
runTest();
}, 0);
});
}
function promiseThenCatchOrderingReject() {
var global = 0;
var f = new Promise(function(r1, r2) {
r2(42);
})
f.then(function() {}, function() {
f.then(function() {
global++;
});
f.catch(function() {
global++;
});
f.then(function() {}, function() {
global++;
});
setTimeout(function() {
is(global, 2, "Many steps... should return 2");
runTest();
}, 0);
});
}
function promiseNestedPromise() {
new Promise(function(resolve, reject) {
resolve(new Promise(function(resolve, reject) {
ok(true, "Nested promise is executed");
resolve(42);
}));
}).then(function(value) {
is(value, 42, "Nested promise is executed and then == 42");
runTest();
});
}
function promiseNestedNestedPromise() {
new Promise(function(resolve, reject) {
resolve(new Promise(function(resolve, reject) {
ok(true, "Nested promise is executed");
resolve(42);
}).then(function(what) { return what+1; }));
}).then(function(value) {
is(value, 43, "Nested promise is executed and then == 43");
runTest();
});
}
function promiseWrongNestedPromise() {
new Promise(function(resolve, reject) {
resolve(new Promise(function(r, r2) {
ok(true, "Nested promise is executed");
r(42);
}));
reject(42);
}).then(function(value) {
is(value, 42, "Nested promise is executed and then == 42");
runTest();
}, function(value) {
ok(false, "This is wrong");
});
}
function promiseLoop() {
new Promise(function(resolve, reject) {
resolve(new Promise(function(r1, r2) {
ok(true, "Nested promise is executed");
r1(new Promise(function(r1, r2) {
ok(true, "Nested nested promise is executed");
r1(42);
}));
}));
}).then(function(value) {
is(value, 42, "Nested nested promise is executed and then == 42");
runTest();
}, function(value) {
ok(false, "This is wrong");
});
}
function promiseStaticReject() {
var promise = Promise.reject(42).then(function(what) {
ok(false, "This should not be called");
}, function(what) {
is(what, 42, "Value == 42");
runTest();
});
}
function promiseStaticResolve() {
var promise = Promise.resolve(42).then(function(what) {
is(what, 42, "Value == 42");
runTest();
}, function() {
ok(false, "This should not be called");
});
}
function promiseResolveNestedPromise() {
var promise = Promise.resolve(new Promise(function(r, r2) {
ok(true, "Nested promise is executed");
r(42);
}, function() {
ok(false, "This should not be called");
})).then(function(what) {
is(what, 42, "Value == 42");
runTest();
}, function() {
ok(false, "This should not be called");
});
}
function promiseRejectNoHandler() {
// This test only checks that the code that reports unhandled errors in the
// Promises implementation does not crash or leak.
var promise = new Promise(function(res, rej) {
noSuchMethod();
});
runTest();
}
var tests = [
promiseResolve,
promiseReject,
promiseException,
promiseAsync,
promiseDoubleThen,
promiseThenException,
promiseThenCatchThen,
promiseRejectThenCatchThen,
promiseRejectThenCatchThen2,
promiseRejectThenCatchExceptionThen,
promiseThenCatchOrderingResolve,
promiseThenCatchOrderingReject,
promiseNestedPromise,
promiseNestedNestedPromise,
promiseWrongNestedPromise,
promiseLoop,
promiseStaticReject,
promiseStaticResolve,
promiseResolveNestedPromise,
promiseRejectNoHandler,
];
function runTest() {
if (!tests.length) {
postMessage({ type: 'finish' });
return;
}
var test = tests.shift();
test();
}
onmessage = function() {
runTest();
}

View File

@ -0,0 +1,44 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Promise object in workers</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
function runTest() {
var worker = new Worker("promise_worker.js");
worker.onmessage = function(event) {
if (event.data.type == 'finish') {
SimpleTest.finish();
} else if (event.data.type == 'status') {
ok(event.data.status, event.data.msg);
}
}
worker.onerror = function(event) {
ok(false, "Worker had an error: " + event.data);
SimpleTest.finish();
};
worker.postMessage(true);
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, runTest);
</script>
</pre>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More