зеркало из 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/
|
# 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"
|
"repo_path": "/integration/gaia-central"
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,6 +189,9 @@
|
||||||
@BINPATH@/components/dom_geolocation.xpt
|
@BINPATH@/components/dom_geolocation.xpt
|
||||||
@BINPATH@/components/dom_media.xpt
|
@BINPATH@/components/dom_media.xpt
|
||||||
@BINPATH@/components/dom_network.xpt
|
@BINPATH@/components/dom_network.xpt
|
||||||
|
#ifdef MOZ_NFC
|
||||||
|
@BINPATH@/components/dom_nfc.xpt
|
||||||
|
#endif
|
||||||
@BINPATH@/components/dom_notification.xpt
|
@BINPATH@/components/dom_notification.xpt
|
||||||
@BINPATH@/components/dom_html.xpt
|
@BINPATH@/components/dom_html.xpt
|
||||||
@BINPATH@/components/dom_indexeddb.xpt
|
@BINPATH@/components/dom_indexeddb.xpt
|
||||||
|
@ -494,6 +497,13 @@
|
||||||
@BINPATH@/components/webvtt.xpt
|
@BINPATH@/components/webvtt.xpt
|
||||||
@BINPATH@/components/WebVTT.manifest
|
@BINPATH@/components/WebVTT.manifest
|
||||||
@BINPATH@/components/WebVTTParserWrapper.js
|
@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
|
#ifdef MOZ_ENABLE_DBUS
|
||||||
@BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
|
@BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1210,6 +1210,11 @@ pref("devtools.webconsole.fontSize", 0);
|
||||||
// be cleared each time page navigation happens.
|
// be cleared each time page navigation happens.
|
||||||
pref("devtools.webconsole.persistlog", false);
|
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,
|
// The number of lines that are displayed in the web console for the Net,
|
||||||
// CSS, JS and Web Developer categories.
|
// CSS, JS and Web Developer categories.
|
||||||
pref("devtools.hud.loglimit.network", 200);
|
pref("devtools.hud.loglimit.network", 200);
|
||||||
|
|
|
@ -731,10 +731,6 @@ var gPluginHandler = {
|
||||||
let principal = contentWindow.document.nodePrincipal;
|
let principal = contentWindow.document.nodePrincipal;
|
||||||
Services.perms.addFromPrincipal(principal, aPluginInfo.permissionString,
|
Services.perms.addFromPrincipal(principal, aPluginInfo.permissionString,
|
||||||
permission, expireType, expireTime);
|
permission, expireType, expireTime);
|
||||||
|
|
||||||
if (aNewState == "block") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manually activate the plugins that would have been automatically
|
// Manually activate the plugins that would have been automatically
|
||||||
|
@ -752,19 +748,23 @@ var gPluginHandler = {
|
||||||
}
|
}
|
||||||
if (aPluginInfo.permissionString == pluginHost.getPermissionStringForType(plugin.actualType)) {
|
if (aPluginInfo.permissionString == pluginHost.getPermissionStringForType(plugin.actualType)) {
|
||||||
pluginFound = true;
|
pluginFound = true;
|
||||||
if (gPluginHandler.canActivatePlugin(plugin)) {
|
if (aNewState == "block") {
|
||||||
let overlay = this.getPluginUI(plugin, "main");
|
plugin.reload(true);
|
||||||
if (overlay) {
|
} else {
|
||||||
overlay.removeEventListener("click", gPluginHandler._overlayClickListener, true);
|
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
|
// 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.
|
// user probably needs is for us to allow and then refresh.
|
||||||
if (!pluginFound) {
|
if (aNewState != "block" && !pluginFound) {
|
||||||
browser.reload();
|
browser.reload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1514,6 +1514,7 @@ SocialStatus = {
|
||||||
"class": "social-panel-frame",
|
"class": "social-panel-frame",
|
||||||
"id": notificationFrameId,
|
"id": notificationFrameId,
|
||||||
"tooltip": "aHTMLTooltip",
|
"tooltip": "aHTMLTooltip",
|
||||||
|
"context": "contentAreaContextMenu",
|
||||||
|
|
||||||
// work around bug 793057 - by making the panel roughly the final size
|
// work around bug 793057 - by making the panel roughly the final size
|
||||||
// we are more likely to have the anchor in the correct position.
|
// 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");
|
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() {
|
function test() {
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
registerCleanupFunction(function() {
|
registerCleanupFunction(function() {
|
||||||
|
|
|
@ -9,52 +9,6 @@ var gRunNextTestAfterPluginRemoved = false;
|
||||||
|
|
||||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
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() {
|
function test() {
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
registerCleanupFunction(function() {
|
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");
|
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() {
|
function test() {
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
registerCleanupFunction(function() {
|
registerCleanupFunction(function() {
|
||||||
|
|
|
@ -90,8 +90,12 @@ function test() {
|
||||||
|
|
||||||
function doTest(aIsPrivateMode, aWindow, aCallback) {
|
function doTest(aIsPrivateMode, aWindow, aCallback) {
|
||||||
aWindow.gBrowser.addEventListener("pageshow", function pageShown(event) {
|
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;
|
return;
|
||||||
|
}
|
||||||
aWindow.gBrowser.removeEventListener("pageshow", pageShown);
|
aWindow.gBrowser.removeEventListener("pageshow", pageShown);
|
||||||
|
|
||||||
executeSoon(function () {
|
executeSoon(function () {
|
||||||
|
|
|
@ -93,7 +93,7 @@ let MessageListener = {
|
||||||
receiveMessage: function ({name, data: {id}}) {
|
receiveMessage: function ({name, data: {id}}) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "SessionStore:collectSessionHistory":
|
case "SessionStore:collectSessionHistory":
|
||||||
let history = SessionHistory.read(docShell);
|
let history = SessionHistory.collect(docShell);
|
||||||
if ("index" in history) {
|
if ("index" in history) {
|
||||||
let tabIndex = history.index - 1;
|
let tabIndex = history.index - 1;
|
||||||
// Don't include private data. It's only needed when duplicating
|
// Don't include private data. It's only needed when duplicating
|
||||||
|
@ -141,7 +141,7 @@ let SyncHandler = {
|
||||||
},
|
},
|
||||||
|
|
||||||
collectSessionHistory: function (includePrivateData) {
|
collectSessionHistory: function (includePrivateData) {
|
||||||
let history = SessionHistory.read(docShell);
|
let history = SessionHistory.collect(docShell);
|
||||||
if ("index" in history) {
|
if ("index" in history) {
|
||||||
let tabIndex = history.index - 1;
|
let tabIndex = history.index - 1;
|
||||||
TextAndScrollData.updateFrame(history.entries[tabIndex],
|
TextAndScrollData.updateFrame(history.entries[tabIndex],
|
||||||
|
|
|
@ -15,6 +15,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
|
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
|
||||||
"resource:///modules/sessionstore/PrivacyLevel.jsm");
|
"resource:///modules/sessionstore/PrivacyLevel.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Utils",
|
||||||
|
"resource:///modules/sessionstore/Utils.jsm");
|
||||||
|
|
||||||
function debug(msg) {
|
function debug(msg) {
|
||||||
Services.console.logStringMessage("SessionHistory: " + msg);
|
Services.console.logStringMessage("SessionHistory: " + msg);
|
||||||
|
@ -36,8 +38,12 @@ XPCOMUtils.defineLazyGetter(this, "gPostData", function () {
|
||||||
* The external API exported by this module.
|
* The external API exported by this module.
|
||||||
*/
|
*/
|
||||||
this.SessionHistory = Object.freeze({
|
this.SessionHistory = Object.freeze({
|
||||||
read: function (docShell, includePrivateData) {
|
collect: function (docShell, includePrivateData) {
|
||||||
return SessionHistoryInternal.read(docShell, includePrivateData);
|
return SessionHistoryInternal.collect(docShell, includePrivateData);
|
||||||
|
},
|
||||||
|
|
||||||
|
restore: function (docShell, tabData) {
|
||||||
|
SessionHistoryInternal.restore(docShell, tabData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -53,7 +59,7 @@ let SessionHistoryInternal = {
|
||||||
* @param includePrivateData (optional)
|
* @param includePrivateData (optional)
|
||||||
* True to always include private data and skip any privacy checks.
|
* 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 data = {entries: []};
|
||||||
let isPinned = docShell.isAppTab;
|
let isPinned = docShell.isAppTab;
|
||||||
let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
|
let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||||
|
@ -63,7 +69,7 @@ let SessionHistoryInternal = {
|
||||||
try {
|
try {
|
||||||
for (let i = 0; i < history.count; i++) {
|
for (let i = 0; i < history.count; i++) {
|
||||||
let shEntry = history.getEntryAtIndex(i, false);
|
let shEntry = history.getEntryAtIndex(i, false);
|
||||||
let entry = this._serializeEntry(shEntry, includePrivateData, isPinned);
|
let entry = this.serializeEntry(shEntry, includePrivateData, isPinned);
|
||||||
data.entries.push(entry);
|
data.entries.push(entry);
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
@ -109,7 +115,7 @@ let SessionHistoryInternal = {
|
||||||
* The tab is pinned and should be treated differently for privacy.
|
* The tab is pinned and should be treated differently for privacy.
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
_serializeEntry: function (shEntry, includePrivateData, isPinned) {
|
serializeEntry: function (shEntry, includePrivateData, isPinned) {
|
||||||
let entry = { url: shEntry.URI.spec };
|
let entry = { url: shEntry.URI.spec };
|
||||||
|
|
||||||
// Save some bytes and don't include the title property
|
// Save some bytes and don't include the title property
|
||||||
|
@ -152,7 +158,7 @@ let SessionHistoryInternal = {
|
||||||
|
|
||||||
// Collect post data for the current history entry.
|
// Collect post data for the current history entry.
|
||||||
try {
|
try {
|
||||||
let postdata = this._serializePostData(shEntry, isPinned);
|
let postdata = this.serializePostData(shEntry, isPinned);
|
||||||
if (postdata) {
|
if (postdata) {
|
||||||
entry.postdata_b64 = postdata;
|
entry.postdata_b64 = postdata;
|
||||||
}
|
}
|
||||||
|
@ -163,7 +169,7 @@ let SessionHistoryInternal = {
|
||||||
|
|
||||||
// Collect owner data for the current history entry.
|
// Collect owner data for the current history entry.
|
||||||
try {
|
try {
|
||||||
let owner = this._serializeOwner(shEntry);
|
let owner = this.serializeOwner(shEntry);
|
||||||
if (owner) {
|
if (owner) {
|
||||||
entry.owner_b64 = owner;
|
entry.owner_b64 = owner;
|
||||||
}
|
}
|
||||||
|
@ -197,7 +203,7 @@ let SessionHistoryInternal = {
|
||||||
break;
|
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.
|
* Whether the docShell is owned by a pinned tab.
|
||||||
* @return The base64 encoded post data.
|
* @return The base64 encoded post data.
|
||||||
*/
|
*/
|
||||||
_serializePostData: function (shEntry, isPinned) {
|
serializePostData: function (shEntry, isPinned) {
|
||||||
let isHttps = shEntry.URI.schemeIs("https");
|
let isHttps = shEntry.URI.schemeIs("https");
|
||||||
if (!shEntry.postData || !gPostData ||
|
if (!shEntry.postData || !gPostData ||
|
||||||
!PrivacyLevel.canSave({isHttps: isHttps, isPinned: isPinned})) {
|
!PrivacyLevel.canSave({isHttps: isHttps, isPinned: isPinned})) {
|
||||||
|
@ -250,7 +256,7 @@ let SessionHistoryInternal = {
|
||||||
* The session history entry.
|
* The session history entry.
|
||||||
* @return The base64 encoded owner data.
|
* @return The base64 encoded owner data.
|
||||||
*/
|
*/
|
||||||
_serializeOwner: function (shEntry) {
|
serializeOwner: function (shEntry) {
|
||||||
if (!shEntry.owner) {
|
if (!shEntry.owner) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -274,5 +280,166 @@ let SessionHistoryInternal = {
|
||||||
// is guaranteed to handle all chars in strings, including embedded
|
// is guaranteed to handle all chars in strings, including embedded
|
||||||
// nulls.
|
// nulls.
|
||||||
return btoa(String.fromCharCode.apply(null, ownerBytes));
|
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");
|
"resource:///modules/sessionstore/TextAndScrollData.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
|
XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
|
||||||
"resource:///modules/sessionstore/SessionFile.jsm");
|
"resource:///modules/sessionstore/SessionFile.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "SessionHistory",
|
||||||
|
"resource:///modules/sessionstore/SessionHistory.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "TabAttributes",
|
XPCOMUtils.defineLazyModuleGetter(this, "TabAttributes",
|
||||||
"resource:///modules/sessionstore/TabAttributes.jsm");
|
"resource:///modules/sessionstore/TabAttributes.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "TabState",
|
XPCOMUtils.defineLazyModuleGetter(this, "TabState",
|
||||||
|
@ -1346,10 +1348,10 @@ let SessionStoreInternal = {
|
||||||
let tab = aWindow.gBrowser.selectedTab;
|
let tab = aWindow.gBrowser.selectedTab;
|
||||||
// If __SS_restoreState is still on the browser and it is
|
// If __SS_restoreState is still on the browser and it is
|
||||||
// TAB_STATE_NEEDS_RESTORE, then then we haven't restored
|
// 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 &&
|
if (tab.linkedBrowser.__SS_restoreState &&
|
||||||
tab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
|
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
|
// attempt to update the current URL we send in a crash report
|
||||||
this._updateCrashReportURL(aWindow);
|
this._updateCrashReportURL(aWindow);
|
||||||
|
@ -1477,7 +1479,7 @@ let SessionStoreInternal = {
|
||||||
// Remove the tab state from the cache.
|
// Remove the tab state from the cache.
|
||||||
// Note that we cannot simply replace the contents of the cache
|
// Note that we cannot simply replace the contents of the cache
|
||||||
// as |aState| can be an incomplete state that will be completed
|
// as |aState| can be an incomplete state that will be completed
|
||||||
// by |restoreHistoryPrecursor|.
|
// by |restoreTabs|.
|
||||||
let tabState = JSON.parse(aState);
|
let tabState = JSON.parse(aState);
|
||||||
if (!tabState) {
|
if (!tabState) {
|
||||||
debug("Empty state argument");
|
debug("Empty state argument");
|
||||||
|
@ -1508,7 +1510,7 @@ let SessionStoreInternal = {
|
||||||
|
|
||||||
TabStateCache.delete(aTab);
|
TabStateCache.delete(aTab);
|
||||||
this._setWindowStateBusy(window);
|
this._setWindowStateBusy(window);
|
||||||
this.restoreHistoryPrecursor(window, [aTab], [tabState], 0);
|
this.restoreTabs(window, [aTab], [tabState], 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
duplicateTab: function ssi_duplicateTab(aWindow, aTab, aDelta = 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(null, {relatedToCurrent: true, ownerTab: aTab}) :
|
||||||
aWindow.gBrowser.addTab();
|
aWindow.gBrowser.addTab();
|
||||||
|
|
||||||
this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0,
|
this.restoreTabs(aWindow, [newTab], [tabState], 0,
|
||||||
true /* Load this tab right away. */);
|
true /* Load this tab right away. */);
|
||||||
|
|
||||||
return newTab;
|
return newTab;
|
||||||
},
|
},
|
||||||
|
@ -1608,7 +1610,7 @@ let SessionStoreInternal = {
|
||||||
let tab = tabbrowser.addTab();
|
let tab = tabbrowser.addTab();
|
||||||
|
|
||||||
// restore tab content
|
// restore tab content
|
||||||
this.restoreHistoryPrecursor(aWindow, [tab], [closedTabState], 1);
|
this.restoreTabs(aWindow, [tab], [closedTabState], 1);
|
||||||
|
|
||||||
// restore the tab's position
|
// restore the tab's position
|
||||||
tabbrowser.moveTabTo(tab, closedTab.pos);
|
tabbrowser.moveTabTo(tab, closedTab.pos);
|
||||||
|
@ -2227,7 +2229,7 @@ let SessionStoreInternal = {
|
||||||
|
|
||||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_RESTORE_WINDOW_MS");
|
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.
|
// for this window, so make sure we send the SSWindowStateBusy event.
|
||||||
this._setWindowStateBusy(aWindow);
|
this._setWindowStateBusy(aWindow);
|
||||||
|
|
||||||
|
@ -2325,7 +2327,7 @@ let SessionStoreInternal = {
|
||||||
// If overwriting tabs, we want to reset each tab's "restoring" state. Since
|
// 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
|
// 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
|
// 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.
|
// We also want to invalidate any cached information on the tab state.
|
||||||
if (overwriteTabs) {
|
if (overwriteTabs) {
|
||||||
for (let i = 0; i < tabbrowser.tabs.length; i++) {
|
for (let i = 0; i < tabbrowser.tabs.length; i++) {
|
||||||
|
@ -2380,7 +2382,7 @@ let SessionStoreInternal = {
|
||||||
this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs || [];
|
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));
|
(overwriteTabs ? (parseInt(winData.selected) || 1) : 0));
|
||||||
|
|
||||||
if (aState.scratchpads) {
|
if (aState.scratchpads) {
|
||||||
|
@ -2474,7 +2476,7 @@ let SessionStoreInternal = {
|
||||||
|
|
||||||
return [aTabs, aTabData];
|
return [aTabs, aTabData];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage history restoration for a window
|
* Manage history restoration for a window
|
||||||
* @param aWindow
|
* @param aWindow
|
||||||
|
@ -2489,9 +2491,8 @@ let SessionStoreInternal = {
|
||||||
* Flag to indicate whether the given set of tabs aTabs should be
|
* Flag to indicate whether the given set of tabs aTabs should be
|
||||||
* restored/loaded immediately even if restore_on_demand = true
|
* restored/loaded immediately even if restore_on_demand = true
|
||||||
*/
|
*/
|
||||||
restoreHistoryPrecursor:
|
restoreTabs: function (aWindow, aTabs, aTabData, aSelectTab,
|
||||||
function ssi_restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab,
|
aRestoreImmediately = false)
|
||||||
aRestoreImmediately = false)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
var tabbrowser = aWindow.gBrowser;
|
var tabbrowser = aWindow.gBrowser;
|
||||||
|
@ -2553,6 +2554,17 @@ let SessionStoreInternal = {
|
||||||
// out of date now that we're restoring it.
|
// out of date now that we're restoring it.
|
||||||
TabState.dropPendingCollections(tab);
|
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;
|
browser.__SS_tabStillLoading = true;
|
||||||
|
|
||||||
// keep the data around to prevent dataloss in case
|
// keep the data around to prevent dataloss in case
|
||||||
|
@ -2562,11 +2574,7 @@ let SessionStoreInternal = {
|
||||||
browser.setAttribute("pending", "true");
|
browser.setAttribute("pending", "true");
|
||||||
tab.setAttribute("pending", "true");
|
tab.setAttribute("pending", "true");
|
||||||
|
|
||||||
// Make sure that set/getTabValue will set/read the correct data by
|
if (tabData.entries.length == 0) {
|
||||||
// wiping out any current value in tab.__SS_extdata.
|
|
||||||
delete tab.__SS_extdata;
|
|
||||||
|
|
||||||
if (!tabData.entries || tabData.entries.length == 0) {
|
|
||||||
// make sure to blank out this tab's content
|
// make sure to blank out this tab's content
|
||||||
// (just purging the tab's history won't be enough)
|
// (just purging the tab's history won't be enough)
|
||||||
browser.loadURIWithFlags("about:blank",
|
browser.loadURIWithFlags("about:blank",
|
||||||
|
@ -2601,114 +2609,84 @@ let SessionStoreInternal = {
|
||||||
tab.crop = "center";
|
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
|
function restoreNextHistory() {
|
||||||
// identifiers.
|
if (aWindow.closed) {
|
||||||
let idMap = { used: {} };
|
return;
|
||||||
let docIdentMap = {};
|
}
|
||||||
this.restoreHistory(aWindow, aTabs, aTabData, idMap, docIdentMap,
|
|
||||||
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
* Restore history for a list of tabs.
|
||||||
* @param aWindow
|
* @param window
|
||||||
* Window reference
|
* Window reference
|
||||||
* @param aTabs
|
* @param tab
|
||||||
* Array of tab references
|
* Tab to be restored
|
||||||
* @param aTabData
|
* @param tabData
|
||||||
* Array of tab data
|
* Tab data to restore
|
||||||
* @param aIdMap
|
* @param restoreImmediately
|
||||||
* Hash for ensuring unique frame IDs
|
|
||||||
* @param aRestoreImmediately
|
|
||||||
* Flag to indicate whether the given set of tabs aTabs should be
|
* Flag to indicate whether the given set of tabs aTabs should be
|
||||||
* restored/loaded immediately even if restore_on_demand = true
|
* restored/loaded immediately even if restore_on_demand = true
|
||||||
*/
|
*/
|
||||||
restoreHistory:
|
restoreHistory: function (window, tab, tabData, restoreImmediately) {
|
||||||
function ssi_restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap,
|
let browser = tab.linkedBrowser;
|
||||||
aRestoreImmediately)
|
let history = browser.webNavigation.sessionHistory;
|
||||||
{
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
browser.__SS_shistoryListener = new SessionStoreSHistoryListener(tab);
|
browser.__SS_shistoryListener = new SessionStoreSHistoryListener(tab);
|
||||||
history.addSHistoryListener(browser.__SS_shistoryListener);
|
history.addSHistoryListener(browser.__SS_shistoryListener);
|
||||||
|
|
||||||
if (!tabData.entries) {
|
SessionHistory.restore(browser.docShell, tabData);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure to reset the capabilities and attributes, in case this tab gets reused
|
// make sure to reset the capabilities and attributes, in case this tab gets reused
|
||||||
let disallow = new Set(tabData.disallow && tabData.disallow.split(","));
|
let disallow = new Set(tabData.disallow && tabData.disallow.split(","));
|
||||||
DocShellCapabilities.restore(browser.docShell, disallow);
|
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)
|
if (tabData.storage && browser.docShell instanceof Ci.nsIDocShell)
|
||||||
SessionStorage.deserialize(browser.docShell, tabData.storage);
|
SessionStorage.deserialize(browser.docShell, tabData.storage);
|
||||||
|
|
||||||
// notify the tabbrowser that the tab chrome has been restored
|
// 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);
|
event.initEvent("SSTabRestoring", true, false);
|
||||||
tab.dispatchEvent(event);
|
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
|
// This could cause us to ignore MAX_CONCURRENT_TAB_RESTORES a bit, but
|
||||||
// it ensures each window will have its selected tab loaded.
|
// it ensures each window will have its selected tab loaded.
|
||||||
if (aRestoreImmediately || aWindow.gBrowser.selectedBrowser == browser) {
|
if (restoreImmediately || window.gBrowser.selectedBrowser == browser) {
|
||||||
this.restoreTab(tab);
|
this.restoreTabContent(tab);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
TabRestoreQueue.add(tab);
|
TabRestoreQueue.add(tab);
|
||||||
this.restoreNextTab();
|
this.restoreNextTab();
|
||||||
}
|
}
|
||||||
|
@ -2731,7 +2709,7 @@ let SessionStoreInternal = {
|
||||||
*
|
*
|
||||||
* @returns true/false indicating whether or not a load actually happened
|
* @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 window = aTab.ownerDocument.defaultView;
|
||||||
let browser = aTab.linkedBrowser;
|
let browser = aTab.linkedBrowser;
|
||||||
let tabData = browser.__SS_data;
|
let tabData = browser.__SS_data;
|
||||||
|
@ -2834,7 +2812,7 @@ let SessionStoreInternal = {
|
||||||
|
|
||||||
let tab = TabRestoreQueue.shift();
|
let tab = TabRestoreQueue.shift();
|
||||||
if (tab) {
|
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
|
// If we don't start a load in the restored tab (eg, no entries) then we
|
||||||
// want to attempt to restore the next tab.
|
// want to attempt to restore the next tab.
|
||||||
if (!didStartLoad)
|
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
|
* Accumulates a list of frames that need to be restored for the
|
||||||
* given browser element. A frame is only restored if its current
|
* 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.
|
* Determine if we can restore history into this tab.
|
||||||
* This will be false when a tab has been removed (usually between
|
* 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.
|
* as loading.
|
||||||
*
|
*
|
||||||
* @param aTab
|
* @param aTab
|
||||||
|
@ -3835,11 +3683,11 @@ let SessionStoreInternal = {
|
||||||
}
|
}
|
||||||
else if (previousState == TAB_STATE_NEEDS_RESTORE) {
|
else if (previousState == TAB_STATE_NEEDS_RESTORE) {
|
||||||
// Make sure the session history listener is removed. This is normally
|
// 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);
|
this._removeSHistoryListener(aTab);
|
||||||
|
|
||||||
// Make sure that the tab is removed from the list of tabs to restore.
|
// 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.
|
// for this tab.
|
||||||
TabRestoreQueue.remove(aTab);
|
TabRestoreQueue.remove(aTab);
|
||||||
}
|
}
|
||||||
|
@ -4129,9 +3977,9 @@ SessionStoreSHistoryListener.prototype = {
|
||||||
},
|
},
|
||||||
OnHistoryReload: function(aReloadURI, aReloadFlags) {
|
OnHistoryReload: function(aReloadURI, aReloadFlags) {
|
||||||
// On reload, we want to make sure that session history loads the right
|
// 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.
|
// 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.
|
// Returning false will stop the load that docshell is attempting.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -404,7 +404,8 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||||
|
|
||||||
DebuggerController.SourceScripts.togglePrettyPrint(source)
|
DebuggerController.SourceScripts.togglePrettyPrint(source)
|
||||||
.then(resetEditor, printError)
|
.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-09.js]
|
||||||
[browser_dbg_pretty-print-10.js]
|
[browser_dbg_pretty-print-10.js]
|
||||||
[browser_dbg_pretty-print-11.js]
|
[browser_dbg_pretty-print-11.js]
|
||||||
|
[browser_dbg_pretty-print-12.js]
|
||||||
[browser_dbg_progress-listener-bug.js]
|
[browser_dbg_progress-listener-bug.js]
|
||||||
[browser_dbg_reload-preferred-script-01.js]
|
[browser_dbg_reload-preferred-script-01.js]
|
||||||
[browser_dbg_reload-preferred-script-02.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;"
|
<checkbox label="&options.enablePersistentLogging.label;"
|
||||||
tooltiptext="&options.enablePersistentLogging.tooltip;"
|
tooltiptext="&options.enablePersistentLogging.tooltip;"
|
||||||
data-pref="devtools.webconsole.persistlog"/>
|
data-pref="devtools.webconsole.persistlog"/>
|
||||||
|
<checkbox id="webconsole-timestamp-messages"
|
||||||
|
label="&options.timestampMessages.label;"
|
||||||
|
tooltiptext="&options.timestampMessages.tooltip;"
|
||||||
|
data-pref="devtools.webconsole.timestampMessages"/>
|
||||||
</vbox>
|
</vbox>
|
||||||
<label value="&options.profiler.label;"/>
|
<label value="&options.profiler.label;"/>
|
||||||
<vbox id="profiler-options" class="options-groupbox">
|
<vbox id="profiler-options" class="options-groupbox">
|
||||||
|
|
|
@ -239,3 +239,4 @@ skip-if = os == "linux"
|
||||||
[browser_webconsole_view_source.js]
|
[browser_webconsole_view_source.js]
|
||||||
[browser_webconsole_reflow.js]
|
[browser_webconsole_reflow.js]
|
||||||
[browser_webconsole_log_file_filter.js]
|
[browser_webconsole_log_file_filter.js]
|
||||||
|
[browser_webconsole_expandable_timestamps.js]
|
||||||
|
|
|
@ -22,6 +22,13 @@ function testFilterButtons(aHud) {
|
||||||
testMenuFilterButton("css");
|
testMenuFilterButton("css");
|
||||||
testMenuFilterButton("js");
|
testMenuFilterButton("js");
|
||||||
testMenuFilterButton("logging");
|
testMenuFilterButton("logging");
|
||||||
|
testMenuFilterButton("security");
|
||||||
|
|
||||||
|
testIsolateFilterButton("net");
|
||||||
|
testIsolateFilterButton("css");
|
||||||
|
testIsolateFilterButton("js");
|
||||||
|
testIsolateFilterButton("logging");
|
||||||
|
testIsolateFilterButton("security");
|
||||||
|
|
||||||
finishTest();
|
finishTest();
|
||||||
}
|
}
|
||||||
|
@ -72,15 +79,7 @@ function testMenuFilterButton(aCategory) {
|
||||||
"checked after turning off its first menu item");
|
"checked after turning off its first menu item");
|
||||||
|
|
||||||
// Turn all the filters off by clicking the main part of the button.
|
// Turn all the filters off by clicking the main part of the button.
|
||||||
let anonymousNodes = hud.ui.document.getAnonymousNodes(button);
|
let subbutton = getMainButton(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ok(subbutton, "we have the subbutton for category " + aCategory);
|
ok(subbutton, "we have the subbutton for category " + aCategory);
|
||||||
|
|
||||||
clickButton(subbutton);
|
clickButton(subbutton);
|
||||||
|
@ -129,10 +128,81 @@ function testMenuFilterButton(aCategory) {
|
||||||
clickButton(subbutton);
|
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) {
|
function clickButton(aNode) {
|
||||||
EventUtils.sendMouseEvent({ type: "click" }, aNode);
|
EventUtils.sendMouseEvent({ type: "click" }, aNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function altClickButton(aNode) {
|
||||||
|
EventUtils.sendMouseEvent({ type: "click", altKey: true }, aNode);
|
||||||
|
}
|
||||||
|
|
||||||
function chooseMenuItem(aNode) {
|
function chooseMenuItem(aNode) {
|
||||||
let event = document.createEvent("XULCommandEvent");
|
let event = document.createEvent("XULCommandEvent");
|
||||||
event.initCommandEvent("command", true, true, window, 0, false, false, false,
|
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_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
|
||||||
const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
|
const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
|
||||||
|
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A WebConsoleFrame instance is an interactive console initialized *per target*
|
* A WebConsoleFrame instance is an interactive console initialized *per target*
|
||||||
|
@ -201,6 +202,7 @@ function WebConsoleFrame(aWebConsoleOwner)
|
||||||
this._toggleFilter = this._toggleFilter.bind(this);
|
this._toggleFilter = this._toggleFilter.bind(this);
|
||||||
this._onPanelSelected = this._onPanelSelected.bind(this);
|
this._onPanelSelected = this._onPanelSelected.bind(this);
|
||||||
this._flushMessageQueue = this._flushMessageQueue.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._outputTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||||
this._outputTimerInitialized = false;
|
this._outputTimerInitialized = false;
|
||||||
|
@ -571,6 +573,13 @@ WebConsoleFrame.prototype = {
|
||||||
if (toolbox) {
|
if (toolbox) {
|
||||||
toolbox.on("webconsole-selected", this._onPanelSelected);
|
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;
|
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";
|
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);
|
target.setAttribute("checked", state);
|
||||||
|
|
||||||
// This is a filter button with a drop-down, and the user clicked the
|
// 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
|
// main part of the button. Go through all the severities and toggle
|
||||||
// their associated filters.
|
// their associated filters.
|
||||||
let menuItems = target.querySelectorAll("menuitem");
|
this._setMenuState(target, state);
|
||||||
for (let i = 0; i < menuItems.length; i++) {
|
|
||||||
menuItems[i].setAttribute("checked", state);
|
|
||||||
let prefKey = menuItems[i].getAttribute("prefKey");
|
|
||||||
this.setFilterState(prefKey, state);
|
|
||||||
}
|
|
||||||
break;
|
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.
|
* Set the filter state for a specific toggle button.
|
||||||
*
|
*
|
||||||
|
@ -2775,6 +2811,29 @@ WebConsoleFrame.prototype = {
|
||||||
}, false);
|
}, 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.
|
* Copies the selected items to the system clipboard.
|
||||||
*
|
*
|
||||||
|
@ -2885,6 +2944,8 @@ WebConsoleFrame.prototype = {
|
||||||
toolbox.off("webconsole-selected", this._onPanelSelected);
|
toolbox.off("webconsole-selected", this._onPanelSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gDevTools.off("pref-changed", this._onToolboxPrefChanged);
|
||||||
|
|
||||||
this._repeatNodes = {};
|
this._repeatNodes = {};
|
||||||
this._outputQueue = [];
|
this._outputQueue = [];
|
||||||
this._pruneCategoriesQueue = {};
|
this._pruneCategoriesQueue = {};
|
||||||
|
|
|
@ -48,8 +48,8 @@ function goUpdateConsoleCommands() {
|
||||||
<command id="cmd_close" oncommand="goDoCommand('cmd_close');" disabled="true"/>
|
<command id="cmd_close" oncommand="goDoCommand('cmd_close');" disabled="true"/>
|
||||||
</commandset>
|
</commandset>
|
||||||
<keyset id="consoleKeys">
|
<keyset id="consoleKeys">
|
||||||
<key id="key_fullZoomReduce" key="&fullZoomReduceCmd.commandkey;" 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 key="&fullZoomReduceCmd.commandkey2;" command="cmd_fullZoomReduce" modifiers="accel"/>
|
||||||
<key id="key_fullZoomEnlarge" key="&fullZoomEnlargeCmd.commandkey;" command="cmd_fullZoomEnlarge" 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.commandkey2;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
|
||||||
<key key="&fullZoomEnlargeCmd.commandkey3;" 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.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">
|
<!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
|
<!-- LOCALIZATION NOTE (options.profiler.label): This is the label for the
|
||||||
- heading of the group of JavaScript Profiler preferences in the options
|
- heading of the group of JavaScript Profiler preferences in the options
|
||||||
- panel. -->
|
- panel. -->
|
||||||
|
|
|
@ -170,9 +170,8 @@ var ContextCommands = {
|
||||||
// Link specific
|
// Link specific
|
||||||
|
|
||||||
openLinkInNewTab: function cc_openLinkInNewTab() {
|
openLinkInNewTab: function cc_openLinkInNewTab() {
|
||||||
let tab = Browser.addTab(ContextMenuUI.popupState.linkURL, false, Browser.selectedTab);
|
let url = ContextMenuUI.popupState.linkURL;
|
||||||
ContextUI.peekTabs(kOpenInNewTabAnimationDelayMsec);
|
BrowserUI.openLinkInNewTab(url, false, Browser.selectedTab);
|
||||||
Elements.tabList.strip.ensureElementIsVisible(tab.chromeTab);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
copyLink: function cc_copyLink() {
|
copyLink: function cc_copyLink() {
|
||||||
|
|
|
@ -161,7 +161,7 @@ var ContextUI = {
|
||||||
* Dismiss tab bar after a delay. Fires context ui events.
|
* Dismiss tab bar after a delay. Fires context ui events.
|
||||||
*/
|
*/
|
||||||
dismissTabsWithDelay: function (aDelay) {
|
dismissTabsWithDelay: function (aDelay) {
|
||||||
aDelay = aDelay || kNewTabAnimationDelayMsec;
|
aDelay = aDelay || kForegroundTabAnimationDelay;
|
||||||
this._clearDelayedTimeout();
|
this._clearDelayedTimeout();
|
||||||
this._hidingId = setTimeout(function () {
|
this._hidingId = setTimeout(function () {
|
||||||
ContextUI.dismissTabs();
|
ContextUI.dismissTabs();
|
||||||
|
|
|
@ -39,6 +39,9 @@ var APZCObserver = {
|
||||||
handleEvent: function APZC_handleEvent(aEvent) {
|
handleEvent: function APZC_handleEvent(aEvent) {
|
||||||
switch (aEvent.type) {
|
switch (aEvent.type) {
|
||||||
case 'pageshow':
|
case 'pageshow':
|
||||||
|
if (aEvent.target != Browser.selectedBrowser.contentDocument)
|
||||||
|
break;
|
||||||
|
// fall through to TabSelect:
|
||||||
case 'TabSelect':
|
case 'TabSelect':
|
||||||
// ROOT_ID doesn't really identify the view we want. When we call
|
// ROOT_ID doesn't really identify the view we want. When we call
|
||||||
// this on a content document (tab), findElementWithViewId will
|
// this on a content document (tab), findElementWithViewId will
|
||||||
|
|
|
@ -66,6 +66,9 @@ XPCOMUtils.defineLazyServiceGetter(window, "gFaviconService",
|
||||||
XPCOMUtils.defineLazyServiceGetter(window, "gFocusManager",
|
XPCOMUtils.defineLazyServiceGetter(window, "gFocusManager",
|
||||||
"@mozilla.org/focus-manager;1",
|
"@mozilla.org/focus-manager;1",
|
||||||
"nsIFocusManager");
|
"nsIFocusManager");
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(window, "gEventListenerService",
|
||||||
|
"@mozilla.org/eventlistenerservice;1",
|
||||||
|
"nsIEventListenerService");
|
||||||
#ifdef MOZ_CRASHREPORTER
|
#ifdef MOZ_CRASHREPORTER
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
|
XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
|
||||||
"@mozilla.org/xre/app-info;1",
|
"@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 debugServerStateChanged = "devtools.debugger.remote-enabled";
|
||||||
const debugServerPortChanged = "devtools.debugger.remote-port";
|
const debugServerPortChanged = "devtools.debugger.remote-port";
|
||||||
|
|
||||||
// delay when showing the tab bar briefly after a new (empty) tab opens
|
// delay when showing the tab bar briefly after a new foreground tab opens
|
||||||
const kNewTabAnimationDelayMsec = 1000;
|
const kForegroundTabAnimationDelay = 1000;
|
||||||
// delay when showing the tab bar after opening a link on a new tab
|
// delay when showing the tab bar after opening a new background tab opens
|
||||||
const kOpenInNewTabAnimationDelayMsec = 3000;
|
const kBackgroundTabAnimationDelay = 3000;
|
||||||
// delay before closing tab bar after selecting another tab
|
// delay before closing tab bar after closing or selecting a tab
|
||||||
const kSelectTabAnimationDelayMsec = 500;
|
const kChangeTabAnimationDelay = 500;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache of commonly used elements.
|
* Cache of commonly used elements.
|
||||||
|
@ -173,6 +173,14 @@ var BrowserUI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
uninit: function() {
|
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);
|
messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps);
|
||||||
Services.obs.removeObserver(this, "handle-xul-text-link");
|
Services.obs.removeObserver(this, "handle-xul-text-link");
|
||||||
|
|
||||||
|
@ -180,7 +188,6 @@ var BrowserUI = {
|
||||||
FlyoutPanelsUI.uninit();
|
FlyoutPanelsUI.uninit();
|
||||||
MetroDownloadsView.uninit();
|
MetroDownloadsView.uninit();
|
||||||
SettingsCharm.uninit();
|
SettingsCharm.uninit();
|
||||||
messageManager.removeMessageListener("Content:StateChange", this);
|
|
||||||
PageThumbs.uninit();
|
PageThumbs.uninit();
|
||||||
this.stopDebugServer();
|
this.stopDebugServer();
|
||||||
},
|
},
|
||||||
|
@ -436,10 +443,25 @@ var BrowserUI = {
|
||||||
* See Browser.addTab for more documentation.
|
* See Browser.addTab for more documentation.
|
||||||
*/
|
*/
|
||||||
addAndShowTab: function (aURI, aOwner) {
|
addAndShowTab: function (aURI, aOwner) {
|
||||||
ContextUI.peekTabs(kNewTabAnimationDelayMsec);
|
ContextUI.peekTabs(kForegroundTabAnimationDelay);
|
||||||
return Browser.addTab(aURI || kStartURI, true, aOwner);
|
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) {
|
setOnTabAnimationEnd: function setOnTabAnimationEnd(aCallback) {
|
||||||
Elements.tabs.addEventListener("animationend", function onAnimationEnd() {
|
Elements.tabs.addEventListener("animationend", function onAnimationEnd() {
|
||||||
Elements.tabs.removeEventListener("animationend", onAnimationEnd);
|
Elements.tabs.removeEventListener("animationend", onAnimationEnd);
|
||||||
|
@ -464,7 +486,7 @@ var BrowserUI = {
|
||||||
this.setOnTabAnimationEnd(function() {
|
this.setOnTabAnimationEnd(function() {
|
||||||
Browser.closeTab(tabToClose, { forceClose: true } );
|
Browser.closeTab(tabToClose, { forceClose: true } );
|
||||||
if (wasCollapsed)
|
if (wasCollapsed)
|
||||||
ContextUI.dismissTabsWithDelay(kNewTabAnimationDelayMsec);
|
ContextUI.dismissTabsWithDelay(kChangeTabAnimationDelay);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -506,7 +528,7 @@ var BrowserUI = {
|
||||||
|
|
||||||
selectTabAndDismiss: function selectTabAndDismiss(aTab) {
|
selectTabAndDismiss: function selectTabAndDismiss(aTab) {
|
||||||
this.selectTab(aTab);
|
this.selectTab(aTab);
|
||||||
ContextUI.dismissTabsWithDelay(kSelectTabAnimationDelayMsec);
|
ContextUI.dismissTabsWithDelay(kChangeTabAnimationDelay);
|
||||||
},
|
},
|
||||||
|
|
||||||
selectTabAtIndex: function selectTabAtIndex(aIndex) {
|
selectTabAtIndex: function selectTabAtIndex(aIndex) {
|
||||||
|
|
|
@ -79,6 +79,7 @@ var Browser = {
|
||||||
// Call InputSourceHelper first so global listeners get called before
|
// Call InputSourceHelper first so global listeners get called before
|
||||||
// we start processing input in TouchModule.
|
// we start processing input in TouchModule.
|
||||||
InputSourceHelper.init();
|
InputSourceHelper.init();
|
||||||
|
ClickEventHandler.init();
|
||||||
|
|
||||||
TouchModule.init();
|
TouchModule.init();
|
||||||
GestureModule.init();
|
GestureModule.init();
|
||||||
|
@ -213,6 +214,7 @@ var Browser = {
|
||||||
shutdown: function shutdown() {
|
shutdown: function shutdown() {
|
||||||
APZCObserver.shutdown();
|
APZCObserver.shutdown();
|
||||||
BrowserUI.uninit();
|
BrowserUI.uninit();
|
||||||
|
ClickEventHandler.uninit();
|
||||||
ContentAreaObserver.shutdown();
|
ContentAreaObserver.shutdown();
|
||||||
Appbar.shutdown();
|
Appbar.shutdown();
|
||||||
|
|
||||||
|
@ -485,7 +487,7 @@ var Browser = {
|
||||||
if (aBringFront)
|
if (aBringFront)
|
||||||
this.selectedTab = newTab;
|
this.selectedTab = newTab;
|
||||||
|
|
||||||
this._announceNewTab(newTab, params, aBringFront);
|
this._announceNewTab(newTab);
|
||||||
return newTab;
|
return newTab;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -511,7 +513,7 @@ var Browser = {
|
||||||
* helper for addTab related methods. Fires events related to
|
* helper for addTab related methods. Fires events related to
|
||||||
* new tab creation.
|
* new tab creation.
|
||||||
*/
|
*/
|
||||||
_announceNewTab: function _announceNewTab(aTab, aParams, aBringFront) {
|
_announceNewTab: function (aTab) {
|
||||||
let event = document.createEvent("UIEvents");
|
let event = document.createEvent("UIEvents");
|
||||||
event.initUIEvent("TabOpen", true, false, window, 0);
|
event.initUIEvent("TabOpen", true, false, window, 0);
|
||||||
aTab.chromeTab.dispatchEvent(event);
|
aTab.chromeTab.dispatchEvent(event);
|
||||||
|
@ -1020,9 +1022,6 @@ Browser.MainDragger.prototype = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const OPEN_APPTAB = 100; // Hack until we get a real API
|
|
||||||
|
|
||||||
function nsBrowserAccess() { }
|
function nsBrowserAccess() { }
|
||||||
|
|
||||||
nsBrowserAccess.prototype = {
|
nsBrowserAccess.prototype = {
|
||||||
|
@ -1032,56 +1031,54 @@ nsBrowserAccess.prototype = {
|
||||||
throw Cr.NS_NOINTERFACE;
|
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) {
|
_getBrowser: function _getBrowser(aURI, aOpener, aWhere, aContext) {
|
||||||
let isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
|
let isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
|
||||||
|
// We don't allow externals apps opening chrome docs
|
||||||
if (isExternal && aURI && aURI.schemeIs("chrome"))
|
if (isExternal && aURI && aURI.schemeIs("chrome"))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
let location;
|
||||||
|
let browser;
|
||||||
let loadflags = isExternal ?
|
let loadflags = isExternal ?
|
||||||
Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
|
Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
|
||||||
Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||||
let location;
|
let openAction = this._getOpenAction(aURI, aOpener, aWhere, aContext);
|
||||||
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 browser;
|
if (openAction == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
|
||||||
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) {
|
|
||||||
let owner = isExternal ? null : Browser.selectedTab;
|
let owner = isExternal ? null : Browser.selectedTab;
|
||||||
let tab = Browser.addTab("about:blank", true, owner);
|
let tab = BrowserUI.openLinkInNewTab("about:blank", true, owner);
|
||||||
if (isExternal)
|
|
||||||
tab.closeOnExit = true;
|
|
||||||
browser = tab.browser;
|
browser = tab.browser;
|
||||||
} else if (aWhere == OPEN_APPTAB) {
|
} else {
|
||||||
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
|
|
||||||
browser = Browser.selectedBrowser;
|
browser = Browser.selectedBrowser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1097,10 +1094,6 @@ nsBrowserAccess.prototype = {
|
||||||
browser.focus();
|
browser.focus();
|
||||||
} catch(e) { }
|
} 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;
|
return browser;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1584,3 +1577,68 @@ function rendererFactory(aBrowser, aCanvas) {
|
||||||
|
|
||||||
return wrapper;
|
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_context_menu_tests_04.html
|
||||||
browser_findbar.html
|
browser_findbar.html
|
||||||
browser_form_auto_complete.html
|
browser_form_auto_complete.html
|
||||||
|
browser_link_click.html
|
||||||
browser_onscreen_keyboard.html
|
browser_onscreen_keyboard.html
|
||||||
browser_progress_indicator.xul
|
browser_progress_indicator.xul
|
||||||
browser_selection_basic.html
|
browser_selection_basic.html
|
||||||
|
@ -42,6 +43,7 @@ support-files =
|
||||||
[browser_form_auto_complete.js]
|
[browser_form_auto_complete.js]
|
||||||
[browser_history.js]
|
[browser_history.js]
|
||||||
[browser_inputsource.js]
|
[browser_inputsource.js]
|
||||||
|
[browser_link_click.js]
|
||||||
[browser_onscreen_keyboard.js]
|
[browser_onscreen_keyboard.js]
|
||||||
[browser_prefs_ui.js]
|
[browser_prefs_ui.js]
|
||||||
[browser_remotetabs.js]
|
[browser_remotetabs.js]
|
||||||
|
|
|
@ -112,6 +112,10 @@ a {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#output-container.hideTimestamps > .message > .timestamp {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.filtered-by-type,
|
.filtered-by-type,
|
||||||
.filtered-by-string {
|
.filtered-by-string {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
XPIDL_SOURCES += [
|
XPIDL_SOURCES += [
|
||||||
|
'nsIDomainPolicy.idl',
|
||||||
'nsIPrincipal.idl',
|
'nsIPrincipal.idl',
|
||||||
'nsIScriptSecurityManager.idl',
|
'nsIScriptSecurityManager.idl',
|
||||||
'nsISecurityCheckedComponent.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 nsIURI;
|
||||||
interface nsIChannel;
|
interface nsIChannel;
|
||||||
interface nsIDocShell;
|
interface nsIDocShell;
|
||||||
|
interface nsIDomainPolicy;
|
||||||
|
|
||||||
[scriptable, uuid(d6475e53-9ece-4dc0-940c-095ac3d85363)]
|
[scriptable, uuid(2911ae60-1b5f-47e6-941e-1bb7b53a167d)]
|
||||||
interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
||||||
{
|
{
|
||||||
///////////////// Security Checks //////////////////
|
///////////////// Security Checks //////////////////
|
||||||
|
@ -103,24 +104,9 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
||||||
in unsigned long flags);
|
in unsigned long flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that the function 'funObj' is allowed to run on 'targetObj'
|
* Return true if scripts may be executed in the scope of the given global.
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
[noscript] void checkFunctionAccess(in JSContextPtr cx, in voidPtr funObj,
|
[noscript,notxpcom] boolean scriptAllowed(in JSObjectPtr aGlobal);
|
||||||
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);
|
|
||||||
|
|
||||||
///////////////// Principals ///////////////////////
|
///////////////// Principals ///////////////////////
|
||||||
/**
|
/**
|
||||||
|
@ -216,6 +202,13 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
||||||
* script to check whether a given principal is system.
|
* script to check whether a given principal is system.
|
||||||
*/
|
*/
|
||||||
boolean isSystemPrincipal(in nsIPrincipal aPrincipal);
|
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
|
* 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.
|
* inMozBrowser has to be true if the app is inside a mozbrowser iframe.
|
||||||
*/
|
*/
|
||||||
AUTF8String getJarPrefix(in unsigned long appId, in boolean inMozBrowser);
|
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++
|
%{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,
|
AppAttributesEqual(nsIPrincipal* aFirst,
|
||||||
nsIPrincipal* aSecond);
|
nsIPrincipal* aSecond);
|
||||||
|
|
||||||
|
void DeactivateDomainPolicy();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// GetScriptSecurityManager is the only call that can make one
|
// GetScriptSecurityManager is the only call that can make one
|
||||||
|
@ -381,7 +383,7 @@ private:
|
||||||
|
|
||||||
// Returns null if a principal cannot be found; generally callers
|
// Returns null if a principal cannot be found; generally callers
|
||||||
// should error out at that point.
|
// 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
|
// 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.
|
// when this happens -- this means that there was no JS running.
|
||||||
|
@ -462,15 +464,6 @@ private:
|
||||||
nsIPrincipal* aSubjectPrincipal,
|
nsIPrincipal* aSubjectPrincipal,
|
||||||
const char* aObjectSecurityLevel);
|
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
|
nsresult
|
||||||
Init();
|
Init();
|
||||||
|
|
||||||
|
@ -496,6 +489,10 @@ private:
|
||||||
bool mIsJavaScriptEnabled;
|
bool mIsJavaScriptEnabled;
|
||||||
bool mPolicyPrefsChanged;
|
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 bool sStrictFileOriginPolicy;
|
||||||
|
|
||||||
static nsIIOService *sIOService;
|
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'
|
MODULE = 'caps'
|
||||||
|
|
||||||
SOURCES += [
|
SOURCES += [
|
||||||
|
'DomainPolicy.cpp',
|
||||||
'nsJSPrincipals.cpp',
|
'nsJSPrincipals.cpp',
|
||||||
'nsNullPrincipal.cpp',
|
'nsNullPrincipal.cpp',
|
||||||
'nsNullPrincipalURI.cpp',
|
'nsNullPrincipalURI.cpp',
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "nsSystemPrincipal.h"
|
#include "nsSystemPrincipal.h"
|
||||||
#include "nsPrincipal.h"
|
#include "nsPrincipal.h"
|
||||||
#include "nsNullPrincipal.h"
|
#include "nsNullPrincipal.h"
|
||||||
|
#include "DomainPolicy.h"
|
||||||
#include "nsXPIDLString.h"
|
#include "nsXPIDLString.h"
|
||||||
#include "nsCRT.h"
|
#include "nsCRT.h"
|
||||||
#include "nsCRTGlue.h"
|
#include "nsCRTGlue.h"
|
||||||
|
@ -63,6 +64,7 @@
|
||||||
#include "mozilla/StaticPtr.h"
|
#include "mozilla/StaticPtr.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsCxPusher.h"
|
#include "nsCxPusher.h"
|
||||||
|
#include "nsJSUtils.h"
|
||||||
|
|
||||||
// This should be probably defined on some other place... but I couldn't find it
|
// This should be probably defined on some other place... but I couldn't find it
|
||||||
#define WEBAPPS_PERM_NAME "webapps-manage"
|
#define WEBAPPS_PERM_NAME "webapps-manage"
|
||||||
|
@ -183,12 +185,6 @@ GetPrincipalDomainOrigin(nsIPrincipal* aPrincipal,
|
||||||
return GetOriginFromURI(uri, aOrigin);
|
return GetOriginFromURI(uri, aOrigin);
|
||||||
}
|
}
|
||||||
|
|
||||||
static nsIScriptContext *
|
|
||||||
GetScriptContext(JSContext *cx)
|
|
||||||
{
|
|
||||||
return GetScriptContextFromJSContext(cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void SetPendingException(JSContext *cx, const char *aMsg)
|
inline void SetPendingException(JSContext *cx, const char *aMsg)
|
||||||
{
|
{
|
||||||
JS_ReportError(cx, "%s", aMsg);
|
JS_ReportError(cx, "%s", aMsg);
|
||||||
|
@ -1601,173 +1597,38 @@ nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(nsIPrincipal* aPrincipal,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
bool
|
||||||
nsScriptSecurityManager::CheckFunctionAccess(JSContext *aCx, void *aFunObj,
|
nsScriptSecurityManager::ScriptAllowed(JSObject *aGlobal)
|
||||||
void *aTargetObj)
|
|
||||||
{
|
{
|
||||||
// This check is called for event handlers
|
MOZ_ASSERT(aGlobal);
|
||||||
nsresult rv;
|
MOZ_ASSERT(JS_IsGlobalObject(aGlobal) || js::IsOuterObject(aGlobal));
|
||||||
JS::Rooted<JSObject*> rootedFunObj(aCx, static_cast<JSObject*>(aFunObj));
|
AutoJSContext cx;
|
||||||
nsIPrincipal* subject = doGetObjectPrincipal(rootedFunObj);
|
JS::RootedObject global(cx, js::UncheckedUnwrap(aGlobal, /* stopAtOuter = */ false));
|
||||||
if (!subject)
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
|
|
||||||
if (subject == mSystemPrincipal)
|
// Check the bits on the compartment private.
|
||||||
// This is the system principal: just allow access
|
xpc::Scriptability& scriptability = xpc::Scriptability::Get(aGlobal);
|
||||||
return NS_OK;
|
if (!scriptability.Allowed()) {
|
||||||
|
return false;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// If the compartment is immune to script policy, we're done.
|
||||||
** Get origin of subject and object and compare.
|
if (scriptability.IsImmuneToScriptPolicy()) {
|
||||||
*/
|
return true;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same thing for nsExpandedPrincipal, which is pseudo-privileged.
|
// Check for a per-site policy.
|
||||||
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
|
|
||||||
static const char jsPrefGroupName[] = "javascript";
|
static const char jsPrefGroupName[] = "javascript";
|
||||||
ClassInfoData nameData(nullptr, jsPrefGroupName);
|
ClassInfoData nameData(nullptr, jsPrefGroupName);
|
||||||
|
|
||||||
SecurityLevel secLevel;
|
SecurityLevel secLevel;
|
||||||
rv = LookupPolicy(aPrincipal, nameData, EnabledID(),
|
nsresult rv = LookupPolicy(doGetObjectPrincipal(global), nameData,
|
||||||
nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
|
EnabledID(),
|
||||||
nullptr, &secLevel);
|
nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
|
||||||
if (NS_FAILED(rv) || secLevel.level == SCRIPT_SECURITY_NO_ACCESS)
|
nullptr, &secLevel);
|
||||||
{
|
if (NS_FAILED(rv) || secLevel.level == SCRIPT_SECURITY_NO_ACCESS) {
|
||||||
*result = false;
|
return false;
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-- Nobody vetoed, so allow the JS to run.
|
return true;
|
||||||
*result = true;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////// Principals ///////////////////////
|
///////////////// Principals ///////////////////////
|
||||||
|
@ -1964,7 +1825,7 @@ nsScriptSecurityManager::GetObjectPrincipal(JSContext *aCx, JSObject *aObj,
|
||||||
|
|
||||||
// static
|
// static
|
||||||
nsIPrincipal*
|
nsIPrincipal*
|
||||||
nsScriptSecurityManager::doGetObjectPrincipal(JS::Handle<JSObject*> aObj)
|
nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
|
||||||
{
|
{
|
||||||
JSCompartment *compartment = js::GetObjectCompartment(aObj);
|
JSCompartment *compartment = js::GetObjectCompartment(aObj);
|
||||||
JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
|
JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
|
||||||
|
@ -2291,6 +2152,9 @@ nsScriptSecurityManager::~nsScriptSecurityManager(void)
|
||||||
if(mDefaultPolicy)
|
if(mDefaultPolicy)
|
||||||
mDefaultPolicy->Drop();
|
mDefaultPolicy->Drop();
|
||||||
delete mCapabilities;
|
delete mCapabilities;
|
||||||
|
if (mDomainPolicy)
|
||||||
|
mDomainPolicy->Deactivate();
|
||||||
|
MOZ_ASSERT(!mDomainPolicy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -2716,3 +2580,74 @@ nsScriptSecurityManager::GetJarPrefix(uint32_t aAppId,
|
||||||
mozilla::GetJarPrefix(aAppId, aInMozBrowser, aJarPrefix);
|
mozilla::GetJarPrefix(aAppId, aInMozBrowser, aJarPrefix);
|
||||||
return NS_OK;
|
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]
|
[DEFAULT]
|
||||||
|
support-files =
|
||||||
|
file_disableScript.html
|
||||||
|
|
||||||
[test_app_principal_equality.html]
|
[test_app_principal_equality.html]
|
||||||
[test_bug246699.html]
|
[test_bug246699.html]
|
||||||
|
|
|
@ -5,4 +5,5 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
MOCHITEST_MANIFESTS += ['mochitest.ini']
|
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"
|
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=1
|
||||||
MOZ_B2G_BT_BLUEZ=1
|
MOZ_B2G_BT_BLUEZ=1
|
||||||
|
MOZ_NFC=1
|
||||||
MOZ_B2G_CAMERA=1
|
MOZ_B2G_CAMERA=1
|
||||||
MOZ_OMX_DECODER=1
|
MOZ_OMX_DECODER=1
|
||||||
AC_SUBST(MOZ_OMX_DECODER)
|
AC_SUBST(MOZ_OMX_DECODER)
|
||||||
|
@ -239,6 +240,7 @@ if test -n "$gonkdir" ; then
|
||||||
MOZ_B2G_BT_BLUEDROID=1
|
MOZ_B2G_BT_BLUEDROID=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
MOZ_NFC=1
|
||||||
MOZ_B2G_CAMERA=1
|
MOZ_B2G_CAMERA=1
|
||||||
MOZ_OMX_DECODER=1
|
MOZ_OMX_DECODER=1
|
||||||
AC_SUBST(MOZ_OMX_DECODER)
|
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_BLUEZ)
|
||||||
AC_SUBST(MOZ_B2G_BT_BLUEDROID)
|
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 ========================================================
|
||||||
dnl = Enable Pico Speech Synthesis (Gonk usually)
|
dnl = Enable Pico Speech Synthesis (Gonk usually)
|
||||||
dnl ========================================================
|
dnl ========================================================
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
|
|
||||||
#include "nsAttrAndChildArray.h"
|
#include "nsAttrAndChildArray.h"
|
||||||
|
|
||||||
|
#include "mozilla/MathAlgorithms.h"
|
||||||
#include "mozilla/MemoryReporting.h"
|
#include "mozilla/MemoryReporting.h"
|
||||||
|
|
||||||
#include "nsMappedAttributeElement.h"
|
#include "nsMappedAttributeElement.h"
|
||||||
#include "prbit.h"
|
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nsHTMLStyleSheet.h"
|
#include "nsHTMLStyleSheet.h"
|
||||||
#include "nsRuleWalker.h"
|
#include "nsRuleWalker.h"
|
||||||
|
@ -772,7 +772,7 @@ nsAttrAndChildArray::GrowBy(uint32_t aGrowSize)
|
||||||
} while (size < minSize);
|
} while (size < minSize);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
size = 1u << PR_CeilingLog2(minSize);
|
size = 1u << mozilla::CeilingLog2(minSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needToInitialize = !mImpl;
|
bool needToInitialize = !mImpl;
|
||||||
|
|
|
@ -253,8 +253,14 @@ public:
|
||||||
|
|
||||||
// nsWrapperCache
|
// nsWrapperCache
|
||||||
using nsWrapperCache::GetWrapperPreserveColor;
|
using nsWrapperCache::GetWrapperPreserveColor;
|
||||||
virtual JSObject* WrapObject(JSContext *cx,
|
virtual JSObject* WrapObject(JSContext* aCx,
|
||||||
JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
|
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||||
|
protected:
|
||||||
|
virtual JSObject* GetWrapperPreserveColorInternal() MOZ_OVERRIDE
|
||||||
|
{
|
||||||
|
return nsWrapperCache::GetWrapperPreserveColor();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
|
||||||
// nsIDOMHTMLCollection
|
// nsIDOMHTMLCollection
|
||||||
NS_DECL_NSIDOMHTMLCOLLECTION
|
NS_DECL_NSIDOMHTMLCOLLECTION
|
||||||
|
|
|
@ -7206,19 +7206,10 @@ nsDocument::IsScriptEnabled()
|
||||||
nsCOMPtr<nsIScriptSecurityManager> sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
|
nsCOMPtr<nsIScriptSecurityManager> sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
|
||||||
NS_ENSURE_TRUE(sm, false);
|
NS_ENSURE_TRUE(sm, false);
|
||||||
|
|
||||||
nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetWindow());
|
nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow());
|
||||||
NS_ENSURE_TRUE(globalObject, false);
|
NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), false);
|
||||||
|
|
||||||
nsIScriptContext *scriptContext = globalObject->GetContext();
|
return sm->ScriptAllowed(globalObject->GetGlobalJSObject());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsRadioGroupStruct*
|
nsRadioGroupStruct*
|
||||||
|
|
|
@ -498,28 +498,6 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||||
return false;
|
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;
|
JSVersion version = JSVERSION_DEFAULT;
|
||||||
|
|
||||||
// Check the type attribute to determine language and version.
|
// Check the type attribute to determine language and version.
|
||||||
|
|
|
@ -1084,9 +1084,9 @@ CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
|
||||||
const PRUnichar *aEncoderOptions,
|
const PRUnichar *aEncoderOptions,
|
||||||
nsIInputStream **aStream)
|
nsIInputStream **aStream)
|
||||||
{
|
{
|
||||||
uint8_t* imageBuffer = nullptr;
|
nsAutoArrayPtr<uint8_t> imageBuffer;
|
||||||
int32_t format = 0;
|
int32_t format = 0;
|
||||||
GetImageBuffer(&imageBuffer, &format);
|
GetImageBuffer(getter_Transfers(imageBuffer), &format);
|
||||||
if (!imageBuffer) {
|
if (!imageBuffer) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -794,9 +794,9 @@ WebGLContext::GetInputStream(const char* aMimeType,
|
||||||
if (!gl)
|
if (!gl)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
uint8_t* imageBuffer = nullptr;
|
nsAutoArrayPtr<uint8_t> imageBuffer;
|
||||||
int32_t format = 0;
|
int32_t format = 0;
|
||||||
GetImageBuffer(&imageBuffer, &format);
|
GetImageBuffer(getter_Transfers(imageBuffer), &format);
|
||||||
if (!imageBuffer) {
|
if (!imageBuffer) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@ class Element;
|
||||||
|
|
||||||
// IID for the nsIHTMLCollection interface
|
// IID for the nsIHTMLCollection interface
|
||||||
#define NS_IHTMLCOLLECTION_IID \
|
#define NS_IHTMLCOLLECTION_IID \
|
||||||
{ 0x5643235d, 0x9a72, 0x4b6a, \
|
{ 0x4e169191, 0x5196, 0x4e17, \
|
||||||
{ 0xa6, 0x0c, 0x64, 0x63, 0x72, 0xb7, 0x53, 0x4a } }
|
{ 0xa4, 0x79, 0xd5, 0x35, 0x0b, 0x5b, 0x0a, 0xcd } }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An internal interface
|
* An internal interface
|
||||||
|
@ -76,11 +76,11 @@ public:
|
||||||
|
|
||||||
JSObject* GetWrapperPreserveColor()
|
JSObject* GetWrapperPreserveColor()
|
||||||
{
|
{
|
||||||
nsWrapperCache* cache;
|
return GetWrapperPreserveColorInternal();
|
||||||
CallQueryInterface(this, &cache);
|
|
||||||
return cache->GetWrapperPreserveColor();
|
|
||||||
}
|
}
|
||||||
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)
|
NS_DEFINE_STATIC_IID_ACCESSOR(nsIHTMLCollection, NS_IHTMLCOLLECTION_IID)
|
||||||
|
|
|
@ -79,8 +79,15 @@ public:
|
||||||
nsresult GetSortedControls(nsTArray<nsGenericHTMLFormElement*>& aControls) const;
|
nsresult GetSortedControls(nsTArray<nsGenericHTMLFormElement*>& aControls) const;
|
||||||
|
|
||||||
// nsWrapperCache
|
// nsWrapperCache
|
||||||
virtual JSObject*
|
using nsWrapperCache::GetWrapperPreserveColor;
|
||||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
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);
|
static bool ShouldBeInElements(nsIFormControl* aFormControl);
|
||||||
|
|
||||||
|
|
|
@ -42,8 +42,14 @@ public:
|
||||||
|
|
||||||
// nsWrapperCache
|
// nsWrapperCache
|
||||||
using nsWrapperCache::GetWrapperPreserveColor;
|
using nsWrapperCache::GetWrapperPreserveColor;
|
||||||
virtual JSObject* WrapObject(JSContext* cx,
|
virtual JSObject* WrapObject(JSContext* aCx,
|
||||||
JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
|
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||||
|
protected:
|
||||||
|
virtual JSObject* GetWrapperPreserveColorInternal() MOZ_OVERRIDE
|
||||||
|
{
|
||||||
|
return nsWrapperCache::GetWrapperPreserveColor();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
|
||||||
// nsIDOMHTMLOptionsCollection interface
|
// nsIDOMHTMLOptionsCollection interface
|
||||||
NS_DECL_NSIDOMHTMLOPTIONSCOLLECTION
|
NS_DECL_NSIDOMHTMLOPTIONSCOLLECTION
|
||||||
|
|
|
@ -55,9 +55,16 @@ public:
|
||||||
HTMLPropertiesCollection(nsGenericHTMLElement* aRoot);
|
HTMLPropertiesCollection(nsGenericHTMLElement* aRoot);
|
||||||
virtual ~HTMLPropertiesCollection();
|
virtual ~HTMLPropertiesCollection();
|
||||||
|
|
||||||
|
// nsWrapperCache
|
||||||
using nsWrapperCache::GetWrapperPreserveColor;
|
using nsWrapperCache::GetWrapperPreserveColor;
|
||||||
virtual JSObject* WrapObject(JSContext *cx,
|
virtual JSObject* WrapObject(JSContext* aCx,
|
||||||
JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
|
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||||
|
protected:
|
||||||
|
virtual JSObject* GetWrapperPreserveColorInternal() MOZ_OVERRIDE
|
||||||
|
{
|
||||||
|
return nsWrapperCache::GetWrapperPreserveColor();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
|
||||||
virtual Element* GetElementAt(uint32_t aIndex);
|
virtual Element* GetElementAt(uint32_t aIndex);
|
||||||
|
|
||||||
|
|
|
@ -50,13 +50,15 @@ public:
|
||||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TableRowsCollection)
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TableRowsCollection)
|
||||||
|
|
||||||
// nsWrapperCache
|
// nsWrapperCache
|
||||||
virtual JSObject* WrapObject(JSContext *cx,
|
using nsWrapperCache::GetWrapperPreserveColor;
|
||||||
JS::Handle<JSObject*> scope) MOZ_OVERRIDE
|
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
|
// Those rows that are not in table sections
|
||||||
HTMLTableElement* mParent;
|
HTMLTableElement* mParent;
|
||||||
nsRefPtr<nsContentList> mOrphanRows;
|
nsRefPtr<nsContentList> mOrphanRows;
|
||||||
|
@ -82,6 +84,13 @@ TableRowsCollection::~TableRowsCollection()
|
||||||
// reference for us.
|
// 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_COLLECTION_WRAPPERCACHE_1(TableRowsCollection, mOrphanRows)
|
||||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TableRowsCollection)
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(TableRowsCollection)
|
||||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TableRowsCollection)
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(TableRowsCollection)
|
||||||
|
|
|
@ -706,24 +706,17 @@ IsScriptEnabled(nsIDocument *aDoc, nsIDocShell *aContainer)
|
||||||
NS_ENSURE_TRUE(aDoc && aContainer, true);
|
NS_ENSURE_TRUE(aDoc && aContainer, true);
|
||||||
|
|
||||||
nsCOMPtr<nsIScriptGlobalObject> globalObject =
|
nsCOMPtr<nsIScriptGlobalObject> globalObject =
|
||||||
do_QueryInterface(aDoc->GetWindow());
|
do_QueryInterface(aDoc->GetInnerWindow());
|
||||||
|
|
||||||
// Getting context is tricky if the document hasn't had its
|
// Getting context is tricky if the document hasn't had its
|
||||||
// GlobalObject set yet
|
// GlobalObject set yet
|
||||||
if (!globalObject) {
|
if (!globalObject) {
|
||||||
globalObject = aContainer->GetScriptGlobalObject();
|
globalObject = aContainer->GetScriptGlobalObject();
|
||||||
NS_ENSURE_TRUE(globalObject, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsIScriptContext *scriptContext = globalObject->GetContext();
|
NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), true);
|
||||||
NS_ENSURE_TRUE(scriptContext, true);
|
return nsContentUtils::GetSecurityManager()->
|
||||||
JSContext *cx = scriptContext->GetNativeContext();
|
ScriptAllowed(globalObject->GetGlobalJSObject());
|
||||||
NS_ENSURE_TRUE(cx, true);
|
|
||||||
|
|
||||||
bool enabled = true;
|
|
||||||
nsContentUtils::GetSecurityManager()->
|
|
||||||
CanExecuteScripts(cx, aDoc->NodePrincipal(), &enabled);
|
|
||||||
return enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
|
|
@ -1080,24 +1080,12 @@ nsXBLBinding::AllowScripts()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(doc->GetWindow());
|
nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(doc->GetInnerWindow());
|
||||||
if (!global) {
|
if (!global || !global->GetGlobalJSObject()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIScriptContext> context = global->GetContext();
|
return mgr->ScriptAllowed(global->GetGlobalJSObject());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsXBLBinding*
|
nsXBLBinding*
|
||||||
|
|
|
@ -328,12 +328,12 @@ nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
|
||||||
|
|
||||||
// Now call the method
|
// Now call the method
|
||||||
|
|
||||||
// Check whether it's OK to call the method.
|
// Check whether script is enabled.
|
||||||
rv = nsContentUtils::GetSecurityManager()->CheckFunctionAccess(cx, method,
|
bool scriptAllowed = nsContentUtils::GetSecurityManager()->
|
||||||
thisObject);
|
ScriptAllowed(js::GetGlobalForObjectCrossCompartment(method));
|
||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (scriptAllowed) {
|
||||||
JS::Rooted<JS::Value> retval(cx);
|
JS::Rooted<JS::Value> retval(cx);
|
||||||
ok = ::JS_CallFunctionValue(cx, thisObject, OBJECT_TO_JSVAL(method),
|
ok = ::JS_CallFunctionValue(cx, thisObject, OBJECT_TO_JSVAL(method),
|
||||||
0 /* argc */, nullptr /* argv */, retval.address());
|
0 /* argc */, nullptr /* argv */, retval.address());
|
||||||
|
|
|
@ -3660,18 +3660,14 @@ XULDocument::ExecuteScript(nsIScriptContext * aContext,
|
||||||
|
|
||||||
NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
|
NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
|
||||||
|
|
||||||
if (!aContext->GetScriptsEnabled())
|
|
||||||
return NS_OK;
|
|
||||||
|
|
||||||
// Execute the precompiled script with the given version
|
// Execute the precompiled script with the given version
|
||||||
nsAutoMicroTask mt;
|
nsAutoMicroTask mt;
|
||||||
JSContext *cx = aContext->GetNativeContext();
|
JSContext *cx = aContext->GetNativeContext();
|
||||||
AutoCxPusher pusher(cx);
|
AutoCxPusher pusher(cx);
|
||||||
JS::Rooted<JSObject*> global(cx, mScriptGlobalObject->GetGlobalJSObject());
|
JS::Rooted<JSObject*> global(cx, mScriptGlobalObject->GetGlobalJSObject());
|
||||||
// XXXkhuey can this ever be null?
|
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
|
||||||
if (global) {
|
NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(global), NS_OK);
|
||||||
JS::ExposeObjectToActiveJS(global);
|
JS::ExposeObjectToActiveJS(global);
|
||||||
}
|
|
||||||
xpc_UnmarkGrayScript(aScriptObject);
|
xpc_UnmarkGrayScript(aScriptObject);
|
||||||
JSAutoCompartment ac(cx, global);
|
JSAutoCompartment ac(cx, global);
|
||||||
JS::Rooted<JS::Value> unused(cx);
|
JS::Rooted<JS::Value> unused(cx);
|
||||||
|
|
|
@ -762,6 +762,7 @@ nsDocShell::nsDocShell():
|
||||||
mUseGlobalHistory(false),
|
mUseGlobalHistory(false),
|
||||||
mInPrivateBrowsing(false),
|
mInPrivateBrowsing(false),
|
||||||
mDeviceSizeIsPageSize(false),
|
mDeviceSizeIsPageSize(false),
|
||||||
|
mCanExecuteScripts(false),
|
||||||
mFiredUnloadEvent(false),
|
mFiredUnloadEvent(false),
|
||||||
mEODForCurrentDocument(false),
|
mEODForCurrentDocument(false),
|
||||||
mURIResultedInDocument(false),
|
mURIResultedInDocument(false),
|
||||||
|
@ -2077,12 +2078,6 @@ nsDocShell::GetAllowJavascript(bool * aAllowJavascript)
|
||||||
NS_ENSURE_ARG_POINTER(aAllowJavascript);
|
NS_ENSURE_ARG_POINTER(aAllowJavascript);
|
||||||
|
|
||||||
*aAllowJavascript = mAllowJavascript;
|
*aAllowJavascript = mAllowJavascript;
|
||||||
if (!mAllowJavascript) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool unsafe;
|
|
||||||
*aAllowJavascript = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2090,6 +2085,7 @@ NS_IMETHODIMP
|
||||||
nsDocShell::SetAllowJavascript(bool aAllowJavascript)
|
nsDocShell::SetAllowJavascript(bool aAllowJavascript)
|
||||||
{
|
{
|
||||||
mAllowJavascript = aAllowJavascript;
|
mAllowJavascript = aAllowJavascript;
|
||||||
|
RecomputeCanExecuteScripts();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2823,6 +2819,48 @@ nsDocShell::GetParentDocshell()
|
||||||
return docshell.forget().downcast<nsDocShell>();
|
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
|
nsresult
|
||||||
nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
|
nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
|
||||||
{
|
{
|
||||||
|
@ -2892,6 +2930,10 @@ nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
|
||||||
nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
|
nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
|
||||||
if (parentURIListener)
|
if (parentURIListener)
|
||||||
mContentListener->SetParentContentListener(parentURIListener);
|
mContentListener->SetParentContentListener(parentURIListener);
|
||||||
|
|
||||||
|
// Our parent has changed. Recompute scriptability.
|
||||||
|
RecomputeCanExecuteScripts();
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3446,6 +3488,16 @@ nsDocShell::SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)
|
||||||
child->SetTreeOwner(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;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12657,20 +12709,7 @@ unsigned long nsDocShell::gNumberOfDocShells = 0;
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsDocShell::GetCanExecuteScripts(bool *aResult)
|
nsDocShell::GetCanExecuteScripts(bool *aResult)
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG_POINTER(aResult);
|
*aResult = mCanExecuteScripts;
|
||||||
*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);
|
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -813,6 +813,12 @@ protected:
|
||||||
bool mInPrivateBrowsing;
|
bool mInPrivateBrowsing;
|
||||||
bool mDeviceSizeIsPageSize;
|
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
|
// 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
|
// unset when we embed a new content viewer. While it's true no navigation
|
||||||
// is allowed in this docshell.
|
// 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
|
* The rule of thumb here is that we disable js if this docshell or any
|
||||||
* of its parents disallow scripting.
|
* 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
|
* 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,
|
privileged: PROMPT_ACTION,
|
||||||
certified: 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
|
#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
|
#ifdef MOZ_TIME_MANAGER
|
||||||
/* static */
|
/* static */
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -287,6 +287,9 @@ public:
|
||||||
#ifdef MOZ_B2G_FM
|
#ifdef MOZ_B2G_FM
|
||||||
static bool HasFMRadioSupport(JSContext* /* unused */, JSObject* aGlobal);
|
static bool HasFMRadioSupport(JSContext* /* unused */, JSObject* aGlobal);
|
||||||
#endif // MOZ_B2G_FM
|
#endif // MOZ_B2G_FM
|
||||||
|
#ifdef MOZ_NFC
|
||||||
|
static bool HasNfcSupport(JSContext* /* unused */, JSObject* aGlobal);
|
||||||
|
#endif // MOZ_NFC
|
||||||
#ifdef MOZ_TIME_MANAGER
|
#ifdef MOZ_TIME_MANAGER
|
||||||
static bool HasTimeSupport(JSContext* /* unused */, JSObject* aGlobal);
|
static bool HasTimeSupport(JSContext* /* unused */, JSObject* aGlobal);
|
||||||
#endif // MOZ_TIME_MANAGER
|
#endif // MOZ_TIME_MANAGER
|
||||||
|
|
|
@ -139,6 +139,7 @@
|
||||||
#endif
|
#endif
|
||||||
#include "nsIDOMCustomEvent.h"
|
#include "nsIDOMCustomEvent.h"
|
||||||
#include "nsIFrameRequestCallback.h"
|
#include "nsIFrameRequestCallback.h"
|
||||||
|
#include "nsIJARChannel.h"
|
||||||
|
|
||||||
#include "xpcprivate.h"
|
#include "xpcprivate.h"
|
||||||
|
|
||||||
|
@ -2061,6 +2062,9 @@ WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow,
|
||||||
mInnerWindowHolder = aHolder;
|
mInnerWindowHolder = aHolder;
|
||||||
|
|
||||||
aWindow->SuspendTimeouts();
|
aWindow->SuspendTimeouts();
|
||||||
|
|
||||||
|
// When a global goes into the bfcache, we disable script.
|
||||||
|
xpc::Scriptability::Get(aWindow->mJSObject).SetDocShellAllowsScript(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowStateHolder::~WindowStateHolder()
|
WindowStateHolder::~WindowStateHolder()
|
||||||
|
@ -2459,6 +2463,10 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||||
// Enter the new global's compartment.
|
// Enter the new global's compartment.
|
||||||
JSAutoCompartment ac(cx, mJSObject);
|
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
|
// If we created a new inner window above, we need to do the last little bit
|
||||||
// of initialization now that the dust has settled.
|
// of initialization now that the dust has settled.
|
||||||
if (createdInnerWindow) {
|
if (createdInnerWindow) {
|
||||||
|
@ -2541,6 +2549,14 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
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) {
|
if (mArguments) {
|
||||||
newInnerWindow->DefineArgumentsProperty(mArguments);
|
newInnerWindow->DefineArgumentsProperty(mArguments);
|
||||||
mArguments = nullptr;
|
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
|
nsresult
|
||||||
nsGlobalWindow::SetArguments(nsIArray *aArguments)
|
nsGlobalWindow::SetArguments(nsIArray *aArguments)
|
||||||
{
|
{
|
||||||
|
@ -8942,11 +8945,14 @@ NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(nsGlobalWindow)
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsGlobalWindow::DispatchEvent(nsIDOMEvent* aEvent, bool* aRetVal)
|
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);
|
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) {
|
if (!mDoc) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -11824,20 +11830,6 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
||||||
continue;
|
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
|
// This timeout is good to run
|
||||||
++timeoutsRan;
|
++timeoutsRan;
|
||||||
bool timeout_was_cleared = RunTimeoutHandler(timeout, scx);
|
bool timeout_was_cleared = RunTimeoutHandler(timeout, scx);
|
||||||
|
|
|
@ -354,7 +354,6 @@ public:
|
||||||
|
|
||||||
void PoisonOuterWindowProxy(JSObject *aObject);
|
void PoisonOuterWindowProxy(JSObject *aObject);
|
||||||
virtual void OnFinalize(JSObject* aObject);
|
virtual void OnFinalize(JSObject* aObject);
|
||||||
virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts);
|
|
||||||
|
|
||||||
virtual bool IsBlackForCC();
|
virtual bool IsBlackForCC();
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,8 @@ class nsIDOMWindow;
|
||||||
class nsIURI;
|
class nsIURI;
|
||||||
|
|
||||||
#define NS_ISCRIPTCONTEXT_IID \
|
#define NS_ISCRIPTCONTEXT_IID \
|
||||||
{ 0xf3859ce7, 0x7551, 0x4760, \
|
{ 0x513c2c1a, 0xf4f1, 0x44da, \
|
||||||
{ 0x84, 0x29, 0x64, 0x4f, 0x26, 0x1e, 0xdb, 0x91 } }
|
{ 0x8e, 0x38, 0xf4, 0x0c, 0x30, 0x9a, 0x5d, 0xef } }
|
||||||
|
|
||||||
/* This MUST match JSVERSION_DEFAULT. This version stuff if we don't
|
/* This MUST match JSVERSION_DEFAULT. This version stuff if we don't
|
||||||
know what language we have is a little silly... */
|
know what language we have is a little silly... */
|
||||||
|
@ -128,12 +128,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void GC(JS::gcreason::Reason aReason) = 0;
|
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
|
// SetProperty is suspect and jst believes should not be needed. Currenly
|
||||||
// used only for "arguments".
|
// used only for "arguments".
|
||||||
virtual nsresult SetProperty(JS::Handle<JSObject*> aTarget,
|
virtual nsresult SetProperty(JS::Handle<JSObject*> aTarget,
|
||||||
|
|
|
@ -27,8 +27,8 @@ NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
|
||||||
|
|
||||||
|
|
||||||
#define NS_ISCRIPTGLOBALOBJECT_IID \
|
#define NS_ISCRIPTGLOBALOBJECT_IID \
|
||||||
{ 0x214fa2f6, 0xcc0c, 0x42cf, \
|
{ 0xa6c0bfae, 0x8be4, 0x4747, \
|
||||||
{ 0x98, 0x4b, 0x45, 0xf5, 0x73, 0x9c, 0x6b, 0x73 } }
|
{ 0xaf, 0x1a, 0xe3, 0xf0, 0x3f, 0xb6, 0x0e, 0xb8 } }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The global object which keeps a script context for each supported script
|
* The global object which keeps a script context for each supported script
|
||||||
|
@ -70,11 +70,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void OnFinalize(JSObject* aObject) = 0;
|
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.
|
* Handle a script error. Generally called by a script context.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -839,7 +839,6 @@ nsJSContext::nsJSContext(bool aGCOnDestruction,
|
||||||
js_options_dot_str, this);
|
js_options_dot_str, this);
|
||||||
}
|
}
|
||||||
mIsInitialized = false;
|
mIsInitialized = false;
|
||||||
mScriptsEnabled = true;
|
|
||||||
mProcessingScriptTag = false;
|
mProcessingScriptTag = false;
|
||||||
HoldJSObjects(this);
|
HoldJSObjects(this);
|
||||||
}
|
}
|
||||||
|
@ -950,10 +949,6 @@ nsJSContext::EvaluateString(const nsAString& aScript,
|
||||||
void **aOffThreadToken)
|
void **aOffThreadToken)
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
|
NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
|
||||||
if (!mScriptsEnabled) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoCxPusher pusher(mContext);
|
AutoCxPusher pusher(mContext);
|
||||||
nsJSUtils::EvaluateOptions evalOptions;
|
nsJSUtils::EvaluateOptions evalOptions;
|
||||||
evalOptions.setCoerceToString(aCoerceToString);
|
evalOptions.setCoerceToString(aCoerceToString);
|
||||||
|
@ -1863,27 +1858,6 @@ nsJSContext::IsContextInitialized()
|
||||||
return mIsInitialized;
|
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
|
bool
|
||||||
nsJSContext::GetProcessingScriptTag()
|
nsJSContext::GetProcessingScriptTag()
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,9 +59,6 @@ public:
|
||||||
virtual nsresult InitContext() MOZ_OVERRIDE;
|
virtual nsresult InitContext() MOZ_OVERRIDE;
|
||||||
virtual bool IsContextInitialized() 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 nsresult SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aVal) MOZ_OVERRIDE;
|
||||||
|
|
||||||
virtual bool GetProcessingScriptTag() MOZ_OVERRIDE;
|
virtual bool GetProcessingScriptTag() MOZ_OVERRIDE;
|
||||||
|
@ -166,7 +163,6 @@ private:
|
||||||
JS::Heap<JSObject*> mWindowProxy;
|
JS::Heap<JSObject*> mWindowProxy;
|
||||||
|
|
||||||
bool mIsInitialized;
|
bool mIsInitialized;
|
||||||
bool mScriptsEnabled;
|
|
||||||
bool mGCOnDestruction;
|
bool mGCOnDestruction;
|
||||||
bool mProcessingScriptTag;
|
bool mProcessingScriptTag;
|
||||||
|
|
||||||
|
|
|
@ -241,15 +241,14 @@ nsJSUtils::EvaluateString(JSContext* aCx,
|
||||||
|
|
||||||
JS::ExposeObjectToActiveJS(aScopeObject);
|
JS::ExposeObjectToActiveJS(aScopeObject);
|
||||||
nsAutoMicroTask mt;
|
nsAutoMicroTask mt;
|
||||||
|
nsresult rv = NS_OK;
|
||||||
|
|
||||||
JSPrincipals* p = JS_GetCompartmentPrincipals(js::GetObjectCompartment(aScopeObject));
|
JSPrincipals* p = JS_GetCompartmentPrincipals(js::GetObjectCompartment(aScopeObject));
|
||||||
aCompileOptions.setPrincipals(p);
|
aCompileOptions.setPrincipals(p);
|
||||||
|
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
nsresult rv = nsContentUtils::GetSecurityManager()->
|
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||||
CanExecuteScripts(aCx, nsJSPrincipals::get(p), &ok);
|
NS_ENSURE_TRUE(ssm->ScriptAllowed(js::GetGlobalForObjectCrossCompartment(aScopeObject)), NS_OK);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
NS_ENSURE_TRUE(ok, NS_OK);
|
|
||||||
|
|
||||||
mozilla::Maybe<AutoDontReportUncaught> dontReport;
|
mozilla::Maybe<AutoDontReportUncaught> dontReport;
|
||||||
if (!aEvaluateOptions.reportUncaught) {
|
if (!aEvaluateOptions.reportUncaught) {
|
||||||
|
|
|
@ -120,14 +120,12 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
|
||||||
|
|
||||||
if (mIsMainThread) {
|
if (mIsMainThread) {
|
||||||
// Check that it's ok to run this callback at all.
|
// 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 to get the global of
|
||||||
// Make sure to unwrap aCallback before passing it in, because
|
// the callback object, not the wrapper.
|
||||||
// getting principals from wrappers is silly.
|
bool allowed = nsContentUtils::GetSecurityManager()->
|
||||||
nsresult rv = nsContentUtils::GetSecurityManager()->
|
ScriptAllowed(js::GetGlobalForObjectCrossCompartment(js::UncheckedUnwrap(aCallback)));
|
||||||
CheckFunctionAccess(cx, js::UncheckedUnwrap(aCallback), nullptr);
|
|
||||||
|
|
||||||
if (NS_FAILED(rv)) {
|
if (!allowed) {
|
||||||
// Security check failed. We're done here.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,7 +249,14 @@ AdapterPropertiesChangeCallback(bt_status_t aStatus, int aNumProperties,
|
||||||
propertiesArray.AppendElement(
|
propertiesArray.AppendElement(
|
||||||
BluetoothNamedValue(NS_LITERAL_STRING("Name"), propertyValue));
|
BluetoothNamedValue(NS_LITERAL_STRING("Name"), propertyValue));
|
||||||
} else if (p.type == BT_PROPERTY_ADAPTER_SCAN_MODE) {
|
} 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(
|
propertiesArray.AppendElement(
|
||||||
BluetoothNamedValue(NS_LITERAL_STRING("Discoverable"), propertyValue));
|
BluetoothNamedValue(NS_LITERAL_STRING("Discoverable"), propertyValue));
|
||||||
} else if (p.type == BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT) {
|
} else if (p.type == BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT) {
|
||||||
|
@ -637,9 +644,12 @@ ReplyStatusError(BluetoothReplyRunnable* aBluetoothReplyRunnable,
|
||||||
int aStatusCode, const nsAString& aCustomMsg)
|
int aStatusCode, const nsAString& aCustomMsg)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aBluetoothReplyRunnable, "Reply runnable is nullptr");
|
MOZ_ASSERT(aBluetoothReplyRunnable, "Reply runnable is nullptr");
|
||||||
nsAutoString replyError;
|
|
||||||
|
|
||||||
|
BT_LOGR("%s: error code(%d)", __FUNCTION__, aStatusCode);
|
||||||
|
|
||||||
|
nsAutoString replyError;
|
||||||
replyError.Assign(aCustomMsg);
|
replyError.Assign(aCustomMsg);
|
||||||
|
|
||||||
if (aStatusCode == BT_STATUS_BUSY) {
|
if (aStatusCode == BT_STATUS_BUSY) {
|
||||||
replyError.AppendLiteral(":BT_STATUS_BUSY");
|
replyError.AppendLiteral(":BT_STATUS_BUSY");
|
||||||
} else if (aStatusCode == BT_STATUS_NOT_READY) {
|
} else if (aStatusCode == BT_STATUS_NOT_READY) {
|
||||||
|
@ -840,6 +850,7 @@ BluetoothServiceBluedroid::SetProperty(BluetoothObjectType aType,
|
||||||
|
|
||||||
const nsString propName = aValue.name();
|
const nsString propName = aValue.name();
|
||||||
bt_property_t prop;
|
bt_property_t prop;
|
||||||
|
bt_scan_mode_t scanMode;
|
||||||
nsString str;
|
nsString str;
|
||||||
|
|
||||||
// For Bluedroid, it's necessary to check property name for SetProperty
|
// For Bluedroid, it's necessary to check property name for SetProperty
|
||||||
|
@ -863,22 +874,23 @@ BluetoothServiceBluedroid::SetProperty(BluetoothObjectType aType,
|
||||||
prop.val = (void*)name;
|
prop.val = (void*)name;
|
||||||
prop.len = strlen(name);
|
prop.len = strlen(name);
|
||||||
} else if (aValue.value().type() == BluetoothValue::Tbool) {
|
} else if (aValue.value().type() == BluetoothValue::Tbool) {
|
||||||
bt_scan_mode_t mode = aValue.value().get_bool() ?
|
scanMode = aValue.value().get_bool() ?
|
||||||
BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE :
|
BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE :
|
||||||
BT_SCAN_MODE_CONNECTABLE;
|
BT_SCAN_MODE_CONNECTABLE;
|
||||||
bt_scan_mode_t* sss = &mode;
|
|
||||||
prop.val = (void*)sss;
|
prop.val = (void*)&scanMode;
|
||||||
prop.len = sizeof(sss);
|
prop.len = sizeof(scanMode);
|
||||||
} else {
|
} else {
|
||||||
BT_LOGR("SetProperty but the property cannot be recognized correctly.");
|
BT_LOGR("SetProperty but the property cannot be recognized correctly.");
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
sSetPropertyRunnableArray.AppendElement(aRunnable);
|
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"));
|
ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("SetProperty"));
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
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,
|
_testReadyLockCount: 0,
|
||||||
_firedTestReady: false,
|
_firedTestReady: false,
|
||||||
lockTestReady: function() {
|
lockTestReady: function() {
|
||||||
|
@ -44,9 +49,11 @@ const browserElementTestHelpers = {
|
||||||
},
|
},
|
||||||
|
|
||||||
enableProcessPriorityManager: function() {
|
enableProcessPriorityManager: function() {
|
||||||
this._setPref('dom.ipc.processPriorityManager.testMode', true);
|
this._setPrefs(
|
||||||
this._setPref('dom.ipc.processPriorityManager.enabled', true);
|
['dom.ipc.processPriorityManager.testMode', true],
|
||||||
this._setPref('dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2);
|
['dom.ipc.processPriorityManager.enabled', true],
|
||||||
|
['dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2]
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
setEnabledPref: function(value) {
|
setEnabledPref: function(value) {
|
||||||
|
|
|
@ -289,11 +289,12 @@ parent:
|
||||||
ContentReceivedTouch(ScrollableLayerGuid aGuid, bool aPreventDefault);
|
ContentReceivedTouch(ScrollableLayerGuid aGuid, bool aPreventDefault);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the parent's zoom constraints for this tab. The zoom controller
|
* Updates the zoom constraints for a scrollable frame in this tab.
|
||||||
* code lives on the parent side and so this allows it to have up-to-date
|
* The zoom controller code lives on the parent side and so this allows it to
|
||||||
* zoom constraints.
|
* 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
|
* Notifies the parent about a scroll event. The pres shell ID and
|
||||||
|
|
|
@ -537,8 +537,17 @@ TabChild::HandlePossibleViewportChange()
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
|
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
|
||||||
|
|
||||||
|
uint32_t presShellId;
|
||||||
|
ViewID viewId;
|
||||||
|
if (!APZCCallbackHelper::GetScrollIdentifiers(document->GetDocumentElement(),
|
||||||
|
&presShellId, &viewId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, mInnerSize);
|
nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, mInnerSize);
|
||||||
SendUpdateZoomConstraints(viewportInfo.IsZoomAllowed(),
|
SendUpdateZoomConstraints(presShellId,
|
||||||
|
viewId,
|
||||||
|
viewportInfo.IsZoomAllowed(),
|
||||||
viewportInfo.GetMinZoom(),
|
viewportInfo.GetMinZoom(),
|
||||||
viewportInfo.GetMaxZoom());
|
viewportInfo.GetMaxZoom());
|
||||||
|
|
||||||
|
|
|
@ -1626,12 +1626,14 @@ TabParent::RecvZoomToRect(const uint32_t& aPresShellId,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TabParent::RecvUpdateZoomConstraints(const bool& aAllowZoom,
|
TabParent::RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
|
||||||
|
const ViewID& aViewId,
|
||||||
|
const bool& aAllowZoom,
|
||||||
const CSSToScreenScale& aMinZoom,
|
const CSSToScreenScale& aMinZoom,
|
||||||
const CSSToScreenScale& aMaxZoom)
|
const CSSToScreenScale& aMaxZoom)
|
||||||
{
|
{
|
||||||
if (RenderFrameParent* rfp = GetRenderFrame()) {
|
if (RenderFrameParent* rfp = GetRenderFrame()) {
|
||||||
rfp->UpdateZoomConstraints(aAllowZoom, aMinZoom, aMaxZoom);
|
rfp->UpdateZoomConstraints(aPresShellId, aViewId, aAllowZoom, aMinZoom, aMaxZoom);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,7 +165,9 @@ public:
|
||||||
virtual bool RecvZoomToRect(const uint32_t& aPresShellId,
|
virtual bool RecvZoomToRect(const uint32_t& aPresShellId,
|
||||||
const ViewID& aViewId,
|
const ViewID& aViewId,
|
||||||
const CSSRect& aRect);
|
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& aMinZoom,
|
||||||
const CSSToScreenScale& aMaxZoom);
|
const CSSToScreenScale& aMaxZoom);
|
||||||
virtual bool RecvUpdateScrollOffset(const uint32_t& aPresShellId, const ViewID& aViewId, const CSSIntPoint& aScrollOffset);
|
virtual bool RecvUpdateScrollOffset(const uint32_t& aPresShellId, const ViewID& aViewId, const CSSIntPoint& aScrollOffset);
|
||||||
|
|
|
@ -95,6 +95,15 @@ this.SystemMessagePermissionsTable = {
|
||||||
"cdma-info-rec-received": {
|
"cdma-info-rec-received": {
|
||||||
"mobileconnection": []
|
"mobileconnection": []
|
||||||
},
|
},
|
||||||
|
"nfc-manager-tech-discovered": {
|
||||||
|
"nfc-manager": []
|
||||||
|
},
|
||||||
|
"nfc-manager-tech-lost": {
|
||||||
|
"nfc-manager": []
|
||||||
|
},
|
||||||
|
"nfc-powerlevel-change": {
|
||||||
|
"settings": ["read", "write"]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.SystemMessagePermissionsChecker = {
|
this.SystemMessagePermissionsChecker = {
|
||||||
|
|
|
@ -97,6 +97,9 @@ if CONFIG['MOZ_PAY']:
|
||||||
if CONFIG['MOZ_GAMEPAD']:
|
if CONFIG['MOZ_GAMEPAD']:
|
||||||
PARALLEL_DIRS += ['gamepad']
|
PARALLEL_DIRS += ['gamepad']
|
||||||
|
|
||||||
|
if CONFIG['MOZ_NFC']:
|
||||||
|
PARALLEL_DIRS += ['nfc']
|
||||||
|
|
||||||
# bindings/test is here, because it needs to build after bindings/, and
|
# bindings/test is here, because it needs to build after bindings/, and
|
||||||
# we build subdirectories before ourselves.
|
# we build subdirectories before ourselves.
|
||||||
TEST_DIRS += [
|
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
|
* Needed to account for multiple possible types that can be provided to
|
||||||
* the socket callbacks as arguments.
|
* the socket callbacks as arguments.
|
||||||
*/
|
*/
|
||||||
[scriptable, uuid(234c664c-3d6c-4859-b45c-4e9a98cb5bdc)]
|
[scriptable, uuid(017f130f-2477-4215-8783-57eada957699)]
|
||||||
interface nsITCPSocketInternal : nsISupports {
|
interface nsITCPSocketInternal : nsISupports {
|
||||||
// Trigger the callback for |type| and provide a DOMError() object with the given data
|
// Trigger the callback for |type| and provide a DOMError() object with the given data
|
||||||
void callListenerError(in DOMString type, in DOMString name);
|
void callListenerError(in DOMString type, in DOMString name);
|
||||||
|
@ -229,8 +230,20 @@ interface nsITCPSocketInternal : nsISupports {
|
||||||
// Trigger the callback for |type| with no argument
|
// Trigger the callback for |type| with no argument
|
||||||
void callListenerVoid(in DOMString type);
|
void callListenerVoid(in DOMString type);
|
||||||
|
|
||||||
// Update the DOM object's readyState and bufferedAmount values with the provided data
|
// Update the DOM object's readyState.
|
||||||
void updateReadyStateAndBuffered(in DOMString readyState, in uint32_t bufferedAmount);
|
// @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.
|
// Create a socket object on the parent side.
|
||||||
// This is called in accepting any open request 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.
|
// Set App ID.
|
||||||
void setAppId(in unsigned long appId);
|
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 nsIDOMWindow;
|
||||||
|
|
||||||
// Interface to allow the content process socket to reach the IPC bridge.
|
// 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
|
interface nsITCPSocketChild : nsISupports
|
||||||
{
|
{
|
||||||
// Tell the chrome process to open a corresponding connection with the given parameters
|
// Tell the chrome process to open a corresponding connection with the given parameters
|
||||||
[implicit_jscontext]
|
[implicit_jscontext]
|
||||||
void open(in nsITCPSocketInternal socket, in DOMString host,
|
void sendOpen(in nsITCPSocketInternal socket, in DOMString host,
|
||||||
in unsigned short port, in boolean ssl, in DOMString binaryType,
|
in unsigned short port, in boolean ssl, in DOMString binaryType,
|
||||||
in nsIDOMWindow window, in jsval windowVal);
|
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
|
// Tell the chrome process to perform equivalent operations to all following methods
|
||||||
[implicit_jscontext]
|
void sendResume();
|
||||||
void send(in jsval data, in unsigned long byteOffset, in unsigned long byteLength);
|
void sendSuspend();
|
||||||
void resume();
|
void sendClose();
|
||||||
void suspend();
|
void sendStartTLS();
|
||||||
void close();
|
|
||||||
void startTLS();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the TCP socket on the child side for IPC. It is called from the child side,
|
* 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 nsITCPServerSocketParent;
|
||||||
interface nsITCPSocketIntermediary;
|
interface nsITCPSocketIntermediary;
|
||||||
|
|
||||||
// Interface required to allow the TCP socket object in the parent process
|
// Interface required to allow the TCP socket object (TCPSocket.js) in the
|
||||||
// to talk to the parent IPC actor
|
// parent process to talk to the parent IPC actor, TCPSocketParent, which
|
||||||
[scriptable, uuid(123f654b-4435-43c8-8447-db1b5420a1c2)]
|
// is written in C++.
|
||||||
|
[scriptable, uuid(868662a4-681c-4b89-9f02-6fe5b7ace265)]
|
||||||
interface nsITCPSocketParent : nsISupports
|
interface nsITCPSocketParent : nsISupports
|
||||||
{
|
{
|
||||||
[implicit_jscontext] void initJS(in jsval intermediary);
|
[implicit_jscontext] void initJS(in jsval intermediary);
|
||||||
|
|
||||||
// Trigger a callback in the content process for |type|, providing a serialized
|
// Trigger a callback in the content process for |type|, providing a serialized
|
||||||
// argument of |data|, and update the child's readyState and bufferedAmount values
|
// argument of |data|, and update the child's readyState value with the given
|
||||||
// with the given values.
|
// values.
|
||||||
[implicit_jscontext] void sendCallback(in DOMString type,
|
//
|
||||||
in jsval data,
|
// @param type
|
||||||
in DOMString readyState,
|
// Event type: 'onopen', 'ondata', 'onerror' or 'onclose'. 'odrain' is
|
||||||
in uint32_t bufferedAmount);
|
// 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,
|
// 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.
|
// which is generated in accepting any open request on the parent side.
|
||||||
|
@ -34,11 +42,27 @@ interface nsITCPSocketParent : nsISupports
|
||||||
// Intermediate class object. See nsITCPSocketIntermediary.
|
// Intermediate class object. See nsITCPSocketIntermediary.
|
||||||
[implicit_jscontext] void setSocketAndIntermediary(in nsIDOMTCPSocket socket,
|
[implicit_jscontext] void setSocketAndIntermediary(in nsIDOMTCPSocket socket,
|
||||||
in nsITCPSocketIntermediary intermediary);
|
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
|
// Intermediate class to handle sending multiple possible data types
|
||||||
// and kicking off the chrome process socket object's connection.
|
// 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 {
|
interface nsITCPSocketIntermediary : nsISupports {
|
||||||
// Open the connection to the server with the given parameters
|
// Open the connection to the server with the given parameters
|
||||||
nsIDOMTCPSocket open(in nsITCPSocketParent parent,
|
nsIDOMTCPSocket open(in nsITCPSocketParent parent,
|
||||||
|
@ -51,9 +75,9 @@ interface nsITCPSocketIntermediary : nsISupports {
|
||||||
in unsigned short port, in unsigned short backlog,
|
in unsigned short port, in unsigned short backlog,
|
||||||
in DOMString binaryType);
|
in DOMString binaryType);
|
||||||
|
|
||||||
// Send a basic string along the connection
|
// Called when received a child request to send a string.
|
||||||
void sendString(in DOMString data);
|
void onRecvSendString(in DOMString data, in uint32_t trackingNumber);
|
||||||
|
|
||||||
// Send a typed array
|
// Called when received a child request to send an array buffer.
|
||||||
void sendArrayBuffer(in jsval data);
|
void onRecvSendArrayBuffer(in jsval data, in uint32_t trackingNumber);
|
||||||
};
|
};
|
||||||
|
|
|
@ -362,6 +362,7 @@ this.NetworkStatsService = {
|
||||||
this.updateQueue.push({netId: aNetId, callbacks: [aCallback]});
|
this.updateQueue.push({netId: aNetId, callbacks: [aCallback]});
|
||||||
} else {
|
} else {
|
||||||
this.updateQueue[index].callbacks.push(aCallback);
|
this.updateQueue[index].callbacks.push(aCallback);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the function that process the elements of the queue.
|
// Call the function that process the elements of the queue.
|
||||||
|
|
|
@ -35,16 +35,35 @@ protocol PTCPSocket
|
||||||
manager PNecko;
|
manager PNecko;
|
||||||
|
|
||||||
parent:
|
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);
|
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();
|
StartTLS();
|
||||||
|
|
||||||
|
// Forward calling to child's send() method to parent.
|
||||||
Suspend();
|
Suspend();
|
||||||
|
|
||||||
|
// Forward calling to child's resume() method to parent.
|
||||||
Resume();
|
Resume();
|
||||||
|
|
||||||
|
// Forward calling to child's close() method to parent.
|
||||||
Close();
|
Close();
|
||||||
|
|
||||||
child:
|
child:
|
||||||
Callback(nsString type, CallbackData data,
|
// Forward events that are dispatched by parent.
|
||||||
nsString readyState, uint32_t bufferedAmount);
|
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:
|
both:
|
||||||
RequestDelete();
|
RequestDelete();
|
||||||
|
|
|
@ -162,6 +162,10 @@ TCPSocket.prototype = {
|
||||||
_waitingForStartTLS: false,
|
_waitingForStartTLS: false,
|
||||||
_pendingDataAfterStartTLS: [],
|
_pendingDataAfterStartTLS: [],
|
||||||
|
|
||||||
|
// Used to notify when update bufferedAmount is updated.
|
||||||
|
_onUpdateBufferedAmount: null,
|
||||||
|
_trackingNumber: 0,
|
||||||
|
|
||||||
#ifdef MOZ_WIDGET_GONK
|
#ifdef MOZ_WIDGET_GONK
|
||||||
// Network statistics (Gonk-specific feature)
|
// Network statistics (Gonk-specific feature)
|
||||||
_txBytes: 0,
|
_txBytes: 0,
|
||||||
|
@ -242,6 +246,12 @@ TCPSocket.prototype = {
|
||||||
.createTransport(options, 1, host, port, null);
|
.createTransport(options, 1, host, port, null);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_sendBufferedAmount: function ts_sendBufferedAmount() {
|
||||||
|
if (this._onUpdateBufferedAmount) {
|
||||||
|
this._onUpdateBufferedAmount(this.bufferedAmount, this._trackingNumber);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_ensureCopying: function ts_ensureCopying() {
|
_ensureCopying: function ts_ensureCopying() {
|
||||||
let self = this;
|
let self = this;
|
||||||
if (this._asyncCopierActive) {
|
if (this._asyncCopierActive) {
|
||||||
|
@ -254,6 +264,7 @@ TCPSocket.prototype = {
|
||||||
onStopRequest: function ts_output_onStopRequest(request, context, status) {
|
onStopRequest: function ts_output_onStopRequest(request, context, status) {
|
||||||
self._asyncCopierActive = false;
|
self._asyncCopierActive = false;
|
||||||
self._multiplexStream.removeStream(0);
|
self._multiplexStream.removeStream(0);
|
||||||
|
self._sendBufferedAmount();
|
||||||
|
|
||||||
if (!Components.isSuccessCode(status)) {
|
if (!Components.isSuccessCode(status)) {
|
||||||
// Note that we can/will get an error here as well as in the
|
// 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._waitingForDrain = false;
|
||||||
self.callListener("drain");
|
self.callListener("drain");
|
||||||
}
|
}
|
||||||
|
@ -382,9 +395,36 @@ TCPSocket.prototype = {
|
||||||
this.callListener(type);
|
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;
|
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;
|
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) {
|
createAcceptedParent: function ts_createAcceptedParent(transport, binaryType) {
|
||||||
|
@ -420,6 +460,25 @@ TCPSocket.prototype = {
|
||||||
#endif
|
#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 */
|
/* end nsITCPSocketInternal methods */
|
||||||
|
|
||||||
initWindowless: function ts_initWindowless() {
|
initWindowless: function ts_initWindowless() {
|
||||||
|
@ -517,8 +576,8 @@ TCPSocket.prototype = {
|
||||||
if (this._inChild) {
|
if (this._inChild) {
|
||||||
that._socketBridge = Cc["@mozilla.org/tcp-socket-child;1"]
|
that._socketBridge = Cc["@mozilla.org/tcp-socket-child;1"]
|
||||||
.createInstance(Ci.nsITCPSocketChild);
|
.createInstance(Ci.nsITCPSocketChild);
|
||||||
that._socketBridge.open(that, host, port, !!that._ssl,
|
that._socketBridge.sendOpen(that, host, port, !!that._ssl,
|
||||||
that._binaryType, this.useWin, this.useWin || this);
|
that._binaryType, this.useWin, this.useWin || this);
|
||||||
return that;
|
return that;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,7 +610,7 @@ TCPSocket.prototype = {
|
||||||
this._ssl = 'ssl';
|
this._ssl = 'ssl';
|
||||||
|
|
||||||
if (this._inChild) {
|
if (this._inChild) {
|
||||||
this._socketBridge.startTLS();
|
this._socketBridge.sendStartTLS();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,7 +644,7 @@ TCPSocket.prototype = {
|
||||||
this._readyState = kCLOSING;
|
this._readyState = kCLOSING;
|
||||||
|
|
||||||
if (this._inChild) {
|
if (this._inChild) {
|
||||||
this._socketBridge.close();
|
this._socketBridge.sendClose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,15 +664,27 @@ TCPSocket.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._inChild) {
|
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 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) {
|
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;
|
let new_stream;
|
||||||
|
@ -633,15 +704,6 @@ TCPSocket.prototype = {
|
||||||
this._multiplexStream.appendStream(new_stream);
|
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();
|
this._ensureCopying();
|
||||||
|
|
||||||
#ifdef MOZ_WIDGET_GONK
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
@ -650,12 +712,12 @@ TCPSocket.prototype = {
|
||||||
this._saveNetworkStats(false);
|
this._saveNetworkStats(false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return bufferNotFull;
|
return !bufferFull;
|
||||||
},
|
},
|
||||||
|
|
||||||
suspend: function ts_suspend() {
|
suspend: function ts_suspend() {
|
||||||
if (this._inChild) {
|
if (this._inChild) {
|
||||||
this._socketBridge.suspend();
|
this._socketBridge.sendSuspend();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -668,7 +730,7 @@ TCPSocket.prototype = {
|
||||||
|
|
||||||
resume: function ts_resume() {
|
resume: function ts_resume() {
|
||||||
if (this._inChild) {
|
if (this._inChild) {
|
||||||
this._socketBridge.resume();
|
this._socketBridge.sendResume();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,10 +77,11 @@ TCPSocketChild::TCPSocketChild()
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
TCPSocketChild::Open(nsITCPSocketInternal* aSocket, const nsAString& aHost,
|
TCPSocketChild::SendOpen(nsITCPSocketInternal* aSocket,
|
||||||
uint16_t aPort, bool aUseSSL, const nsAString& aBinaryType,
|
const nsAString& aHost, uint16_t aPort,
|
||||||
nsIDOMWindow* aWindow, const JS::Value& aWindowObj,
|
bool aUseSSL, const nsAString& aBinaryType,
|
||||||
JSContext* aCx)
|
nsIDOMWindow* aWindow, const JS::Value& aWindowObj,
|
||||||
|
JSContext* aCx)
|
||||||
{
|
{
|
||||||
mSocket = aSocket;
|
mSocket = aSocket;
|
||||||
|
|
||||||
|
@ -91,7 +92,8 @@ TCPSocketChild::Open(nsITCPSocketInternal* aSocket, const nsAString& aHost,
|
||||||
}
|
}
|
||||||
AddIPDLReference();
|
AddIPDLReference();
|
||||||
gNeckoChild->SendPTCPSocketConstructor(this);
|
gNeckoChild->SendPTCPSocketConstructor(this);
|
||||||
SendOpen(nsString(aHost), aPort, aUseSSL, nsString(aBinaryType));
|
PTCPSocketChild::SendOpen(nsString(aHost), aPort,
|
||||||
|
aUseSSL, nsString(aBinaryType));
|
||||||
return NS_OK;
|
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
|
bool
|
||||||
TCPSocketChild::RecvCallback(const nsString& aType,
|
TCPSocketChild::RecvCallback(const nsString& aType,
|
||||||
const CallbackData& aData,
|
const CallbackData& aData,
|
||||||
const nsString& aReadyState,
|
const nsString& aReadyState)
|
||||||
const uint32_t& aBuffered)
|
|
||||||
{
|
{
|
||||||
if (NS_FAILED(mSocket->UpdateReadyStateAndBuffered(aReadyState, aBuffered)))
|
if (NS_FAILED(mSocket->UpdateReadyState(aReadyState)))
|
||||||
NS_ERROR("Shouldn't fail!");
|
NS_ERROR("Shouldn't fail!");
|
||||||
|
|
||||||
nsresult rv = NS_ERROR_FAILURE;
|
nsresult rv = NS_ERROR_FAILURE;
|
||||||
|
@ -159,46 +170,46 @@ TCPSocketChild::RecvCallback(const nsString& aType,
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
TCPSocketChild::StartTLS()
|
TCPSocketChild::SendStartTLS()
|
||||||
{
|
{
|
||||||
SendStartTLS();
|
PTCPSocketChild::SendStartTLS();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
TCPSocketChild::Suspend()
|
TCPSocketChild::SendSuspend()
|
||||||
{
|
{
|
||||||
SendSuspend();
|
PTCPSocketChild::SendSuspend();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
TCPSocketChild::Resume()
|
TCPSocketChild::SendResume()
|
||||||
{
|
{
|
||||||
SendResume();
|
PTCPSocketChild::SendResume();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
TCPSocketChild::Close()
|
TCPSocketChild::SendClose()
|
||||||
{
|
{
|
||||||
SendClose();
|
PTCPSocketChild::SendClose();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
TCPSocketChild::Send(const JS::Value& aData,
|
TCPSocketChild::SendSend(const JS::Value& aData,
|
||||||
uint32_t aByteOffset,
|
uint32_t aByteOffset,
|
||||||
uint32_t aByteLength,
|
uint32_t aByteLength,
|
||||||
JSContext* aCx)
|
uint32_t aTrackingNumber,
|
||||||
|
JSContext* aCx)
|
||||||
{
|
{
|
||||||
if (aData.isString()) {
|
if (aData.isString()) {
|
||||||
JSString* jsstr = aData.toString();
|
JSString* jsstr = aData.toString();
|
||||||
nsDependentJSString str;
|
nsDependentJSString str;
|
||||||
bool ok = str.init(aCx, jsstr);
|
bool ok = str.init(aCx, jsstr);
|
||||||
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
||||||
SendData(str);
|
SendData(str, aTrackingNumber);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
NS_ENSURE_TRUE(aData.isObject(), NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(aData.isObject(), NS_ERROR_FAILURE);
|
||||||
JS::Rooted<JSObject*> obj(aCx, &aData.toObject());
|
JS::Rooted<JSObject*> obj(aCx, &aData.toObject());
|
||||||
|
@ -216,15 +227,15 @@ TCPSocketChild::Send(const JS::Value& aData,
|
||||||
}
|
}
|
||||||
InfallibleTArray<uint8_t> arr;
|
InfallibleTArray<uint8_t> arr;
|
||||||
arr.SwapElements(fallibleArr);
|
arr.SwapElements(fallibleArr);
|
||||||
SendData(arr);
|
SendData(arr, aTrackingNumber);
|
||||||
}
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
TCPSocketChild::SetSocketAndWindow(nsITCPSocketInternal *aSocket,
|
TCPSocketChild::SetSocketAndWindow(nsITCPSocketInternal *aSocket,
|
||||||
const JS::Value& aWindowObj,
|
const JS::Value& aWindowObj,
|
||||||
JSContext* aCx)
|
JSContext* aCx)
|
||||||
{
|
{
|
||||||
mSocket = aSocket;
|
mSocket = aSocket;
|
||||||
MOZ_ASSERT(aWindowObj.isObject());
|
MOZ_ASSERT(aWindowObj.isObject());
|
||||||
|
|
|
@ -44,9 +44,10 @@ public:
|
||||||
|
|
||||||
virtual bool RecvCallback(const nsString& aType,
|
virtual bool RecvCallback(const nsString& aType,
|
||||||
const CallbackData& aData,
|
const CallbackData& aData,
|
||||||
const nsString& aReadyState,
|
const nsString& aReadyState) MOZ_OVERRIDE;
|
||||||
const uint32_t& aBuffered) MOZ_OVERRIDE;
|
|
||||||
virtual bool RecvRequestDelete() MOZ_OVERRIDE;
|
virtual bool RecvRequestDelete() MOZ_OVERRIDE;
|
||||||
|
virtual bool RecvUpdateBufferedAmount(const uint32_t& aBufferred,
|
||||||
|
const uint32_t& aTrackingNumber) MOZ_OVERRIDE;
|
||||||
private:
|
private:
|
||||||
JSObject* mWindowObj;
|
JSObject* mWindowObj;
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,7 +35,7 @@ FireInteralError(mozilla::net::PTCPSocketParent* aActor, uint32_t aLineNo)
|
||||||
mozilla::unused <<
|
mozilla::unused <<
|
||||||
aActor->SendCallback(NS_LITERAL_STRING("onerror"),
|
aActor->SendCallback(NS_LITERAL_STRING("onerror"),
|
||||||
TCPError(NS_LITERAL_STRING("InvalidStateError")),
|
TCPError(NS_LITERAL_STRING("InvalidStateError")),
|
||||||
NS_LITERAL_STRING("connecting"), 0);
|
NS_LITERAL_STRING("connecting"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_2(TCPSocketParentBase, mSocket, mIntermediary)
|
NS_IMPL_CYCLE_COLLECTION_2(TCPSocketParentBase, mSocket, mIntermediary)
|
||||||
|
@ -156,7 +156,8 @@ TCPSocketParent::RecvResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TCPSocketParent::RecvData(const SendableData& aData)
|
TCPSocketParent::RecvData(const SendableData& aData,
|
||||||
|
const uint32_t& aTrackingNumber)
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(mIntermediary, true);
|
NS_ENSURE_TRUE(mIntermediary, true);
|
||||||
|
|
||||||
|
@ -168,13 +169,13 @@ TCPSocketParent::RecvData(const SendableData& aData)
|
||||||
JS::Rooted<JS::Value> val(cx);
|
JS::Rooted<JS::Value> val(cx);
|
||||||
JS::Rooted<JSObject*> obj(cx, mIntermediaryObj);
|
JS::Rooted<JSObject*> obj(cx, mIntermediaryObj);
|
||||||
IPC::DeserializeArrayBuffer(obj, aData.get_ArrayOfuint8_t(), &val);
|
IPC::DeserializeArrayBuffer(obj, aData.get_ArrayOfuint8_t(), &val);
|
||||||
rv = mIntermediary->SendArrayBuffer(val);
|
rv = mIntermediary->OnRecvSendArrayBuffer(val, aTrackingNumber);
|
||||||
NS_ENSURE_SUCCESS(rv, true);
|
NS_ENSURE_SUCCESS(rv, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SendableData::TnsString:
|
case SendableData::TnsString:
|
||||||
rv = mIntermediary->SendString(aData.get_nsString());
|
rv = mIntermediary->OnRecvSendString(aData.get_nsString(), aTrackingNumber);
|
||||||
NS_ENSURE_SUCCESS(rv, true);
|
NS_ENSURE_SUCCESS(rv, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -194,9 +195,8 @@ TCPSocketParent::RecvClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
TCPSocketParent::SendCallback(const nsAString& aType, const JS::Value& aDataVal,
|
TCPSocketParent::SendEvent(const nsAString& aType, const JS::Value& aDataVal,
|
||||||
const nsAString& aReadyState, uint32_t aBuffered,
|
const nsAString& aReadyState, JSContext* aCx)
|
||||||
JSContext* aCx)
|
|
||||||
{
|
{
|
||||||
if (!mIPCOpen) {
|
if (!mIPCOpen) {
|
||||||
NS_WARNING("Dropping callback due to no IPC connection");
|
NS_WARNING("Dropping callback due to no IPC connection");
|
||||||
|
@ -255,7 +255,7 @@ TCPSocketParent::SendCallback(const nsAString& aType, const JS::Value& aDataVal,
|
||||||
}
|
}
|
||||||
mozilla::unused <<
|
mozilla::unused <<
|
||||||
PTCPSocketParent::SendCallback(nsString(aType), data,
|
PTCPSocketParent::SendCallback(nsString(aType), data,
|
||||||
nsString(aReadyState), aBuffered);
|
nsString(aReadyState));
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +269,15 @@ TCPSocketParent::SetSocketAndIntermediary(nsIDOMTCPSocket *socket,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
TCPSocketParent::SendUpdateBufferedAmount(uint32_t aBufferedAmount,
|
||||||
|
uint32_t aTrackingNumber)
|
||||||
|
{
|
||||||
|
mozilla::unused << PTCPSocketParent::SendUpdateBufferedAmount(aBufferedAmount,
|
||||||
|
aTrackingNumber);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TCPSocketParent::ActorDestroy(ActorDestroyReason why)
|
TCPSocketParent::ActorDestroy(ActorDestroyReason why)
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,7 +51,8 @@ public:
|
||||||
virtual bool RecvSuspend() MOZ_OVERRIDE;
|
virtual bool RecvSuspend() MOZ_OVERRIDE;
|
||||||
virtual bool RecvResume() MOZ_OVERRIDE;
|
virtual bool RecvResume() MOZ_OVERRIDE;
|
||||||
virtual bool RecvClose() 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;
|
virtual bool RecvRequestDelete() MOZ_OVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -20,15 +20,21 @@ TCPSocketParentIntermediary.prototype = {
|
||||||
|
|
||||||
// Create handlers for every possible callback that attempt to trigger
|
// Create handlers for every possible callback that attempt to trigger
|
||||||
// corresponding callbacks on the child object.
|
// 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) {
|
function(p) {
|
||||||
socket["on" + p] = function(data) {
|
socket["on" + p] = function(data) {
|
||||||
aParentSide.sendCallback(p, data.data, socket.readyState,
|
aParentSide.sendEvent(p, data.data, socket.readyState,
|
||||||
socket.bufferedAmount);
|
socket.bufferedAmount);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onUpdateBufferedAmountHandler: function(aParentSide, aBufferedAmount, aTrackingNumber) {
|
||||||
|
aParentSide.sendUpdateBufferedAmount(aBufferedAmount, aTrackingNumber);
|
||||||
|
},
|
||||||
|
|
||||||
open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType, aAppId) {
|
open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType, aAppId) {
|
||||||
let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
|
let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
|
||||||
|
@ -41,6 +47,10 @@ TCPSocketParentIntermediary.prototype = {
|
||||||
socketInternal.setAppId(aAppId);
|
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.
|
// Handlers are set to the JS-implemented socket object on the parent side.
|
||||||
this._setCallbacks(aParentSide, socket);
|
this._setCallbacks(aParentSide, socket);
|
||||||
return socket;
|
return socket;
|
||||||
|
@ -79,12 +89,15 @@ TCPSocketParentIntermediary.prototype = {
|
||||||
return serverSocket;
|
return serverSocket;
|
||||||
},
|
},
|
||||||
|
|
||||||
sendString: function(aData) {
|
onRecvSendString: function(aData, aTrackingNumber) {
|
||||||
return this._socket.send(aData);
|
let socketInternal = this._socket.QueryInterface(Ci.nsITCPSocketInternal);
|
||||||
|
return socketInternal.onRecvSendFromChild(aData, 0, 0, aTrackingNumber);
|
||||||
},
|
},
|
||||||
|
|
||||||
sendArrayBuffer: function(aData) {
|
onRecvSendArrayBuffer: function(aData, aTrackingNumber) {
|
||||||
return this._socket.send(aData, 0, aData.byteLength);
|
let socketInternal = this._socket.QueryInterface(Ci.nsITCPSocketInternal);
|
||||||
|
return socketInternal.onRecvSendFromChild(aData, 0, aData.byteLength,
|
||||||
|
aTrackingNumber);
|
||||||
},
|
},
|
||||||
|
|
||||||
classID: Components.ID("{afa42841-a6cb-4a91-912f-93099f6a3d18}"),
|
classID: Components.ID("{afa42841-a6cb-4a91-912f-93099f6a3d18}"),
|
||||||
|
|
|
@ -80,6 +80,11 @@ function get_platform() {
|
||||||
return xulRuntime.OS;
|
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
|
* Spin up a listening socket and associate at most one live, accepted socket
|
||||||
* with ourselves.
|
* with ourselves.
|
||||||
|
@ -418,9 +423,15 @@ function drainTwice() {
|
||||||
['ondrain', 'ondrain2',
|
['ondrain', 'ondrain2',
|
||||||
'ondata', 'ondata2',
|
'ondata', 'ondata2',
|
||||||
'serverclose', 'clientclose']);
|
'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(
|
server.ondata = makeExpectData(
|
||||||
"ondata2", BIG_TYPED_ARRAY_2, false, yays.ondata2);
|
"ondata2", BIG_TYPED_ARRAY_2, false, yays.ondata2);
|
||||||
|
|
||||||
|
@ -433,12 +444,24 @@ function drainTwice() {
|
||||||
sock.close();
|
sock.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clientOndrain() {
|
||||||
|
yays.ondrain();
|
||||||
|
ondrainCalled = true;
|
||||||
|
maybeSendNextData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function serverSideCallback() {
|
||||||
|
yays.ondata();
|
||||||
|
ondataCalled = true;
|
||||||
|
maybeSendNextData();
|
||||||
|
}
|
||||||
|
|
||||||
server.onclose = yays.serverclose;
|
server.onclose = yays.serverclose;
|
||||||
server.ondata = makeExpectData(
|
server.ondata = makeExpectData(
|
||||||
"ondata", BIG_TYPED_ARRAY, false, serverSideCallback);
|
"ondata", BIG_TYPED_ARRAY, false, serverSideCallback);
|
||||||
|
|
||||||
sock.onclose = yays.clientclose;
|
sock.onclose = yays.clientclose;
|
||||||
sock.ondrain = yays.ondrain;
|
sock.ondrain = clientOndrain;
|
||||||
|
|
||||||
if (sock.send(BIG_ARRAY_BUFFER)) {
|
if (sock.send(BIG_ARRAY_BUFFER)) {
|
||||||
throw new Error("sock.send(BIG_TYPED_ARRAY) did not return false to indicate buffering");
|
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
|
// - connect, data and events work both ways
|
||||||
add_test(connectSock);
|
add_test(connectSock);
|
||||||
add_test(sendData);
|
add_test(sendData);
|
||||||
|
@ -513,6 +592,14 @@ add_test(drainTwice);
|
||||||
add_test(connectSock);
|
add_test(connectSock);
|
||||||
add_test(bufferTwice);
|
add_test(bufferTwice);
|
||||||
|
|
||||||
|
if (is_content()) {
|
||||||
|
add_test(connectSock);
|
||||||
|
add_test(childnotbuffered);
|
||||||
|
|
||||||
|
add_test(connectSock);
|
||||||
|
add_test(childbuffered);
|
||||||
|
}
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
add_test(cleanup);
|
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__
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче