зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to elm
This commit is contained in:
Коммит
512ba56709
2
CLOBBER
2
CLOBBER
|
@ -18,4 +18,4 @@
|
|||
# Modifying this file will now automatically clobber the buildbot machines \o/
|
||||
#
|
||||
|
||||
Another Windows WebIDL clobber needed due to bug 928195
|
||||
Another Windows WebIDL clobber needed due to bug 674741
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"revision": "d9e07db2d0512169f304985a070eade8e81e6ba7",
|
||||
"revision": "88cc9854d6daff8c577e3867b95a1e523e429112",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -189,6 +189,9 @@
|
|||
@BINPATH@/components/dom_geolocation.xpt
|
||||
@BINPATH@/components/dom_media.xpt
|
||||
@BINPATH@/components/dom_network.xpt
|
||||
#ifdef MOZ_NFC
|
||||
@BINPATH@/components/dom_nfc.xpt
|
||||
#endif
|
||||
@BINPATH@/components/dom_notification.xpt
|
||||
@BINPATH@/components/dom_html.xpt
|
||||
@BINPATH@/components/dom_indexeddb.xpt
|
||||
|
@ -494,6 +497,13 @@
|
|||
@BINPATH@/components/webvtt.xpt
|
||||
@BINPATH@/components/WebVTT.manifest
|
||||
@BINPATH@/components/WebVTTParserWrapper.js
|
||||
#ifdef MOZ_NFC
|
||||
@BINPATH@/components/nsNfc.manifest
|
||||
@BINPATH@/components/nsNfc.js
|
||||
@BINPATH@/components/Nfc.manifest
|
||||
@BINPATH@/components/Nfc.js
|
||||
@BINPATH@/components/NfcContentHelper.js
|
||||
#endif
|
||||
#ifdef MOZ_ENABLE_DBUS
|
||||
@BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
|
||||
#endif
|
||||
|
|
|
@ -1210,6 +1210,11 @@ pref("devtools.webconsole.fontSize", 0);
|
|||
// be cleared each time page navigation happens.
|
||||
pref("devtools.webconsole.persistlog", false);
|
||||
|
||||
// Web Console timestamp: |true| if you want the logs and instructions
|
||||
// in the Web Console to display a timestamp, or |false| to not display
|
||||
// any timestamps.
|
||||
pref("devtools.webconsole.timestampMessages", false);
|
||||
|
||||
// The number of lines that are displayed in the web console for the Net,
|
||||
// CSS, JS and Web Developer categories.
|
||||
pref("devtools.hud.loglimit.network", 200);
|
||||
|
|
|
@ -731,10 +731,6 @@ var gPluginHandler = {
|
|||
let principal = contentWindow.document.nodePrincipal;
|
||||
Services.perms.addFromPrincipal(principal, aPluginInfo.permissionString,
|
||||
permission, expireType, expireTime);
|
||||
|
||||
if (aNewState == "block") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Manually activate the plugins that would have been automatically
|
||||
|
@ -752,19 +748,23 @@ var gPluginHandler = {
|
|||
}
|
||||
if (aPluginInfo.permissionString == pluginHost.getPermissionStringForType(plugin.actualType)) {
|
||||
pluginFound = true;
|
||||
if (gPluginHandler.canActivatePlugin(plugin)) {
|
||||
let overlay = this.getPluginUI(plugin, "main");
|
||||
if (overlay) {
|
||||
overlay.removeEventListener("click", gPluginHandler._overlayClickListener, true);
|
||||
if (aNewState == "block") {
|
||||
plugin.reload(true);
|
||||
} else {
|
||||
if (gPluginHandler.canActivatePlugin(plugin)) {
|
||||
let overlay = this.getPluginUI(plugin, "main");
|
||||
if (overlay) {
|
||||
overlay.removeEventListener("click", gPluginHandler._overlayClickListener, true);
|
||||
}
|
||||
plugin.playPlugin();
|
||||
}
|
||||
plugin.playPlugin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no instances of the plugin on the page any more, what the
|
||||
// user probably needs is for us to allow and then refresh.
|
||||
if (!pluginFound) {
|
||||
if (aNewState != "block" && !pluginFound) {
|
||||
browser.reload();
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1514,6 +1514,7 @@ SocialStatus = {
|
|||
"class": "social-panel-frame",
|
||||
"id": notificationFrameId,
|
||||
"tooltip": "aHTMLTooltip",
|
||||
"context": "contentAreaContextMenu",
|
||||
|
||||
// work around bug 793057 - by making the panel roughly the final size
|
||||
// we are more likely to have the anchor in the correct position.
|
||||
|
|
|
@ -8,52 +8,6 @@ var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Co
|
|||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// This listens for the next opened tab and checks it is of the right url.
|
||||
// opencallback is called when the new tab is fully loaded
|
||||
// closecallback is called when the tab is closed
|
||||
function TabOpenListener(url, opencallback, closecallback) {
|
||||
this.url = url;
|
||||
this.opencallback = opencallback;
|
||||
this.closecallback = closecallback;
|
||||
|
||||
gBrowser.tabContainer.addEventListener("TabOpen", this, false);
|
||||
}
|
||||
|
||||
TabOpenListener.prototype = {
|
||||
url: null,
|
||||
opencallback: null,
|
||||
closecallback: null,
|
||||
tab: null,
|
||||
browser: null,
|
||||
|
||||
handleEvent: function(event) {
|
||||
if (event.type == "TabOpen") {
|
||||
gBrowser.tabContainer.removeEventListener("TabOpen", this, false);
|
||||
this.tab = event.originalTarget;
|
||||
this.browser = this.tab.linkedBrowser;
|
||||
gBrowser.addEventListener("pageshow", this, false);
|
||||
} else if (event.type == "pageshow") {
|
||||
if (event.target.location.href != this.url)
|
||||
return;
|
||||
gBrowser.removeEventListener("pageshow", this, false);
|
||||
this.tab.addEventListener("TabClose", this, false);
|
||||
var url = this.browser.contentDocument.location.href;
|
||||
is(url, this.url, "Should have opened the correct tab");
|
||||
this.opencallback(this.tab, this.browser.contentWindow);
|
||||
} else if (event.type == "TabClose") {
|
||||
if (event.originalTarget != this.tab)
|
||||
return;
|
||||
this.tab.removeEventListener("TabClose", this, false);
|
||||
this.opencallback = null;
|
||||
this.tab = null;
|
||||
this.browser = null;
|
||||
// Let the window close complete
|
||||
executeSoon(this.closecallback);
|
||||
this.closecallback = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
registerCleanupFunction(function() {
|
||||
|
|
|
@ -9,52 +9,6 @@ var gRunNextTestAfterPluginRemoved = false;
|
|||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// This listens for the next opened tab and checks it is of the right url.
|
||||
// opencallback is called when the new tab is fully loaded
|
||||
// closecallback is called when the tab is closed
|
||||
function TabOpenListener(url, opencallback, closecallback) {
|
||||
this.url = url;
|
||||
this.opencallback = opencallback;
|
||||
this.closecallback = closecallback;
|
||||
|
||||
gBrowser.tabContainer.addEventListener("TabOpen", this, false);
|
||||
}
|
||||
|
||||
TabOpenListener.prototype = {
|
||||
url: null,
|
||||
opencallback: null,
|
||||
closecallback: null,
|
||||
tab: null,
|
||||
browser: null,
|
||||
|
||||
handleEvent: function(event) {
|
||||
if (event.type == "TabOpen") {
|
||||
gBrowser.tabContainer.removeEventListener("TabOpen", this, false);
|
||||
this.tab = event.originalTarget;
|
||||
this.browser = this.tab.linkedBrowser;
|
||||
gBrowser.addEventListener("pageshow", this, false);
|
||||
} else if (event.type == "pageshow") {
|
||||
if (event.target.location.href != this.url)
|
||||
return;
|
||||
gBrowser.removeEventListener("pageshow", this, false);
|
||||
this.tab.addEventListener("TabClose", this, false);
|
||||
var url = this.browser.contentDocument.location.href;
|
||||
is(url, this.url, "Should have opened the correct tab");
|
||||
this.opencallback(this.tab, this.browser.contentWindow);
|
||||
} else if (event.type == "TabClose") {
|
||||
if (event.originalTarget != this.tab)
|
||||
return;
|
||||
this.tab.removeEventListener("TabClose", this, false);
|
||||
this.opencallback = null;
|
||||
this.tab = null;
|
||||
this.browser = null;
|
||||
// Let the window close complete
|
||||
executeSoon(this.closecallback);
|
||||
this.closecallback = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
registerCleanupFunction(function() {
|
||||
|
|
|
@ -8,52 +8,6 @@ var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Co
|
|||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// This listens for the next opened tab and checks it is of the right url.
|
||||
// opencallback is called when the new tab is fully loaded
|
||||
// closecallback is called when the tab is closed
|
||||
function TabOpenListener(url, opencallback, closecallback) {
|
||||
this.url = url;
|
||||
this.opencallback = opencallback;
|
||||
this.closecallback = closecallback;
|
||||
|
||||
gBrowser.tabContainer.addEventListener("TabOpen", this, false);
|
||||
}
|
||||
|
||||
TabOpenListener.prototype = {
|
||||
url: null,
|
||||
opencallback: null,
|
||||
closecallback: null,
|
||||
tab: null,
|
||||
browser: null,
|
||||
|
||||
handleEvent: function(event) {
|
||||
if (event.type == "TabOpen") {
|
||||
gBrowser.tabContainer.removeEventListener("TabOpen", this, false);
|
||||
this.tab = event.originalTarget;
|
||||
this.browser = this.tab.linkedBrowser;
|
||||
gBrowser.addEventListener("pageshow", this, false);
|
||||
} else if (event.type == "pageshow") {
|
||||
if (event.target.location.href != this.url)
|
||||
return;
|
||||
gBrowser.removeEventListener("pageshow", this, false);
|
||||
this.tab.addEventListener("TabClose", this, false);
|
||||
var url = this.browser.contentDocument.location.href;
|
||||
is(url, this.url, "Should have opened the correct tab");
|
||||
this.opencallback(this.tab, this.browser.contentWindow);
|
||||
} else if (event.type == "TabClose") {
|
||||
if (event.originalTarget != this.tab)
|
||||
return;
|
||||
this.tab.removeEventListener("TabClose", this, false);
|
||||
this.opencallback = null;
|
||||
this.tab = null;
|
||||
this.browser = null;
|
||||
// Let the window close complete
|
||||
executeSoon(this.closecallback);
|
||||
this.closecallback = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
registerCleanupFunction(function() {
|
||||
|
|
|
@ -90,8 +90,12 @@ function test() {
|
|||
|
||||
function doTest(aIsPrivateMode, aWindow, aCallback) {
|
||||
aWindow.gBrowser.addEventListener("pageshow", function pageShown(event) {
|
||||
if (event.target.location == "about:blank")
|
||||
// If data: -url PAC file isn't loaded soon enough, we may get about:privatebrowsing loaded
|
||||
if (event.target.location == "about:blank" ||
|
||||
event.target.location == "about:privatebrowsing") {
|
||||
aWindow.gBrowser.selectedBrowser.loadURI(testURI);
|
||||
return;
|
||||
}
|
||||
aWindow.gBrowser.removeEventListener("pageshow", pageShown);
|
||||
|
||||
executeSoon(function () {
|
||||
|
|
|
@ -93,7 +93,7 @@ let MessageListener = {
|
|||
receiveMessage: function ({name, data: {id}}) {
|
||||
switch (name) {
|
||||
case "SessionStore:collectSessionHistory":
|
||||
let history = SessionHistory.read(docShell);
|
||||
let history = SessionHistory.collect(docShell);
|
||||
if ("index" in history) {
|
||||
let tabIndex = history.index - 1;
|
||||
// Don't include private data. It's only needed when duplicating
|
||||
|
@ -141,7 +141,7 @@ let SyncHandler = {
|
|||
},
|
||||
|
||||
collectSessionHistory: function (includePrivateData) {
|
||||
let history = SessionHistory.read(docShell);
|
||||
let history = SessionHistory.collect(docShell);
|
||||
if ("index" in history) {
|
||||
let tabIndex = history.index - 1;
|
||||
TextAndScrollData.updateFrame(history.entries[tabIndex],
|
||||
|
|
|
@ -15,6 +15,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
|
||||
"resource:///modules/sessionstore/PrivacyLevel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Utils",
|
||||
"resource:///modules/sessionstore/Utils.jsm");
|
||||
|
||||
function debug(msg) {
|
||||
Services.console.logStringMessage("SessionHistory: " + msg);
|
||||
|
@ -36,8 +38,12 @@ XPCOMUtils.defineLazyGetter(this, "gPostData", function () {
|
|||
* The external API exported by this module.
|
||||
*/
|
||||
this.SessionHistory = Object.freeze({
|
||||
read: function (docShell, includePrivateData) {
|
||||
return SessionHistoryInternal.read(docShell, includePrivateData);
|
||||
collect: function (docShell, includePrivateData) {
|
||||
return SessionHistoryInternal.collect(docShell, includePrivateData);
|
||||
},
|
||||
|
||||
restore: function (docShell, tabData) {
|
||||
SessionHistoryInternal.restore(docShell, tabData);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -53,7 +59,7 @@ let SessionHistoryInternal = {
|
|||
* @param includePrivateData (optional)
|
||||
* True to always include private data and skip any privacy checks.
|
||||
*/
|
||||
read: function (docShell, includePrivateData = false) {
|
||||
collect: function (docShell, includePrivateData = false) {
|
||||
let data = {entries: []};
|
||||
let isPinned = docShell.isAppTab;
|
||||
let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
|
@ -63,7 +69,7 @@ let SessionHistoryInternal = {
|
|||
try {
|
||||
for (let i = 0; i < history.count; i++) {
|
||||
let shEntry = history.getEntryAtIndex(i, false);
|
||||
let entry = this._serializeEntry(shEntry, includePrivateData, isPinned);
|
||||
let entry = this.serializeEntry(shEntry, includePrivateData, isPinned);
|
||||
data.entries.push(entry);
|
||||
}
|
||||
} catch (ex) {
|
||||
|
@ -109,7 +115,7 @@ let SessionHistoryInternal = {
|
|||
* The tab is pinned and should be treated differently for privacy.
|
||||
* @return object
|
||||
*/
|
||||
_serializeEntry: function (shEntry, includePrivateData, isPinned) {
|
||||
serializeEntry: function (shEntry, includePrivateData, isPinned) {
|
||||
let entry = { url: shEntry.URI.spec };
|
||||
|
||||
// Save some bytes and don't include the title property
|
||||
|
@ -152,7 +158,7 @@ let SessionHistoryInternal = {
|
|||
|
||||
// Collect post data for the current history entry.
|
||||
try {
|
||||
let postdata = this._serializePostData(shEntry, isPinned);
|
||||
let postdata = this.serializePostData(shEntry, isPinned);
|
||||
if (postdata) {
|
||||
entry.postdata_b64 = postdata;
|
||||
}
|
||||
|
@ -163,7 +169,7 @@ let SessionHistoryInternal = {
|
|||
|
||||
// Collect owner data for the current history entry.
|
||||
try {
|
||||
let owner = this._serializeOwner(shEntry);
|
||||
let owner = this.serializeOwner(shEntry);
|
||||
if (owner) {
|
||||
entry.owner_b64 = owner;
|
||||
}
|
||||
|
@ -197,7 +203,7 @@ let SessionHistoryInternal = {
|
|||
break;
|
||||
}
|
||||
|
||||
children.push(this._serializeEntry(child, includePrivateData, isPinned));
|
||||
children.push(this.serializeEntry(child, includePrivateData, isPinned));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,7 +224,7 @@ let SessionHistoryInternal = {
|
|||
* Whether the docShell is owned by a pinned tab.
|
||||
* @return The base64 encoded post data.
|
||||
*/
|
||||
_serializePostData: function (shEntry, isPinned) {
|
||||
serializePostData: function (shEntry, isPinned) {
|
||||
let isHttps = shEntry.URI.schemeIs("https");
|
||||
if (!shEntry.postData || !gPostData ||
|
||||
!PrivacyLevel.canSave({isHttps: isHttps, isPinned: isPinned})) {
|
||||
|
@ -250,7 +256,7 @@ let SessionHistoryInternal = {
|
|||
* The session history entry.
|
||||
* @return The base64 encoded owner data.
|
||||
*/
|
||||
_serializeOwner: function (shEntry) {
|
||||
serializeOwner: function (shEntry) {
|
||||
if (!shEntry.owner) {
|
||||
return null;
|
||||
}
|
||||
|
@ -274,5 +280,166 @@ let SessionHistoryInternal = {
|
|||
// is guaranteed to handle all chars in strings, including embedded
|
||||
// nulls.
|
||||
return btoa(String.fromCharCode.apply(null, ownerBytes));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Restores session history data for a given docShell.
|
||||
*
|
||||
* @param docShell
|
||||
* The docShell that owns the session history.
|
||||
* @param tabData
|
||||
* The tabdata including all history entries.
|
||||
*/
|
||||
restore: function (docShell, tabData) {
|
||||
let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let history = webNavigation.sessionHistory;
|
||||
|
||||
if (history.count > 0) {
|
||||
history.PurgeHistory(history.count);
|
||||
}
|
||||
history.QueryInterface(Ci.nsISHistoryInternal);
|
||||
|
||||
let idMap = { used: {} };
|
||||
let docIdentMap = {};
|
||||
for (let i = 0; i < tabData.entries.length; i++) {
|
||||
//XXXzpao Wallpaper patch for bug 514751
|
||||
if (!tabData.entries[i].url)
|
||||
continue;
|
||||
history.addEntry(this.deserializeEntry(tabData.entries[i],
|
||||
idMap, docIdentMap), true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Expands serialized history data into a session-history-entry instance.
|
||||
*
|
||||
* @param entry
|
||||
* Object containing serialized history data for a URL
|
||||
* @param idMap
|
||||
* Hash for ensuring unique frame IDs
|
||||
* @param docIdentMap
|
||||
* Hash to ensure reuse of BFCache entries
|
||||
* @returns nsISHEntry
|
||||
*/
|
||||
deserializeEntry: function (entry, idMap, docIdentMap) {
|
||||
|
||||
var shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].
|
||||
createInstance(Ci.nsISHEntry);
|
||||
|
||||
shEntry.setURI(Utils.makeURI(entry.url));
|
||||
shEntry.setTitle(entry.title || entry.url);
|
||||
if (entry.subframe)
|
||||
shEntry.setIsSubFrame(entry.subframe || false);
|
||||
shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
|
||||
if (entry.contentType)
|
||||
shEntry.contentType = entry.contentType;
|
||||
if (entry.referrer)
|
||||
shEntry.referrerURI = Utils.makeURI(entry.referrer);
|
||||
if (entry.isSrcdocEntry)
|
||||
shEntry.srcdocData = entry.srcdocData;
|
||||
|
||||
if (entry.cacheKey) {
|
||||
var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].
|
||||
createInstance(Ci.nsISupportsPRUint32);
|
||||
cacheKey.data = entry.cacheKey;
|
||||
shEntry.cacheKey = cacheKey;
|
||||
}
|
||||
|
||||
if (entry.ID) {
|
||||
// get a new unique ID for this frame (since the one from the last
|
||||
// start might already be in use)
|
||||
var id = idMap[entry.ID] || 0;
|
||||
if (!id) {
|
||||
for (id = Date.now(); id in idMap.used; id++);
|
||||
idMap[entry.ID] = id;
|
||||
idMap.used[id] = true;
|
||||
}
|
||||
shEntry.ID = id;
|
||||
}
|
||||
|
||||
if (entry.docshellID)
|
||||
shEntry.docshellID = entry.docshellID;
|
||||
|
||||
if (entry.structuredCloneState && entry.structuredCloneVersion) {
|
||||
shEntry.stateData =
|
||||
Cc["@mozilla.org/docshell/structured-clone-container;1"].
|
||||
createInstance(Ci.nsIStructuredCloneContainer);
|
||||
|
||||
shEntry.stateData.initFromBase64(entry.structuredCloneState,
|
||||
entry.structuredCloneVersion);
|
||||
}
|
||||
|
||||
if (entry.scroll) {
|
||||
var scrollPos = (entry.scroll || "0,0").split(",");
|
||||
scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
|
||||
shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
|
||||
}
|
||||
|
||||
if (entry.postdata_b64) {
|
||||
var postdata = atob(entry.postdata_b64);
|
||||
var stream = Cc["@mozilla.org/io/string-input-stream;1"].
|
||||
createInstance(Ci.nsIStringInputStream);
|
||||
stream.setData(postdata, postdata.length);
|
||||
shEntry.postData = stream;
|
||||
}
|
||||
|
||||
let childDocIdents = {};
|
||||
if (entry.docIdentifier) {
|
||||
// If we have a serialized document identifier, try to find an SHEntry
|
||||
// which matches that doc identifier and adopt that SHEntry's
|
||||
// BFCacheEntry. If we don't find a match, insert shEntry as the match
|
||||
// for the document identifier.
|
||||
let matchingEntry = docIdentMap[entry.docIdentifier];
|
||||
if (!matchingEntry) {
|
||||
matchingEntry = {shEntry: shEntry, childDocIdents: childDocIdents};
|
||||
docIdentMap[entry.docIdentifier] = matchingEntry;
|
||||
}
|
||||
else {
|
||||
shEntry.adoptBFCacheEntry(matchingEntry.shEntry);
|
||||
childDocIdents = matchingEntry.childDocIdents;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.owner_b64) {
|
||||
var ownerInput = Cc["@mozilla.org/io/string-input-stream;1"].
|
||||
createInstance(Ci.nsIStringInputStream);
|
||||
var binaryData = atob(entry.owner_b64);
|
||||
ownerInput.setData(binaryData, binaryData.length);
|
||||
var binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
|
||||
createInstance(Ci.nsIObjectInputStream);
|
||||
binaryStream.setInputStream(ownerInput);
|
||||
try { // Catch possible deserialization exceptions
|
||||
shEntry.owner = binaryStream.readObject(true);
|
||||
} catch (ex) { debug(ex); }
|
||||
}
|
||||
|
||||
if (entry.children && shEntry instanceof Ci.nsISHContainer) {
|
||||
for (var i = 0; i < entry.children.length; i++) {
|
||||
//XXXzpao Wallpaper patch for bug 514751
|
||||
if (!entry.children[i].url)
|
||||
continue;
|
||||
|
||||
// We're getting sessionrestore.js files with a cycle in the
|
||||
// doc-identifier graph, likely due to bug 698656. (That is, we have
|
||||
// an entry where doc identifier A is an ancestor of doc identifier B,
|
||||
// and another entry where doc identifier B is an ancestor of A.)
|
||||
//
|
||||
// If we were to respect these doc identifiers, we'd create a cycle in
|
||||
// the SHEntries themselves, which causes the docshell to loop forever
|
||||
// when it looks for the root SHEntry.
|
||||
//
|
||||
// So as a hack to fix this, we restrict the scope of a doc identifier
|
||||
// to be a node's siblings and cousins, and pass childDocIdents, not
|
||||
// aDocIdents, to _deserializeHistoryEntry. That is, we say that two
|
||||
// SHEntries with the same doc identifier have the same document iff
|
||||
// they have the same parent or their parents have the same document.
|
||||
|
||||
shEntry.AddChild(this.deserializeEntry(entry.children[i], idMap,
|
||||
childDocIdents), i);
|
||||
}
|
||||
}
|
||||
|
||||
return shEntry;
|
||||
},
|
||||
|
||||
};
|
||||
|
|
|
@ -117,6 +117,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "TextAndScrollData",
|
|||
"resource:///modules/sessionstore/TextAndScrollData.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
|
||||
"resource:///modules/sessionstore/SessionFile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionHistory",
|
||||
"resource:///modules/sessionstore/SessionHistory.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TabAttributes",
|
||||
"resource:///modules/sessionstore/TabAttributes.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TabState",
|
||||
|
@ -1346,10 +1348,10 @@ let SessionStoreInternal = {
|
|||
let tab = aWindow.gBrowser.selectedTab;
|
||||
// If __SS_restoreState is still on the browser and it is
|
||||
// TAB_STATE_NEEDS_RESTORE, then then we haven't restored
|
||||
// this tab yet. Explicitly call restoreTab to kick off the restore.
|
||||
// this tab yet. Explicitly call restoreTabContent to kick off the restore.
|
||||
if (tab.linkedBrowser.__SS_restoreState &&
|
||||
tab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
|
||||
this.restoreTab(tab);
|
||||
this.restoreTabContent(tab);
|
||||
|
||||
// attempt to update the current URL we send in a crash report
|
||||
this._updateCrashReportURL(aWindow);
|
||||
|
@ -1477,7 +1479,7 @@ let SessionStoreInternal = {
|
|||
// Remove the tab state from the cache.
|
||||
// Note that we cannot simply replace the contents of the cache
|
||||
// as |aState| can be an incomplete state that will be completed
|
||||
// by |restoreHistoryPrecursor|.
|
||||
// by |restoreTabs|.
|
||||
let tabState = JSON.parse(aState);
|
||||
if (!tabState) {
|
||||
debug("Empty state argument");
|
||||
|
@ -1508,7 +1510,7 @@ let SessionStoreInternal = {
|
|||
|
||||
TabStateCache.delete(aTab);
|
||||
this._setWindowStateBusy(window);
|
||||
this.restoreHistoryPrecursor(window, [aTab], [tabState], 0);
|
||||
this.restoreTabs(window, [aTab], [tabState], 0);
|
||||
},
|
||||
|
||||
duplicateTab: function ssi_duplicateTab(aWindow, aTab, aDelta = 0) {
|
||||
|
@ -1528,8 +1530,8 @@ let SessionStoreInternal = {
|
|||
aWindow.gBrowser.addTab(null, {relatedToCurrent: true, ownerTab: aTab}) :
|
||||
aWindow.gBrowser.addTab();
|
||||
|
||||
this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0,
|
||||
true /* Load this tab right away. */);
|
||||
this.restoreTabs(aWindow, [newTab], [tabState], 0,
|
||||
true /* Load this tab right away. */);
|
||||
|
||||
return newTab;
|
||||
},
|
||||
|
@ -1608,7 +1610,7 @@ let SessionStoreInternal = {
|
|||
let tab = tabbrowser.addTab();
|
||||
|
||||
// restore tab content
|
||||
this.restoreHistoryPrecursor(aWindow, [tab], [closedTabState], 1);
|
||||
this.restoreTabs(aWindow, [tab], [closedTabState], 1);
|
||||
|
||||
// restore the tab's position
|
||||
tabbrowser.moveTabTo(tab, closedTab.pos);
|
||||
|
@ -2227,7 +2229,7 @@ let SessionStoreInternal = {
|
|||
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_RESTORE_WINDOW_MS");
|
||||
|
||||
// We're not returning from this before we end up calling restoreHistoryPrecursor
|
||||
// We're not returning from this before we end up calling restoreTabs
|
||||
// for this window, so make sure we send the SSWindowStateBusy event.
|
||||
this._setWindowStateBusy(aWindow);
|
||||
|
||||
|
@ -2325,7 +2327,7 @@ let SessionStoreInternal = {
|
|||
// If overwriting tabs, we want to reset each tab's "restoring" state. Since
|
||||
// we're overwriting those tabs, they should no longer be restoring. The
|
||||
// tabs will be rebuilt and marked if they need to be restored after loading
|
||||
// state (in restoreHistoryPrecursor).
|
||||
// state (in restoreTabs).
|
||||
// We also want to invalidate any cached information on the tab state.
|
||||
if (overwriteTabs) {
|
||||
for (let i = 0; i < tabbrowser.tabs.length; i++) {
|
||||
|
@ -2380,7 +2382,7 @@ let SessionStoreInternal = {
|
|||
this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs || [];
|
||||
}
|
||||
|
||||
this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs,
|
||||
this.restoreTabs(aWindow, tabs, winData.tabs,
|
||||
(overwriteTabs ? (parseInt(winData.selected) || 1) : 0));
|
||||
|
||||
if (aState.scratchpads) {
|
||||
|
@ -2474,7 +2476,7 @@ let SessionStoreInternal = {
|
|||
|
||||
return [aTabs, aTabData];
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Manage history restoration for a window
|
||||
* @param aWindow
|
||||
|
@ -2489,9 +2491,8 @@ let SessionStoreInternal = {
|
|||
* Flag to indicate whether the given set of tabs aTabs should be
|
||||
* restored/loaded immediately even if restore_on_demand = true
|
||||
*/
|
||||
restoreHistoryPrecursor:
|
||||
function ssi_restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab,
|
||||
aRestoreImmediately = false)
|
||||
restoreTabs: function (aWindow, aTabs, aTabData, aSelectTab,
|
||||
aRestoreImmediately = false)
|
||||
{
|
||||
|
||||
var tabbrowser = aWindow.gBrowser;
|
||||
|
@ -2553,6 +2554,17 @@ let SessionStoreInternal = {
|
|||
// out of date now that we're restoring it.
|
||||
TabState.dropPendingCollections(tab);
|
||||
|
||||
if (!tabData.entries) {
|
||||
tabData.entries = [];
|
||||
}
|
||||
if (tabData.extData) {
|
||||
tab.__SS_extdata = {};
|
||||
for (let key in tabData.extData)
|
||||
tab.__SS_extdata[key] = tabData.extData[key];
|
||||
} else {
|
||||
delete tab.__SS_extdata;
|
||||
}
|
||||
|
||||
browser.__SS_tabStillLoading = true;
|
||||
|
||||
// keep the data around to prevent dataloss in case
|
||||
|
@ -2562,11 +2574,7 @@ let SessionStoreInternal = {
|
|||
browser.setAttribute("pending", "true");
|
||||
tab.setAttribute("pending", "true");
|
||||
|
||||
// Make sure that set/getTabValue will set/read the correct data by
|
||||
// wiping out any current value in tab.__SS_extdata.
|
||||
delete tab.__SS_extdata;
|
||||
|
||||
if (!tabData.entries || tabData.entries.length == 0) {
|
||||
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",
|
||||
|
@ -2601,114 +2609,84 @@ let SessionStoreInternal = {
|
|||
tab.crop = "center";
|
||||
}
|
||||
}
|
||||
|
||||
// Restore tab attributes.
|
||||
if ("attributes" in tabData) {
|
||||
TabAttributes.set(tab, tabData.attributes);
|
||||
}
|
||||
|
||||
// Restore the tab icon.
|
||||
if ("image" in tabData) {
|
||||
tabbrowser.setIcon(tab, tabData.image);
|
||||
}
|
||||
}
|
||||
|
||||
// helper hashes for ensuring unique frame IDs and unique document
|
||||
// identifiers.
|
||||
let idMap = { used: {} };
|
||||
let docIdentMap = {};
|
||||
this.restoreHistory(aWindow, aTabs, aTabData, idMap, docIdentMap,
|
||||
aRestoreImmediately);
|
||||
function restoreNextHistory() {
|
||||
if (aWindow.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the tab got removed before being completely restored, then skip it
|
||||
while (aTabs.length > 0 && !this._canRestoreTabHistory(aTabs[0])) {
|
||||
aTabs.shift();
|
||||
aTabData.shift();
|
||||
}
|
||||
if (aTabs.length == 0) {
|
||||
// At this point we're essentially ready for consumers to read/write data
|
||||
// via the sessionstore API so we'll send the SSWindowStateReady event.
|
||||
this._setWindowStateReady(aWindow);
|
||||
return; // no more tabs to restore
|
||||
}
|
||||
|
||||
let tab = aTabs.shift();
|
||||
let tabData = aTabData.shift();
|
||||
this.restoreHistory(aWindow, tab, tabData, aRestoreImmediately);
|
||||
|
||||
// Restore the history in the next tab
|
||||
aWindow.setTimeout(restoreNextHistory.bind(this), 0);
|
||||
}
|
||||
|
||||
restoreNextHistory.call(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Restore history for a list of tabs.
|
||||
* @param aWindow
|
||||
* @param window
|
||||
* Window reference
|
||||
* @param aTabs
|
||||
* Array of tab references
|
||||
* @param aTabData
|
||||
* Array of tab data
|
||||
* @param aIdMap
|
||||
* Hash for ensuring unique frame IDs
|
||||
* @param aRestoreImmediately
|
||||
* @param tab
|
||||
* Tab to be restored
|
||||
* @param tabData
|
||||
* Tab data to restore
|
||||
* @param restoreImmediately
|
||||
* Flag to indicate whether the given set of tabs aTabs should be
|
||||
* restored/loaded immediately even if restore_on_demand = true
|
||||
*/
|
||||
restoreHistory:
|
||||
function ssi_restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap,
|
||||
aRestoreImmediately)
|
||||
{
|
||||
// if the tab got removed before being completely restored, then skip it
|
||||
while (aTabs.length > 0 && !(this._canRestoreTabHistory(aTabs[0]))) {
|
||||
aTabs.shift();
|
||||
aTabData.shift();
|
||||
}
|
||||
if (aTabs.length == 0) {
|
||||
// At this point we're essentially ready for consumers to read/write data
|
||||
// via the sessionstore API so we'll send the SSWindowStateReady event.
|
||||
this._setWindowStateReady(aWindow);
|
||||
return; // no more tabs to restore
|
||||
}
|
||||
|
||||
var tab = aTabs.shift();
|
||||
var tabData = aTabData.shift();
|
||||
var browser = aWindow.gBrowser.getBrowserForTab(tab);
|
||||
var history = browser.webNavigation.sessionHistory;
|
||||
|
||||
if (history.count > 0) {
|
||||
history.PurgeHistory(history.count);
|
||||
}
|
||||
history.QueryInterface(Ci.nsISHistoryInternal);
|
||||
restoreHistory: function (window, tab, tabData, restoreImmediately) {
|
||||
let browser = tab.linkedBrowser;
|
||||
let history = browser.webNavigation.sessionHistory;
|
||||
|
||||
browser.__SS_shistoryListener = new SessionStoreSHistoryListener(tab);
|
||||
history.addSHistoryListener(browser.__SS_shistoryListener);
|
||||
|
||||
if (!tabData.entries) {
|
||||
tabData.entries = [];
|
||||
}
|
||||
if (tabData.extData) {
|
||||
tab.__SS_extdata = {};
|
||||
for (let key in tabData.extData)
|
||||
tab.__SS_extdata[key] = tabData.extData[key];
|
||||
}
|
||||
else
|
||||
delete tab.__SS_extdata;
|
||||
|
||||
for (var i = 0; i < tabData.entries.length; i++) {
|
||||
//XXXzpao Wallpaper patch for bug 514751
|
||||
if (!tabData.entries[i].url)
|
||||
continue;
|
||||
history.addEntry(this._deserializeHistoryEntry(tabData.entries[i],
|
||||
aIdMap, aDocIdentMap), true);
|
||||
}
|
||||
SessionHistory.restore(browser.docShell, tabData);
|
||||
|
||||
// make sure to reset the capabilities and attributes, in case this tab gets reused
|
||||
let disallow = new Set(tabData.disallow && tabData.disallow.split(","));
|
||||
DocShellCapabilities.restore(browser.docShell, disallow);
|
||||
|
||||
// Restore tab attributes.
|
||||
if ("attributes" in tabData) {
|
||||
TabAttributes.set(tab, tabData.attributes);
|
||||
}
|
||||
|
||||
// Restore the tab icon.
|
||||
if ("image" in tabData) {
|
||||
aWindow.gBrowser.setIcon(tab, tabData.image);
|
||||
}
|
||||
|
||||
if (tabData.storage && browser.docShell instanceof Ci.nsIDocShell)
|
||||
SessionStorage.deserialize(browser.docShell, tabData.storage);
|
||||
|
||||
// notify the tabbrowser that the tab chrome has been restored
|
||||
var event = aWindow.document.createEvent("Events");
|
||||
var event = window.document.createEvent("Events");
|
||||
event.initEvent("SSTabRestoring", true, false);
|
||||
tab.dispatchEvent(event);
|
||||
|
||||
// Restore the history in the next tab
|
||||
aWindow.setTimeout(() => {
|
||||
if (!aWindow.closed) {
|
||||
this.restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap,
|
||||
aRestoreImmediately);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
// This could cause us to ignore MAX_CONCURRENT_TAB_RESTORES a bit, but
|
||||
// it ensures each window will have its selected tab loaded.
|
||||
if (aRestoreImmediately || aWindow.gBrowser.selectedBrowser == browser) {
|
||||
this.restoreTab(tab);
|
||||
}
|
||||
else {
|
||||
if (restoreImmediately || window.gBrowser.selectedBrowser == browser) {
|
||||
this.restoreTabContent(tab);
|
||||
} else {
|
||||
TabRestoreQueue.add(tab);
|
||||
this.restoreNextTab();
|
||||
}
|
||||
|
@ -2731,7 +2709,7 @@ let SessionStoreInternal = {
|
|||
*
|
||||
* @returns true/false indicating whether or not a load actually happened
|
||||
*/
|
||||
restoreTab: function ssi_restoreTab(aTab) {
|
||||
restoreTabContent: function (aTab) {
|
||||
let window = aTab.ownerDocument.defaultView;
|
||||
let browser = aTab.linkedBrowser;
|
||||
let tabData = browser.__SS_data;
|
||||
|
@ -2834,7 +2812,7 @@ let SessionStoreInternal = {
|
|||
|
||||
let tab = TabRestoreQueue.shift();
|
||||
if (tab) {
|
||||
let didStartLoad = this.restoreTab(tab);
|
||||
let didStartLoad = this.restoreTabContent(tab);
|
||||
// If we don't start a load in the restored tab (eg, no entries) then we
|
||||
// want to attempt to restore the next tab.
|
||||
if (!didStartLoad)
|
||||
|
@ -2842,136 +2820,6 @@ let SessionStoreInternal = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* expands serialized history data into a session-history-entry instance
|
||||
* @param aEntry
|
||||
* Object containing serialized history data for a URL
|
||||
* @param aIdMap
|
||||
* Hash for ensuring unique frame IDs
|
||||
* @returns nsISHEntry
|
||||
*/
|
||||
_deserializeHistoryEntry:
|
||||
function ssi_deserializeHistoryEntry(aEntry, aIdMap, aDocIdentMap) {
|
||||
|
||||
var shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].
|
||||
createInstance(Ci.nsISHEntry);
|
||||
|
||||
shEntry.setURI(Utils.makeURI(aEntry.url));
|
||||
shEntry.setTitle(aEntry.title || aEntry.url);
|
||||
if (aEntry.subframe)
|
||||
shEntry.setIsSubFrame(aEntry.subframe || false);
|
||||
shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
|
||||
if (aEntry.contentType)
|
||||
shEntry.contentType = aEntry.contentType;
|
||||
if (aEntry.referrer)
|
||||
shEntry.referrerURI = Utils.makeURI(aEntry.referrer);
|
||||
if (aEntry.isSrcdocEntry)
|
||||
shEntry.srcdocData = aEntry.srcdocData;
|
||||
|
||||
if (aEntry.cacheKey) {
|
||||
var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].
|
||||
createInstance(Ci.nsISupportsPRUint32);
|
||||
cacheKey.data = aEntry.cacheKey;
|
||||
shEntry.cacheKey = cacheKey;
|
||||
}
|
||||
|
||||
if (aEntry.ID) {
|
||||
// get a new unique ID for this frame (since the one from the last
|
||||
// start might already be in use)
|
||||
var id = aIdMap[aEntry.ID] || 0;
|
||||
if (!id) {
|
||||
for (id = Date.now(); id in aIdMap.used; id++);
|
||||
aIdMap[aEntry.ID] = id;
|
||||
aIdMap.used[id] = true;
|
||||
}
|
||||
shEntry.ID = id;
|
||||
}
|
||||
|
||||
if (aEntry.docshellID)
|
||||
shEntry.docshellID = aEntry.docshellID;
|
||||
|
||||
if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) {
|
||||
shEntry.stateData =
|
||||
Cc["@mozilla.org/docshell/structured-clone-container;1"].
|
||||
createInstance(Ci.nsIStructuredCloneContainer);
|
||||
|
||||
shEntry.stateData.initFromBase64(aEntry.structuredCloneState,
|
||||
aEntry.structuredCloneVersion);
|
||||
}
|
||||
|
||||
if (aEntry.scroll) {
|
||||
var scrollPos = (aEntry.scroll || "0,0").split(",");
|
||||
scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
|
||||
shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
|
||||
}
|
||||
|
||||
if (aEntry.postdata_b64) {
|
||||
var postdata = atob(aEntry.postdata_b64);
|
||||
var stream = Cc["@mozilla.org/io/string-input-stream;1"].
|
||||
createInstance(Ci.nsIStringInputStream);
|
||||
stream.setData(postdata, postdata.length);
|
||||
shEntry.postData = stream;
|
||||
}
|
||||
|
||||
let childDocIdents = {};
|
||||
if (aEntry.docIdentifier) {
|
||||
// If we have a serialized document identifier, try to find an SHEntry
|
||||
// which matches that doc identifier and adopt that SHEntry's
|
||||
// BFCacheEntry. If we don't find a match, insert shEntry as the match
|
||||
// for the document identifier.
|
||||
let matchingEntry = aDocIdentMap[aEntry.docIdentifier];
|
||||
if (!matchingEntry) {
|
||||
matchingEntry = {shEntry: shEntry, childDocIdents: childDocIdents};
|
||||
aDocIdentMap[aEntry.docIdentifier] = matchingEntry;
|
||||
}
|
||||
else {
|
||||
shEntry.adoptBFCacheEntry(matchingEntry.shEntry);
|
||||
childDocIdents = matchingEntry.childDocIdents;
|
||||
}
|
||||
}
|
||||
|
||||
if (aEntry.owner_b64) {
|
||||
var ownerInput = Cc["@mozilla.org/io/string-input-stream;1"].
|
||||
createInstance(Ci.nsIStringInputStream);
|
||||
var binaryData = atob(aEntry.owner_b64);
|
||||
ownerInput.setData(binaryData, binaryData.length);
|
||||
var binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
|
||||
createInstance(Ci.nsIObjectInputStream);
|
||||
binaryStream.setInputStream(ownerInput);
|
||||
try { // Catch possible deserialization exceptions
|
||||
shEntry.owner = binaryStream.readObject(true);
|
||||
} catch (ex) { debug(ex); }
|
||||
}
|
||||
|
||||
if (aEntry.children && shEntry instanceof Ci.nsISHContainer) {
|
||||
for (var i = 0; i < aEntry.children.length; i++) {
|
||||
//XXXzpao Wallpaper patch for bug 514751
|
||||
if (!aEntry.children[i].url)
|
||||
continue;
|
||||
|
||||
// We're getting sessionrestore.js files with a cycle in the
|
||||
// doc-identifier graph, likely due to bug 698656. (That is, we have
|
||||
// an entry where doc identifier A is an ancestor of doc identifier B,
|
||||
// and another entry where doc identifier B is an ancestor of A.)
|
||||
//
|
||||
// If we were to respect these doc identifiers, we'd create a cycle in
|
||||
// the SHEntries themselves, which causes the docshell to loop forever
|
||||
// when it looks for the root SHEntry.
|
||||
//
|
||||
// So as a hack to fix this, we restrict the scope of a doc identifier
|
||||
// to be a node's siblings and cousins, and pass childDocIdents, not
|
||||
// aDocIdents, to _deserializeHistoryEntry. That is, we say that two
|
||||
// SHEntries with the same doc identifier have the same document iff
|
||||
// they have the same parent or their parents have the same document.
|
||||
|
||||
shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap,
|
||||
childDocIdents), i);
|
||||
}
|
||||
}
|
||||
|
||||
return shEntry;
|
||||
},
|
||||
|
||||
/**
|
||||
* Accumulates a list of frames that need to be restored for the
|
||||
* given browser element. A frame is only restored if its current
|
||||
|
@ -3508,7 +3356,7 @@ let SessionStoreInternal = {
|
|||
/**
|
||||
* Determine if we can restore history into this tab.
|
||||
* This will be false when a tab has been removed (usually between
|
||||
* restoreHistoryPrecursor && restoreHistory) or if the tab is still marked
|
||||
* restoreTabs && restoreHistory) or if the tab is still marked
|
||||
* as loading.
|
||||
*
|
||||
* @param aTab
|
||||
|
@ -3835,11 +3683,11 @@ let SessionStoreInternal = {
|
|||
}
|
||||
else if (previousState == TAB_STATE_NEEDS_RESTORE) {
|
||||
// Make sure the session history listener is removed. This is normally
|
||||
// done in restoreTab, but this tab is being removed before that gets called.
|
||||
// done in restoreTabContent, but this tab is being removed before that gets called.
|
||||
this._removeSHistoryListener(aTab);
|
||||
|
||||
// Make sure that the tab is removed from the list of tabs to restore.
|
||||
// Again, this is normally done in restoreTab, but that isn't being called
|
||||
// Again, this is normally done in restoreTabContent, but that isn't being called
|
||||
// for this tab.
|
||||
TabRestoreQueue.remove(aTab);
|
||||
}
|
||||
|
@ -4129,9 +3977,9 @@ SessionStoreSHistoryListener.prototype = {
|
|||
},
|
||||
OnHistoryReload: function(aReloadURI, aReloadFlags) {
|
||||
// On reload, we want to make sure that session history loads the right
|
||||
// URI. In order to do that, we will juet call restoreTab. That will remove
|
||||
// URI. In order to do that, we will juet call restoreTabContent. That will remove
|
||||
// the history listener and load the right URI.
|
||||
SessionStoreInternal.restoreTab(this.tab);
|
||||
SessionStoreInternal.restoreTabContent(this.tab);
|
||||
// Returning false will stop the load that docshell is attempting.
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -404,7 +404,8 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
|
||||
DebuggerController.SourceScripts.togglePrettyPrint(source)
|
||||
.then(resetEditor, printError)
|
||||
.then(DebuggerView.showEditor);
|
||||
.then(DebuggerView.showEditor)
|
||||
.then(this.updateToolbarButtonsState);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -128,6 +128,7 @@ skip-if = true
|
|||
[browser_dbg_pretty-print-09.js]
|
||||
[browser_dbg_pretty-print-10.js]
|
||||
[browser_dbg_pretty-print-11.js]
|
||||
[browser_dbg_pretty-print-12.js]
|
||||
[browser_dbg_progress-listener-bug.js]
|
||||
[browser_dbg_reload-preferred-script-01.js]
|
||||
[browser_dbg_reload-preferred-script-02.js]
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Make sure that we don't leave the pretty print button checked when we fail to
|
||||
* pretty print a source (because it isn't a JS file, for example).
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gEditor, gSources;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gEditor = gDebugger.DebuggerView.editor;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
|
||||
waitForSourceShown(gPanel, "")
|
||||
.then(() => {
|
||||
let shown = ensureSourceIs(gPanel, TAB_URL, true)
|
||||
gSources.selectedValue = TAB_URL;
|
||||
return shown;
|
||||
})
|
||||
.then(clickPrettyPrintButton)
|
||||
.then(testButtonIsntChecked)
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(aError));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function clickPrettyPrintButton() {
|
||||
gDebugger.document.getElementById("pretty-print").click();
|
||||
}
|
||||
|
||||
function testButtonIsntChecked() {
|
||||
is(gDebugger.document.getElementById("pretty-print").checked, false,
|
||||
"The button shouldn't be checked after trying to pretty print a non-js file.");
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gEditor = null;
|
||||
gSources = null;
|
||||
});
|
|
@ -54,6 +54,10 @@
|
|||
<checkbox label="&options.enablePersistentLogging.label;"
|
||||
tooltiptext="&options.enablePersistentLogging.tooltip;"
|
||||
data-pref="devtools.webconsole.persistlog"/>
|
||||
<checkbox id="webconsole-timestamp-messages"
|
||||
label="&options.timestampMessages.label;"
|
||||
tooltiptext="&options.timestampMessages.tooltip;"
|
||||
data-pref="devtools.webconsole.timestampMessages"/>
|
||||
</vbox>
|
||||
<label value="&options.profiler.label;"/>
|
||||
<vbox id="profiler-options" class="options-groupbox">
|
||||
|
|
|
@ -239,3 +239,4 @@ skip-if = os == "linux"
|
|||
[browser_webconsole_view_source.js]
|
||||
[browser_webconsole_reflow.js]
|
||||
[browser_webconsole_log_file_filter.js]
|
||||
[browser_webconsole_expandable_timestamps.js]
|
||||
|
|
|
@ -22,6 +22,13 @@ function testFilterButtons(aHud) {
|
|||
testMenuFilterButton("css");
|
||||
testMenuFilterButton("js");
|
||||
testMenuFilterButton("logging");
|
||||
testMenuFilterButton("security");
|
||||
|
||||
testIsolateFilterButton("net");
|
||||
testIsolateFilterButton("css");
|
||||
testIsolateFilterButton("js");
|
||||
testIsolateFilterButton("logging");
|
||||
testIsolateFilterButton("security");
|
||||
|
||||
finishTest();
|
||||
}
|
||||
|
@ -72,15 +79,7 @@ function testMenuFilterButton(aCategory) {
|
|||
"checked after turning off its first menu item");
|
||||
|
||||
// Turn all the filters off by clicking the main part of the button.
|
||||
let anonymousNodes = hud.ui.document.getAnonymousNodes(button);
|
||||
let subbutton;
|
||||
for (let i = 0; i < anonymousNodes.length; i++) {
|
||||
let node = anonymousNodes[i];
|
||||
if (node.classList.contains("toolbarbutton-menubutton-button")) {
|
||||
subbutton = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let subbutton = getMainButton(button);
|
||||
ok(subbutton, "we have the subbutton for category " + aCategory);
|
||||
|
||||
clickButton(subbutton);
|
||||
|
@ -129,10 +128,81 @@ function testMenuFilterButton(aCategory) {
|
|||
clickButton(subbutton);
|
||||
}
|
||||
|
||||
function testIsolateFilterButton(aCategory) {
|
||||
let selector = ".webconsole-filter-button[category=\"" + aCategory + "\"]";
|
||||
let targetButton = hudBox.querySelector(selector);
|
||||
ok(targetButton, "we have the \"" + aCategory + "\" button");
|
||||
|
||||
// Get the main part of the filter button.
|
||||
let subbutton = getMainButton(targetButton);
|
||||
ok(subbutton, "we have the subbutton for category " + aCategory);
|
||||
|
||||
// Turn on all the filters by alt clicking the main part of the button.
|
||||
altClickButton(subbutton);
|
||||
ok(isChecked(targetButton), "the button for category " + aCategory +
|
||||
" is checked after isolating for filter");
|
||||
|
||||
// Check if all the filters for the target button are on.
|
||||
let menuItems = targetButton.querySelectorAll("menuitem");
|
||||
Array.forEach(menuItems, (item) => {
|
||||
let prefKey = item.getAttribute("prefKey");
|
||||
ok(isChecked(item), "menu item " + prefKey + " for category " +
|
||||
aCategory + " is checked after isolating for " + aCategory);
|
||||
ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
|
||||
"turned on after isolating for " + aCategory);
|
||||
});
|
||||
|
||||
// Ensure all other filter buttons are toggled off and their
|
||||
// associated filters are turned off
|
||||
let buttons = hudBox.querySelectorAll(".webconsole-filter-button[category]");
|
||||
Array.forEach(buttons, (filterButton) => {
|
||||
if (filterButton !== targetButton) {
|
||||
let category = filterButton.getAttribute("category");
|
||||
ok(!isChecked(filterButton), "the button for category " +
|
||||
category + " is unchecked after isolating for " + aCategory);
|
||||
|
||||
menuItems = filterButton.querySelectorAll("menuitem");
|
||||
Array.forEach(menuItems, (item) => {
|
||||
let prefKey = item.getAttribute("prefKey");
|
||||
ok(!isChecked(item), "menu item " + prefKey + " for category " +
|
||||
aCategory + " is unchecked after isolating for " + aCategory);
|
||||
ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
|
||||
"turned off after isolating for " + aCategory);
|
||||
});
|
||||
|
||||
// Turn all the filters on again by clicking the button.
|
||||
let mainButton = getMainButton(filterButton);
|
||||
clickButton(mainButton);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the main part of the target filter button.
|
||||
*/
|
||||
function getMainButton(aTargetButton) {
|
||||
let anonymousNodes = hud.ui.document.getAnonymousNodes(aTargetButton);
|
||||
let subbutton;
|
||||
|
||||
for (let i = 0; i < anonymousNodes.length; i++) {
|
||||
let node = anonymousNodes[i];
|
||||
if (node.classList.contains("toolbarbutton-menubutton-button")) {
|
||||
subbutton = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return subbutton;
|
||||
}
|
||||
|
||||
function clickButton(aNode) {
|
||||
EventUtils.sendMouseEvent({ type: "click" }, aNode);
|
||||
}
|
||||
|
||||
function altClickButton(aNode) {
|
||||
EventUtils.sendMouseEvent({ type: "click", altKey: true }, aNode);
|
||||
}
|
||||
|
||||
function chooseMenuItem(aNode) {
|
||||
let event = document.createEvent("XULCommandEvent");
|
||||
event.initCommandEvent("command", true, true, window, 0, false, false, false,
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test for the message timestamps option: check if the preference toggles the
|
||||
// display of messages in the console output. See bug 722267.
|
||||
|
||||
function test()
|
||||
{
|
||||
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
|
||||
let hud;
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref(PREF_MESSAGE_TIMESTAMP);
|
||||
});
|
||||
|
||||
addTab("data:text/html;charset=utf-8,Web Console test for bug 722267 - " +
|
||||
"preference for toggling timestamps in messages");
|
||||
|
||||
browser.addEventListener("load", function tabLoad() {
|
||||
browser.removeEventListener("load", tabLoad, true);
|
||||
openConsole(null, consoleOpened);
|
||||
}, true);
|
||||
|
||||
function consoleOpened(aHud)
|
||||
{
|
||||
hud = aHud;
|
||||
|
||||
info("console opened");
|
||||
let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
|
||||
ok(!prefValue, "messages have no timestamp by default (pref check)");
|
||||
ok(hud.outputNode.classList.contains("hideTimestamps"),
|
||||
"messages have no timestamp (class name check)");
|
||||
|
||||
let toolbox = gDevTools.getToolbox(hud.target);
|
||||
toolbox.selectTool("options").then(onOptionsPanelSelected);
|
||||
}
|
||||
|
||||
function onOptionsPanelSelected(panel)
|
||||
{
|
||||
info("options panel opened");
|
||||
|
||||
gDevTools.once("pref-changed", onPrefChanged);
|
||||
|
||||
let checkbox = panel.panelDoc.getElementById("webconsole-timestamp-messages");
|
||||
checkbox.click();
|
||||
}
|
||||
|
||||
function onPrefChanged()
|
||||
{
|
||||
info("pref changed");
|
||||
let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
|
||||
ok(prefValue, "messages have timestamps (pref check)");
|
||||
ok(!hud.outputNode.classList.contains("hideTimestamps"),
|
||||
"messages have timestamps (class name check)");
|
||||
finishTest();
|
||||
}
|
||||
}
|
|
@ -171,6 +171,7 @@ const MAX_LONG_STRING_LENGTH = 200000;
|
|||
|
||||
const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
|
||||
const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
|
||||
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
|
||||
|
||||
/**
|
||||
* A WebConsoleFrame instance is an interactive console initialized *per target*
|
||||
|
@ -201,6 +202,7 @@ function WebConsoleFrame(aWebConsoleOwner)
|
|||
this._toggleFilter = this._toggleFilter.bind(this);
|
||||
this._onPanelSelected = this._onPanelSelected.bind(this);
|
||||
this._flushMessageQueue = this._flushMessageQueue.bind(this);
|
||||
this._onToolboxPrefChanged = this._onToolboxPrefChanged.bind(this);
|
||||
|
||||
this._outputTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this._outputTimerInitialized = false;
|
||||
|
@ -571,6 +573,13 @@ WebConsoleFrame.prototype = {
|
|||
if (toolbox) {
|
||||
toolbox.on("webconsole-selected", this._onPanelSelected);
|
||||
}
|
||||
|
||||
// Toggle the timestamp on preference change
|
||||
gDevTools.on("pref-changed", this._onToolboxPrefChanged);
|
||||
this._onToolboxPrefChanged("pref-changed", {
|
||||
pref: PREF_MESSAGE_TIMESTAMP,
|
||||
newValue: Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP),
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -801,18 +810,26 @@ WebConsoleFrame.prototype = {
|
|||
break;
|
||||
}
|
||||
|
||||
// Toggle on the targeted filter button, and if the user alt clicked,
|
||||
// toggle off all other filter buttons and their associated filters.
|
||||
let state = target.getAttribute("checked") !== "true";
|
||||
if (aEvent.getModifierState("Alt")) {
|
||||
let buttons = this.document
|
||||
.querySelectorAll(".webconsole-filter-button");
|
||||
Array.forEach(buttons, (button) => {
|
||||
if (button !== target) {
|
||||
button.setAttribute("checked", false);
|
||||
this._setMenuState(button, false);
|
||||
}
|
||||
});
|
||||
state = true;
|
||||
}
|
||||
target.setAttribute("checked", state);
|
||||
|
||||
// This is a filter button with a drop-down, and the user clicked the
|
||||
// main part of the button. Go through all the severities and toggle
|
||||
// their associated filters.
|
||||
let menuItems = target.querySelectorAll("menuitem");
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
menuItems[i].setAttribute("checked", state);
|
||||
let prefKey = menuItems[i].getAttribute("prefKey");
|
||||
this.setFilterState(prefKey, state);
|
||||
}
|
||||
this._setMenuState(target, state);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -851,6 +868,25 @@ WebConsoleFrame.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the menu attributes for a specific toggle button.
|
||||
*
|
||||
* @private
|
||||
* @param XULElement aTarget
|
||||
* Button with drop down items to be toggled.
|
||||
* @param boolean aState
|
||||
* True if the menu item is being toggled on, and false otherwise.
|
||||
*/
|
||||
_setMenuState: function WCF__setMenuState(aTarget, aState)
|
||||
{
|
||||
let menuItems = aTarget.querySelectorAll("menuitem");
|
||||
Array.forEach(menuItems, (item) => {
|
||||
item.setAttribute("checked", aState);
|
||||
let prefKey = item.getAttribute("prefKey");
|
||||
this.setFilterState(prefKey, aState);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the filter state for a specific toggle button.
|
||||
*
|
||||
|
@ -2775,6 +2811,29 @@ WebConsoleFrame.prototype = {
|
|||
}, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the pref-changed event coming from the toolbox.
|
||||
* Currently this function only handles the timestamps preferences.
|
||||
*
|
||||
* @private
|
||||
* @param object aEvent
|
||||
* This parameter is a string that holds the event name
|
||||
* pref-changed in this case.
|
||||
* @param object aData
|
||||
* This is the pref-changed data object.
|
||||
*/
|
||||
_onToolboxPrefChanged: function WCF__onToolboxPrefChanged(aEvent, aData)
|
||||
{
|
||||
if (aData.pref == PREF_MESSAGE_TIMESTAMP) {
|
||||
if (aData.newValue) {
|
||||
this.outputNode.classList.remove("hideTimestamps");
|
||||
}
|
||||
else {
|
||||
this.outputNode.classList.add("hideTimestamps");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copies the selected items to the system clipboard.
|
||||
*
|
||||
|
@ -2885,6 +2944,8 @@ WebConsoleFrame.prototype = {
|
|||
toolbox.off("webconsole-selected", this._onPanelSelected);
|
||||
}
|
||||
|
||||
gDevTools.off("pref-changed", this._onToolboxPrefChanged);
|
||||
|
||||
this._repeatNodes = {};
|
||||
this._outputQueue = [];
|
||||
this._pruneCategoriesQueue = {};
|
||||
|
|
|
@ -48,8 +48,8 @@ function goUpdateConsoleCommands() {
|
|||
<command id="cmd_close" oncommand="goDoCommand('cmd_close');" disabled="true"/>
|
||||
</commandset>
|
||||
<keyset id="consoleKeys">
|
||||
<key id="key_fullZoomReduce" key="&fullZoomReduceCmd.commandkey;" command="cmd_fullZoomReduce" modifiers="accel"/>
|
||||
<key key="&fullZoomReduceCmd.commandkey2;" command="cmd_fullZoomReduce" modifiers="accel"/>
|
||||
<key id="key_fullZoomReduce" key="&fullZoomReduceCmd.commandkey;" command="cmd_fullZoomReduce" modifiers="accel"/>
|
||||
<key key="&fullZoomReduceCmd.commandkey2;" command="cmd_fullZoomReduce" modifiers="accel"/>
|
||||
<key id="key_fullZoomEnlarge" key="&fullZoomEnlargeCmd.commandkey;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
|
||||
<key key="&fullZoomEnlargeCmd.commandkey2;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
|
||||
<key key="&fullZoomEnlargeCmd.commandkey3;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
|
||||
|
|
|
@ -109,6 +109,11 @@
|
|||
<!ENTITY options.enablePersistentLogging.label "Enable persistent logs">
|
||||
<!ENTITY options.enablePersistentLogging.tooltip "If you enable this option the Web Console will not clear the output each time you navigate to a new page">
|
||||
|
||||
<!-- LOCALIZATION NOTE (options.timestampMessages.label): This is the
|
||||
- label for the checkbox that toggles timestamps in the Web Console -->
|
||||
<!ENTITY options.timestampMessages.label "Enable timestamps">
|
||||
<!ENTITY options.timestampMessages.tooltip "If you enable this option commands and output in the Web Console will display a timestamp">
|
||||
|
||||
<!-- LOCALIZATION NOTE (options.profiler.label): This is the label for the
|
||||
- heading of the group of JavaScript Profiler preferences in the options
|
||||
- panel. -->
|
||||
|
|
|
@ -170,9 +170,8 @@ var ContextCommands = {
|
|||
// Link specific
|
||||
|
||||
openLinkInNewTab: function cc_openLinkInNewTab() {
|
||||
let tab = Browser.addTab(ContextMenuUI.popupState.linkURL, false, Browser.selectedTab);
|
||||
ContextUI.peekTabs(kOpenInNewTabAnimationDelayMsec);
|
||||
Elements.tabList.strip.ensureElementIsVisible(tab.chromeTab);
|
||||
let url = ContextMenuUI.popupState.linkURL;
|
||||
BrowserUI.openLinkInNewTab(url, false, Browser.selectedTab);
|
||||
},
|
||||
|
||||
copyLink: function cc_copyLink() {
|
||||
|
|
|
@ -161,7 +161,7 @@ var ContextUI = {
|
|||
* Dismiss tab bar after a delay. Fires context ui events.
|
||||
*/
|
||||
dismissTabsWithDelay: function (aDelay) {
|
||||
aDelay = aDelay || kNewTabAnimationDelayMsec;
|
||||
aDelay = aDelay || kForegroundTabAnimationDelay;
|
||||
this._clearDelayedTimeout();
|
||||
this._hidingId = setTimeout(function () {
|
||||
ContextUI.dismissTabs();
|
||||
|
|
|
@ -39,6 +39,9 @@ var APZCObserver = {
|
|||
handleEvent: function APZC_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case 'pageshow':
|
||||
if (aEvent.target != Browser.selectedBrowser.contentDocument)
|
||||
break;
|
||||
// fall through to TabSelect:
|
||||
case 'TabSelect':
|
||||
// ROOT_ID doesn't really identify the view we want. When we call
|
||||
// this on a content document (tab), findElementWithViewId will
|
||||
|
|
|
@ -66,6 +66,9 @@ XPCOMUtils.defineLazyServiceGetter(window, "gFaviconService",
|
|||
XPCOMUtils.defineLazyServiceGetter(window, "gFocusManager",
|
||||
"@mozilla.org/focus-manager;1",
|
||||
"nsIFocusManager");
|
||||
XPCOMUtils.defineLazyServiceGetter(window, "gEventListenerService",
|
||||
"@mozilla.org/eventlistenerservice;1",
|
||||
"nsIEventListenerService");
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
|
||||
"@mozilla.org/xre/app-info;1",
|
||||
|
|
|
@ -14,12 +14,12 @@ Cu.import("resource://gre/modules/devtools/dbg-server.jsm")
|
|||
const debugServerStateChanged = "devtools.debugger.remote-enabled";
|
||||
const debugServerPortChanged = "devtools.debugger.remote-port";
|
||||
|
||||
// delay when showing the tab bar briefly after a new (empty) tab opens
|
||||
const kNewTabAnimationDelayMsec = 1000;
|
||||
// delay when showing the tab bar after opening a link on a new tab
|
||||
const kOpenInNewTabAnimationDelayMsec = 3000;
|
||||
// delay before closing tab bar after selecting another tab
|
||||
const kSelectTabAnimationDelayMsec = 500;
|
||||
// delay when showing the tab bar briefly after a new foreground tab opens
|
||||
const kForegroundTabAnimationDelay = 1000;
|
||||
// delay when showing the tab bar after opening a new background tab opens
|
||||
const kBackgroundTabAnimationDelay = 3000;
|
||||
// delay before closing tab bar after closing or selecting a tab
|
||||
const kChangeTabAnimationDelay = 500;
|
||||
|
||||
/**
|
||||
* Cache of commonly used elements.
|
||||
|
@ -173,6 +173,14 @@ var BrowserUI = {
|
|||
},
|
||||
|
||||
uninit: function() {
|
||||
messageManager.removeMessageListener("DOMTitleChanged", this);
|
||||
messageManager.removeMessageListener("DOMWillOpenModalDialog", this);
|
||||
messageManager.removeMessageListener("DOMWindowClose", this);
|
||||
|
||||
messageManager.removeMessageListener("Browser:OpenURI", this);
|
||||
messageManager.removeMessageListener("Browser:SaveAs:Return", this);
|
||||
messageManager.removeMessageListener("Content:StateChange", this);
|
||||
|
||||
messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps);
|
||||
Services.obs.removeObserver(this, "handle-xul-text-link");
|
||||
|
||||
|
@ -180,7 +188,6 @@ var BrowserUI = {
|
|||
FlyoutPanelsUI.uninit();
|
||||
MetroDownloadsView.uninit();
|
||||
SettingsCharm.uninit();
|
||||
messageManager.removeMessageListener("Content:StateChange", this);
|
||||
PageThumbs.uninit();
|
||||
this.stopDebugServer();
|
||||
},
|
||||
|
@ -436,10 +443,25 @@ var BrowserUI = {
|
|||
* See Browser.addTab for more documentation.
|
||||
*/
|
||||
addAndShowTab: function (aURI, aOwner) {
|
||||
ContextUI.peekTabs(kNewTabAnimationDelayMsec);
|
||||
ContextUI.peekTabs(kForegroundTabAnimationDelay);
|
||||
return Browser.addTab(aURI || kStartURI, true, aOwner);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open a new tab in response to clicking a link in an existing tab.
|
||||
* See Browser.addTab for more documentation.
|
||||
*/
|
||||
openLinkInNewTab: function (aURI, aBringFront, aOwner) {
|
||||
ContextUI.peekTabs(aBringFront ? kForegroundTabAnimationDelay
|
||||
: kBackgroundTabAnimationDelay);
|
||||
let tab = Browser.addTab(aURI, aBringFront, aOwner, {
|
||||
referrerURI: aOwner.browser.documentURI,
|
||||
charset: aOwner.browser.characterSet,
|
||||
});
|
||||
Elements.tabList.strip.ensureElementIsVisible(tab.chromeTab);
|
||||
return tab;
|
||||
},
|
||||
|
||||
setOnTabAnimationEnd: function setOnTabAnimationEnd(aCallback) {
|
||||
Elements.tabs.addEventListener("animationend", function onAnimationEnd() {
|
||||
Elements.tabs.removeEventListener("animationend", onAnimationEnd);
|
||||
|
@ -464,7 +486,7 @@ var BrowserUI = {
|
|||
this.setOnTabAnimationEnd(function() {
|
||||
Browser.closeTab(tabToClose, { forceClose: true } );
|
||||
if (wasCollapsed)
|
||||
ContextUI.dismissTabsWithDelay(kNewTabAnimationDelayMsec);
|
||||
ContextUI.dismissTabsWithDelay(kChangeTabAnimationDelay);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -506,7 +528,7 @@ var BrowserUI = {
|
|||
|
||||
selectTabAndDismiss: function selectTabAndDismiss(aTab) {
|
||||
this.selectTab(aTab);
|
||||
ContextUI.dismissTabsWithDelay(kSelectTabAnimationDelayMsec);
|
||||
ContextUI.dismissTabsWithDelay(kChangeTabAnimationDelay);
|
||||
},
|
||||
|
||||
selectTabAtIndex: function selectTabAtIndex(aIndex) {
|
||||
|
|
|
@ -79,6 +79,7 @@ var Browser = {
|
|||
// Call InputSourceHelper first so global listeners get called before
|
||||
// we start processing input in TouchModule.
|
||||
InputSourceHelper.init();
|
||||
ClickEventHandler.init();
|
||||
|
||||
TouchModule.init();
|
||||
GestureModule.init();
|
||||
|
@ -213,6 +214,7 @@ var Browser = {
|
|||
shutdown: function shutdown() {
|
||||
APZCObserver.shutdown();
|
||||
BrowserUI.uninit();
|
||||
ClickEventHandler.uninit();
|
||||
ContentAreaObserver.shutdown();
|
||||
Appbar.shutdown();
|
||||
|
||||
|
@ -485,7 +487,7 @@ var Browser = {
|
|||
if (aBringFront)
|
||||
this.selectedTab = newTab;
|
||||
|
||||
this._announceNewTab(newTab, params, aBringFront);
|
||||
this._announceNewTab(newTab);
|
||||
return newTab;
|
||||
},
|
||||
|
||||
|
@ -511,7 +513,7 @@ var Browser = {
|
|||
* helper for addTab related methods. Fires events related to
|
||||
* new tab creation.
|
||||
*/
|
||||
_announceNewTab: function _announceNewTab(aTab, aParams, aBringFront) {
|
||||
_announceNewTab: function (aTab) {
|
||||
let event = document.createEvent("UIEvents");
|
||||
event.initUIEvent("TabOpen", true, false, window, 0);
|
||||
aTab.chromeTab.dispatchEvent(event);
|
||||
|
@ -1020,9 +1022,6 @@ Browser.MainDragger.prototype = {
|
|||
};
|
||||
|
||||
|
||||
|
||||
const OPEN_APPTAB = 100; // Hack until we get a real API
|
||||
|
||||
function nsBrowserAccess() { }
|
||||
|
||||
nsBrowserAccess.prototype = {
|
||||
|
@ -1032,56 +1031,54 @@ nsBrowserAccess.prototype = {
|
|||
throw Cr.NS_NOINTERFACE;
|
||||
},
|
||||
|
||||
_getOpenAction: function _getOpenAction(aURI, aOpener, aWhere, aContext) {
|
||||
let where = aWhere;
|
||||
/*
|
||||
* aWhere:
|
||||
* OPEN_DEFAULTWINDOW: default action
|
||||
* OPEN_CURRENTWINDOW: current window/tab
|
||||
* OPEN_NEWWINDOW: not allowed, converted to newtab below
|
||||
* OPEN_NEWTAB: open a new tab
|
||||
* OPEN_SWITCHTAB: open in an existing tab if it matches, otherwise open
|
||||
* a new tab. afaict we always open these in the current tab.
|
||||
*/
|
||||
if (where == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
|
||||
// query standard browser prefs indicating what to do for default action
|
||||
switch (aContext) {
|
||||
// indicates this is an open request from a 3rd party app.
|
||||
case Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL :
|
||||
where = Services.prefs.getIntPref("browser.link.open_external");
|
||||
break;
|
||||
// internal request
|
||||
default :
|
||||
where = Services.prefs.getIntPref("browser.link.open_newwindow");
|
||||
}
|
||||
}
|
||||
if (where == Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW) {
|
||||
Util.dumpLn("Invalid request - we can't open links in new windows.");
|
||||
where = Ci.nsIBrowserDOMWindow.OPEN_NEWTAB;
|
||||
}
|
||||
return where;
|
||||
},
|
||||
|
||||
_getBrowser: function _getBrowser(aURI, aOpener, aWhere, aContext) {
|
||||
let isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
|
||||
// We don't allow externals apps opening chrome docs
|
||||
if (isExternal && aURI && aURI.schemeIs("chrome"))
|
||||
return null;
|
||||
|
||||
let location;
|
||||
let browser;
|
||||
let loadflags = isExternal ?
|
||||
Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
|
||||
Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||
let location;
|
||||
if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
|
||||
switch (aContext) {
|
||||
case Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL :
|
||||
aWhere = Services.prefs.getIntPref("browser.link.open_external");
|
||||
break;
|
||||
default : // OPEN_NEW or an illegal value
|
||||
aWhere = Services.prefs.getIntPref("browser.link.open_newwindow");
|
||||
}
|
||||
}
|
||||
let openAction = this._getOpenAction(aURI, aOpener, aWhere, aContext);
|
||||
|
||||
let browser;
|
||||
if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW) {
|
||||
let url = aURI ? aURI.spec : "about:blank";
|
||||
let newWindow = openDialog("chrome://browser/content/browser.xul", "_blank",
|
||||
"all,dialog=no", url, null, null, null);
|
||||
// since newWindow.Browser doesn't exist yet, just return null
|
||||
return null;
|
||||
} else if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
|
||||
if (openAction == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
|
||||
let owner = isExternal ? null : Browser.selectedTab;
|
||||
let tab = Browser.addTab("about:blank", true, owner);
|
||||
if (isExternal)
|
||||
tab.closeOnExit = true;
|
||||
let tab = BrowserUI.openLinkInNewTab("about:blank", true, owner);
|
||||
browser = tab.browser;
|
||||
} else if (aWhere == OPEN_APPTAB) {
|
||||
Browser.tabs.forEach(function(aTab) {
|
||||
if ("appURI" in aTab.browser && aTab.browser.appURI.spec == aURI.spec) {
|
||||
Browser.selectedTab = aTab;
|
||||
browser = aTab.browser;
|
||||
}
|
||||
});
|
||||
|
||||
if (!browser) {
|
||||
// Make a new tab to hold the app
|
||||
let tab = Browser.addTab("about:blank", true);
|
||||
browser = tab.browser;
|
||||
browser.appURI = aURI;
|
||||
} else {
|
||||
// Just use the existing browser, but return null to keep the system from trying to load the URI again
|
||||
browser = null;
|
||||
}
|
||||
} else { // OPEN_CURRENTWINDOW and illegal values
|
||||
} else {
|
||||
browser = Browser.selectedBrowser;
|
||||
}
|
||||
|
||||
|
@ -1097,10 +1094,6 @@ nsBrowserAccess.prototype = {
|
|||
browser.focus();
|
||||
} catch(e) { }
|
||||
|
||||
// We are loading web content into this window, so make sure content is visible
|
||||
// XXX Can we remove this? It seems to be reproduced in BrowserUI already.
|
||||
BrowserUI.showContent();
|
||||
|
||||
return browser;
|
||||
},
|
||||
|
||||
|
@ -1584,3 +1577,68 @@ function rendererFactory(aBrowser, aCanvas) {
|
|||
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
// Based on ClickEventHandler from /browser/base/content/content.js
|
||||
let ClickEventHandler = {
|
||||
init: function () {
|
||||
gEventListenerService.addSystemEventListener(Elements.browsers, "click", this, true);
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
gEventListenerService.removeSystemEventListener(Elements.browsers, "click", this, true);
|
||||
},
|
||||
|
||||
handleEvent: function (aEvent) {
|
||||
if (!aEvent.isTrusted || aEvent.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
let [href, node] = this._hrefAndLinkNodeForClickEvent(aEvent);
|
||||
if (href && (aEvent.button == 1 || aEvent.ctrlKey)) {
|
||||
// Open link in a new tab for middle-click or ctrl-click
|
||||
BrowserUI.openLinkInNewTab(href, aEvent.shiftKey, Browser.selectedTab);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Extracts linkNode and href for the current click target.
|
||||
*
|
||||
* @param event
|
||||
* The click event.
|
||||
* @return [href, linkNode].
|
||||
*
|
||||
* @note linkNode will be null if the click wasn't on an anchor
|
||||
* element (or XLink).
|
||||
*/
|
||||
_hrefAndLinkNodeForClickEvent: function(event) {
|
||||
function isHTMLLink(aNode) {
|
||||
return ((aNode instanceof content.HTMLAnchorElement && aNode.href) ||
|
||||
(aNode instanceof content.HTMLAreaElement && aNode.href) ||
|
||||
aNode instanceof content.HTMLLinkElement);
|
||||
}
|
||||
|
||||
let node = event.target;
|
||||
while (node && !isHTMLLink(node)) {
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
if (node)
|
||||
return [node.href, node];
|
||||
|
||||
// If there is no linkNode, try simple XLink.
|
||||
let href, baseURI;
|
||||
node = event.target;
|
||||
while (node && !href) {
|
||||
if (node.nodeType == content.Node.ELEMENT_NODE) {
|
||||
href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
||||
if (href)
|
||||
baseURI = node.ownerDocument.baseURIObject;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
// In case of XLink, we don't return the node we got href from since
|
||||
// callers expect <a>-like elements.
|
||||
// Note: makeURI() will throw if aUri is not a valid URI.
|
||||
return [href ? Services.io.newURI(href, null, baseURI).spec : null, null];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>link click test</title>
|
||||
</head>
|
||||
<body>
|
||||
<a id="link" href="about:blank">link</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,97 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
runTests();
|
||||
}
|
||||
|
||||
gTests.push({
|
||||
desc: "regular link click",
|
||||
run: function () {
|
||||
let tab = yield addTab(chromeRoot + "browser_link_click.html");
|
||||
let tabCount = Browser.tabs.length;
|
||||
|
||||
EventUtils.sendMouseEvent({type: "click"}, "link", tab.browser.contentWindow);
|
||||
yield waitForCondition(() => tab.browser.currentURI.spec == "about:blank");
|
||||
is(Browser.tabs.length, tabCount, "link loaded in the same tab");
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "middle-click opens link in background tab",
|
||||
run: function () {
|
||||
let tab = yield addTab(chromeRoot + "browser_link_click.html");
|
||||
|
||||
let tabOpen = waitForEvent(window, "TabOpen");
|
||||
EventUtils.sendMouseEvent({type: "click", button: 1}, "link", tab.browser.contentWindow);
|
||||
let event = yield tabOpen;
|
||||
|
||||
let newTab = Browser.getTabFromChrome(event.originalTarget);
|
||||
yield waitForEvent(newTab.browser, "pageshow");
|
||||
|
||||
is(newTab.browser.currentURI.spec, "about:blank");
|
||||
ok(newTab != Browser.selectedTab, "new tab is in the background");
|
||||
|
||||
Browser.closeTab(newTab, { forceClose: true });
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "shift-middle-click opens link in background tab",
|
||||
run: function () {
|
||||
let tab = yield addTab(chromeRoot + "browser_link_click.html");
|
||||
|
||||
let tabOpen = waitForEvent(window, "TabOpen");
|
||||
EventUtils.sendMouseEvent({type: "click", button: 1, shiftKey: true}, "link", tab.browser.contentWindow);
|
||||
let event = yield tabOpen;
|
||||
|
||||
let newTab = Browser.getTabFromChrome(event.originalTarget);
|
||||
yield waitForEvent(newTab.browser, "pageshow");
|
||||
|
||||
is(newTab.browser.currentURI.spec, "about:blank");
|
||||
ok(newTab == Browser.selectedTab, "new tab is in the foreground");
|
||||
|
||||
Browser.closeTab(newTab, { forceClose: true });
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "ctrl-click opens link in background tab",
|
||||
run: function () {
|
||||
let tab = yield addTab(chromeRoot + "browser_link_click.html");
|
||||
|
||||
let tabOpen = waitForEvent(window, "TabOpen");
|
||||
EventUtils.sendMouseEvent({type: "click", ctrlKey: true}, "link", tab.browser.contentWindow);
|
||||
let event = yield tabOpen;
|
||||
|
||||
let newTab = Browser.getTabFromChrome(event.originalTarget);
|
||||
yield waitForEvent(newTab.browser, "pageshow");
|
||||
|
||||
is(newTab.browser.currentURI.spec, "about:blank");
|
||||
ok(newTab != Browser.selectedTab, "new tab is in the background");
|
||||
|
||||
Browser.closeTab(newTab, { forceClose: true });
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "shift-ctrl-click opens link in background tab",
|
||||
run: function () {
|
||||
let tab = yield addTab(chromeRoot + "browser_link_click.html");
|
||||
|
||||
let tabOpen = waitForEvent(window, "TabOpen");
|
||||
EventUtils.sendMouseEvent({type: "click", ctrlKey: true, shiftKey: true}, "link", tab.browser.contentWindow);
|
||||
let event = yield tabOpen;
|
||||
|
||||
let newTab = Browser.getTabFromChrome(event.originalTarget);
|
||||
yield waitForEvent(newTab.browser, "pageshow");
|
||||
|
||||
is(newTab.browser.currentURI.spec, "about:blank");
|
||||
ok(newTab == Browser.selectedTab, "new tab is in the foreground");
|
||||
|
||||
Browser.closeTab(newTab, { forceClose: true });
|
||||
}
|
||||
});
|
|
@ -6,6 +6,7 @@ support-files =
|
|||
browser_context_menu_tests_04.html
|
||||
browser_findbar.html
|
||||
browser_form_auto_complete.html
|
||||
browser_link_click.html
|
||||
browser_onscreen_keyboard.html
|
||||
browser_progress_indicator.xul
|
||||
browser_selection_basic.html
|
||||
|
@ -42,6 +43,7 @@ support-files =
|
|||
[browser_form_auto_complete.js]
|
||||
[browser_history.js]
|
||||
[browser_inputsource.js]
|
||||
[browser_link_click.js]
|
||||
[browser_onscreen_keyboard.js]
|
||||
[browser_prefs_ui.js]
|
||||
[browser_remotetabs.js]
|
||||
|
|
|
@ -112,6 +112,10 @@ a {
|
|||
align-items: flex-start;
|
||||
}
|
||||
|
||||
#output-container.hideTimestamps > .message > .timestamp {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.filtered-by-type,
|
||||
.filtered-by-string {
|
||||
display: none;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDomainPolicy.idl',
|
||||
'nsIPrincipal.idl',
|
||||
'nsIScriptSecurityManager.idl',
|
||||
'nsISecurityCheckedComponent.idl',
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIURI;
|
||||
interface nsIDomainSet;
|
||||
|
||||
/*
|
||||
* When a domain policy is instantiated by invoking activateDomainPolicy() on
|
||||
* nsIScriptSecurityManager, these domain sets are consulted when each new
|
||||
* global is created (they have no effect on already-created globals).
|
||||
* If javascript is globally enabled with |javascript.enabled|, the blacklists
|
||||
* are consulted. If globally disabled, the whitelists are consulted. Lookups
|
||||
* on blacklist and whitelist happen with contains(), and lookups on
|
||||
* superBlacklist and superWhitelist happen with containsSuperDomain().
|
||||
*
|
||||
* When deactivate() is invoked, the domain sets are emptied, and the
|
||||
* nsIDomainPolicy ceases to have any effect on the system.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(27b10f54-f34b-42b7-8594-4348d3ad7953)]
|
||||
interface nsIDomainPolicy : nsISupports
|
||||
{
|
||||
readonly attribute nsIDomainSet blacklist;
|
||||
readonly attribute nsIDomainSet superBlacklist;
|
||||
readonly attribute nsIDomainSet whitelist;
|
||||
readonly attribute nsIDomainSet superWhitelist;
|
||||
|
||||
void deactivate();
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(946a01ff-6525-4007-a2c2-447ebe1875d3)]
|
||||
interface nsIDomainSet : nsISupports
|
||||
{
|
||||
/*
|
||||
* Add a domain to the set. No-op if it already exists.
|
||||
*/
|
||||
void add(in nsIURI aDomain);
|
||||
|
||||
/*
|
||||
* Remove a domain from the set. No-op if it doesn't exist.
|
||||
*/
|
||||
void remove(in nsIURI aDomain);
|
||||
|
||||
/*
|
||||
* Remove all entries from the set.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/*
|
||||
* Returns true if a given domain is in the set.
|
||||
*/
|
||||
bool contains(in nsIURI aDomain);
|
||||
|
||||
/*
|
||||
* Returns true if a given domain is a subdomain of one of the entries in
|
||||
* the set.
|
||||
*/
|
||||
bool containsSuperDomain(in nsIURI aDomain);
|
||||
};
|
|
@ -9,8 +9,9 @@
|
|||
interface nsIURI;
|
||||
interface nsIChannel;
|
||||
interface nsIDocShell;
|
||||
interface nsIDomainPolicy;
|
||||
|
||||
[scriptable, uuid(d6475e53-9ece-4dc0-940c-095ac3d85363)]
|
||||
[scriptable, uuid(2911ae60-1b5f-47e6-941e-1bb7b53a167d)]
|
||||
interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
||||
{
|
||||
///////////////// Security Checks //////////////////
|
||||
|
@ -103,24 +104,9 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
|||
in unsigned long flags);
|
||||
|
||||
/**
|
||||
* Check that the function 'funObj' is allowed to run on 'targetObj'
|
||||
*
|
||||
* Will return error code NS_ERROR_DOM_SECURITY_ERR if the function
|
||||
* should not run
|
||||
*
|
||||
* @param cx The current active JavaScript context.
|
||||
* @param funObj The function trying to run..
|
||||
* @param targetObj The object the function will run on.
|
||||
* Return true if scripts may be executed in the scope of the given global.
|
||||
*/
|
||||
[noscript] void checkFunctionAccess(in JSContextPtr cx, in voidPtr funObj,
|
||||
in voidPtr targetObj);
|
||||
|
||||
/**
|
||||
* Return true if content from the given principal is allowed to
|
||||
* execute scripts.
|
||||
*/
|
||||
[noscript] boolean canExecuteScripts(in JSContextPtr cx,
|
||||
in nsIPrincipal principal);
|
||||
[noscript,notxpcom] boolean scriptAllowed(in JSObjectPtr aGlobal);
|
||||
|
||||
///////////////// Principals ///////////////////////
|
||||
/**
|
||||
|
@ -216,6 +202,13 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
|||
* script to check whether a given principal is system.
|
||||
*/
|
||||
boolean isSystemPrincipal(in nsIPrincipal aPrincipal);
|
||||
%{C++
|
||||
bool IsSystemPrincipal(nsIPrincipal* aPrincipal) {
|
||||
bool isSystem = false;
|
||||
IsSystemPrincipal(aPrincipal, &isSystem);
|
||||
return isSystem;
|
||||
}
|
||||
%}
|
||||
|
||||
/**
|
||||
* Same as getSubjectPrincipal(), only faster. cx must *never* be
|
||||
|
@ -236,6 +229,29 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
|||
* inMozBrowser has to be true if the app is inside a mozbrowser iframe.
|
||||
*/
|
||||
AUTF8String getJarPrefix(in unsigned long appId, in boolean inMozBrowser);
|
||||
|
||||
/**
|
||||
* Per-domain controls to enable and disable script. This system is designed
|
||||
* to be used by at most one consumer, and enforces this with its semantics.
|
||||
*
|
||||
* Initially, domainPolicyActive is false. When activateDomainPolicy() is
|
||||
* invoked, domainPolicyActive becomes true, and subsequent calls to
|
||||
* activateDomainPolicy() will fail until deactivate() is invoked on the
|
||||
* nsIDomainPolicy returned from activateDomainPolicy(). At this point,
|
||||
* domainPolicyActive becomes false again, and a new consumer may acquire
|
||||
* control of the system by invoking activateDomainPolicy().
|
||||
*/
|
||||
nsIDomainPolicy activateDomainPolicy();
|
||||
readonly attribute boolean domainPolicyActive;
|
||||
|
||||
/**
|
||||
* Query mechanism for the above policy.
|
||||
*
|
||||
* If domainPolicyEnabled is false, this simply returns the current value
|
||||
* of javascript.enabled. Otherwise, it returns the same value, but taking
|
||||
* the various blacklist/whitelist exceptions into account.
|
||||
*/
|
||||
bool policyAllowsScript(in nsIURI aDomain);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DomainPolicy_h__
|
||||
#define DomainPolicy_h__
|
||||
|
||||
#include "nsIDomainPolicy.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsURIHashKey.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// The name "DomainPolicy" conflicts with some of the old-style policy machinery
|
||||
// in nsScriptSecurityManager.cpp, which needs to #include this file. So we
|
||||
// temporarily use a sub-namespace until that machinery goes away in bug 913734.
|
||||
namespace hotness {
|
||||
|
||||
class DomainPolicy : public nsIDomainPolicy
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMAINPOLICY
|
||||
DomainPolicy();
|
||||
virtual ~DomainPolicy();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIDomainSet> mBlacklist;
|
||||
nsCOMPtr<nsIDomainSet> mSuperBlacklist;
|
||||
nsCOMPtr<nsIDomainSet> mWhitelist;
|
||||
nsCOMPtr<nsIDomainSet> mSuperWhitelist;
|
||||
};
|
||||
|
||||
class DomainSet : public nsIDomainSet
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMAINSET
|
||||
|
||||
DomainSet() {}
|
||||
virtual ~DomainSet() {}
|
||||
|
||||
protected:
|
||||
nsTHashtable<nsURIHashKey> mHashTable;
|
||||
};
|
||||
|
||||
} /* namespace hotness */
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* DomainPolicy_h__ */
|
|
@ -362,6 +362,8 @@ public:
|
|||
AppAttributesEqual(nsIPrincipal* aFirst,
|
||||
nsIPrincipal* aSecond);
|
||||
|
||||
void DeactivateDomainPolicy();
|
||||
|
||||
private:
|
||||
|
||||
// GetScriptSecurityManager is the only call that can make one
|
||||
|
@ -381,7 +383,7 @@ private:
|
|||
|
||||
// Returns null if a principal cannot be found; generally callers
|
||||
// should error out at that point.
|
||||
static nsIPrincipal* doGetObjectPrincipal(JS::Handle<JSObject*> obj);
|
||||
static nsIPrincipal* doGetObjectPrincipal(JSObject* obj);
|
||||
|
||||
// Returns null if a principal cannot be found. Note that rv can be NS_OK
|
||||
// when this happens -- this means that there was no JS running.
|
||||
|
@ -462,15 +464,6 @@ private:
|
|||
nsIPrincipal* aSubjectPrincipal,
|
||||
const char* aObjectSecurityLevel);
|
||||
|
||||
/**
|
||||
* Helper for CanExecuteScripts that allows the caller to specify
|
||||
* whether execution should be allowed if cx has no
|
||||
* nsIScriptContext.
|
||||
*/
|
||||
nsresult
|
||||
CanExecuteScripts(JSContext* cx, nsIPrincipal *aPrincipal,
|
||||
bool aAllowIfNoScriptContext, bool *result);
|
||||
|
||||
nsresult
|
||||
Init();
|
||||
|
||||
|
@ -496,6 +489,10 @@ private:
|
|||
bool mIsJavaScriptEnabled;
|
||||
bool mPolicyPrefsChanged;
|
||||
|
||||
// This machinery controls new-style domain policies. The old-style
|
||||
// policy machinery will be removed soon.
|
||||
nsCOMPtr<nsIDomainPolicy> mDomainPolicy;
|
||||
|
||||
static bool sStrictFileOriginPolicy;
|
||||
|
||||
static nsIIOService *sIOService;
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=4 et sw=4 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DomainPolicy.h"
|
||||
#include "nsScriptSecurityManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace hotness {
|
||||
|
||||
NS_IMPL_ISUPPORTS1(DomainPolicy, nsIDomainPolicy)
|
||||
|
||||
DomainPolicy::DomainPolicy() : mBlacklist(new DomainSet())
|
||||
, mSuperBlacklist(new DomainSet())
|
||||
, mWhitelist(new DomainSet())
|
||||
, mSuperWhitelist(new DomainSet())
|
||||
{}
|
||||
|
||||
DomainPolicy::~DomainPolicy()
|
||||
{
|
||||
// The SSM holds a strong ref to the DomainPolicy until Deactivate() is
|
||||
// invoked, so we should never hit the destructor until that happens.
|
||||
MOZ_ASSERT(!mBlacklist && !mSuperBlacklist &&
|
||||
!mWhitelist && !mSuperWhitelist);
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
DomainPolicy::GetBlacklist(nsIDomainSet** aSet)
|
||||
{
|
||||
nsCOMPtr<nsIDomainSet> set = mBlacklist;
|
||||
set.forget(aSet);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DomainPolicy::GetSuperBlacklist(nsIDomainSet** aSet)
|
||||
{
|
||||
nsCOMPtr<nsIDomainSet> set = mSuperBlacklist;
|
||||
set.forget(aSet);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DomainPolicy::GetWhitelist(nsIDomainSet** aSet)
|
||||
{
|
||||
nsCOMPtr<nsIDomainSet> set = mWhitelist;
|
||||
set.forget(aSet);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DomainPolicy::GetSuperWhitelist(nsIDomainSet** aSet)
|
||||
{
|
||||
nsCOMPtr<nsIDomainSet> set = mSuperWhitelist;
|
||||
set.forget(aSet);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DomainPolicy::Deactivate()
|
||||
{
|
||||
// Clear the hashtables first to free up memory, since script might
|
||||
// hold the doomed sets alive indefinitely.
|
||||
mBlacklist->Clear();
|
||||
mSuperBlacklist->Clear();
|
||||
mWhitelist->Clear();
|
||||
mSuperWhitelist->Clear();
|
||||
|
||||
// Null them out.
|
||||
mBlacklist = nullptr;
|
||||
mSuperBlacklist = nullptr;
|
||||
mWhitelist = nullptr;
|
||||
mSuperWhitelist = nullptr;
|
||||
|
||||
// Inform the SSM.
|
||||
nsScriptSecurityManager::GetScriptSecurityManager()->DeactivateDomainPolicy();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static already_AddRefed<nsIURI>
|
||||
GetCanonicalClone(nsIURI* aURI)
|
||||
{
|
||||
nsCOMPtr<nsIURI> clone;
|
||||
nsresult rv = aURI->Clone(getter_AddRefs(clone));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
rv = clone->SetUserPass(EmptyCString());
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
rv = clone->SetPath(EmptyCString());
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
return clone.forget();
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(DomainSet, nsIDomainSet)
|
||||
|
||||
NS_IMETHODIMP
|
||||
DomainSet::Add(nsIURI* aDomain)
|
||||
{
|
||||
nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
|
||||
NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
|
||||
mHashTable.PutEntry(clone);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DomainSet::Remove(nsIURI* aDomain)
|
||||
{
|
||||
nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
|
||||
NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
|
||||
mHashTable.RemoveEntry(clone);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DomainSet::Clear()
|
||||
{
|
||||
mHashTable.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DomainSet::Contains(nsIURI* aDomain, bool* aContains)
|
||||
{
|
||||
*aContains = false;
|
||||
nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
|
||||
NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
|
||||
*aContains = mHashTable.Contains(clone);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DomainSet::ContainsSuperDomain(nsIURI* aDomain, bool* aContains)
|
||||
{
|
||||
*aContains = false;
|
||||
nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
|
||||
NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
|
||||
nsAutoCString domain;
|
||||
nsresult rv = clone->GetHost(domain);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
while (true) {
|
||||
// Check the current domain.
|
||||
if (mHashTable.Contains(clone)) {
|
||||
*aContains = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Chop off everything before the first dot, or break if there are no
|
||||
// dots left.
|
||||
int32_t index = domain.Find(".");
|
||||
if (index == kNotFound)
|
||||
break;
|
||||
domain.Assign(Substring(domain, index + 1));
|
||||
rv = clone->SetHost(domain);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// No match.
|
||||
return NS_OK;
|
||||
|
||||
}
|
||||
|
||||
} /* namespace hotness */
|
||||
} /* namespace mozilla */
|
|
@ -7,6 +7,7 @@
|
|||
MODULE = 'caps'
|
||||
|
||||
SOURCES += [
|
||||
'DomainPolicy.cpp',
|
||||
'nsJSPrincipals.cpp',
|
||||
'nsNullPrincipal.cpp',
|
||||
'nsNullPrincipalURI.cpp',
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "nsSystemPrincipal.h"
|
||||
#include "nsPrincipal.h"
|
||||
#include "nsNullPrincipal.h"
|
||||
#include "DomainPolicy.h"
|
||||
#include "nsXPIDLString.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsCRTGlue.h"
|
||||
|
@ -63,6 +64,7 @@
|
|||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsJSUtils.h"
|
||||
|
||||
// This should be probably defined on some other place... but I couldn't find it
|
||||
#define WEBAPPS_PERM_NAME "webapps-manage"
|
||||
|
@ -183,12 +185,6 @@ GetPrincipalDomainOrigin(nsIPrincipal* aPrincipal,
|
|||
return GetOriginFromURI(uri, aOrigin);
|
||||
}
|
||||
|
||||
static nsIScriptContext *
|
||||
GetScriptContext(JSContext *cx)
|
||||
{
|
||||
return GetScriptContextFromJSContext(cx);
|
||||
}
|
||||
|
||||
inline void SetPendingException(JSContext *cx, const char *aMsg)
|
||||
{
|
||||
JS_ReportError(cx, "%s", aMsg);
|
||||
|
@ -1601,173 +1597,38 @@ nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(nsIPrincipal* aPrincipal,
|
|||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::CheckFunctionAccess(JSContext *aCx, void *aFunObj,
|
||||
void *aTargetObj)
|
||||
bool
|
||||
nsScriptSecurityManager::ScriptAllowed(JSObject *aGlobal)
|
||||
{
|
||||
// This check is called for event handlers
|
||||
nsresult rv;
|
||||
JS::Rooted<JSObject*> rootedFunObj(aCx, static_cast<JSObject*>(aFunObj));
|
||||
nsIPrincipal* subject = doGetObjectPrincipal(rootedFunObj);
|
||||
if (!subject)
|
||||
return NS_ERROR_FAILURE;
|
||||
MOZ_ASSERT(aGlobal);
|
||||
MOZ_ASSERT(JS_IsGlobalObject(aGlobal) || js::IsOuterObject(aGlobal));
|
||||
AutoJSContext cx;
|
||||
JS::RootedObject global(cx, js::UncheckedUnwrap(aGlobal, /* stopAtOuter = */ false));
|
||||
|
||||
if (subject == mSystemPrincipal)
|
||||
// This is the system principal: just allow access
|
||||
return NS_OK;
|
||||
|
||||
// Check if the principal the function was compiled under is
|
||||
// allowed to execute scripts.
|
||||
|
||||
bool result;
|
||||
rv = CanExecuteScripts(aCx, subject, true, &result);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (!result)
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
|
||||
if (!aTargetObj) {
|
||||
// We're done here
|
||||
return NS_OK;
|
||||
// Check the bits on the compartment private.
|
||||
xpc::Scriptability& scriptability = xpc::Scriptability::Get(aGlobal);
|
||||
if (!scriptability.Allowed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
** Get origin of subject and object and compare.
|
||||
*/
|
||||
JS::Rooted<JSObject*> obj(aCx, (JSObject*)aTargetObj);
|
||||
nsIPrincipal* object = doGetObjectPrincipal(obj);
|
||||
|
||||
if (!object)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
bool subsumes;
|
||||
rv = subject->Subsumes(object, &subsumes);
|
||||
if (NS_SUCCEEDED(rv) && !subsumes) {
|
||||
rv = NS_ERROR_DOM_PROP_ACCESS_DENIED;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::CanExecuteScripts(JSContext* cx,
|
||||
nsIPrincipal *aPrincipal,
|
||||
bool *result)
|
||||
{
|
||||
return CanExecuteScripts(cx, aPrincipal, false, result);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScriptSecurityManager::CanExecuteScripts(JSContext* cx,
|
||||
nsIPrincipal *aPrincipal,
|
||||
bool aAllowIfNoScriptContext,
|
||||
bool *result)
|
||||
{
|
||||
*result = false;
|
||||
|
||||
if (aPrincipal == mSystemPrincipal)
|
||||
{
|
||||
// Even if JavaScript is disabled, we must still execute system scripts
|
||||
*result = true;
|
||||
return NS_OK;
|
||||
// If the compartment is immune to script policy, we're done.
|
||||
if (scriptability.IsImmuneToScriptPolicy()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Same thing for nsExpandedPrincipal, which is pseudo-privileged.
|
||||
nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
|
||||
if (ep)
|
||||
{
|
||||
*result = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-- See if the current window allows JS execution
|
||||
nsIScriptContext *scriptContext = GetScriptContext(cx);
|
||||
if (!scriptContext) {
|
||||
if (aAllowIfNoScriptContext) {
|
||||
*result = true;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!scriptContext->GetScriptsEnabled()) {
|
||||
// No scripting on this context, folks
|
||||
*result = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIScriptGlobalObject *sgo = scriptContext->GetGlobalObject();
|
||||
|
||||
if (!sgo) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// window can be null here if we're running with a non-DOM window
|
||||
// as the script global (i.e. a XUL prototype document).
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(sgo);
|
||||
nsCOMPtr<nsIDocShell> docshell;
|
||||
nsresult rv;
|
||||
|
||||
if (window) {
|
||||
docshell = window->GetDocShell();
|
||||
}
|
||||
|
||||
if (docshell) {
|
||||
rv = docshell->GetCanExecuteScripts(result);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (!*result) return NS_OK;
|
||||
}
|
||||
|
||||
// OK, the docshell doesn't have script execution explicitly disabled.
|
||||
// Check whether our URI is an "about:" URI that allows scripts. If it is,
|
||||
// we need to allow JS to run. In this case, don't apply the JS enabled
|
||||
// pref or policies. On failures, just press on and don't do this special
|
||||
// case.
|
||||
nsCOMPtr<nsIURI> principalURI;
|
||||
aPrincipal->GetURI(getter_AddRefs(principalURI));
|
||||
if (!principalURI) {
|
||||
// Broken principal of some sort. Disallow.
|
||||
*result = false;
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
bool isAbout;
|
||||
rv = principalURI->SchemeIs("about", &isAbout);
|
||||
if (NS_SUCCEEDED(rv) && isAbout) {
|
||||
nsCOMPtr<nsIAboutModule> module;
|
||||
rv = NS_GetAboutModule(principalURI, getter_AddRefs(module));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
uint32_t flags;
|
||||
rv = module->GetURIFlags(principalURI, &flags);
|
||||
if (NS_SUCCEEDED(rv) &&
|
||||
(flags & nsIAboutModule::ALLOW_SCRIPT)) {
|
||||
*result = true;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*result = mIsJavaScriptEnabled;
|
||||
if (!*result)
|
||||
return NS_OK; // Do not run scripts
|
||||
|
||||
//-- Check for a per-site policy
|
||||
// Check for a per-site policy.
|
||||
static const char jsPrefGroupName[] = "javascript";
|
||||
ClassInfoData nameData(nullptr, jsPrefGroupName);
|
||||
|
||||
SecurityLevel secLevel;
|
||||
rv = LookupPolicy(aPrincipal, nameData, EnabledID(),
|
||||
nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
|
||||
nullptr, &secLevel);
|
||||
if (NS_FAILED(rv) || secLevel.level == SCRIPT_SECURITY_NO_ACCESS)
|
||||
{
|
||||
*result = false;
|
||||
return rv;
|
||||
nsresult rv = LookupPolicy(doGetObjectPrincipal(global), nameData,
|
||||
EnabledID(),
|
||||
nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
|
||||
nullptr, &secLevel);
|
||||
if (NS_FAILED(rv) || secLevel.level == SCRIPT_SECURITY_NO_ACCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//-- Nobody vetoed, so allow the JS to run.
|
||||
*result = true;
|
||||
return NS_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////// Principals ///////////////////////
|
||||
|
@ -1964,7 +1825,7 @@ nsScriptSecurityManager::GetObjectPrincipal(JSContext *aCx, JSObject *aObj,
|
|||
|
||||
// static
|
||||
nsIPrincipal*
|
||||
nsScriptSecurityManager::doGetObjectPrincipal(JS::Handle<JSObject*> aObj)
|
||||
nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
|
||||
{
|
||||
JSCompartment *compartment = js::GetObjectCompartment(aObj);
|
||||
JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
|
||||
|
@ -2291,6 +2152,9 @@ nsScriptSecurityManager::~nsScriptSecurityManager(void)
|
|||
if(mDefaultPolicy)
|
||||
mDefaultPolicy->Drop();
|
||||
delete mCapabilities;
|
||||
if (mDomainPolicy)
|
||||
mDomainPolicy->Deactivate();
|
||||
MOZ_ASSERT(!mDomainPolicy);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2716,3 +2580,74 @@ nsScriptSecurityManager::GetJarPrefix(uint32_t aAppId,
|
|||
mozilla::GetJarPrefix(aAppId, aInMozBrowser, aJarPrefix);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::GetDomainPolicyActive(bool *aRv)
|
||||
{
|
||||
*aRv = !!mDomainPolicy;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv)
|
||||
{
|
||||
// We only allow one domain policy at a time. The holder of the previous
|
||||
// policy must explicitly deactivate it first.
|
||||
if (mDomainPolicy) {
|
||||
return NS_ERROR_SERVICE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mDomainPolicy = new mozilla::hotness::DomainPolicy();
|
||||
nsCOMPtr<nsIDomainPolicy> ptr = mDomainPolicy;
|
||||
ptr.forget(aRv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Intentionally non-scriptable. Script must have a reference to the
|
||||
// nsIDomainPolicy to deactivate it.
|
||||
void
|
||||
nsScriptSecurityManager::DeactivateDomainPolicy()
|
||||
{
|
||||
mDomainPolicy = nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool *aRv)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Compute our rule. If we don't have any domain policy set up that might
|
||||
// provide exceptions to this rule, we're done.
|
||||
*aRv = mIsJavaScriptEnabled;
|
||||
if (!mDomainPolicy) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We have a domain policy. Grab the appropriate set of exceptions to the
|
||||
// rule (either the blacklist or the whitelist, depending on whether script
|
||||
// is enabled or disabled by default).
|
||||
nsCOMPtr<nsIDomainSet> exceptions;
|
||||
nsCOMPtr<nsIDomainSet> superExceptions;
|
||||
if (*aRv) {
|
||||
mDomainPolicy->GetBlacklist(getter_AddRefs(exceptions));
|
||||
mDomainPolicy->GetSuperBlacklist(getter_AddRefs(superExceptions));
|
||||
} else {
|
||||
mDomainPolicy->GetWhitelist(getter_AddRefs(exceptions));
|
||||
mDomainPolicy->GetSuperWhitelist(getter_AddRefs(superExceptions));
|
||||
}
|
||||
|
||||
bool contains;
|
||||
rv = exceptions->Contains(aURI, &contains);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (contains) {
|
||||
*aRv = !*aRv;
|
||||
return NS_OK;
|
||||
}
|
||||
rv = superExceptions->ContainsSuperDomain(aURI, &contains);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (contains) {
|
||||
*aRv = !*aRv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# jarPrefix test doesn't work on Windows, see bug 776296.
|
||||
ifneq ($(OS_ARCH),WINNT)
|
||||
MOCHITEST_CHROME_FILES = test_principal_jarprefix_origin_appid_appstatus.html \
|
||||
$(NULL)
|
||||
endif
|
|
@ -0,0 +1,4 @@
|
|||
[test_disableScript.xul]
|
||||
[test_principal_jarprefix_origin_appid_appstatus.html]
|
||||
# jarPrefix test doesn't work on Windows, see bug 776296.
|
||||
skip-if = os == "win"
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
var gFiredOnload = false;
|
||||
var gFiredOnclick = false;
|
||||
</script>
|
||||
</head>
|
||||
<body onload="gFiredOnload = true;" onclick="gFiredOnclick = true;">
|
||||
</body>
|
||||
</html>
|
|
@ -1,4 +1,6 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
file_disableScript.html
|
||||
|
||||
[test_app_principal_equality.html]
|
||||
[test_bug246699.html]
|
||||
|
|
|
@ -5,4 +5,5 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
MOCHITEST_MANIFESTS += ['mochitest.ini']
|
||||
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
|
||||
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=840488
|
||||
-->
|
||||
<window title="Mozilla Bug 840488"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=840488"
|
||||
target="_blank">Mozilla Bug 840488</a>
|
||||
</body>
|
||||
|
||||
<iframe id="root" name="root" onload="go();" type="content"/>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
/** Test for all the different ways that script can be disabled for a given global. **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
const ssm = Services.scriptSecurityManager;
|
||||
function makeURI(uri) { return Services.io.newURI(uri, null, null); }
|
||||
const path = "/tests/caps/tests/mochitest/file_disableScript.html";
|
||||
const uri = "http://www.example.com" + path;
|
||||
var rootFrame = document.getElementById('root');
|
||||
rootFrame.setAttribute('src', uri + "?name=rootframe");
|
||||
|
||||
function navigateFrame(ifr, src) {
|
||||
let deferred = Promise.defer();
|
||||
function onload() {
|
||||
ifr.removeEventListener('load', onload);
|
||||
deferred.resolve();
|
||||
}
|
||||
ifr.addEventListener('load', onload, false);
|
||||
ifr.setAttribute('src', src);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function navigateBack(ifr) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
// pageshow events don't fire on the iframe element, so we need to use the
|
||||
// chrome event handler for the docshell.
|
||||
var browser = ifr.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler;
|
||||
function onpageshow(evt) {
|
||||
info("Navigated back. Persisted: " + evt.persisted);
|
||||
browser.removeEventListener('pageshow', onpageshow);
|
||||
deferred.resolve();
|
||||
}
|
||||
browser.addEventListener('pageshow', onpageshow, false);
|
||||
ifr.contentWindow.history.back();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function addFrame(parentWin, name, expectOnload) {
|
||||
let ifr = parentWin.document.createElement('iframe');
|
||||
parentWin.document.body.appendChild(ifr);
|
||||
ifr.setAttribute('name', name);
|
||||
let deferred = Promise.defer();
|
||||
// We need to append 'name' to avoid running afoul of recursive frame detection.
|
||||
let frameURI = uri + "?name=" + name;
|
||||
navigateFrame(ifr, frameURI).then(function() {
|
||||
is(ifr.contentWindow.location, frameURI, "Successful load");
|
||||
is(!!ifr.contentWindow.wrappedJSObject.gFiredOnload, expectOnload,
|
||||
"onload should only fire when scripts are enabled");
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function checkScriptEnabled(win, expectEnabled) {
|
||||
win.wrappedJSObject.gFiredOnclick = false;
|
||||
win.document.body.dispatchEvent(new win.Event('click'));
|
||||
is(win.wrappedJSObject.gFiredOnclick, expectEnabled, "Checking script-enabled for " + win.name + " (" + win.location + ")");
|
||||
}
|
||||
|
||||
function setScriptEnabledForDocShell(win, enabled) {
|
||||
win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell)
|
||||
.allowJavascript = enabled;
|
||||
}
|
||||
|
||||
function testList(expectEnabled, win, list, idx) {
|
||||
let idx = idx || 0;
|
||||
let deferred = Promise.defer();
|
||||
let target = list[idx] + path;
|
||||
info("Testing scriptability for: " + target + ". expecting " + expectEnabled);
|
||||
navigateFrame(win.frameElement, target).then(function() {
|
||||
checkScriptEnabled(win, expectEnabled);
|
||||
if (idx == list.length - 1)
|
||||
deferred.resolve();
|
||||
else
|
||||
testList(expectEnabled, win, list, idx + 1).then(function() { deferred.resolve(); });
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function testDomainPolicy(defaultScriptability, exceptions, superExceptions,
|
||||
exempt, notExempt, set, superSet, win) {
|
||||
// Populate our sets.
|
||||
for (var e of exceptions)
|
||||
set.add(makeURI(e));
|
||||
for (var e of superExceptions)
|
||||
superSet.add(makeURI(e));
|
||||
|
||||
return testList(defaultScriptability, win, notExempt).then(function() {
|
||||
return testList(!defaultScriptability, win, exempt);
|
||||
});
|
||||
}
|
||||
|
||||
function setScriptEnabledForBrowser(enabled) {
|
||||
var prefname = "javascript.enabled";
|
||||
Services.prefs.setBoolPref(prefname, enabled);
|
||||
}
|
||||
|
||||
function reloadFrame(frame) {
|
||||
let deferred = Promise.defer();
|
||||
frame.addEventListener('load', function onload() {
|
||||
deferred.resolve();
|
||||
frame.removeEventListener('load', onload);
|
||||
}, false);
|
||||
frame.contentWindow.location.reload(true);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function go() {
|
||||
rootFrame.setAttribute('onload', null);
|
||||
|
||||
// Test simple docshell enable/disable.
|
||||
var rootWin = rootFrame.contentWindow;
|
||||
checkScriptEnabled(rootWin, true);
|
||||
setScriptEnabledForDocShell(rootWin, false);
|
||||
checkScriptEnabled(rootWin, false);
|
||||
setScriptEnabledForDocShell(rootWin, true);
|
||||
checkScriptEnabled(rootWin, true);
|
||||
|
||||
// Play around with the docshell tree and make sure everything works as
|
||||
// we expect.
|
||||
addFrame(rootWin, 'parent', true).then(function() {
|
||||
checkScriptEnabled(rootWin[0], true);
|
||||
return addFrame(rootWin[0], 'childA', true);
|
||||
}).then(function() {
|
||||
checkScriptEnabled(rootWin[0][0], true);
|
||||
setScriptEnabledForDocShell(rootWin[0], false);
|
||||
checkScriptEnabled(rootWin, true);
|
||||
checkScriptEnabled(rootWin[0], false);
|
||||
checkScriptEnabled(rootWin[0][0], false);
|
||||
return addFrame(rootWin[0], 'childB', false);
|
||||
}).then(function() {
|
||||
checkScriptEnabled(rootWin[0][1], false);
|
||||
setScriptEnabledForDocShell(rootWin[0][0], false);
|
||||
setScriptEnabledForDocShell(rootWin[0], true);
|
||||
checkScriptEnabled(rootWin[0], true);
|
||||
checkScriptEnabled(rootWin[0][0], false);
|
||||
setScriptEnabledForDocShell(rootWin[0][0], true);
|
||||
|
||||
// Flags are inherited from the parent docshell at attach time. Note that
|
||||
// the flag itself is inherited, regardless of whether or not scripts are
|
||||
// currently allowed on the parent (which could depend on the parent's
|
||||
// parent). Check that.
|
||||
checkScriptEnabled(rootWin[0][1], false);
|
||||
setScriptEnabledForDocShell(rootWin[0], false);
|
||||
setScriptEnabledForDocShell(rootWin[0][1], true);
|
||||
return addFrame(rootWin[0][1], 'grandchild', false);
|
||||
}).then(function() {
|
||||
checkScriptEnabled(rootWin[0], false);
|
||||
checkScriptEnabled(rootWin[0][1], false);
|
||||
checkScriptEnabled(rootWin[0][1][0], false);
|
||||
setScriptEnabledForDocShell(rootWin[0], true);
|
||||
checkScriptEnabled(rootWin[0], true);
|
||||
checkScriptEnabled(rootWin[0][1], true);
|
||||
checkScriptEnabled(rootWin[0][1][0], true);
|
||||
|
||||
// Try navigating two frames, then munging docshell scriptability, then
|
||||
// pulling the frames out of the bfcache to make sure that flags are
|
||||
// properly propagated to inactive inner windows. We do this both for an
|
||||
// 'own' docshell, as well as for an ancestor docshell.
|
||||
return navigateFrame(rootWin[0][0].frameElement, rootWin[0][0].location + '-navigated');
|
||||
}).then(function() { return navigateFrame(rootWin[0][1][0].frameElement, rootWin[0][1][0].location + '-navigated'); })
|
||||
.then(function() {
|
||||
checkScriptEnabled(rootWin[0][0], true);
|
||||
checkScriptEnabled(rootWin[0][1][0], true);
|
||||
setScriptEnabledForDocShell(rootWin[0][0], false);
|
||||
setScriptEnabledForDocShell(rootWin[0][1], false);
|
||||
checkScriptEnabled(rootWin[0][0], false);
|
||||
checkScriptEnabled(rootWin[0][1][0], false);
|
||||
return navigateBack(rootWin[0][0].frameElement);
|
||||
}).then(function() { return navigateBack(rootWin[0][1][0].frameElement); })
|
||||
.then(function() {
|
||||
checkScriptEnabled(rootWin[0][0], false);
|
||||
checkScriptEnabled(rootWin[0][1][0], false);
|
||||
|
||||
// Disable JS via the global pref pref. This is only guaranteed to have an effect
|
||||
// for subsequent loads.
|
||||
setScriptEnabledForBrowser(false);
|
||||
return reloadFrame(rootFrame);
|
||||
}).then(function() {
|
||||
checkScriptEnabled(rootWin, false);
|
||||
setScriptEnabledForBrowser(true);
|
||||
return reloadFrame(rootFrame);
|
||||
}).then(function() {
|
||||
checkScriptEnabled(rootWin, true);
|
||||
|
||||
// Play around with dynamically blocking script for a given global.
|
||||
// This takes effect immediately.
|
||||
Cu.blockScriptForGlobal(rootWin);
|
||||
Cu.blockScriptForGlobal(rootWin);
|
||||
Cu.unblockScriptForGlobal(rootWin);
|
||||
checkScriptEnabled(rootWin, false);
|
||||
Cu.unblockScriptForGlobal(rootWin);
|
||||
checkScriptEnabled(rootWin, true);
|
||||
Cu.blockScriptForGlobal(rootWin);
|
||||
return reloadFrame(rootFrame);
|
||||
}).then(function() {
|
||||
checkScriptEnabled(rootWin, true);
|
||||
|
||||
// Test system-wide domain policy. This only takes effect for subsequently-
|
||||
// loaded globals.
|
||||
|
||||
// Check the basic semantics of the sets.
|
||||
is(ssm.domainPolicyActive, false, "not enabled");
|
||||
window.policy = ssm.activateDomainPolicy();
|
||||
ok(policy instanceof Ci.nsIDomainPolicy, "Got a policy");
|
||||
try {
|
||||
ssm.activateDomainPolicy();
|
||||
ok(false, "Should have thrown");
|
||||
} catch (e) {
|
||||
ok(true, "can't have two live domain policies");
|
||||
}
|
||||
var sbRef = policy.superBlacklist;
|
||||
isnot(sbRef, null, "superBlacklist non-null");
|
||||
ok(!sbRef.contains(makeURI('http://www.example.com')));
|
||||
sbRef.add(makeURI('http://www.example.com/foopy'));
|
||||
ok(sbRef.contains(makeURI('http://www.example.com')));
|
||||
sbRef.remove(makeURI('http://www.example.com'));
|
||||
ok(!sbRef.contains(makeURI('http://www.example.com')));
|
||||
sbRef.add(makeURI('http://www.example.com/foopy/this.that/'));
|
||||
ok(sbRef.contains(makeURI('http://www.example.com/baz')));
|
||||
ok(!sbRef.contains(makeURI('https://www.example.com')));
|
||||
ok(!sbRef.contains(makeURI('https://www.example.com:88')));
|
||||
ok(!sbRef.contains(makeURI('http://foo.www.example.com')));
|
||||
ok(sbRef.containsSuperDomain(makeURI('http://foo.www.example.com')));
|
||||
ok(sbRef.containsSuperDomain(makeURI('http://foo.bar.www.example.com')));
|
||||
ok(!sbRef.containsSuperDomain(makeURI('http://foo.bar.www.exxample.com')));
|
||||
ok(!sbRef.containsSuperDomain(makeURI('http://example.com')));
|
||||
ok(!sbRef.containsSuperDomain(makeURI('http://com/this.that/')));
|
||||
ok(!sbRef.containsSuperDomain(makeURI('https://foo.www.example.com')));
|
||||
ok(sbRef.contains(makeURI('http://www.example.com')));
|
||||
policy.deactivate();
|
||||
is(ssm.domainPolicyActive, false, "back to inactive");
|
||||
ok(!sbRef.contains(makeURI('http://www.example.com')),
|
||||
"Disabling domain policy clears the set");
|
||||
policy = ssm.activateDomainPolicy();
|
||||
ok(policy.superBlacklist);
|
||||
isnot(sbRef, policy.superBlacklist, "Mint new sets each time!");
|
||||
policy.deactivate();
|
||||
is(policy.blacklist, null, "blacklist nulled out");
|
||||
policy = ssm.activateDomainPolicy();
|
||||
isnot(policy.blacklist, null, "non-null again");
|
||||
isnot(policy.blacklist, sbRef, "freshly minted");
|
||||
policy.deactivate();
|
||||
|
||||
//
|
||||
// Now, create and apply a mock-policy. We check the same policy both as
|
||||
// a blacklist and as a whitelist.
|
||||
//
|
||||
|
||||
window.testPolicy = {
|
||||
// The policy.
|
||||
exceptions: ['http://test1.example.com', 'http://example.com'],
|
||||
superExceptions: ['http://test2.example.org', 'https://test1.example.com'],
|
||||
|
||||
// The testcases.
|
||||
exempt: ['http://test1.example.com', 'http://example.com',
|
||||
'http://test2.example.org', 'http://sub1.test2.example.org',
|
||||
'https://sub1.test1.example.com'],
|
||||
|
||||
notExempt: ['http://test2.example.com', 'http://sub1.test1.example.com',
|
||||
'http://www.example.com', 'https://test2.example.com',
|
||||
'https://example.com', 'http://test1.example.org'],
|
||||
};
|
||||
|
||||
policy = ssm.activateDomainPolicy();
|
||||
info("Testing Blacklist-style Domain Policy");
|
||||
return testDomainPolicy(true, testPolicy.exceptions,
|
||||
testPolicy.superExceptions, testPolicy.exempt,
|
||||
testPolicy.notExempt, policy.blacklist,
|
||||
policy.superBlacklist, rootWin);
|
||||
}).then(function() {
|
||||
policy.deactivate();
|
||||
policy = ssm.activateDomainPolicy();
|
||||
info("Testing Whitelist-style Domain Policy");
|
||||
setScriptEnabledForBrowser(false);
|
||||
return testDomainPolicy(false, testPolicy.exceptions,
|
||||
testPolicy.superExceptions, testPolicy.exempt,
|
||||
testPolicy.notExempt, policy.whitelist,
|
||||
policy.superWhitelist, rootWin);
|
||||
}).then(function() {
|
||||
setScriptEnabledForBrowser(true);
|
||||
policy.deactivate();
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
14
configure.in
14
configure.in
|
@ -223,6 +223,7 @@ if test -n "$gonkdir" ; then
|
|||
GONK_INCLUDES="-I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/frameworks/base/include -I$gonkdir/frameworks/base/services/camera -I$gonkdir/frameworks/base/include/media/stagefright -I$gonkdir/frameworks/base/include/media/stagefright/openmax -I$gonkdir/frameworks/base/media/libstagefright/rtsp -I$gonkdir/frameworks/base/media/libstagefright/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib -I$gonkdir/dalvik/libnativehelper/include/nativehelper"
|
||||
MOZ_B2G_BT=1
|
||||
MOZ_B2G_BT_BLUEZ=1
|
||||
MOZ_NFC=1
|
||||
MOZ_B2G_CAMERA=1
|
||||
MOZ_OMX_DECODER=1
|
||||
AC_SUBST(MOZ_OMX_DECODER)
|
||||
|
@ -239,6 +240,7 @@ if test -n "$gonkdir" ; then
|
|||
MOZ_B2G_BT_BLUEDROID=1
|
||||
fi
|
||||
|
||||
MOZ_NFC=1
|
||||
MOZ_B2G_CAMERA=1
|
||||
MOZ_OMX_DECODER=1
|
||||
AC_SUBST(MOZ_OMX_DECODER)
|
||||
|
@ -7306,6 +7308,18 @@ AC_SUBST(MOZ_B2G_BT)
|
|||
AC_SUBST(MOZ_B2G_BT_BLUEZ)
|
||||
AC_SUBST(MOZ_B2G_BT_BLUEDROID)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable NFC Interface for B2G (Gonk usually)
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(b2g-nfc,
|
||||
[ --enable-nfc Set compile flags necessary for compiling NFC API ],
|
||||
MOZ_NFC=1,
|
||||
MOZ_NFC= )
|
||||
if test -n "$MOZ_NFC"; then
|
||||
AC_DEFINE(MOZ_NFC)
|
||||
fi
|
||||
AC_SUBST(MOZ_NFC)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable Pico Speech Synthesis (Gonk usually)
|
||||
dnl ========================================================
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
|
||||
#include "nsAttrAndChildArray.h"
|
||||
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "nsMappedAttributeElement.h"
|
||||
#include "prbit.h"
|
||||
#include "nsString.h"
|
||||
#include "nsHTMLStyleSheet.h"
|
||||
#include "nsRuleWalker.h"
|
||||
|
@ -772,7 +772,7 @@ nsAttrAndChildArray::GrowBy(uint32_t aGrowSize)
|
|||
} while (size < minSize);
|
||||
}
|
||||
else {
|
||||
size = 1u << PR_CeilingLog2(minSize);
|
||||
size = 1u << mozilla::CeilingLog2(minSize);
|
||||
}
|
||||
|
||||
bool needToInitialize = !mImpl;
|
||||
|
|
|
@ -253,8 +253,14 @@ public:
|
|||
|
||||
// nsWrapperCache
|
||||
using nsWrapperCache::GetWrapperPreserveColor;
|
||||
virtual JSObject* WrapObject(JSContext *cx,
|
||||
JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
protected:
|
||||
virtual JSObject* GetWrapperPreserveColorInternal() MOZ_OVERRIDE
|
||||
{
|
||||
return nsWrapperCache::GetWrapperPreserveColor();
|
||||
}
|
||||
public:
|
||||
|
||||
// nsIDOMHTMLCollection
|
||||
NS_DECL_NSIDOMHTMLCOLLECTION
|
||||
|
|
|
@ -7206,19 +7206,10 @@ nsDocument::IsScriptEnabled()
|
|||
nsCOMPtr<nsIScriptSecurityManager> sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
|
||||
NS_ENSURE_TRUE(sm, false);
|
||||
|
||||
nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetWindow());
|
||||
NS_ENSURE_TRUE(globalObject, false);
|
||||
nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow());
|
||||
NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), false);
|
||||
|
||||
nsIScriptContext *scriptContext = globalObject->GetContext();
|
||||
NS_ENSURE_TRUE(scriptContext, false);
|
||||
|
||||
AutoPushJSContext cx(scriptContext->GetNativeContext());
|
||||
NS_ENSURE_TRUE(cx, false);
|
||||
|
||||
bool enabled;
|
||||
nsresult rv = sm->CanExecuteScripts(cx, NodePrincipal(), &enabled);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
return enabled;
|
||||
return sm->ScriptAllowed(globalObject->GetGlobalJSObject());
|
||||
}
|
||||
|
||||
nsRadioGroupStruct*
|
||||
|
|
|
@ -498,28 +498,6 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Script evaluation can also be disabled in the current script
|
||||
// context even though it's enabled in the document.
|
||||
// XXX - still hard-coded for JS here, even though another language
|
||||
// may be specified. Should this check be made *after* we examine
|
||||
// the attributes to locate the script-type?
|
||||
// For now though, if JS is disabled we assume every language is
|
||||
// disabled.
|
||||
// XXX is this different from the mDocument->IsScriptEnabled() call?
|
||||
nsCOMPtr<nsIScriptGlobalObject> globalObject =
|
||||
do_QueryInterface(mDocument->GetWindow());
|
||||
if (!globalObject) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIScriptContext *context = globalObject->GetScriptContext();
|
||||
|
||||
// If scripts aren't enabled in the current context, there's no
|
||||
// point in going on.
|
||||
if (!context || !context->GetScriptsEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSVersion version = JSVERSION_DEFAULT;
|
||||
|
||||
// Check the type attribute to determine language and version.
|
||||
|
|
|
@ -1084,9 +1084,9 @@ CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
|
|||
const PRUnichar *aEncoderOptions,
|
||||
nsIInputStream **aStream)
|
||||
{
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
nsAutoArrayPtr<uint8_t> imageBuffer;
|
||||
int32_t format = 0;
|
||||
GetImageBuffer(&imageBuffer, &format);
|
||||
GetImageBuffer(getter_Transfers(imageBuffer), &format);
|
||||
if (!imageBuffer) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -794,9 +794,9 @@ WebGLContext::GetInputStream(const char* aMimeType,
|
|||
if (!gl)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
nsAutoArrayPtr<uint8_t> imageBuffer;
|
||||
int32_t format = 0;
|
||||
GetImageBuffer(&imageBuffer, &format);
|
||||
GetImageBuffer(getter_Transfers(imageBuffer), &format);
|
||||
if (!imageBuffer) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@ class Element;
|
|||
|
||||
// IID for the nsIHTMLCollection interface
|
||||
#define NS_IHTMLCOLLECTION_IID \
|
||||
{ 0x5643235d, 0x9a72, 0x4b6a, \
|
||||
{ 0xa6, 0x0c, 0x64, 0x63, 0x72, 0xb7, 0x53, 0x4a } }
|
||||
{ 0x4e169191, 0x5196, 0x4e17, \
|
||||
{ 0xa4, 0x79, 0xd5, 0x35, 0x0b, 0x5b, 0x0a, 0xcd } }
|
||||
|
||||
/**
|
||||
* An internal interface
|
||||
|
@ -76,11 +76,11 @@ public:
|
|||
|
||||
JSObject* GetWrapperPreserveColor()
|
||||
{
|
||||
nsWrapperCache* cache;
|
||||
CallQueryInterface(this, &cache);
|
||||
return cache->GetWrapperPreserveColor();
|
||||
return GetWrapperPreserveColorInternal();
|
||||
}
|
||||
virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> scope) = 0;
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) = 0;
|
||||
protected:
|
||||
virtual JSObject* GetWrapperPreserveColorInternal() = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIHTMLCollection, NS_IHTMLCOLLECTION_IID)
|
||||
|
|
|
@ -79,8 +79,15 @@ public:
|
|||
nsresult GetSortedControls(nsTArray<nsGenericHTMLFormElement*>& aControls) const;
|
||||
|
||||
// nsWrapperCache
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
using nsWrapperCache::GetWrapperPreserveColor;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
protected:
|
||||
virtual JSObject* GetWrapperPreserveColorInternal() MOZ_OVERRIDE
|
||||
{
|
||||
return nsWrapperCache::GetWrapperPreserveColor();
|
||||
}
|
||||
public:
|
||||
|
||||
static bool ShouldBeInElements(nsIFormControl* aFormControl);
|
||||
|
||||
|
|
|
@ -42,8 +42,14 @@ public:
|
|||
|
||||
// nsWrapperCache
|
||||
using nsWrapperCache::GetWrapperPreserveColor;
|
||||
virtual JSObject* WrapObject(JSContext* cx,
|
||||
JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
protected:
|
||||
virtual JSObject* GetWrapperPreserveColorInternal() MOZ_OVERRIDE
|
||||
{
|
||||
return nsWrapperCache::GetWrapperPreserveColor();
|
||||
}
|
||||
public:
|
||||
|
||||
// nsIDOMHTMLOptionsCollection interface
|
||||
NS_DECL_NSIDOMHTMLOPTIONSCOLLECTION
|
||||
|
|
|
@ -55,9 +55,16 @@ public:
|
|||
HTMLPropertiesCollection(nsGenericHTMLElement* aRoot);
|
||||
virtual ~HTMLPropertiesCollection();
|
||||
|
||||
// nsWrapperCache
|
||||
using nsWrapperCache::GetWrapperPreserveColor;
|
||||
virtual JSObject* WrapObject(JSContext *cx,
|
||||
JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
protected:
|
||||
virtual JSObject* GetWrapperPreserveColorInternal() MOZ_OVERRIDE
|
||||
{
|
||||
return nsWrapperCache::GetWrapperPreserveColor();
|
||||
}
|
||||
public:
|
||||
|
||||
virtual Element* GetElementAt(uint32_t aIndex);
|
||||
|
||||
|
|
|
@ -50,13 +50,15 @@ public:
|
|||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TableRowsCollection)
|
||||
|
||||
// nsWrapperCache
|
||||
virtual JSObject* WrapObject(JSContext *cx,
|
||||
JS::Handle<JSObject*> scope) MOZ_OVERRIDE
|
||||
using nsWrapperCache::GetWrapperPreserveColor;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
protected:
|
||||
virtual JSObject* GetWrapperPreserveColorInternal() MOZ_OVERRIDE
|
||||
{
|
||||
return mozilla::dom::HTMLCollectionBinding::Wrap(cx, scope, this);
|
||||
return nsWrapperCache::GetWrapperPreserveColor();
|
||||
}
|
||||
|
||||
protected:
|
||||
// Those rows that are not in table sections
|
||||
HTMLTableElement* mParent;
|
||||
nsRefPtr<nsContentList> mOrphanRows;
|
||||
|
@ -82,6 +84,13 @@ TableRowsCollection::~TableRowsCollection()
|
|||
// reference for us.
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TableRowsCollection::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope)
|
||||
{
|
||||
return HTMLCollectionBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(TableRowsCollection, mOrphanRows)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TableRowsCollection)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TableRowsCollection)
|
||||
|
|
|
@ -706,24 +706,17 @@ IsScriptEnabled(nsIDocument *aDoc, nsIDocShell *aContainer)
|
|||
NS_ENSURE_TRUE(aDoc && aContainer, true);
|
||||
|
||||
nsCOMPtr<nsIScriptGlobalObject> globalObject =
|
||||
do_QueryInterface(aDoc->GetWindow());
|
||||
do_QueryInterface(aDoc->GetInnerWindow());
|
||||
|
||||
// Getting context is tricky if the document hasn't had its
|
||||
// GlobalObject set yet
|
||||
if (!globalObject) {
|
||||
globalObject = aContainer->GetScriptGlobalObject();
|
||||
NS_ENSURE_TRUE(globalObject, true);
|
||||
}
|
||||
|
||||
nsIScriptContext *scriptContext = globalObject->GetContext();
|
||||
NS_ENSURE_TRUE(scriptContext, true);
|
||||
JSContext *cx = scriptContext->GetNativeContext();
|
||||
NS_ENSURE_TRUE(cx, true);
|
||||
|
||||
bool enabled = true;
|
||||
nsContentUtils::GetSecurityManager()->
|
||||
CanExecuteScripts(cx, aDoc->NodePrincipal(), &enabled);
|
||||
return enabled;
|
||||
NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), true);
|
||||
return nsContentUtils::GetSecurityManager()->
|
||||
ScriptAllowed(globalObject->GetGlobalJSObject());
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -1080,24 +1080,12 @@ nsXBLBinding::AllowScripts()
|
|||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(doc->GetWindow());
|
||||
if (!global) {
|
||||
nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(doc->GetInnerWindow());
|
||||
if (!global || !global->GetGlobalJSObject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptContext> context = global->GetContext();
|
||||
if (!context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoPushJSContext cx(context->GetNativeContext());
|
||||
|
||||
nsCOMPtr<nsIDocument> ourDocument =
|
||||
mPrototypeBinding->XBLDocumentInfo()->GetDocument();
|
||||
bool canExecute;
|
||||
nsresult rv =
|
||||
mgr->CanExecuteScripts(cx, ourDocument->NodePrincipal(), &canExecute);
|
||||
return NS_SUCCEEDED(rv) && canExecute;
|
||||
return mgr->ScriptAllowed(global->GetGlobalJSObject());
|
||||
}
|
||||
|
||||
nsXBLBinding*
|
||||
|
|
|
@ -328,12 +328,12 @@ nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
|
|||
|
||||
// Now call the method
|
||||
|
||||
// Check whether it's OK to call the method.
|
||||
rv = nsContentUtils::GetSecurityManager()->CheckFunctionAccess(cx, method,
|
||||
thisObject);
|
||||
// Check whether script is enabled.
|
||||
bool scriptAllowed = nsContentUtils::GetSecurityManager()->
|
||||
ScriptAllowed(js::GetGlobalForObjectCrossCompartment(method));
|
||||
|
||||
bool ok = true;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (scriptAllowed) {
|
||||
JS::Rooted<JS::Value> retval(cx);
|
||||
ok = ::JS_CallFunctionValue(cx, thisObject, OBJECT_TO_JSVAL(method),
|
||||
0 /* argc */, nullptr /* argv */, retval.address());
|
||||
|
|
|
@ -3660,18 +3660,14 @@ XULDocument::ExecuteScript(nsIScriptContext * aContext,
|
|||
|
||||
NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (!aContext->GetScriptsEnabled())
|
||||
return NS_OK;
|
||||
|
||||
// Execute the precompiled script with the given version
|
||||
nsAutoMicroTask mt;
|
||||
JSContext *cx = aContext->GetNativeContext();
|
||||
AutoCxPusher pusher(cx);
|
||||
JS::Rooted<JSObject*> global(cx, mScriptGlobalObject->GetGlobalJSObject());
|
||||
// XXXkhuey can this ever be null?
|
||||
if (global) {
|
||||
JS::ExposeObjectToActiveJS(global);
|
||||
}
|
||||
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(global), NS_OK);
|
||||
JS::ExposeObjectToActiveJS(global);
|
||||
xpc_UnmarkGrayScript(aScriptObject);
|
||||
JSAutoCompartment ac(cx, global);
|
||||
JS::Rooted<JS::Value> unused(cx);
|
||||
|
|
|
@ -762,6 +762,7 @@ nsDocShell::nsDocShell():
|
|||
mUseGlobalHistory(false),
|
||||
mInPrivateBrowsing(false),
|
||||
mDeviceSizeIsPageSize(false),
|
||||
mCanExecuteScripts(false),
|
||||
mFiredUnloadEvent(false),
|
||||
mEODForCurrentDocument(false),
|
||||
mURIResultedInDocument(false),
|
||||
|
@ -2077,12 +2078,6 @@ nsDocShell::GetAllowJavascript(bool * aAllowJavascript)
|
|||
NS_ENSURE_ARG_POINTER(aAllowJavascript);
|
||||
|
||||
*aAllowJavascript = mAllowJavascript;
|
||||
if (!mAllowJavascript) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool unsafe;
|
||||
*aAllowJavascript = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2090,6 +2085,7 @@ NS_IMETHODIMP
|
|||
nsDocShell::SetAllowJavascript(bool aAllowJavascript)
|
||||
{
|
||||
mAllowJavascript = aAllowJavascript;
|
||||
RecomputeCanExecuteScripts();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2823,6 +2819,48 @@ nsDocShell::GetParentDocshell()
|
|||
return docshell.forget().downcast<nsDocShell>();
|
||||
}
|
||||
|
||||
void
|
||||
nsDocShell::RecomputeCanExecuteScripts()
|
||||
{
|
||||
bool old = mCanExecuteScripts;
|
||||
nsRefPtr<nsDocShell> parent = GetParentDocshell();
|
||||
|
||||
// If we have no tree owner, that means that we've been detached from the
|
||||
// docshell tree (this is distinct from having no parent dochshell, which
|
||||
// is the case for root docshells). In that case, don't allow script.
|
||||
if (!mTreeOwner) {
|
||||
mCanExecuteScripts = false;
|
||||
// If scripting has been explicitly disabled on our docshell, we're done.
|
||||
} else if (!mAllowJavascript) {
|
||||
mCanExecuteScripts = false;
|
||||
// If we have a parent, inherit.
|
||||
} else if (parent) {
|
||||
mCanExecuteScripts = parent->mCanExecuteScripts;
|
||||
// Otherwise, we're the root of the tree, and we haven't explicitly disabled
|
||||
// script. Allow.
|
||||
} else {
|
||||
mCanExecuteScripts = true;
|
||||
}
|
||||
|
||||
// Inform our active DOM window.
|
||||
//
|
||||
// This will pass the outer, which will be in the scope of the active inner.
|
||||
if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) {
|
||||
xpc::Scriptability& scriptability =
|
||||
xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject());
|
||||
scriptability.SetDocShellAllowsScript(mCanExecuteScripts);
|
||||
}
|
||||
|
||||
// If our value has changed, our children might be affected. Recompute their
|
||||
// value as well.
|
||||
if (old != mCanExecuteScripts) {
|
||||
nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
|
||||
while (iter.HasMore()) {
|
||||
static_cast<nsDocShell*>(iter.GetNext())->RecomputeCanExecuteScripts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
|
||||
{
|
||||
|
@ -2892,6 +2930,10 @@ nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
|
|||
nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
|
||||
if (parentURIListener)
|
||||
mContentListener->SetParentContentListener(parentURIListener);
|
||||
|
||||
// Our parent has changed. Recompute scriptability.
|
||||
RecomputeCanExecuteScripts();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3446,6 +3488,16 @@ nsDocShell::SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)
|
|||
child->SetTreeOwner(aTreeOwner);
|
||||
}
|
||||
|
||||
// Our tree owner has changed. Recompute scriptability.
|
||||
//
|
||||
// Note that this is near-redundant with the recomputation in
|
||||
// SetDocLoaderParent(), but not so for the root DocShell, where the call to
|
||||
// SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(),
|
||||
// and we never set another parent. Given that this is neither expensive nor
|
||||
// performance-critical, let's be safe and unconditionally recompute this
|
||||
// state whenever dependent state changes.
|
||||
RecomputeCanExecuteScripts();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -12657,20 +12709,7 @@ unsigned long nsDocShell::gNumberOfDocShells = 0;
|
|||
NS_IMETHODIMP
|
||||
nsDocShell::GetCanExecuteScripts(bool *aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
*aResult = false; // disallow by default
|
||||
|
||||
nsRefPtr<nsDocShell> docshell = this;
|
||||
do {
|
||||
nsresult rv = docshell->GetAllowJavascript(aResult);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (!*aResult) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
docshell = docshell->GetParentDocshell();
|
||||
} while (docshell);
|
||||
|
||||
*aResult = mCanExecuteScripts;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -813,6 +813,12 @@ protected:
|
|||
bool mInPrivateBrowsing;
|
||||
bool mDeviceSizeIsPageSize;
|
||||
|
||||
// Because scriptability depends on the mAllowJavascript values of our
|
||||
// ancestors, we cache the effective scriptability and recompute it when
|
||||
// it might have changed;
|
||||
bool mCanExecuteScripts;
|
||||
void RecomputeCanExecuteScripts();
|
||||
|
||||
// This boolean is set to true right before we fire pagehide and generally
|
||||
// unset when we embed a new content viewer. While it's true no navigation
|
||||
// is allowed in this docshell.
|
||||
|
|
|
@ -570,7 +570,7 @@ interface nsIDocShell : nsIDocShellTreeItem
|
|||
* The rule of thumb here is that we disable js if this docshell or any
|
||||
* of its parents disallow scripting.
|
||||
*/
|
||||
readonly attribute boolean canExecuteScripts;
|
||||
[infallible] readonly attribute boolean canExecuteScripts;
|
||||
|
||||
/**
|
||||
* Sets whether a docshell is active. An active docshell is one that is
|
||||
|
|
Двоичные данные
docshell/test/bug369814.zip
Двоичные данные
docshell/test/bug369814.zip
Двоичный файл не отображается.
|
@ -302,6 +302,17 @@ this.PermissionsTable = { geolocation: {
|
|||
privileged: PROMPT_ACTION,
|
||||
certified: PROMPT_ACTION
|
||||
},
|
||||
"nfc": {
|
||||
app: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
access: ["read", "write"]
|
||||
},
|
||||
"nfc-manager": {
|
||||
app: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1793,6 +1793,18 @@ Navigator::HasFMRadioSupport(JSContext* /* unused */, JSObject* aGlobal)
|
|||
}
|
||||
#endif // MOZ_B2G_FM
|
||||
|
||||
#ifdef MOZ_NFC
|
||||
/* static */
|
||||
bool
|
||||
Navigator::HasNfcSupport(JSContext* /* unused */, JSObject* aGlobal)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
|
||||
return win && (CheckPermission(win, "nfc-read") ||
|
||||
CheckPermission(win, "nfc-write"));
|
||||
}
|
||||
#endif // MOZ_NFC
|
||||
|
||||
|
||||
#ifdef MOZ_TIME_MANAGER
|
||||
/* static */
|
||||
bool
|
||||
|
|
|
@ -287,6 +287,9 @@ public:
|
|||
#ifdef MOZ_B2G_FM
|
||||
static bool HasFMRadioSupport(JSContext* /* unused */, JSObject* aGlobal);
|
||||
#endif // MOZ_B2G_FM
|
||||
#ifdef MOZ_NFC
|
||||
static bool HasNfcSupport(JSContext* /* unused */, JSObject* aGlobal);
|
||||
#endif // MOZ_NFC
|
||||
#ifdef MOZ_TIME_MANAGER
|
||||
static bool HasTimeSupport(JSContext* /* unused */, JSObject* aGlobal);
|
||||
#endif // MOZ_TIME_MANAGER
|
||||
|
|
|
@ -139,6 +139,7 @@
|
|||
#endif
|
||||
#include "nsIDOMCustomEvent.h"
|
||||
#include "nsIFrameRequestCallback.h"
|
||||
#include "nsIJARChannel.h"
|
||||
|
||||
#include "xpcprivate.h"
|
||||
|
||||
|
@ -2061,6 +2062,9 @@ WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow,
|
|||
mInnerWindowHolder = aHolder;
|
||||
|
||||
aWindow->SuspendTimeouts();
|
||||
|
||||
// When a global goes into the bfcache, we disable script.
|
||||
xpc::Scriptability::Get(aWindow->mJSObject).SetDocShellAllowsScript(false);
|
||||
}
|
||||
|
||||
WindowStateHolder::~WindowStateHolder()
|
||||
|
@ -2459,6 +2463,10 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
|||
// Enter the new global's compartment.
|
||||
JSAutoCompartment ac(cx, mJSObject);
|
||||
|
||||
// Set scriptability based on the state of the docshell.
|
||||
bool allow = GetDocShell()->GetCanExecuteScripts();
|
||||
xpc::Scriptability::Get(mJSObject).SetDocShellAllowsScript(allow);
|
||||
|
||||
// If we created a new inner window above, we need to do the last little bit
|
||||
// of initialization now that the dust has settled.
|
||||
if (createdInnerWindow) {
|
||||
|
@ -2541,6 +2549,14 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// If the document comes from a JAR, check if the channel was determined
|
||||
// to be unsafe. If so, permanently disable script on the compartment by
|
||||
// calling Block() and throwing away the key.
|
||||
nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aDocument->GetChannel());
|
||||
if (jarChannel && jarChannel->GetIsUnsafe()) {
|
||||
xpc::Scriptability::Get(newInnerWindow->mJSObject).Block();
|
||||
}
|
||||
|
||||
if (mArguments) {
|
||||
newInnerWindow->DefineArgumentsProperty(mArguments);
|
||||
mArguments = nullptr;
|
||||
|
@ -3174,19 +3190,6 @@ nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetScriptsEnabled(bool aEnabled, bool aFireTimeouts)
|
||||
{
|
||||
FORWARD_TO_INNER_VOID(SetScriptsEnabled, (aEnabled, aFireTimeouts));
|
||||
|
||||
if (aEnabled && aFireTimeouts) {
|
||||
// Scripts are enabled (again?) on this context, run timeouts that
|
||||
// fired on this context while scripts were disabled.
|
||||
void (nsGlobalWindow::*run)() = &nsGlobalWindow::RunTimeout;
|
||||
NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, run));
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGlobalWindow::SetArguments(nsIArray *aArguments)
|
||||
{
|
||||
|
@ -8942,11 +8945,14 @@ NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(nsGlobalWindow)
|
|||
NS_IMETHODIMP
|
||||
nsGlobalWindow::DispatchEvent(nsIDOMEvent* aEvent, bool* aRetVal)
|
||||
{
|
||||
MOZ_ASSERT(!IsInnerWindow() || IsCurrentInnerWindow(),
|
||||
"We should only fire events on the current inner window.");
|
||||
|
||||
FORWARD_TO_INNER(DispatchEvent, (aEvent, aRetVal), NS_OK);
|
||||
|
||||
if (!IsCurrentInnerWindow()) {
|
||||
NS_WARNING("DispatchEvent called on non-current inner window, dropping. "
|
||||
"Please check the window in the caller instead.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mDoc) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -11824,20 +11830,6 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
|||
continue;
|
||||
}
|
||||
|
||||
// The "scripts disabled" concept is still a little vague wrt
|
||||
// multiple languages. Prepare for the day when languages can be
|
||||
// disabled independently of the other languages...
|
||||
if (!scx->GetScriptsEnabled()) {
|
||||
// Scripts were enabled once in this window (unless aTimeout ==
|
||||
// nullptr) but now scripts are disabled (we might be in
|
||||
// print-preview, for instance), this means we shouldn't run any
|
||||
// timeouts at this point.
|
||||
//
|
||||
// If scripts are enabled for this language in this window again
|
||||
// we'll fire the timeouts that are due at that point.
|
||||
continue;
|
||||
}
|
||||
|
||||
// This timeout is good to run
|
||||
++timeoutsRan;
|
||||
bool timeout_was_cleared = RunTimeoutHandler(timeout, scx);
|
||||
|
|
|
@ -354,7 +354,6 @@ public:
|
|||
|
||||
void PoisonOuterWindowProxy(JSObject *aObject);
|
||||
virtual void OnFinalize(JSObject* aObject);
|
||||
virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts);
|
||||
|
||||
virtual bool IsBlackForCC();
|
||||
|
||||
|
|
|
@ -27,8 +27,8 @@ class nsIDOMWindow;
|
|||
class nsIURI;
|
||||
|
||||
#define NS_ISCRIPTCONTEXT_IID \
|
||||
{ 0xf3859ce7, 0x7551, 0x4760, \
|
||||
{ 0x84, 0x29, 0x64, 0x4f, 0x26, 0x1e, 0xdb, 0x91 } }
|
||||
{ 0x513c2c1a, 0xf4f1, 0x44da, \
|
||||
{ 0x8e, 0x38, 0xf4, 0x0c, 0x30, 0x9a, 0x5d, 0xef } }
|
||||
|
||||
/* This MUST match JSVERSION_DEFAULT. This version stuff if we don't
|
||||
know what language we have is a little silly... */
|
||||
|
@ -128,12 +128,6 @@ public:
|
|||
*/
|
||||
virtual void GC(JS::gcreason::Reason aReason) = 0;
|
||||
|
||||
/**
|
||||
* Called to disable/enable script execution in this context.
|
||||
*/
|
||||
virtual bool GetScriptsEnabled() = 0;
|
||||
virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts) = 0;
|
||||
|
||||
// SetProperty is suspect and jst believes should not be needed. Currenly
|
||||
// used only for "arguments".
|
||||
virtual nsresult SetProperty(JS::Handle<JSObject*> aTarget,
|
||||
|
|
|
@ -27,8 +27,8 @@ NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
|
|||
|
||||
|
||||
#define NS_ISCRIPTGLOBALOBJECT_IID \
|
||||
{ 0x214fa2f6, 0xcc0c, 0x42cf, \
|
||||
{ 0x98, 0x4b, 0x45, 0xf5, 0x73, 0x9c, 0x6b, 0x73 } }
|
||||
{ 0xa6c0bfae, 0x8be4, 0x4747, \
|
||||
{ 0xaf, 0x1a, 0xe3, 0xf0, 0x3f, 0xb6, 0x0e, 0xb8 } }
|
||||
|
||||
/**
|
||||
* The global object which keeps a script context for each supported script
|
||||
|
@ -70,11 +70,6 @@ public:
|
|||
*/
|
||||
virtual void OnFinalize(JSObject* aObject) = 0;
|
||||
|
||||
/**
|
||||
* Called to enable/disable scripts.
|
||||
*/
|
||||
virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts) = 0;
|
||||
|
||||
/**
|
||||
* Handle a script error. Generally called by a script context.
|
||||
*/
|
||||
|
|
|
@ -839,7 +839,6 @@ nsJSContext::nsJSContext(bool aGCOnDestruction,
|
|||
js_options_dot_str, this);
|
||||
}
|
||||
mIsInitialized = false;
|
||||
mScriptsEnabled = true;
|
||||
mProcessingScriptTag = false;
|
||||
HoldJSObjects(this);
|
||||
}
|
||||
|
@ -950,10 +949,6 @@ nsJSContext::EvaluateString(const nsAString& aScript,
|
|||
void **aOffThreadToken)
|
||||
{
|
||||
NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
|
||||
if (!mScriptsEnabled) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AutoCxPusher pusher(mContext);
|
||||
nsJSUtils::EvaluateOptions evalOptions;
|
||||
evalOptions.setCoerceToString(aCoerceToString);
|
||||
|
@ -1863,27 +1858,6 @@ nsJSContext::IsContextInitialized()
|
|||
return mIsInitialized;
|
||||
}
|
||||
|
||||
bool
|
||||
nsJSContext::GetScriptsEnabled()
|
||||
{
|
||||
return mScriptsEnabled;
|
||||
}
|
||||
|
||||
void
|
||||
nsJSContext::SetScriptsEnabled(bool aEnabled, bool aFireTimeouts)
|
||||
{
|
||||
// eeek - this seems the wrong way around - the global should callback
|
||||
// into each context, so every language is disabled.
|
||||
mScriptsEnabled = aEnabled;
|
||||
|
||||
nsIScriptGlobalObject *global = GetGlobalObject();
|
||||
|
||||
if (global) {
|
||||
global->SetScriptsEnabled(aEnabled, aFireTimeouts);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
nsJSContext::GetProcessingScriptTag()
|
||||
{
|
||||
|
|
|
@ -59,9 +59,6 @@ public:
|
|||
virtual nsresult InitContext() MOZ_OVERRIDE;
|
||||
virtual bool IsContextInitialized() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool GetScriptsEnabled() MOZ_OVERRIDE;
|
||||
virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aVal) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool GetProcessingScriptTag() MOZ_OVERRIDE;
|
||||
|
@ -166,7 +163,6 @@ private:
|
|||
JS::Heap<JSObject*> mWindowProxy;
|
||||
|
||||
bool mIsInitialized;
|
||||
bool mScriptsEnabled;
|
||||
bool mGCOnDestruction;
|
||||
bool mProcessingScriptTag;
|
||||
|
||||
|
|
|
@ -241,15 +241,14 @@ nsJSUtils::EvaluateString(JSContext* aCx,
|
|||
|
||||
JS::ExposeObjectToActiveJS(aScopeObject);
|
||||
nsAutoMicroTask mt;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
JSPrincipals* p = JS_GetCompartmentPrincipals(js::GetObjectCompartment(aScopeObject));
|
||||
aCompileOptions.setPrincipals(p);
|
||||
|
||||
bool ok = false;
|
||||
nsresult rv = nsContentUtils::GetSecurityManager()->
|
||||
CanExecuteScripts(aCx, nsJSPrincipals::get(p), &ok);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(ok, NS_OK);
|
||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||
NS_ENSURE_TRUE(ssm->ScriptAllowed(js::GetGlobalForObjectCrossCompartment(aScopeObject)), NS_OK);
|
||||
|
||||
mozilla::Maybe<AutoDontReportUncaught> dontReport;
|
||||
if (!aEvaluateOptions.reportUncaught) {
|
||||
|
|
|
@ -120,14 +120,12 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
|
|||
|
||||
if (mIsMainThread) {
|
||||
// Check that it's ok to run this callback at all.
|
||||
// FIXME: Bug 807371: we want a less silly check here.
|
||||
// Make sure to unwrap aCallback before passing it in, because
|
||||
// getting principals from wrappers is silly.
|
||||
nsresult rv = nsContentUtils::GetSecurityManager()->
|
||||
CheckFunctionAccess(cx, js::UncheckedUnwrap(aCallback), nullptr);
|
||||
// Make sure to unwrap aCallback before passing it in to get the global of
|
||||
// the callback object, not the wrapper.
|
||||
bool allowed = nsContentUtils::GetSecurityManager()->
|
||||
ScriptAllowed(js::GetGlobalForObjectCrossCompartment(js::UncheckedUnwrap(aCallback)));
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
// Security check failed. We're done here.
|
||||
if (!allowed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -249,7 +249,14 @@ AdapterPropertiesChangeCallback(bt_status_t aStatus, int aNumProperties,
|
|||
propertiesArray.AppendElement(
|
||||
BluetoothNamedValue(NS_LITERAL_STRING("Name"), propertyValue));
|
||||
} else if (p.type == BT_PROPERTY_ADAPTER_SCAN_MODE) {
|
||||
propertyValue = sAdapterDiscoverable = *(uint32_t*)p.val;
|
||||
bt_scan_mode_t newMode = *(bt_scan_mode_t*)p.val;
|
||||
|
||||
if (newMode == BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
|
||||
propertyValue = sAdapterDiscoverable = true;
|
||||
} else {
|
||||
propertyValue = sAdapterDiscoverable = false;
|
||||
}
|
||||
|
||||
propertiesArray.AppendElement(
|
||||
BluetoothNamedValue(NS_LITERAL_STRING("Discoverable"), propertyValue));
|
||||
} else if (p.type == BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT) {
|
||||
|
@ -637,9 +644,12 @@ ReplyStatusError(BluetoothReplyRunnable* aBluetoothReplyRunnable,
|
|||
int aStatusCode, const nsAString& aCustomMsg)
|
||||
{
|
||||
MOZ_ASSERT(aBluetoothReplyRunnable, "Reply runnable is nullptr");
|
||||
nsAutoString replyError;
|
||||
|
||||
BT_LOGR("%s: error code(%d)", __FUNCTION__, aStatusCode);
|
||||
|
||||
nsAutoString replyError;
|
||||
replyError.Assign(aCustomMsg);
|
||||
|
||||
if (aStatusCode == BT_STATUS_BUSY) {
|
||||
replyError.AppendLiteral(":BT_STATUS_BUSY");
|
||||
} else if (aStatusCode == BT_STATUS_NOT_READY) {
|
||||
|
@ -840,6 +850,7 @@ BluetoothServiceBluedroid::SetProperty(BluetoothObjectType aType,
|
|||
|
||||
const nsString propName = aValue.name();
|
||||
bt_property_t prop;
|
||||
bt_scan_mode_t scanMode;
|
||||
nsString str;
|
||||
|
||||
// For Bluedroid, it's necessary to check property name for SetProperty
|
||||
|
@ -863,22 +874,23 @@ BluetoothServiceBluedroid::SetProperty(BluetoothObjectType aType,
|
|||
prop.val = (void*)name;
|
||||
prop.len = strlen(name);
|
||||
} else if (aValue.value().type() == BluetoothValue::Tbool) {
|
||||
bt_scan_mode_t mode = aValue.value().get_bool() ?
|
||||
BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE :
|
||||
BT_SCAN_MODE_CONNECTABLE;
|
||||
bt_scan_mode_t* sss = &mode;
|
||||
prop.val = (void*)sss;
|
||||
prop.len = sizeof(sss);
|
||||
scanMode = aValue.value().get_bool() ?
|
||||
BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE :
|
||||
BT_SCAN_MODE_CONNECTABLE;
|
||||
|
||||
prop.val = (void*)&scanMode;
|
||||
prop.len = sizeof(scanMode);
|
||||
} else {
|
||||
BT_LOGR("SetProperty but the property cannot be recognized correctly.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
sSetPropertyRunnableArray.AppendElement(aRunnable);
|
||||
int ret = sBtInterface->set_adapter_property(&prop);
|
||||
|
||||
if (ret != BT_STATUS_SUCCESS)
|
||||
int ret = sBtInterface->set_adapter_property(&prop);
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("SetProperty"));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,11 @@ const browserElementTestHelpers = {
|
|||
}
|
||||
},
|
||||
|
||||
_setPrefs: function() {
|
||||
this.lockTestReady();
|
||||
SpecialPowers.pushPrefEnv({'set': Array.slice(arguments)}, this.unlockTestReady.bind(this));
|
||||
},
|
||||
|
||||
_testReadyLockCount: 0,
|
||||
_firedTestReady: false,
|
||||
lockTestReady: function() {
|
||||
|
@ -44,9 +49,11 @@ const browserElementTestHelpers = {
|
|||
},
|
||||
|
||||
enableProcessPriorityManager: function() {
|
||||
this._setPref('dom.ipc.processPriorityManager.testMode', true);
|
||||
this._setPref('dom.ipc.processPriorityManager.enabled', true);
|
||||
this._setPref('dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2);
|
||||
this._setPrefs(
|
||||
['dom.ipc.processPriorityManager.testMode', true],
|
||||
['dom.ipc.processPriorityManager.enabled', true],
|
||||
['dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2]
|
||||
);
|
||||
},
|
||||
|
||||
setEnabledPref: function(value) {
|
||||
|
|
|
@ -289,11 +289,12 @@ parent:
|
|||
ContentReceivedTouch(ScrollableLayerGuid aGuid, bool aPreventDefault);
|
||||
|
||||
/**
|
||||
* Updates the parent's zoom constraints for this tab. The zoom controller
|
||||
* code lives on the parent side and so this allows it to have up-to-date
|
||||
* zoom constraints.
|
||||
* Updates the zoom constraints for a scrollable frame in this tab.
|
||||
* The zoom controller code lives on the parent side and so this allows it to
|
||||
* have up-to-date zoom constraints.
|
||||
*/
|
||||
UpdateZoomConstraints(bool aAllowZoom, CSSToScreenScale aMinZoom, CSSToScreenScale aMaxZoom);
|
||||
UpdateZoomConstraints(uint32_t aPresShellId, ViewID aViewId,
|
||||
bool aAllowZoom, CSSToScreenScale aMinZoom, CSSToScreenScale aMaxZoom);
|
||||
|
||||
/**
|
||||
* Notifies the parent about a scroll event. The pres shell ID and
|
||||
|
|
|
@ -537,8 +537,17 @@ TabChild::HandlePossibleViewportChange()
|
|||
|
||||
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
|
||||
|
||||
uint32_t presShellId;
|
||||
ViewID viewId;
|
||||
if (!APZCCallbackHelper::GetScrollIdentifiers(document->GetDocumentElement(),
|
||||
&presShellId, &viewId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, mInnerSize);
|
||||
SendUpdateZoomConstraints(viewportInfo.IsZoomAllowed(),
|
||||
SendUpdateZoomConstraints(presShellId,
|
||||
viewId,
|
||||
viewportInfo.IsZoomAllowed(),
|
||||
viewportInfo.GetMinZoom(),
|
||||
viewportInfo.GetMaxZoom());
|
||||
|
||||
|
|
|
@ -1626,12 +1626,14 @@ TabParent::RecvZoomToRect(const uint32_t& aPresShellId,
|
|||
}
|
||||
|
||||
bool
|
||||
TabParent::RecvUpdateZoomConstraints(const bool& aAllowZoom,
|
||||
TabParent::RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
|
||||
const ViewID& aViewId,
|
||||
const bool& aAllowZoom,
|
||||
const CSSToScreenScale& aMinZoom,
|
||||
const CSSToScreenScale& aMaxZoom)
|
||||
{
|
||||
if (RenderFrameParent* rfp = GetRenderFrame()) {
|
||||
rfp->UpdateZoomConstraints(aAllowZoom, aMinZoom, aMaxZoom);
|
||||
rfp->UpdateZoomConstraints(aPresShellId, aViewId, aAllowZoom, aMinZoom, aMaxZoom);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -165,7 +165,9 @@ public:
|
|||
virtual bool RecvZoomToRect(const uint32_t& aPresShellId,
|
||||
const ViewID& aViewId,
|
||||
const CSSRect& aRect);
|
||||
virtual bool RecvUpdateZoomConstraints(const bool& aAllowZoom,
|
||||
virtual bool RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
|
||||
const ViewID& aViewId,
|
||||
const bool& aAllowZoom,
|
||||
const CSSToScreenScale& aMinZoom,
|
||||
const CSSToScreenScale& aMaxZoom);
|
||||
virtual bool RecvUpdateScrollOffset(const uint32_t& aPresShellId, const ViewID& aViewId, const CSSIntPoint& aScrollOffset);
|
||||
|
|
|
@ -95,6 +95,15 @@ this.SystemMessagePermissionsTable = {
|
|||
"cdma-info-rec-received": {
|
||||
"mobileconnection": []
|
||||
},
|
||||
"nfc-manager-tech-discovered": {
|
||||
"nfc-manager": []
|
||||
},
|
||||
"nfc-manager-tech-lost": {
|
||||
"nfc-manager": []
|
||||
},
|
||||
"nfc-powerlevel-change": {
|
||||
"settings": ["read", "write"]
|
||||
}
|
||||
};
|
||||
|
||||
this.SystemMessagePermissionsChecker = {
|
||||
|
|
|
@ -97,6 +97,9 @@ if CONFIG['MOZ_PAY']:
|
|||
if CONFIG['MOZ_GAMEPAD']:
|
||||
PARALLEL_DIRS += ['gamepad']
|
||||
|
||||
if CONFIG['MOZ_NFC']:
|
||||
PARALLEL_DIRS += ['nfc']
|
||||
|
||||
# bindings/test is here, because it needs to build after bindings/, and
|
||||
# we build subdirectories before ourselves.
|
||||
TEST_DIRS += [
|
||||
|
|
|
@ -211,11 +211,12 @@ interface nsIDOMTCPSocket : nsISupports
|
|||
};
|
||||
|
||||
/*
|
||||
* Internal interfaces for use in cross-process socket implementation.
|
||||
* This interface is implemented in TCPSocket.js as an internal interfaces
|
||||
* for use in cross-process socket implementation.
|
||||
* Needed to account for multiple possible types that can be provided to
|
||||
* the socket callbacks as arguments.
|
||||
*/
|
||||
[scriptable, uuid(234c664c-3d6c-4859-b45c-4e9a98cb5bdc)]
|
||||
[scriptable, uuid(017f130f-2477-4215-8783-57eada957699)]
|
||||
interface nsITCPSocketInternal : nsISupports {
|
||||
// Trigger the callback for |type| and provide a DOMError() object with the given data
|
||||
void callListenerError(in DOMString type, in DOMString name);
|
||||
|
@ -229,8 +230,20 @@ interface nsITCPSocketInternal : nsISupports {
|
|||
// Trigger the callback for |type| with no argument
|
||||
void callListenerVoid(in DOMString type);
|
||||
|
||||
// Update the DOM object's readyState and bufferedAmount values with the provided data
|
||||
void updateReadyStateAndBuffered(in DOMString readyState, in uint32_t bufferedAmount);
|
||||
// Update the DOM object's readyState.
|
||||
// @param readyState
|
||||
// new ready state to be set to TCPSocket.
|
||||
void updateReadyState(in DOMString readyState);
|
||||
|
||||
// Update the DOM object's bufferedAmount value with a tracking number to
|
||||
// ensure the update request is sent after child's send() invocation.
|
||||
// @param bufferedAmount
|
||||
// TCPSocket parent's bufferedAmount.
|
||||
// @param trackingNumber
|
||||
// A number to ensure the bufferedAmount is updated after data
|
||||
// from child are sent to parent.
|
||||
void updateBufferedAmount(in uint32_t bufferedAmount,
|
||||
in uint32_t trackingNumber);
|
||||
|
||||
// Create a socket object on the parent side.
|
||||
// This is called in accepting any open request on the parent side.
|
||||
|
@ -259,6 +272,18 @@ interface nsITCPSocketInternal : nsISupports {
|
|||
|
||||
// Set App ID.
|
||||
void setAppId(in unsigned long appId);
|
||||
|
||||
// Set a callback that handles the request from a TCP socket parent when that
|
||||
// socket parent wants to notify that its bufferedAmount is updated.
|
||||
void setOnUpdateBufferedAmountHandler(in jsval handler);
|
||||
|
||||
// Providing child process with ability to pass more arguments to parent's
|
||||
// send() function.
|
||||
// @param trackingNumber
|
||||
// To ensure the request to update bufferedAmount in child is after
|
||||
// lastest send() invocation from child.
|
||||
void onRecvSendFromChild(in jsval data, in unsigned long byteOffset,
|
||||
in unsigned long byteLength, in unsigned long trackingNumber);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,22 +8,26 @@ interface nsITCPSocketInternal;
|
|||
interface nsIDOMWindow;
|
||||
|
||||
// Interface to allow the content process socket to reach the IPC bridge.
|
||||
[scriptable, uuid(ada5342d-6d45-4ff1-a7d3-6a4b150d0385)]
|
||||
// Implemented in C++ as TCPSocketChild, referenced as _socketBridge in TCPSocket.js
|
||||
[scriptable, uuid(292ebb3a-beac-4e06-88b0-b5b4e88ebd1c)]
|
||||
interface nsITCPSocketChild : nsISupports
|
||||
{
|
||||
// Tell the chrome process to open a corresponding connection with the given parameters
|
||||
[implicit_jscontext]
|
||||
void open(in nsITCPSocketInternal socket, in DOMString host,
|
||||
in unsigned short port, in boolean ssl, in DOMString binaryType,
|
||||
in nsIDOMWindow window, in jsval windowVal);
|
||||
void sendOpen(in nsITCPSocketInternal socket, in DOMString host,
|
||||
in unsigned short port, in boolean ssl, in DOMString binaryType,
|
||||
in nsIDOMWindow window, in jsval windowVal);
|
||||
|
||||
// Tell the chrome process to perform send and update the tracking number.
|
||||
[implicit_jscontext]
|
||||
void sendSend(in jsval data, in unsigned long byteOffset,
|
||||
in unsigned long byteLength, in unsigned long trackingNumber);
|
||||
|
||||
// Tell the chrome process to perform equivalent operations to all following methods
|
||||
[implicit_jscontext]
|
||||
void send(in jsval data, in unsigned long byteOffset, in unsigned long byteLength);
|
||||
void resume();
|
||||
void suspend();
|
||||
void close();
|
||||
void startTLS();
|
||||
void sendResume();
|
||||
void sendSuspend();
|
||||
void sendClose();
|
||||
void sendStartTLS();
|
||||
|
||||
/**
|
||||
* Initialize the TCP socket on the child side for IPC. It is called from the child side,
|
||||
|
|
|
@ -9,20 +9,28 @@ interface nsIDOMTCPServerSocket;
|
|||
interface nsITCPServerSocketParent;
|
||||
interface nsITCPSocketIntermediary;
|
||||
|
||||
// Interface required to allow the TCP socket object in the parent process
|
||||
// to talk to the parent IPC actor
|
||||
[scriptable, uuid(123f654b-4435-43c8-8447-db1b5420a1c2)]
|
||||
// Interface required to allow the TCP socket object (TCPSocket.js) in the
|
||||
// parent process to talk to the parent IPC actor, TCPSocketParent, which
|
||||
// is written in C++.
|
||||
[scriptable, uuid(868662a4-681c-4b89-9f02-6fe5b7ace265)]
|
||||
interface nsITCPSocketParent : nsISupports
|
||||
{
|
||||
[implicit_jscontext] void initJS(in jsval intermediary);
|
||||
|
||||
// Trigger a callback in the content process for |type|, providing a serialized
|
||||
// argument of |data|, and update the child's readyState and bufferedAmount values
|
||||
// with the given values.
|
||||
[implicit_jscontext] void sendCallback(in DOMString type,
|
||||
in jsval data,
|
||||
in DOMString readyState,
|
||||
in uint32_t bufferedAmount);
|
||||
// argument of |data|, and update the child's readyState value with the given
|
||||
// values.
|
||||
//
|
||||
// @param type
|
||||
// Event type: 'onopen', 'ondata', 'onerror' or 'onclose'. 'odrain' is
|
||||
// controlled by child.
|
||||
// @param data
|
||||
// Serialized data that is passed to event handler.
|
||||
// @param readyState
|
||||
// Current ready state.
|
||||
[implicit_jscontext] void sendEvent(in DOMString type,
|
||||
in jsval data,
|
||||
in DOMString readyState);
|
||||
|
||||
// Initialize a parent socket object. It is called from the parent side socket,
|
||||
// which is generated in accepting any open request on the parent side.
|
||||
|
@ -34,11 +42,27 @@ interface nsITCPSocketParent : nsISupports
|
|||
// Intermediate class object. See nsITCPSocketIntermediary.
|
||||
[implicit_jscontext] void setSocketAndIntermediary(in nsIDOMTCPSocket socket,
|
||||
in nsITCPSocketIntermediary intermediary);
|
||||
|
||||
// When parent's buffered amount is updated and it wants to inform child to
|
||||
// update the bufferedAmount as well.
|
||||
//
|
||||
// @param bufferedAmount
|
||||
// The new value of bufferedAmount that is going to be set to child's
|
||||
// bufferedAmount.
|
||||
// @param trackingNumber
|
||||
// Parent's current tracking number, reflecting the number of calls to
|
||||
// send() on the child process. This number is sent back to the child
|
||||
// to make sure the bufferedAmount updated on the child will correspond
|
||||
// to the latest call of send().
|
||||
void sendUpdateBufferedAmount(in uint32_t bufferedAmount, in uint32_t trackingNumber);
|
||||
};
|
||||
|
||||
// Intermediate class to handle sending multiple possible data types
|
||||
// and kicking off the chrome process socket object's connection.
|
||||
[scriptable, uuid(be67b1b8-03b0-4171-a791-d004458021b6)]
|
||||
// This interface is the bridge of TCPSocketParent, which is written in C++,
|
||||
// and TCPSocket, which is written in Javascript. TCPSocketParentIntermediary
|
||||
// implements nsITCPSocketIntermediary in Javascript.
|
||||
[scriptable, uuid(c434224a-dbb7-4869-8b2b-e49cee990e85)]
|
||||
interface nsITCPSocketIntermediary : nsISupports {
|
||||
// Open the connection to the server with the given parameters
|
||||
nsIDOMTCPSocket open(in nsITCPSocketParent parent,
|
||||
|
@ -51,9 +75,9 @@ interface nsITCPSocketIntermediary : nsISupports {
|
|||
in unsigned short port, in unsigned short backlog,
|
||||
in DOMString binaryType);
|
||||
|
||||
// Send a basic string along the connection
|
||||
void sendString(in DOMString data);
|
||||
// Called when received a child request to send a string.
|
||||
void onRecvSendString(in DOMString data, in uint32_t trackingNumber);
|
||||
|
||||
// Send a typed array
|
||||
void sendArrayBuffer(in jsval data);
|
||||
// Called when received a child request to send an array buffer.
|
||||
void onRecvSendArrayBuffer(in jsval data, in uint32_t trackingNumber);
|
||||
};
|
||||
|
|
|
@ -362,6 +362,7 @@ this.NetworkStatsService = {
|
|||
this.updateQueue.push({netId: aNetId, callbacks: [aCallback]});
|
||||
} else {
|
||||
this.updateQueue[index].callbacks.push(aCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the function that process the elements of the queue.
|
||||
|
|
|
@ -35,16 +35,35 @@ protocol PTCPSocket
|
|||
manager PNecko;
|
||||
|
||||
parent:
|
||||
// Forward calling to child's open() method to parent, expect TCPOptions
|
||||
// is expanded to |useSSL| (from TCPOptions.useSecureTransport) and
|
||||
// |binaryType| (from TCPOption.binaryType).
|
||||
Open(nsString host, uint16_t port, bool useSSL, nsString binaryType);
|
||||
Data(SendableData data);
|
||||
|
||||
// When child's send() is called, this message requrests parent to send
|
||||
// data and update it's trackingNumber.
|
||||
Data(SendableData data, uint32_t trackingNumber);
|
||||
|
||||
// Forward calling to child's upgradeToSecure() method to parent.
|
||||
StartTLS();
|
||||
|
||||
// Forward calling to child's send() method to parent.
|
||||
Suspend();
|
||||
|
||||
// Forward calling to child's resume() method to parent.
|
||||
Resume();
|
||||
|
||||
// Forward calling to child's close() method to parent.
|
||||
Close();
|
||||
|
||||
child:
|
||||
Callback(nsString type, CallbackData data,
|
||||
nsString readyState, uint32_t bufferedAmount);
|
||||
// Forward events that are dispatched by parent.
|
||||
Callback(nsString type, CallbackData data, nsString readyState);
|
||||
|
||||
// Update child's bufferedAmount when parent's bufferedAmount is updated.
|
||||
// trackingNumber is also passed back to child to ensure the bufferedAmount
|
||||
// is corresponding the last call to send().
|
||||
UpdateBufferedAmount(uint32_t bufferedAmount, uint32_t trackingNumber);
|
||||
|
||||
both:
|
||||
RequestDelete();
|
||||
|
|
|
@ -162,6 +162,10 @@ TCPSocket.prototype = {
|
|||
_waitingForStartTLS: false,
|
||||
_pendingDataAfterStartTLS: [],
|
||||
|
||||
// Used to notify when update bufferedAmount is updated.
|
||||
_onUpdateBufferedAmount: null,
|
||||
_trackingNumber: 0,
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Network statistics (Gonk-specific feature)
|
||||
_txBytes: 0,
|
||||
|
@ -242,6 +246,12 @@ TCPSocket.prototype = {
|
|||
.createTransport(options, 1, host, port, null);
|
||||
},
|
||||
|
||||
_sendBufferedAmount: function ts_sendBufferedAmount() {
|
||||
if (this._onUpdateBufferedAmount) {
|
||||
this._onUpdateBufferedAmount(this.bufferedAmount, this._trackingNumber);
|
||||
}
|
||||
},
|
||||
|
||||
_ensureCopying: function ts_ensureCopying() {
|
||||
let self = this;
|
||||
if (this._asyncCopierActive) {
|
||||
|
@ -254,6 +264,7 @@ TCPSocket.prototype = {
|
|||
onStopRequest: function ts_output_onStopRequest(request, context, status) {
|
||||
self._asyncCopierActive = false;
|
||||
self._multiplexStream.removeStream(0);
|
||||
self._sendBufferedAmount();
|
||||
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
// Note that we can/will get an error here as well as in the
|
||||
|
@ -280,7 +291,9 @@ TCPSocket.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
if (self._waitingForDrain) {
|
||||
// If we have a callback to update bufferedAmount, we let child to
|
||||
// decide whether ondrain should be dispatched.
|
||||
if (self._waitingForDrain && !self._onUpdateBufferedAmount) {
|
||||
self._waitingForDrain = false;
|
||||
self.callListener("drain");
|
||||
}
|
||||
|
@ -382,9 +395,36 @@ TCPSocket.prototype = {
|
|||
this.callListener(type);
|
||||
},
|
||||
|
||||
updateReadyStateAndBuffered: function ts_setReadyState(readyState, bufferedAmount) {
|
||||
/**
|
||||
* This method is expected to be called by TCPSocketChild to update child's
|
||||
* readyState.
|
||||
*/
|
||||
updateReadyState: function ts_updateReadyState(readyState) {
|
||||
if (!this._inChild) {
|
||||
LOG("Calling updateReadyState in parent, which should only be called " +
|
||||
"in child");
|
||||
return;
|
||||
}
|
||||
this._readyState = readyState;
|
||||
},
|
||||
|
||||
updateBufferedAmount: function ts_updateBufferedAmount(bufferedAmount, trackingNumber) {
|
||||
if (trackingNumber != this._trackingNumber) {
|
||||
LOG("updateBufferedAmount is called but trackingNumber is not matched " +
|
||||
"parent's trackingNumber: " + trackingNumber + ", child's trackingNumber: " +
|
||||
this._trackingNumber);
|
||||
return;
|
||||
}
|
||||
this._bufferedAmount = bufferedAmount;
|
||||
if (bufferedAmount == 0) {
|
||||
if (this._waitingForDrain) {
|
||||
this._waitingForDrain = false;
|
||||
this.callListener("drain");
|
||||
}
|
||||
} else {
|
||||
LOG("bufferedAmount is updated but haven't reaches zero. bufferedAmount: " +
|
||||
bufferedAmount);
|
||||
}
|
||||
},
|
||||
|
||||
createAcceptedParent: function ts_createAcceptedParent(transport, binaryType) {
|
||||
|
@ -420,6 +460,25 @@ TCPSocket.prototype = {
|
|||
#endif
|
||||
},
|
||||
|
||||
setOnUpdateBufferedAmountHandler: function(aFunction) {
|
||||
if (typeof(aFunction) == 'function') {
|
||||
this._onUpdateBufferedAmount = aFunction;
|
||||
} else {
|
||||
throw new Error("only function can be passed to " +
|
||||
"setOnUpdateBufferedAmountHandler");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the requst of sending data and update trackingNumber from
|
||||
* child.
|
||||
* This function is expected to be called by TCPSocketChild.
|
||||
*/
|
||||
onRecvSendFromChild: function(data, byteOffset, byteLength, trackingNumber) {
|
||||
this._trackingNumber = trackingNumber;
|
||||
this.send(data, byteOffset, byteLength);
|
||||
},
|
||||
|
||||
/* end nsITCPSocketInternal methods */
|
||||
|
||||
initWindowless: function ts_initWindowless() {
|
||||
|
@ -517,8 +576,8 @@ TCPSocket.prototype = {
|
|||
if (this._inChild) {
|
||||
that._socketBridge = Cc["@mozilla.org/tcp-socket-child;1"]
|
||||
.createInstance(Ci.nsITCPSocketChild);
|
||||
that._socketBridge.open(that, host, port, !!that._ssl,
|
||||
that._binaryType, this.useWin, this.useWin || this);
|
||||
that._socketBridge.sendOpen(that, host, port, !!that._ssl,
|
||||
that._binaryType, this.useWin, this.useWin || this);
|
||||
return that;
|
||||
}
|
||||
|
||||
|
@ -551,7 +610,7 @@ TCPSocket.prototype = {
|
|||
this._ssl = 'ssl';
|
||||
|
||||
if (this._inChild) {
|
||||
this._socketBridge.startTLS();
|
||||
this._socketBridge.sendStartTLS();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -585,7 +644,7 @@ TCPSocket.prototype = {
|
|||
this._readyState = kCLOSING;
|
||||
|
||||
if (this._inChild) {
|
||||
this._socketBridge.close();
|
||||
this._socketBridge.sendClose();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -605,15 +664,27 @@ TCPSocket.prototype = {
|
|||
}
|
||||
|
||||
if (this._inChild) {
|
||||
this._socketBridge.send(data, byteOffset, byteLength);
|
||||
this._socketBridge.sendSend(data, byteOffset, byteLength, ++this._trackingNumber);
|
||||
}
|
||||
|
||||
let length = this._binaryType === "arraybuffer" ? byteLength : data.length;
|
||||
let newBufferedAmount = this.bufferedAmount + length;
|
||||
let bufferFull = newBufferedAmount >= BUFFER_SIZE;
|
||||
|
||||
if (bufferFull) {
|
||||
// If we buffered more than some arbitrary amount of data,
|
||||
// (65535 right now) we should tell the caller so they can
|
||||
// wait until ondrain is called if they so desire. Once all the
|
||||
// buffered data has been written to the socket, ondrain is
|
||||
// called.
|
||||
this._waitingForDrain = true;
|
||||
}
|
||||
|
||||
var newBufferedAmount = this.bufferedAmount + length;
|
||||
var bufferNotFull = newBufferedAmount < BUFFER_SIZE;
|
||||
if (this._inChild) {
|
||||
return bufferNotFull;
|
||||
// In child, we just add buffer length to our bufferedAmount and let
|
||||
// parent to update our bufferedAmount when data have been sent.
|
||||
this._bufferedAmount = newBufferedAmount;
|
||||
return !bufferFull;
|
||||
}
|
||||
|
||||
let new_stream;
|
||||
|
@ -633,15 +704,6 @@ TCPSocket.prototype = {
|
|||
this._multiplexStream.appendStream(new_stream);
|
||||
}
|
||||
|
||||
if (newBufferedAmount >= BUFFER_SIZE) {
|
||||
// If we buffered more than some arbitrary amount of data,
|
||||
// (65535 right now) we should tell the caller so they can
|
||||
// wait until ondrain is called if they so desire. Once all the
|
||||
//buffered data has been written to the socket, ondrain is
|
||||
// called.
|
||||
this._waitingForDrain = true;
|
||||
}
|
||||
|
||||
this._ensureCopying();
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
|
@ -650,12 +712,12 @@ TCPSocket.prototype = {
|
|||
this._saveNetworkStats(false);
|
||||
#endif
|
||||
|
||||
return bufferNotFull;
|
||||
return !bufferFull;
|
||||
},
|
||||
|
||||
suspend: function ts_suspend() {
|
||||
if (this._inChild) {
|
||||
this._socketBridge.suspend();
|
||||
this._socketBridge.sendSuspend();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -668,7 +730,7 @@ TCPSocket.prototype = {
|
|||
|
||||
resume: function ts_resume() {
|
||||
if (this._inChild) {
|
||||
this._socketBridge.resume();
|
||||
this._socketBridge.sendResume();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,10 +77,11 @@ TCPSocketChild::TCPSocketChild()
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocketChild::Open(nsITCPSocketInternal* aSocket, const nsAString& aHost,
|
||||
uint16_t aPort, bool aUseSSL, const nsAString& aBinaryType,
|
||||
nsIDOMWindow* aWindow, const JS::Value& aWindowObj,
|
||||
JSContext* aCx)
|
||||
TCPSocketChild::SendOpen(nsITCPSocketInternal* aSocket,
|
||||
const nsAString& aHost, uint16_t aPort,
|
||||
bool aUseSSL, const nsAString& aBinaryType,
|
||||
nsIDOMWindow* aWindow, const JS::Value& aWindowObj,
|
||||
JSContext* aCx)
|
||||
{
|
||||
mSocket = aSocket;
|
||||
|
||||
|
@ -91,7 +92,8 @@ TCPSocketChild::Open(nsITCPSocketInternal* aSocket, const nsAString& aHost,
|
|||
}
|
||||
AddIPDLReference();
|
||||
gNeckoChild->SendPTCPSocketConstructor(this);
|
||||
SendOpen(nsString(aHost), aPort, aUseSSL, nsString(aBinaryType));
|
||||
PTCPSocketChild::SendOpen(nsString(aHost), aPort,
|
||||
aUseSSL, nsString(aBinaryType));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -115,13 +117,22 @@ TCPSocketChild::~TCPSocketChild()
|
|||
{
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketChild::RecvUpdateBufferedAmount(const uint32_t& aBuffered,
|
||||
const uint32_t& aTrackingNumber)
|
||||
{
|
||||
if (NS_FAILED(mSocket->UpdateBufferedAmount(aBuffered, aTrackingNumber))) {
|
||||
NS_ERROR("Shouldn't fail!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TCPSocketChild::RecvCallback(const nsString& aType,
|
||||
const CallbackData& aData,
|
||||
const nsString& aReadyState,
|
||||
const uint32_t& aBuffered)
|
||||
const nsString& aReadyState)
|
||||
{
|
||||
if (NS_FAILED(mSocket->UpdateReadyStateAndBuffered(aReadyState, aBuffered)))
|
||||
if (NS_FAILED(mSocket->UpdateReadyState(aReadyState)))
|
||||
NS_ERROR("Shouldn't fail!");
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
|
@ -159,46 +170,46 @@ TCPSocketChild::RecvCallback(const nsString& aType,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocketChild::StartTLS()
|
||||
TCPSocketChild::SendStartTLS()
|
||||
{
|
||||
SendStartTLS();
|
||||
PTCPSocketChild::SendStartTLS();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocketChild::Suspend()
|
||||
TCPSocketChild::SendSuspend()
|
||||
{
|
||||
SendSuspend();
|
||||
PTCPSocketChild::SendSuspend();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocketChild::Resume()
|
||||
TCPSocketChild::SendResume()
|
||||
{
|
||||
SendResume();
|
||||
PTCPSocketChild::SendResume();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocketChild::Close()
|
||||
TCPSocketChild::SendClose()
|
||||
{
|
||||
SendClose();
|
||||
PTCPSocketChild::SendClose();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocketChild::Send(const JS::Value& aData,
|
||||
uint32_t aByteOffset,
|
||||
uint32_t aByteLength,
|
||||
JSContext* aCx)
|
||||
TCPSocketChild::SendSend(const JS::Value& aData,
|
||||
uint32_t aByteOffset,
|
||||
uint32_t aByteLength,
|
||||
uint32_t aTrackingNumber,
|
||||
JSContext* aCx)
|
||||
{
|
||||
if (aData.isString()) {
|
||||
JSString* jsstr = aData.toString();
|
||||
nsDependentJSString str;
|
||||
bool ok = str.init(aCx, jsstr);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
||||
SendData(str);
|
||||
|
||||
SendData(str, aTrackingNumber);
|
||||
} else {
|
||||
NS_ENSURE_TRUE(aData.isObject(), NS_ERROR_FAILURE);
|
||||
JS::Rooted<JSObject*> obj(aCx, &aData.toObject());
|
||||
|
@ -216,15 +227,15 @@ TCPSocketChild::Send(const JS::Value& aData,
|
|||
}
|
||||
InfallibleTArray<uint8_t> arr;
|
||||
arr.SwapElements(fallibleArr);
|
||||
SendData(arr);
|
||||
SendData(arr, aTrackingNumber);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocketChild::SetSocketAndWindow(nsITCPSocketInternal *aSocket,
|
||||
const JS::Value& aWindowObj,
|
||||
JSContext* aCx)
|
||||
const JS::Value& aWindowObj,
|
||||
JSContext* aCx)
|
||||
{
|
||||
mSocket = aSocket;
|
||||
MOZ_ASSERT(aWindowObj.isObject());
|
||||
|
|
|
@ -44,9 +44,10 @@ public:
|
|||
|
||||
virtual bool RecvCallback(const nsString& aType,
|
||||
const CallbackData& aData,
|
||||
const nsString& aReadyState,
|
||||
const uint32_t& aBuffered) MOZ_OVERRIDE;
|
||||
const nsString& aReadyState) MOZ_OVERRIDE;
|
||||
virtual bool RecvRequestDelete() MOZ_OVERRIDE;
|
||||
virtual bool RecvUpdateBufferedAmount(const uint32_t& aBufferred,
|
||||
const uint32_t& aTrackingNumber) MOZ_OVERRIDE;
|
||||
private:
|
||||
JSObject* mWindowObj;
|
||||
};
|
||||
|
|
|
@ -35,7 +35,7 @@ FireInteralError(mozilla::net::PTCPSocketParent* aActor, uint32_t aLineNo)
|
|||
mozilla::unused <<
|
||||
aActor->SendCallback(NS_LITERAL_STRING("onerror"),
|
||||
TCPError(NS_LITERAL_STRING("InvalidStateError")),
|
||||
NS_LITERAL_STRING("connecting"), 0);
|
||||
NS_LITERAL_STRING("connecting"));
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_2(TCPSocketParentBase, mSocket, mIntermediary)
|
||||
|
@ -156,7 +156,8 @@ TCPSocketParent::RecvResume()
|
|||
}
|
||||
|
||||
bool
|
||||
TCPSocketParent::RecvData(const SendableData& aData)
|
||||
TCPSocketParent::RecvData(const SendableData& aData,
|
||||
const uint32_t& aTrackingNumber)
|
||||
{
|
||||
NS_ENSURE_TRUE(mIntermediary, true);
|
||||
|
||||
|
@ -168,13 +169,13 @@ TCPSocketParent::RecvData(const SendableData& aData)
|
|||
JS::Rooted<JS::Value> val(cx);
|
||||
JS::Rooted<JSObject*> obj(cx, mIntermediaryObj);
|
||||
IPC::DeserializeArrayBuffer(obj, aData.get_ArrayOfuint8_t(), &val);
|
||||
rv = mIntermediary->SendArrayBuffer(val);
|
||||
rv = mIntermediary->OnRecvSendArrayBuffer(val, aTrackingNumber);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case SendableData::TnsString:
|
||||
rv = mIntermediary->SendString(aData.get_nsString());
|
||||
rv = mIntermediary->OnRecvSendString(aData.get_nsString(), aTrackingNumber);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
break;
|
||||
|
||||
|
@ -194,9 +195,8 @@ TCPSocketParent::RecvClose()
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocketParent::SendCallback(const nsAString& aType, const JS::Value& aDataVal,
|
||||
const nsAString& aReadyState, uint32_t aBuffered,
|
||||
JSContext* aCx)
|
||||
TCPSocketParent::SendEvent(const nsAString& aType, const JS::Value& aDataVal,
|
||||
const nsAString& aReadyState, JSContext* aCx)
|
||||
{
|
||||
if (!mIPCOpen) {
|
||||
NS_WARNING("Dropping callback due to no IPC connection");
|
||||
|
@ -255,7 +255,7 @@ TCPSocketParent::SendCallback(const nsAString& aType, const JS::Value& aDataVal,
|
|||
}
|
||||
mozilla::unused <<
|
||||
PTCPSocketParent::SendCallback(nsString(aType), data,
|
||||
nsString(aReadyState), aBuffered);
|
||||
nsString(aReadyState));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -269,6 +269,15 @@ TCPSocketParent::SetSocketAndIntermediary(nsIDOMTCPSocket *socket,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocketParent::SendUpdateBufferedAmount(uint32_t aBufferedAmount,
|
||||
uint32_t aTrackingNumber)
|
||||
{
|
||||
mozilla::unused << PTCPSocketParent::SendUpdateBufferedAmount(aBufferedAmount,
|
||||
aTrackingNumber);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
TCPSocketParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
|
|
|
@ -51,7 +51,8 @@ public:
|
|||
virtual bool RecvSuspend() MOZ_OVERRIDE;
|
||||
virtual bool RecvResume() MOZ_OVERRIDE;
|
||||
virtual bool RecvClose() MOZ_OVERRIDE;
|
||||
virtual bool RecvData(const SendableData& aData) MOZ_OVERRIDE;
|
||||
virtual bool RecvData(const SendableData& aData,
|
||||
const uint32_t& aTrackingNumber) MOZ_OVERRIDE;
|
||||
virtual bool RecvRequestDelete() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
|
|
@ -20,15 +20,21 @@ TCPSocketParentIntermediary.prototype = {
|
|||
|
||||
// Create handlers for every possible callback that attempt to trigger
|
||||
// corresponding callbacks on the child object.
|
||||
["open", "drain", "data", "error", "close"].forEach(
|
||||
// ondrain event is not forwarded, since the decision of firing ondrain
|
||||
// is made in child.
|
||||
["open", "data", "error", "close"].forEach(
|
||||
function(p) {
|
||||
socket["on" + p] = function(data) {
|
||||
aParentSide.sendCallback(p, data.data, socket.readyState,
|
||||
socket.bufferedAmount);
|
||||
aParentSide.sendEvent(p, data.data, socket.readyState,
|
||||
socket.bufferedAmount);
|
||||
};
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
_onUpdateBufferedAmountHandler: function(aParentSide, aBufferedAmount, aTrackingNumber) {
|
||||
aParentSide.sendUpdateBufferedAmount(aBufferedAmount, aTrackingNumber);
|
||||
},
|
||||
|
||||
open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType, aAppId) {
|
||||
let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
|
||||
|
@ -41,6 +47,10 @@ TCPSocketParentIntermediary.prototype = {
|
|||
socketInternal.setAppId(aAppId);
|
||||
}
|
||||
|
||||
// Handle parent's request to update buffered amount.
|
||||
socketInternal.setOnUpdateBufferedAmountHandler(
|
||||
this._onUpdateBufferedAmountHandler.bind(this, aParentSide));
|
||||
|
||||
// Handlers are set to the JS-implemented socket object on the parent side.
|
||||
this._setCallbacks(aParentSide, socket);
|
||||
return socket;
|
||||
|
@ -79,12 +89,15 @@ TCPSocketParentIntermediary.prototype = {
|
|||
return serverSocket;
|
||||
},
|
||||
|
||||
sendString: function(aData) {
|
||||
return this._socket.send(aData);
|
||||
onRecvSendString: function(aData, aTrackingNumber) {
|
||||
let socketInternal = this._socket.QueryInterface(Ci.nsITCPSocketInternal);
|
||||
return socketInternal.onRecvSendFromChild(aData, 0, 0, aTrackingNumber);
|
||||
},
|
||||
|
||||
sendArrayBuffer: function(aData) {
|
||||
return this._socket.send(aData, 0, aData.byteLength);
|
||||
onRecvSendArrayBuffer: function(aData, aTrackingNumber) {
|
||||
let socketInternal = this._socket.QueryInterface(Ci.nsITCPSocketInternal);
|
||||
return socketInternal.onRecvSendFromChild(aData, 0, aData.byteLength,
|
||||
aTrackingNumber);
|
||||
},
|
||||
|
||||
classID: Components.ID("{afa42841-a6cb-4a91-912f-93099f6a3d18}"),
|
||||
|
|
|
@ -80,6 +80,11 @@ function get_platform() {
|
|||
return xulRuntime.OS;
|
||||
}
|
||||
|
||||
function is_content() {
|
||||
return this._inChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
|
||||
.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spin up a listening socket and associate at most one live, accepted socket
|
||||
* with ourselves.
|
||||
|
@ -418,9 +423,15 @@ function drainTwice() {
|
|||
['ondrain', 'ondrain2',
|
||||
'ondata', 'ondata2',
|
||||
'serverclose', 'clientclose']);
|
||||
let ondrainCalled = false,
|
||||
ondataCalled = false;
|
||||
|
||||
function maybeSendNextData() {
|
||||
if (!ondrainCalled || !ondataCalled) {
|
||||
// make sure server got data and client got ondrain.
|
||||
return;
|
||||
}
|
||||
|
||||
function serverSideCallback() {
|
||||
yays.ondata();
|
||||
server.ondata = makeExpectData(
|
||||
"ondata2", BIG_TYPED_ARRAY_2, false, yays.ondata2);
|
||||
|
||||
|
@ -433,12 +444,24 @@ function drainTwice() {
|
|||
sock.close();
|
||||
}
|
||||
|
||||
function clientOndrain() {
|
||||
yays.ondrain();
|
||||
ondrainCalled = true;
|
||||
maybeSendNextData();
|
||||
}
|
||||
|
||||
function serverSideCallback() {
|
||||
yays.ondata();
|
||||
ondataCalled = true;
|
||||
maybeSendNextData();
|
||||
}
|
||||
|
||||
server.onclose = yays.serverclose;
|
||||
server.ondata = makeExpectData(
|
||||
"ondata", BIG_TYPED_ARRAY, false, serverSideCallback);
|
||||
|
||||
sock.onclose = yays.clientclose;
|
||||
sock.ondrain = yays.ondrain;
|
||||
sock.ondrain = clientOndrain;
|
||||
|
||||
if (sock.send(BIG_ARRAY_BUFFER)) {
|
||||
throw new Error("sock.send(BIG_TYPED_ARRAY) did not return false to indicate buffering");
|
||||
|
@ -482,6 +505,62 @@ function bufferTwice() {
|
|||
}
|
||||
}
|
||||
|
||||
// Test child behavior when child thinks it's buffering but parent doesn't
|
||||
// buffer.
|
||||
// 1. set bufferedAmount of content socket to a value that will make next
|
||||
// send() call return false.
|
||||
// 2. send a small data to make send() return false, but it won't make
|
||||
// parent buffer.
|
||||
// 3. we should get a ondrain.
|
||||
function childbuffered() {
|
||||
let yays = makeJointSuccess(['ondrain', 'serverdata',
|
||||
'clientclose', 'serverclose']);
|
||||
sock.ondrain = function() {
|
||||
yays.ondrain();
|
||||
sock.close();
|
||||
};
|
||||
|
||||
server.ondata = makeExpectData(
|
||||
'ondata', DATA_ARRAY, false, yays.serverdata);
|
||||
|
||||
let internalSocket = sock.QueryInterface(Ci.nsITCPSocketInternal);
|
||||
internalSocket.updateBufferedAmount(65535, // almost reach buffering threshold
|
||||
0);
|
||||
if (sock.send(DATA_ARRAY_BUFFER)) {
|
||||
do_throw("expected sock.send to return false.");
|
||||
}
|
||||
|
||||
sock.onclose = yays.clientclose;
|
||||
server.onclose = yays.serverclose;
|
||||
}
|
||||
|
||||
// Test child's behavior when send() of child return true but parent buffers
|
||||
// data.
|
||||
// 1. send BIG_ARRAY to make parent buffer. This would make child wait for
|
||||
// drain as well.
|
||||
// 2. set child's bufferedAmount to zero, so child will no longer wait for
|
||||
// drain but parent will dispatch a drain event.
|
||||
// 3. wait for 1 second, to make sure there's no ondrain event dispatched in
|
||||
// child.
|
||||
function childnotbuffered() {
|
||||
let yays = makeJointSuccess(['serverdata', 'clientclose', 'serverclose']);
|
||||
server.ondata = makeExpectData('ondata', BIG_ARRAY, false, yays.serverdata);
|
||||
if (sock.send(BIG_ARRAY_BUFFER)) {
|
||||
do_throw("sock.send(BIG_TYPED_ARRAY) did not return false to indicate buffering");
|
||||
}
|
||||
let internalSocket = sock.QueryInterface(Ci.nsITCPSocketInternal);
|
||||
internalSocket.updateBufferedAmount(0, // setting zero will clear waitForDrain in sock.
|
||||
1);
|
||||
|
||||
// shouldn't get ondrain, even after parent have cleared its buffer.
|
||||
sock.ondrain = makeFailureCase('drain');
|
||||
sock.onclose = yays.clientclose;
|
||||
server.onclose = yays.serverclose;
|
||||
do_timeout(1000, function() {
|
||||
sock.close();
|
||||
});
|
||||
};
|
||||
|
||||
// - connect, data and events work both ways
|
||||
add_test(connectSock);
|
||||
add_test(sendData);
|
||||
|
@ -513,6 +592,14 @@ add_test(drainTwice);
|
|||
add_test(connectSock);
|
||||
add_test(bufferTwice);
|
||||
|
||||
if (is_content()) {
|
||||
add_test(connectSock);
|
||||
add_test(childnotbuffered);
|
||||
|
||||
add_test(connectSock);
|
||||
add_test(childbuffered);
|
||||
}
|
||||
|
||||
// clean up
|
||||
add_test(cleanup);
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Copyright © 2013 Deutsche Telekom, Inc. */
|
||||
|
||||
#include "MozNdefRecord.h"
|
||||
#include "mozilla/dom/MozNdefRecordBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(MozNdefRecord, mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(MozNdefRecord)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(MozNdefRecord)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MozNdefRecord)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
/* static */
|
||||
already_AddRefed<MozNdefRecord>
|
||||
MozNdefRecord::Constructor(const GlobalObject& aGlobal,
|
||||
uint8_t aTnf, const nsAString& aType,
|
||||
const nsAString& aId, const nsAString& aPayload,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!win) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
nsRefPtr<MozNdefRecord> ndefrecord =
|
||||
new MozNdefRecord(win, aTnf, aType, aId, aPayload);
|
||||
return ndefrecord.forget();
|
||||
}
|
||||
|
||||
MozNdefRecord::MozNdefRecord(nsPIDOMWindow* aWindow,
|
||||
uint8_t aTnf, const nsAString& aType,
|
||||
const nsAString& aId, const nsAString& aPayload)
|
||||
: mTnf(aTnf)
|
||||
, mType(aType)
|
||||
, mId(aId)
|
||||
, mPayload(aPayload)
|
||||
{
|
||||
mWindow = aWindow;
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
MozNdefRecord::~MozNdefRecord()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
MozNdefRecord::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
||||
{
|
||||
return MozNdefRecordBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,88 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Copyright © 2013 Deutsche Telekom, Inc. */
|
||||
|
||||
#ifndef mozilla_dom_MozNdefRecord_h__
|
||||
#define mozilla_dom_MozNdefRecord_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
|
||||
struct JSContext;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class MozNdefRecord MOZ_FINAL : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MozNdefRecord)
|
||||
|
||||
public:
|
||||
|
||||
MozNdefRecord(nsPIDOMWindow* aWindow,
|
||||
uint8_t aTnf, const nsAString& aType,
|
||||
const nsAString& aId, const nsAString& aPlayload);
|
||||
|
||||
~MozNdefRecord();
|
||||
|
||||
nsIDOMWindow* GetParentObject() const
|
||||
{
|
||||
return mWindow;
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
||||
static already_AddRefed<MozNdefRecord> Constructor(
|
||||
const GlobalObject& aGlobal,
|
||||
uint8_t aTnf, const nsAString& aType,
|
||||
const nsAString& aId,
|
||||
const nsAString& aPayload,
|
||||
ErrorResult& aRv);
|
||||
|
||||
uint8_t Tnf() const
|
||||
{
|
||||
return mTnf;
|
||||
}
|
||||
|
||||
void GetType(nsString& aType) const
|
||||
{
|
||||
aType = mType;
|
||||
}
|
||||
|
||||
void GetId(nsString& aId) const
|
||||
{
|
||||
aId = mId;
|
||||
}
|
||||
|
||||
void GetPayload(nsString& aPayload) const
|
||||
{
|
||||
aPayload = mPayload;
|
||||
}
|
||||
|
||||
private:
|
||||
MozNdefRecord() MOZ_DELETE;
|
||||
nsRefPtr<nsPIDOMWindow> mWindow;
|
||||
|
||||
uint8_t mTnf;
|
||||
nsString mType;
|
||||
nsString mId;
|
||||
nsString mPayload;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_MozNdefRecord_h__
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче