import change from rmottola/Arctic-Fox:

- Bug 1134800 - Properly cache file matching patterns; r=glandium (0c65935e9)
- Bug 1136177 - Amount by mouse wheel scrolling is wrong. r=jimm (6cd8824a3)
- Bug 1055369 - Assertion failure: !handle || !handle->IsDoomed(). r=honzab (696f6d801)
- Bug 1132728 - Don't draw focus rings on b2g. r=fabrice,bz (0bf2719a2)
- Bug 1133201 - part 1 - treat null links in BrowserUtils.linkHasNoReferrer as specifying rel="noreferrer"; r=Gijs,mconley (97031e93c)
- Bug 906190 - Persist 'disable protection' option for Mixed Content Blocker in child tabs - tabbrowser part. r=gavin (7467fee83)
- Bug 947895 - [e10s] Null check browser.docShell in context-menu Open in new tab. r=gavin (5236b883e)
- Bug 1038604 - Rename disableMCB to allowMixedContent. r=dao (2df2b924b)
- Bug 1151349 - Make lldb ns(Int)Region summary handle the numRects==0 case. r=jrmuizel (19be5e2c0)
- Bug 1146585 - Add a test for Cache.delete; r=bkelly (db28bcc13)
- Bug 899222 - Make about:home work via message passing (r=felipe) Bug 900865: Make about:home call nsISearchEngine.getSubmission for all searches. r=gavin (15f8ae2f8)
- Bug 910523 - about:home now takes into account async startup of SessionRestore;r=felipe (ceaa0687c)
- Bug 927132 - Fix about:home in e10s (r=mdeboer) (53c586ee2)
- Bug 897062 - Handle special clicks in e10s. r=felipe,smaug (7ec4573c8)
- Bug 899348 - Implement about:tabcrashed page. r=jaws (547bb3e7c)
- Bug 899348 - Dispatch an event when an out-of-process browser crashes and display an error page when that occurs (missing parts) (88c87ce75)
- Bug 897066 - In e10s builds, some pages should be loaded in the chrome process (r=gavin) (4cc0f8ed5)
- Bug 1038811 - Push Notifications - WebIDL changes. r=nsm. sr=jst (2ed030bf3)
- Bug 1038811 - Push Notifications - ServiceWorker changes, push event implementation. r=nsm (6313c8c10)
- reinstantiate file as of 2015-02-05 (7f12724c8)
- Bug 1038811 - Push Notifications - Tests. r=nsm (cbac31308)
- Bug 1038811 - Push Notifications - Allow MOZ_DISABLE_NONLOCAL_CONNECTIONS for push subsuite. r=ahalberstadt/jgriffin (859e182ce)
- Bug 1038811 - Push Notifications - Push implementation changes. r=nsm (161a739f4)
- Bug 898170 - Avoid swapping docshells in e10s mode (r=felipe) (55e96d59a)
- Bug 862078 - Use an about:config preference to control multiprocess browsing, part 2 (r=felipe) [missing bits] (9fac04b17)
- Bug 666809 - Support SecurityUI in e10s mode. r=felipe f=gavin [missing bits] (0e7aa1368)
- Bug 691610 - e10s support for useDefaultIcon. r=felipe sr=smaug (cbf7e5341)
- Bug 897066 - Underline tab titles if the tab is remote (r=gavin) [+ followup fix] (2eda1d81e)
- Bug 899348 - Make reload of the about:tabcrashed work as expected. r=jaws (72843ef8f)
- Bug 1133846 - Add missing arguments to logging call in ActivateTimeoutTick. r=mcmanus (a784a7ce8)
- Bug 1135682 - Do not update the all MediaStreamGraph if it's not dirty, r=padenot (49eeeeef5)
- Bug 1135255 - Fix mozdevice tempfile handling on Windows. r=gbrown (53a2353bd)
- Bug 1134735 - Don't use tee->InitAsync in nsHttpChannel::InstallCacheListener when using cache2, r=michal (3fa833982)
- Bug 1132081 - Speed up ConvertHostARGBRow() in the PNG encoder. r=jmuizelaar (6b7890586)
- pointer style before patch (3336fab8f)
- Bug 1135100 - Don't update GC thing pointers that haven't changed after marking r=terrence (0df3ea820)
This commit is contained in:
2019-08-30 23:22:17 +08:00
parent c106e1d83f
commit 18efcca64b
85 changed files with 3779 additions and 511 deletions
+3
View File
@@ -34,6 +34,9 @@ pref("browser.tabs.remote.autostart.1", false);
// Bug 945235: Prevent all bars to be considered visible:
pref("toolkit.defaultChromeFeatures", "chrome,dialog=no,close,resizable,scrollbars,extrachrome");
// Disable focus rings
pref("browser.display.focus_ring_width", 0);
// Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
pref("browser.viewport.scaleRatio", -1);
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % browserDTD
SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/skin/aboutTabCrashed.css"/>
</head>
<body dir="&locale.dir;">
<div id="error-box">
<p id="main-error-msg">&tabCrashed.header;</p>
<p id="helper-error-msg">&tabCrashed.message;</p>
</div>
<div id="button-box">
<button id="tryAgain">&tabCrashed.tryAgain;</button>
</div>
</body>
<script type="text/javascript;version=1.8"><![CDATA[
function parseQueryString() {
let url = document.documentURI;
let queryString = url.replace(/^about:tabcrashed?e=tabcrashed/, "");
let urlMatch = queryString.match(/u=([^&]+)/);
let url = urlMatch && urlMatch[1] ? decodeURIComponent(urlMatch[1]) : "";
let titleMatch = queryString.match(/d=([^&]*)/);
title = titleMatch && titleMatch[1] ? decodeURIComponent(titleMatch[1]) : "";
return [url, title];
}
let [url, title] = parseQueryString();
document.title = title;
document.getElementById("tryAgain").setAttribute("url", url);
]]></script>
</html>
+14 -45
View File
@@ -237,7 +237,7 @@ const SEARCH_ENGINES = {
let gInitialized = false;
let gObserver = new MutationObserver(function (mutations) {
for (let mutation of mutations) {
if (mutation.attributeName == "searchEngineURL") {
if (mutation.attributeName == "searchEngineName") {
setupSearchEngine();
if (!gInitialized) {
gInitialized = true;
@@ -253,6 +253,10 @@ window.addEventListener("pageshow", function () {
window.gObserver.observe(document.documentElement, { attributes: true });
fitToWidth();
window.addEventListener("resize", fitToWidth);
// Ask chrome to update snippets.
var event = new CustomEvent("AboutHomeLoad", {bubbles:true});
document.dispatchEvent(event);
});
window.addEventListener("pagehide", function() {
@@ -263,52 +267,17 @@ window.addEventListener("pagehide", function() {
function onSearchSubmit(aEvent)
{
let searchTerms = document.getElementById("searchText").value;
let searchURL = document.documentElement.getAttribute("searchEngineURL");
let engineName = document.documentElement.getAttribute("searchEngineName");
if (searchURL && searchTerms.length > 0) {
// Send an event that a search was performed. This was originally
// added so Firefox Health Report could record that a search from
// about:home had occurred.
let engineName = document.documentElement.getAttribute("searchEngineName");
let event = new CustomEvent("AboutHomeSearchEvent", {detail: engineName});
if (engineName && searchTerms.length > 0) {
// Send an event that will perform a search and Firefox Health Report will
// record that a search from about:home has occurred.
let eventData = JSON.stringify({
engineName: engineName,
searchTerms: searchTerms
});
let event = new CustomEvent("AboutHomeSearchEvent", {detail: eventData});
document.dispatchEvent(event);
const SEARCH_TOKEN = "_searchTerms_";
let searchPostData = document.documentElement.getAttribute("searchEnginePostData");
if (searchPostData) {
// Check if a post form already exists. If so, remove it.
const POST_FORM_NAME = "searchFormPost";
let form = document.forms[POST_FORM_NAME];
if (form) {
form.parentNode.removeChild(form);
}
// Create a new post form.
form = document.body.appendChild(document.createElement("form"));
form.setAttribute("name", POST_FORM_NAME);
// Set the URL to submit the form to.
form.setAttribute("action", searchURL.replace(SEARCH_TOKEN, searchTerms));
form.setAttribute("method", "post");
// Create new <input type=hidden> elements for search param.
searchPostData = searchPostData.split("&");
for (let postVar of searchPostData) {
let [name, value] = postVar.split("=");
if (value == SEARCH_TOKEN) {
value = searchTerms;
}
let input = document.createElement("input");
input.setAttribute("type", "hidden");
input.setAttribute("name", name);
input.setAttribute("value", value);
form.appendChild(input);
}
// Submit the form.
form.submit();
} else {
searchURL = searchURL.replace(SEARCH_TOKEN, encodeURIComponent(searchTerms));
window.location.href = searchURL;
}
}
aEvent.preventDefault();
+12
View File
@@ -49,6 +49,10 @@ var FullZoom = {
// Initialization & Destruction
init: function FullZoom_init() {
// Bug 691614 - zooming support for electrolysis
if (gMultiProcessBrowser)
return;
// Listen for scrollwheel events so we can save scrollwheel-based changes.
window.addEventListener("DOMMouseScroll", this, false);
@@ -79,6 +83,10 @@ var FullZoom = {
},
destroy: function FullZoom_destroy() {
// Bug 691614 - zooming support for electrolysis
if (gMultiProcessBrowser)
return;
gPrefService.removeObserver("browser.zoom.", this);
this._cps2.removeObserverForName(this.name, this);
window.removeEventListener("DOMMouseScroll", this, false);
@@ -233,6 +241,10 @@ var FullZoom = {
* (optional) browser object displaying the document
*/
onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
// Bug 691614 - zooming support for electrolysis
if (gMultiProcessBrowser)
return;
let browser = aBrowser || gBrowser.selectedBrowser;
// If we haven't been initialized yet but receive an onLocationChange
// notification then let's store and replay it upon initialization.
+87 -141
View File
@@ -88,9 +88,6 @@ this.__defineSetter__("PluralForm", function (val) {
return this.PluralForm = val;
});
XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils",
"resource:///modules/AboutHomeUtils.jsm");
#ifdef MOZ_SERVICES_SYNC
XPCOMUtils.defineLazyModuleGetter(this, "Weave",
"resource://services-sync/main.js");
@@ -710,9 +707,12 @@ var gBrowserInit = {
var mustLoadSidebar = false;
Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService)
.addSystemEventListener(gBrowser, "click", contentAreaClick, true);
if (!gMultiProcessBrowser) {
// There is a Content:Click message manually sent from content.
Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService)
.addSystemEventListener(gBrowser, "click", contentAreaClick, true);
}
gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false);
@@ -2322,51 +2322,6 @@ function PageProxyClickHandler(aEvent)
middleMousePaste(aEvent);
}
/**
* Handle load of some pages (about:*) so that we can make modifications
* to the DOM for unprivileged pages.
*/
function BrowserOnAboutPageLoad(doc) {
if (doc.documentURI.toLowerCase() == "about:home") {
let ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
getService(Components.interfaces.nsISessionStore);
if (ss.canRestoreLastSession &&
!PrivateBrowsingUtils.isWindowPrivate(window))
doc.getElementById("launcher").setAttribute("session", "true");
// Inject search engine and snippets URL.
let docElt = doc.documentElement;
// set the following attributes BEFORE searchEngineURL, which triggers to
// show the snippets when it's set.
docElt.setAttribute("snippetsURL", AboutHomeUtils.snippetsURL);
if (AboutHomeUtils.showKnowYourRights) {
docElt.setAttribute("showKnowYourRights", "true");
// Set pref to indicate we've shown the notification.
let currentVersion = Services.prefs.getIntPref("browser.rights.version");
Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
}
docElt.setAttribute("snippetsVersion", AboutHomeUtils.snippetsVersion);
function updateSearchEngine() {
let engine = AboutHomeUtils.defaultSearchEngine;
docElt.setAttribute("searchEngineName", engine.name);
docElt.setAttribute("searchEnginePostData", engine.postDataString || "");
docElt.setAttribute("searchEngineURL", engine.searchURL);
}
updateSearchEngine();
// Listen for the event that's triggered when the user changes search engine.
// At this point we simply reload about:home to reflect the change.
Services.obs.addObserver(updateSearchEngine, "browser-search-engine-modified", false);
// Remove the observer when the page is reloaded or closed.
doc.defaultView.addEventListener("pagehide", function removeObserver() {
doc.defaultView.removeEventListener("pagehide", removeObserver);
Services.obs.removeObserver(updateSearchEngine, "browser-search-engine-modified");
}, false);
}
}
/**
* Handle command events bubbling up from error page content
*/
@@ -2388,8 +2343,8 @@ let BrowserOnClick = {
else if (ownerDoc.documentURI.startsWith("about:neterror")) {
this.onAboutNetError(originalTarget, ownerDoc);
}
else if (ownerDoc.documentURI.toLowerCase() == "about:home") {
this.onAboutHome(originalTarget, ownerDoc);
else if (ownerDoc.documentURI.startsWith("about:tabcrashed")) {
this.onAboutTabCrashed(aEvent, ownerDoc);
}
},
@@ -2434,51 +2389,28 @@ let BrowserOnClick = {
}
},
/**
* The about:tabcrashed can't do window.reload() because that
* would reload the page but not use a remote browser.
*/
onAboutTabCrashed: function(aEvent, aOwnerDoc) {
let isTopFrame = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
if (!isTopFrame) {
return;
}
let button = aEvent.originalTarget;
if (button.id == "tryAgain") {
openUILinkIn(button.getAttribute("url"), "current");
}
},
onAboutNetError: function BrowserOnClick_onAboutNetError(aTargetElm, aOwnerDoc) {
let elmId = aTargetElm.getAttribute("id");
if (elmId != "errorTryAgain" || !/e=netOffline/.test(aOwnerDoc.documentURI))
return;
Services.io.offline = false;
},
onAboutHome: function BrowserOnClick_onAboutHome(aTargetElm, aOwnerDoc) {
let elmId = aTargetElm.getAttribute("id");
switch (elmId) {
case "restorePreviousSession":
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
if (ss.canRestoreLastSession) {
ss.restoreLastSession();
}
aOwnerDoc.getElementById("launcher").removeAttribute("session");
break;
case "downloads":
BrowserDownloadsUI();
break;
case "bookmarks":
PlacesCommandHook.showPlacesOrganizer("AllBookmarks");
break;
case "history":
PlacesCommandHook.showPlacesOrganizer("History");
break;
case "addons":
BrowserOpenAddonsMgr();
break;
case "sync":
openPreferences("paneSync");
break;
case "settings":
openPreferences();
break;
}
},
};
/**
@@ -2532,6 +2464,16 @@ function getWebNavigation()
}
function BrowserReloadWithFlags(reloadFlags) {
let url = gBrowser.currentURI.spec;
if (gBrowser._updateBrowserRemoteness(gBrowser.selectedBrowser,
gBrowser._shouldBrowserBeRemote(url))) {
// If the remoteness has changed, the new browser doesn't have any
// information of what was loaded before, so we need to load the previous
// URL again.
gBrowser.loadURIWithFlags(url, reloadFlags);
return;
}
/* First, we'll try to use the session history object to reload so
* that framesets are handled properly. If we're in a special
* window (such as view-source) that has no session history, fall
@@ -3545,10 +3487,6 @@ var XULBrowserWindow = {
init: function () {
this.throbberElement = document.getElementById("navigator-throbber");
// Bug 666809 - SecurityUI support for e10s
if (gMultiProcessBrowser)
return;
// Initialize the security button's state and tooltip text. Remember to reset
// _hostChanged, otherwise onSecurityChange will short circuit.
var securityUI = gBrowser.securityUI;
@@ -3967,26 +3905,11 @@ var XULBrowserWindow = {
gURLBar.removeAttribute("level");
}
if (gMultiProcessBrowser)
return;
// Don't pass in the actual location object, since it can cause us to
// hold on to the window object too long. Just pass in the fields we
// care about. (bug 424829)
var location = gBrowser.contentWindow.location;
var locationObj = {};
let uri = gBrowser.currentURI;
try {
// about:blank can be used by webpages so pretend it is http
locationObj.protocol = location == "about:blank" ? "http:" : location.protocol;
locationObj.host = location.host;
locationObj.hostname = location.hostname;
locationObj.port = location.port;
} catch (ex) {
// Can sometimes throw if the URL being visited has no host/hostname,
// e.g. about:blank. The _state for these pages means we won't need these
// properties anyways, though.
}
gIdentityHandler.checkIdentity(this._state, locationObj);
uri = Services.uriFixup.createExposableURI(uri);
} catch (e) {}
gIdentityHandler.checkIdentity(this._state, uri);
},
// simulate all change notifications after switching tabs
@@ -4175,6 +4098,7 @@ var TabsProgressListener = {
Components.isSuccessCode(aStatus) &&
doc.documentURI.startsWith("about:") &&
!doc.documentURI.toLowerCase().startsWith("about:blank") &&
!doc.documentURI.toLowerCase().startsWith("about:home") &&
!doc.documentElement.hasAttribute("hasBrowserHandlers")) {
// STATE_STOP may be received twice for documents, thus store an
// attribute to ensure handling it just once.
@@ -4188,9 +4112,6 @@ var TabsProgressListener = {
if (event.target.documentElement)
event.target.documentElement.removeAttribute("hasBrowserHandlers");
}, true);
// We also want to make changes to page UI for unprivileged about pages.
BrowserOnAboutPageLoad(doc);
}
},
@@ -5075,9 +4996,26 @@ function handleLinkClick(event, href, linkNode) {
return true;
}
var referrerURI = doc.documentURIObject;
// if the mixedContentChannel is present and the referring URI passes
// a same origin check with the target URI, we can preserve the users
// decision of disabling MCB on a page for it's child tabs.
var persistAllowMixedContentInChildTab = false;
if (where == "tab" && gBrowser.docShell.mixedContentChannel) {
const sm = Services.scriptSecurityManager;
try {
var targetURI = makeURI(href);
sm.checkSameOriginURI(referrerURI, targetURI, false);
persistAllowMixedContentInChildTab = true;
}
catch (e) { }
}
urlSecurityCheck(href, doc.nodePrincipal);
openLinkIn(href, where, { referrerURI: doc.documentURIObject,
charset: doc.characterSet });
openLinkIn(href, where, { referrerURI: referrerURI,
charset: doc.characterSet,
allowMixedContent: persistAllowMixedContentInChildTab });
event.preventDefault();
return true;
}
@@ -6220,7 +6158,7 @@ var gIdentityHandler = {
// Cache the most recent SSLStatus and Location seen in checkIdentity
_lastStatus : null,
_lastLocation : null,
_lastUri : null,
_mode : "unknownIdentity",
// smart getters
@@ -6358,19 +6296,29 @@ var gIdentityHandler = {
* be called by onSecurityChange
*
* @param PRUint32 state
* @param JS Object location that mirrors an nsLocation (i.e. has .host and
* .hostname and .port)
* @param nsIURI uri The address for which the UI should be updated.
*/
checkIdentity : function(state, location) {
checkIdentity : function(state, uri) {
var currentStatus = gBrowser.securityUI
.QueryInterface(Components.interfaces.nsISSLStatusProvider)
.SSLStatus;
this._lastStatus = currentStatus;
this._lastLocation = location;
this._lastUri = uri;
let nsIWebProgressListener = Ci.nsIWebProgressListener;
if (location.protocol == "chrome:" || location.protocol == "about:") {
// For some URIs like data: we can't get a host and so can't do
// anything useful here. Chrome URIs however get special treatment.
let unknown = false;
try {
uri.host;
} catch (e) { unknown = true; }
if ((uri.scheme == "chrome" || uri.scheme == "about") &&
uri.spec !== "about:blank") {
this.setMode(this.IDENTITY_MODE_CHROMEUI);
} else if (unknown) {
this.setMode(this.IDENTITY_MODE_UNKNOWN);
} else if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
this.setMode(this.IDENTITY_MODE_IDENTIFIED);
} else if (state & nsIWebProgressListener.STATE_IS_SECURE) {
@@ -6433,12 +6381,12 @@ var gIdentityHandler = {
getEffectiveHost : function() {
try {
let baseDomain =
Services.eTLD.getBaseDomainFromHost(this._lastLocation.hostname);
Services.eTLD.getBaseDomainFromHost(this._lastUri.host);
return this._IDNService.convertToDisplayIDN(baseDomain, {});
} catch (e) {
// If something goes wrong (e.g. hostname is an IP address) just fail back
// If something goes wrong (e.g. host is an IP address) just fail back
// to the full domain.
return this._lastLocation.hostname;
return this._lastUri.host;
}
},
@@ -6507,19 +6455,17 @@ var gIdentityHandler = {
tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
[iData.caOrg]);
// Check whether this site is a security exception. XPConnect does the right
// thing here in terms of converting _lastLocation.port from string to int, but
// the overrideService doesn't like undefined ports, so make sure we have
// something in the default case (bug 432241).
// .hostname can return an empty string in some exceptional cases -
// hasMatchingOverride does not handle that, so avoid calling it.
// Updating the tooltip value in those cases isn't critical.
// FIXME: Fixing bug 646690 would probably makes this check unnecessary
if (this._lastLocation.hostname &&
this._overrideService.hasMatchingOverride(this._lastLocation.hostname,
(this._lastLocation.port || 443),
iData.cert, {}, {}))
// This can't throw, because URI's with a host that throw don't end up in this case.
let host = this._lastUri.host;
let port = 443;
try {
if (this._lastUri.port > 0)
port = this._lastUri.port;
} catch (e) {}
if (this._overrideService.hasMatchingOverride(host, port, iData.cert, {}, {}))
tooltip = gNavigatorBundle.getString("identity.identified.verified_by_you");
break; }
case this.IDENTITY_MODE_IDENTIFIED: {
// If it's identified, then we can populate the dialog with credentials
+219
View File
@@ -15,6 +15,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
"resource://gre/modules/LoginManagerContent.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "InsecurePasswordUtils",
"resource://gre/modules/InsecurePasswordUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormSubmitObserver",
"resource:///modules/FormSubmitObserver.jsm");
@@ -57,6 +59,223 @@ addEventListener("blur", function(event) {
LoginManagerContent.onUsernameInput(event);
});
let AboutHomeListener = {
init: function(chromeGlobal) {
let self = this;
chromeGlobal.addEventListener('AboutHomeLoad', function(e) { self.onPageLoad(); }, false, true);
},
handleEvent: function(aEvent) {
switch (aEvent.type) {
case "AboutHomeLoad":
this.onPageLoad();
break;
}
},
receiveMessage: function(aMessage) {
switch (aMessage.name) {
case "AboutHome:Update":
this.onUpdate(aMessage.data);
break;
}
},
onUpdate: function(aData) {
let doc = content.document;
if (doc.documentURI.toLowerCase() != "about:home")
return;
if (aData.showRestoreLastSession && !PrivateBrowsingUtils.isWindowPrivate(content))
doc.getElementById("launcher").setAttribute("session", "true");
// Inject search engine and snippets URL.
let docElt = doc.documentElement;
// set the following attributes BEFORE searchEngineName, which triggers to
// show the snippets when it's set.
docElt.setAttribute("snippetsURL", aData.snippetsURL);
if (aData.showKnowYourRights)
docElt.setAttribute("showKnowYourRights", "true");
docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
docElt.setAttribute("searchEngineName", aData.defaultEngineName);
},
onPageLoad: function() {
let doc = content.document;
if (doc.documentURI.toLowerCase() != "about:home" ||
doc.documentElement.hasAttribute("hasBrowserHandlers")) {
return;
}
doc.documentElement.setAttribute("hasBrowserHandlers", "true");
let updateListener = this;
addMessageListener("AboutHome:Update", updateListener);
addEventListener("click", this.onClick, true);
addEventListener("pagehide", function onPageHide(event) {
if (event.target.defaultView.frameElement)
return;
removeMessageListener("AboutHome:Update", updateListener);
removeEventListener("click", this.onClick, true);
removeEventListener("pagehide", onPageHide, true);
if (event.target.documentElement)
event.target.documentElement.removeAttribute("hasBrowserHandlers");
}, true);
// XXX bug 738646 - when Marketplace is launched, remove this statement and
// the hidden attribute set on the apps button in aboutHome.xhtml
if (Services.prefs.getPrefType("browser.aboutHome.apps") == Services.prefs.PREF_BOOL &&
Services.prefs.getBoolPref("browser.aboutHome.apps"))
doc.getElementById("apps").removeAttribute("hidden");
sendAsyncMessage("AboutHome:RequestUpdate");
doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
sendAsyncMessage("AboutHome:Search", { searchData: e.detail });
}, true, true);
},
onClick: function(aEvent) {
if (!aEvent.isTrusted || // Don't trust synthetic events
aEvent.button == 2 || aEvent.target.localName != "button") {
return;
}
let originalTarget = aEvent.originalTarget;
let ownerDoc = originalTarget.ownerDocument;
let elmId = originalTarget.getAttribute("id");
switch (elmId) {
case "restorePreviousSession":
sendAsyncMessage("AboutHome:RestorePreviousSession");
ownerDoc.getElementById("launcher").removeAttribute("session");
break;
case "downloads":
sendAsyncMessage("AboutHome:Downloads");
break;
case "bookmarks":
sendAsyncMessage("AboutHome:Bookmarks");
break;
case "history":
sendAsyncMessage("AboutHome:History");
break;
case "apps":
sendAsyncMessage("AboutHome:Apps");
break;
case "addons":
sendAsyncMessage("AboutHome:Addons");
break;
case "sync":
sendAsyncMessage("AboutHome:Sync");
break;
case "settings":
sendAsyncMessage("AboutHome:Settings");
break;
}
},
};
AboutHomeListener.init(this);
var global = this;
let ClickEventHandler = {
init: function init() {
Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService)
.addSystemEventListener(global, "click", this, true);
},
handleEvent: function(event) {
// Bug 903016: Most of this code is an unfortunate duplication from
// contentAreaClick in browser.js.
if (!event.isTrusted || event.defaultPrevented || event.button == 2)
return;
let [href, node] = this._hrefAndLinkNodeForClickEvent(event);
let json = { button: event.button, shiftKey: event.shiftKey,
ctrlKey: event.ctrlKey, metaKey: event.metaKey,
altKey: event.altKey, href: null, title: null,
bookmark: false };
if (href) {
json.href = href;
if (node) {
json.title = node.getAttribute("title");
if (event.button == 0 && !event.ctrlKey && !event.shiftKey &&
!event.altKey && !event.metaKey) {
json.bookmark = node.getAttribute("rel") == "sidebar";
if (json.bookmark)
event.preventDefault(); // Need to prevent the pageload.
}
}
sendAsyncMessage("Content:Click", json);
return;
}
// This might be middle mouse navigation.
if (event.button == 1)
sendAsyncMessage("Content:Click", json);
},
/**
* 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) {
// Be consistent with what nsContextMenu.js does.
return ((aNode instanceof content.HTMLAnchorElement && aNode.href) ||
(aNode instanceof content.HTMLAreaElement && aNode.href) ||
aNode instanceof content.HTMLLinkElement);
}
function makeURLAbsolute(aBase, aUrl) {
// Note: makeURI() will throw if aUri is not a valid URI
return makeURI(aUrl, null, makeURI(aBase)).spec;
}
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.baseURI;
}
node = node.parentNode;
}
// In case of XLink, we don't return the node we got href from since
// callers expect <a>-like elements.
return [href ? makeURLAbsolute(baseURI, href) : null, null];
}
};
ClickEventHandler.init();
// Lazily load the finder code
addMessageListener("Finder:Initialize", function () {
let {RemoteFinderListener} = Cu.import("resource://gre/modules/RemoteFinder.jsm", {});
+19 -1
View File
@@ -762,9 +762,27 @@ nsContextMenu.prototype = {
openLinkInTab: function() {
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, doc.nodePrincipal);
var referrerURI = doc.documentURIObject;
// if the mixedContentChannel is present and the referring URI passes
// a same origin check with the target URI, we can preserve the users
// decision of disabling MCB on a page for it's child tabs.
var persistAllowMixedContentInChildTab = false;
if (this.browser.docShell && this.browser.docShell.mixedContentChannel) {
const sm = Services.scriptSecurityManager;
try {
var targetURI = this.linkURI;
sm.checkSameOriginURI(referrerURI, targetURI, false);
persistAllowMixedContentInChildTab = true;
}
catch (e) { }
}
openLinkIn(this.linkURL, "tab",
{ charset: doc.characterSet,
referrerURI: doc.documentURIObject });
referrerURI: referrerURI,
allowMixedContent: persistAllowMixedContentInChildTab });
},
// open URL in current tab
+143 -20
View File
@@ -9,6 +9,11 @@
%tabBrowserDTD;
]>
# MAKE_E10S_WORK surrounds code needed to have the front-end try to be smart
# about using non-remote browsers for loading certain URIs when remote tabs
# (browser.tabs.remote) are enabled.
#define MAKE_E10S_WORK 1
<bindings id="tabBrowserBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
@@ -592,11 +597,9 @@
if (this._shouldShowProgress(aRequest)) {
if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
this.mTab.setAttribute("busy", "true");
if (!gMultiProcessBrowser) {
if (aWebProgress.isTopLevel &&
!(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
this.mTabBrowser.setTabTitleLoading(this.mTab);
}
if (aWebProgress.isTopLevel &&
!(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
this.mTabBrowser.setTabTitleLoading(this.mTab);
}
if (this.mTab.selected)
@@ -699,10 +702,9 @@
// Don't clear the favicon if this onLocationChange was
// triggered by a pushState or a replaceState. See bug 550565.
if (!gMultiProcessBrowser) {
if (aWebProgress.isLoadingDocument &&
!(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE))
this.mBrowser.mIconURL = null;
if (aWebProgress.isLoadingDocument &&
!(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE)) {
this.mBrowser.mIconURL = null;
}
let autocomplete = this.mTabBrowser._placesAutocomplete;
@@ -1214,12 +1216,14 @@
// At this point, we now have a URI.
// Let's try to unescape it using a character set
// in case the URI is not ASCII.
try {
var characterSet = browser.characterSet;
const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
.getService(Components.interfaces.nsITextToSubURI);
title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
} catch(ex) { /* Do nothing. */ }
if (!gMultiProcessBrowser) {
try {
var characterSet = browser.contentDocument.characterSet;
const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
.getService(Components.interfaces.nsITextToSubURI);
title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
} catch(ex) { /* Do nothing. */ }
}
crop = "center";
@@ -1254,6 +1258,7 @@
<![CDATA[
var aFromExternal;
var aRelatedToCurrent;
var aAllowMixedContent;
if (arguments.length == 2 &&
typeof arguments[1] == "object" &&
!(arguments[1] instanceof Ci.nsIURI)) {
@@ -1265,6 +1270,7 @@
aAllowThirdPartyFixup = params.allowThirdPartyFixup;
aFromExternal = params.fromExternal;
aRelatedToCurrent = params.relatedToCurrent;
aAllowMixedContent = params.allowMixedContent;
}
var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
@@ -1277,7 +1283,8 @@
ownerTab: owner,
allowThirdPartyFixup: aAllowThirdPartyFixup,
fromExternal: aFromExternal,
relatedToCurrent: aRelatedToCurrent});
relatedToCurrent: aRelatedToCurrent,
allowMixedContent: aAllowMixedContent});
if (!bgLoad)
this.selectedTab = tab;
@@ -1338,6 +1345,75 @@
]]></body>
</method>
#ifdef MAKE_E10S_WORK
<method name="_updateBrowserRemoteness">
<parameter name="aBrowser"/>
<parameter name="aRemote"/>
<body>
<![CDATA[
let isRemote = aBrowser.getAttribute("remote") == "true";
if (isRemote == aRemote)
return false;
// Unhook our progress listener.
let tab = this._getTabForBrowser(aBrowser);
let index = tab._tPos;
let filter = this.mTabFilters[index];
aBrowser.webProgress.removeProgressListener(filter);
// Change the "remote" attribute.
let parent = aBrowser.parentNode;
parent.removeChild(aBrowser);
aBrowser.setAttribute("remote", aRemote ? "true" : "false");
parent.appendChild(aBrowser);
// Restore the progress listener.
aBrowser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
if (aRemote)
tab.setAttribute("remote", "true");
else
tab.removeAttribute("remote");
return true;
]]>
</body>
</method>
<!--
Returns true if we want to load the content for this URL in a
remote process. Eventually this should just check whether aURL
is unprivileged. Right now, though, we would like to load
some unprivileged URLs (like about:neterror) in the main
process since they interact with chrome code through
BrowserOnClick.
-->
<method name="_shouldBrowserBeRemote">
<parameter name="aURL"/>
<body>
<![CDATA[
if (!gMultiProcessBrowser)
return false;
// loadURI in browser.xml treats null as about:blank
if (!aURL)
aURL = "about:blank";
if (aURL.startsWith("about:") &&
aURL.toLowerCase() != "about:home" &&
aURL.toLowerCase() != "about:blank") {
return false;
}
if (aURL.startsWith("chrome:"))
return false;
return true;
]]>
</body>
</method>
#endif
<method name="addTab">
<parameter name="aURI"/>
<parameter name="aReferrerURI"/>
@@ -1351,6 +1427,7 @@
var aFromExternal;
var aRelatedToCurrent;
var aSkipAnimation;
var aAllowMixedContent;
if (arguments.length == 2 &&
typeof arguments[1] == "object" &&
!(arguments[1] instanceof Ci.nsIURI)) {
@@ -1363,6 +1440,7 @@
aFromExternal = params.fromExternal;
aRelatedToCurrent = params.relatedToCurrent;
aSkipAnimation = params.skipAnimation;
aAllowMixedContent = params.allowMixedContent;
}
// if we're adding tabs, we're past interrupt mode, ditch the owner
@@ -1382,6 +1460,13 @@
t.setAttribute("validate", "never"); //PMed
t.setAttribute("onerror", "this.removeAttribute('image');");
t.className = "tabbrowser-tab";
#ifdef MAKE_E10S_WORK
let remote = this._shouldBrowserBeRemote(aURI);
#else
let remote = gMultiProcessBrowser;
#endif
if (remote)
t.setAttribute("remote", "true");
this.tabContainer._unlockTabSizing();
@@ -1416,10 +1501,8 @@
b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
if (Services.prefs.getPrefType("browser.tabs.remote") == Services.prefs.PREF_BOOL &&
Services.prefs.getBoolPref("browser.tabs.remote")) {
if (remote)
b.setAttribute("remote", "true");
}
if (window.gShowPageResizers && document.getElementById("addon-bar").collapsed &&
window.windowState == window.STATE_NORMAL) {
@@ -1496,7 +1579,8 @@
// Do nothing if we're a private window.
let docShellsSwapped = false;
if (aURI == BROWSER_NEW_TAB_URL &&
!PrivateBrowsingUtils.isWindowPrivate(window)) {
!PrivateBrowsingUtils.isWindowPrivate(window) &&
!gMultiProcessBrowser) {
docShellsSwapped = gBrowserNewTabPreloader.newTab(t);
}
@@ -1522,6 +1606,8 @@
}
if (aFromExternal)
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
if (aAllowMixedContent)
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
try {
b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
} catch (ex) {
@@ -2619,7 +2705,17 @@
<parameter name="aCharset"/>
<body>
<![CDATA[
#ifdef MAKE_E10S_WORK
this._updateBrowserRemoteness(this.mCurrentBrowser, this._shouldBrowserBeRemote(aURI));
try {
#endif
return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
#ifdef MAKE_E10S_WORK
} catch (e) {
let url = this.mCurrentBrowser.currentURI.spec;
this._updateBrowserRemoteness(this.mCurrentBrowser, this._shouldBrowserBeRemote(url));
}
#endif
]]>
</body>
</method>
@@ -2633,7 +2729,17 @@
<parameter name="aPostData"/>
<body>
<![CDATA[
#ifdef MAKE_E10S_WORK
this._updateBrowserRemoteness(this.mCurrentBrowser, this._shouldBrowserBeRemote(aURI));
try {
#endif
return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData);
#ifdef MAKE_E10S_WORK
} catch (e) {
let url = this.mCurrentBrowser.currentURI.spec;
this._updateBrowserRemoteness(this.mCurrentBrowser, this._shouldBrowserBeRemote(url));
}
#endif
]]>
</body>
</method>
@@ -2979,6 +3085,7 @@
browserStack.removeChild(this.mCurrentBrowser);
this.mCurrentBrowser.setAttribute("remote", true);
browserStack.appendChild(this.mCurrentBrowser);
this.tabContainer.firstChild.setAttribute("remote", "true");
}
this.mCurrentTab = this.tabContainer.firstChild;
@@ -3182,6 +3289,22 @@
tab.setAttribute("titlechanged", "true");
]]>
</handler>
<handler event="oop-browser-crashed">
<![CDATA[
if (!event.isTrusted)
return;
let browser = event.originalTarget;
let title = browser.contentTitle;
let uri = browser.currentURI;
this._updateBrowserRemoteness(browser, false);
browser.setAttribute("crashedPageTitle", title);
browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
browser.removeAttribute("crashedPageTitle");
]]>
</handler>
</handlers>
</binding>
@@ -0,0 +1,526 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/commonjs/sdk/core/promise.js");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils",
"resource:///modules/AboutHome.jsm");
let gRightsVersion = Services.prefs.getIntPref("browser.rights.version");
registerCleanupFunction(function() {
// Ensure we don't pollute prefs for next tests.
Services.prefs.clearUserPref("network.cookies.cookieBehavior");
Services.prefs.clearUserPref("network.cookie.lifetimePolicy");
Services.prefs.clearUserPref("browser.rights.override");
Services.prefs.clearUserPref("browser.rights." + gRightsVersion + ".shown");
});
let gTests = [
{
desc: "Check that clearing cookies does not clear storage",
setup: function ()
{
Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService)
.notifyObservers(null, "cookie-changed", "cleared");
},
run: function (aSnippetsMap)
{
isnot(aSnippetsMap.get("snippets-last-update"), null,
"snippets-last-update should have a value");
}
},
{
desc: "Check default snippets are shown",
setup: function () { },
run: function ()
{
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let snippetsElt = doc.getElementById("snippets");
ok(snippetsElt, "Found snippets element")
is(snippetsElt.getElementsByTagName("span").length, 1,
"A default snippet is present.");
}
},
{
desc: "Check default snippets are shown if snippets are invalid xml",
setup: function (aSnippetsMap)
{
// This must be some incorrect xhtml code.
aSnippetsMap.set("snippets", "<p><b></p></b>");
},
run: function (aSnippetsMap)
{
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let snippetsElt = doc.getElementById("snippets");
ok(snippetsElt, "Found snippets element");
is(snippetsElt.getElementsByTagName("span").length, 1,
"A default snippet is present.");
aSnippetsMap.delete("snippets");
}
},
{
desc: "Check that search engine logo has alt text",
setup: function () { },
run: function ()
{
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let searchEngineLogoElt = doc.getElementById("searchEngineLogo");
ok(searchEngineLogoElt, "Found search engine logo");
let altText = searchEngineLogoElt.alt;
ok(typeof altText == "string" && altText.length > 0,
"Search engine logo's alt text is a nonempty string");
isnot(altText, "undefined",
"Search engine logo's alt text shouldn't be the string 'undefined'");
}
},
{
desc: "Check that performing a search fires a search event and records to " +
"Firefox Health Report.",
setup: function () { },
run: function () {
try {
let cm = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
cm.getCategoryEntry("healthreport-js-provider-default", "SearchesProvider");
} catch (ex) {
// Health Report disabled, or no SearchesProvider.
return Promise.resolve();
}
let numSearchesBefore = 0;
let deferred = Promise.defer();
let doc = gBrowser.contentDocument;
let engineName = doc.documentElement.getAttribute("searchEngineName");
doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
let data = JSON.parse(e.detail);
is(data.engineName, engineName, "Detail is search engine name");
// We use executeSoon() to ensure that this code runs after the
// count has been updated in browser.js, since it uses the same
// event.
executeSoon(function () {
getNumberOfSearches(engineName).then(num => {
is(num, numSearchesBefore + 1, "One more search recorded.");
deferred.resolve();
});
});
}, true, true);
// Get the current number of recorded searches.
getNumberOfSearches(engineName).then(num => {
numSearchesBefore = num;
info("Perform a search.");
doc.getElementById("searchText").value = "a search";
doc.getElementById("searchSubmit").click();
gBrowser.stop();
});
return deferred.promise;
}
},
{
desc: "Check snippets map is cleared if cached version is old",
setup: function (aSnippetsMap)
{
aSnippetsMap.set("snippets", "test");
aSnippetsMap.set("snippets-cached-version", 0);
},
run: function (aSnippetsMap)
{
ok(!aSnippetsMap.has("snippets"), "snippets have been properly cleared");
ok(!aSnippetsMap.has("snippets-cached-version"),
"cached-version has been properly cleared");
}
},
{
desc: "Check cached snippets are shown if cached version is current",
setup: function (aSnippetsMap)
{
aSnippetsMap.set("snippets", "test");
},
run: function (aSnippetsMap)
{
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let snippetsElt = doc.getElementById("snippets");
ok(snippetsElt, "Found snippets element");
is(snippetsElt.innerHTML, "test", "Cached snippet is present.");
is(aSnippetsMap.get("snippets"), "test", "snippets still cached");
is(aSnippetsMap.get("snippets-cached-version"),
AboutHomeUtils.snippetsVersion,
"cached-version is correct");
ok(aSnippetsMap.has("snippets-last-update"), "last-update still exists");
}
},
{
desc: "Check if the 'Know Your Rights default snippet is shown when 'browser.rights.override' pref is set",
beforeRun: function ()
{
Services.prefs.setBoolPref("browser.rights.override", false);
},
setup: function () { },
run: function (aSnippetsMap)
{
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let showRights = AboutHomeUtils.showKnowYourRights;
ok(showRights, "AboutHomeUtils.showKnowYourRights should be TRUE");
let snippetsElt = doc.getElementById("snippets");
ok(snippetsElt, "Found snippets element");
is(snippetsElt.getElementsByTagName("a")[0].href, "about:rights", "Snippet link is present.");
Services.prefs.clearUserPref("browser.rights.override");
}
},
{
desc: "Check if the 'Know Your Rights default snippet is NOT shown when 'browser.rights.override' pref is NOT set",
beforeRun: function ()
{
Services.prefs.setBoolPref("browser.rights.override", true);
},
setup: function () { },
run: function (aSnippetsMap)
{
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let rightsData = AboutHomeUtils.knowYourRightsData;
ok(!rightsData, "AboutHomeUtils.knowYourRightsData should be FALSE");
let snippetsElt = doc.getElementById("snippets");
ok(snippetsElt, "Found snippets element");
ok(snippetsElt.getElementsByTagName("a")[0].href != "about:rights", "Snippet link should not point to about:rights.");
Services.prefs.clearUserPref("browser.rights.override");
}
},
{
desc: "Check that the search UI/ action is updated when the search engine is changed",
setup: function() {},
run: function()
{
let currEngine = Services.search.currentEngine;
let unusedEngines = [].concat(Services.search.getVisibleEngines()).filter(x => x != currEngine);
let searchbar = document.getElementById("searchbar");
function checkSearchUI(engine) {
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let searchText = doc.getElementById("searchText");
let logoElt = doc.getElementById("searchEngineLogo");
let engineName = doc.documentElement.getAttribute("searchEngineName");
is(engineName, engine.name, "Engine name should've been updated");
if (!logoElt.parentNode.hidden) {
is(logoElt.alt, engineName, "Alt text of logo image should match search engine name")
} else {
is(searchText.placeholder, engineName, "Placeholder text should match search engine name");
}
}
// Do a sanity check that all attributes are correctly set to begin with
checkSearchUI(currEngine);
let deferred = Promise.defer();
promiseBrowserAttributes(gBrowser.selectedTab).then(function() {
// Test if the update propagated
checkSearchUI(unusedEngines[0]);
searchbar.currentEngine = currEngine;
deferred.resolve();
});
// The following cleanup function will set currentEngine back to the previous
// engine if we fail to do so above.
registerCleanupFunction(function() {
searchbar.currentEngine = currEngine;
});
// Set the current search engine to an unused one
searchbar.currentEngine = unusedEngines[0];
searchbar.select();
return deferred.promise;
}
},
{
desc: "Check POST search engine support",
setup: function() {},
run: function()
{
let deferred = Promise.defer();
let currEngine = Services.search.defaultEngine;
let searchObserver = function search_observer(aSubject, aTopic, aData) {
let engine = aSubject.QueryInterface(Ci.nsISearchEngine);
info("Observer: " + aData + " for " + engine.name);
if (aData != "engine-added")
return;
if (engine.name != "POST Search")
return;
// Ready to execute the tests!
let needle = "Search for something awesome.";
let document = gBrowser.selectedTab.linkedBrowser.contentDocument;
let searchText = document.getElementById("searchText");
// We're about to change the search engine. Once the change has
// propagated to the about:home content, we want to perform a search.
let mutationObserver = new MutationObserver(function (mutations) {
for (let mutation of mutations) {
if (mutation.attributeName == "searchEngineName") {
searchText.value = needle;
searchText.focus();
EventUtils.synthesizeKey("VK_RETURN", {});
}
}
});
mutationObserver.observe(document.documentElement, { attributes: true });
// Change the search engine, triggering the observer above.
Services.search.defaultEngine = engine;
registerCleanupFunction(function() {
mutationObserver.disconnect();
Services.search.removeEngine(engine);
Services.search.defaultEngine = currEngine;
});
// When the search results load, check them for correctness.
waitForLoad(function() {
let loadedText = gBrowser.contentDocument.body.textContent;
ok(loadedText, "search page loaded");
is(loadedText, "searchterms=" + escape(needle.replace(/\s/g, "+")),
"Search text should arrive correctly");
deferred.resolve();
});
};
Services.obs.addObserver(searchObserver, "browser-search-engine-modified", false);
registerCleanupFunction(function () {
Services.obs.removeObserver(searchObserver, "browser-search-engine-modified");
});
Services.search.addEngine("http://test:80/browser/browser/base/content/test/POSTSearchEngine.xml",
Ci.nsISearchEngine.DATA_XML, null, false);
return deferred.promise;
}
}
];
function test()
{
waitForExplicitFinish();
requestLongerTimeout(2);
ignoreAllUncaughtExceptions();
Task.spawn(function () {
for (let test of gTests) {
info(test.desc);
if (test.beforeRun)
yield test.beforeRun();
let tab = yield promiseNewTabLoadEvent("about:home", "DOMContentLoaded");
// Must wait for both the snippets map and the browser attributes, since
// can't guess the order they will happen.
// So, start listening now, but verify the promise is fulfilled only
// after the snippets map setup.
let promise = promiseBrowserAttributes(tab);
// Prepare the snippets map with default values, then run the test setup.
let snippetsMap = yield promiseSetupSnippetsMap(tab, test.setup);
// Ensure browser has set attributes already, or wait for them.
yield promise;
info("Running test");
yield test.run(snippetsMap);
info("Cleanup");
gBrowser.removeCurrentTab();
}
}).then(finish, ex => {
ok(false, "Unexpected Exception: " + ex);
finish();
});
}
/**
* Creates a new tab and waits for a load event.
*
* @param aUrl
* The url to load in a new tab.
* @param aEvent
* The load event type to wait for. Defaults to "load".
* @return {Promise} resolved when the event is handled. Gets the new tab.
*/
function promiseNewTabLoadEvent(aUrl, aEventType="load")
{
let deferred = Promise.defer();
let tab = gBrowser.selectedTab = gBrowser.addTab(aUrl);
info("Wait tab event: " + aEventType);
tab.linkedBrowser.addEventListener(aEventType, function load(event) {
if (event.originalTarget != tab.linkedBrowser.contentDocument ||
event.target.location.href == "about:blank") {
info("skipping spurious load event");
return;
}
tab.linkedBrowser.removeEventListener(aEventType, load, true);
info("Tab event received: " + aEventType);
deferred.resolve(tab);
}, true);
return deferred.promise;
}
/**
* Cleans up snippets and ensures that by default we don't try to check for
* remote snippets since that may cause network bustage or slowness.
*
* @param aTab
* The tab containing about:home.
* @param aSetupFn
* The setup function to be run.
* @return {Promise} resolved when the snippets are ready. Gets the snippets map.
*/
function promiseSetupSnippetsMap(aTab, aSetupFn)
{
let deferred = Promise.defer();
let cw = aTab.linkedBrowser.contentWindow.wrappedJSObject;
info("Waiting for snippets map");
cw.ensureSnippetsMapThen(function (aSnippetsMap) {
info("Got snippets map: " +
"{ last-update: " + aSnippetsMap.get("snippets-last-update") +
", cached-version: " + aSnippetsMap.get("snippets-cached-version") +
" }");
// Don't try to update.
aSnippetsMap.set("snippets-last-update", Date.now());
aSnippetsMap.set("snippets-cached-version", AboutHomeUtils.snippetsVersion);
// Clear snippets.
aSnippetsMap.delete("snippets");
aSetupFn(aSnippetsMap);
// Must be sure to continue after the page snippets map setup.
executeSoon(function() deferred.resolve(aSnippetsMap));
});
return deferred.promise;
}
/**
* Waits for the attributes being set by browser.js and overwrites snippetsURL
* to ensure we won't try to hit the network and we can force xhr to throw.
*
* @param aTab
* The tab containing about:home.
* @return {Promise} resolved when the attributes are ready.
*/
function promiseBrowserAttributes(aTab)
{
let deferred = Promise.defer();
let docElt = aTab.linkedBrowser.contentDocument.documentElement;
//docElt.setAttribute("snippetsURL", "nonexistent://test");
let observer = new MutationObserver(function (mutations) {
for (let mutation of mutations) {
info("Got attribute mutation: " + mutation.attributeName +
" from " + mutation.oldValue);
if (mutation.attributeName == "snippetsURL" &&
docElt.getAttribute("snippetsURL") != "nonexistent://test") {
docElt.setAttribute("snippetsURL", "nonexistent://test");
}
// Now we just have to wait for the last attribute.
if (mutation.attributeName == "searchEngineName") {
info("Remove attributes observer");
observer.disconnect();
// Must be sure to continue after the page mutation observer.
executeSoon(function() deferred.resolve());
break;
}
}
});
info("Add attributes observer");
observer.observe(docElt, { attributes: true });
return deferred.promise;
}
/**
* Retrieves the number of about:home searches recorded for the current day.
*
* @param aEngineName
* name of the setup search engine.
*
* @return {Promise} Returns a promise resolving to the number of searches.
*/
function getNumberOfSearches(aEngineName) {
let reporter = Components.classes["@mozilla.org/datareporting/service;1"]
.getService()
.wrappedJSObject
.healthReporter;
ok(reporter, "Health Reporter instance available.");
return reporter.onInit().then(function onInit() {
let provider = reporter.getProvider("org.mozilla.searches");
ok(provider, "Searches provider is available.");
let m = provider.getMeasurement("counts", 2);
return m.getValues().then(data => {
let now = new Date();
let yday = new Date(now);
yday.setDate(yday.getDate() - 1);
// Add the number of searches recorded yesterday to the number of searches
// recorded today. This makes the test not fail intermittently when it is
// run at midnight and we accidentally compare the number of searches from
// different days. Tests are always run with an empty profile so there
// are no searches from yesterday, normally. Should the test happen to run
// past midnight we make sure to count them in as well.
return getNumberOfSearchesByDate(aEngineName, data, now) +
getNumberOfSearchesByDate(aEngineName, data, yday);
});
});
}
function getNumberOfSearchesByDate(aEngineName, aData, aDate) {
if (aData.days.hasDay(aDate)) {
let id = Services.search.getEngineByName(aEngineName).identifier;
let day = aData.days.getDay(aDate);
let field = id + ".abouthome";
if (day.has(field)) {
return day.get(field) || 0;
}
}
return 0; // No records found.
}
function waitForLoad(cb) {
let browser = gBrowser.selectedBrowser;
browser.addEventListener("load", function listener() {
if (browser.currentURI.spec == "about:blank")
return;
info("Page loaded: " + browser.currentURI.spec);
browser.removeEventListener("load", listener, true);
cb();
}, true);
}
+3 -1
View File
@@ -215,6 +215,7 @@ function openLinkIn(url, where, params) {
var aCharset = params.charset;
var aReferrerURI = params.referrerURI;
var aRelatedToCurrent = params.relatedToCurrent;
var aAllowMixedContent = params.allowMixedContent;
var aInBackground = params.inBackground;
var aDisallowInheritPrincipal = params.disallowInheritPrincipal;
var aInitiatingDoc = params.initiatingDoc;
@@ -328,7 +329,8 @@ function openLinkIn(url, where, params) {
postData: aPostData,
inBackground: loadInBackground,
allowThirdPartyFixup: aAllowThirdPartyFixup,
relatedToCurrent: aRelatedToCurrent});
relatedToCurrent: aRelatedToCurrent,
allowMixedContent: aAllowMixedContent});
break;
}
+1
View File
@@ -47,6 +47,7 @@ browser.jar:
content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png)
content/browser/autorecovery.js (content/autorecovery.js)
content/browser/autorecovery.xul (content/autorecovery.xul)
content/browser/aboutTabCrashed.xhtml (content/aboutTabCrashed.xhtml)
* content/browser/browser.css (content/browser.css)
content/browser/browser-menudragging.xul (content/browser-menudragging.xul)
content/browser/browser-menudragging.js (content/browser-menudragging.js)
@@ -37,6 +37,10 @@ static RedirEntry kRedirMap[] = {
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
{ "tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
{ "feeds", "chrome://browser/content/feeds/subscribe.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
+1
View File
@@ -87,6 +87,7 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
{ NS_FEEDSNIFFER_CONTRACTID, &kNS_FEEDSNIFFER_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "certerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "socialerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "tabcrashed", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "feeds", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "privatebrowsing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "rights", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
+10
View File
@@ -13,9 +13,15 @@ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
"resource:///modules/AboutHome.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContentClick",
"resource:///modules/ContentClick.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
@@ -501,6 +507,7 @@ BrowserGlue.prototype = {
NewTabUtils.init();
BrowserNewTabPreloader.init();
webrtcUI.init();
AboutHome.init();
FormValidationHandler.init();
LoginManagerParent.init();
@@ -510,6 +517,9 @@ BrowserGlue.prototype = {
Services.prefs.setBoolPref('media.mediasource.webm.enabled', false);
}
if (Services.prefs.getBoolPref("browser.tabs.remote"))
ContentClick.init();
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
AddonWatcher.init(this._notifySlowAddon);
@@ -534,6 +534,7 @@ let AboutPermissions = {
Services.prefs.addObserver("dom.webnotifications.enabled", this, false);
Services.prefs.addObserver("xpinstall.whitelist.required", this, false);
Services.prefs.addObserver("geo.enabled", this, false);
Services.prefs.addObserver("dom.push.enabled", this, false);
Services.prefs.addObserver("dom.indexedDB.enabled", this, false);
Services.prefs.addObserver("plugins.click_to_play", this, false);
Services.prefs.addObserver("full-screen-api.enabled", this, false);
@@ -687,6 +688,7 @@ let AboutPermissions = {
Services.prefs.removeObserver("dom.webnotifications.enabled", this, false);
Services.prefs.removeObserver("xpinstall.whitelist.required", this, false);
Services.prefs.removeObserver("geo.enabled", this, false);
Services.prefs.removeObserver("dom.push.enabled", this, false);
Services.prefs.removeObserver("dom.indexedDB.enabled", this, false);
Services.prefs.removeObserver("plugins.click_to_play", this, false);
Services.prefs.removeObserver("full-screen-api.enabled", this, false);
@@ -0,0 +1,331 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/NetUtil.jsm");
const ABOUT_PERMISSIONS_SPEC = "about:permissions";
const TEST_URI_1 = NetUtil.newURI("http://mozilla.com/");
const TEST_URI_2 = NetUtil.newURI("http://mozilla.org/");
const TEST_PRINCIPAL_1 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_URI_1);
const TEST_PRINCIPAL_2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_URI_2);
// values from DefaultPermissions object
const PERM_UNKNOWN = 0;
const PERM_ALLOW = 1;
const PERM_DENY = 2;
// cookie specific permissions
const PERM_FIRST_PARTY_ONLY = 9;
// used to set permissions on test sites
const TEST_PERMS = {
"password": PERM_ALLOW,
"cookie": PERM_ALLOW,
"geo": PERM_UNKNOWN,
"push": PERM_DENY,
"indexedDB": PERM_UNKNOWN,
"popup": PERM_DENY,
"fullscreen" : PERM_UNKNOWN,
"camera": PERM_UNKNOWN,
"microphone": PERM_UNKNOWN
};
const NO_GLOBAL_ALLOW = [
"geo",
"indexedDB",
"fullscreen"
];
// number of managed permissions in the interface
const TEST_PERMS_COUNT = 9;
function test() {
waitForExplicitFinish();
registerCleanupFunction(cleanUp);
// add test history visit
PlacesTestUtils.addVisits(TEST_URI_1).then(() => {
// set permissions ourselves to avoid problems with different defaults
// from test harness configuration
for (let type in TEST_PERMS) {
if (type == "password") {
Services.logins.setLoginSavingEnabled(TEST_URI_2.prePath, true);
} else {
// set permissions on a site without history visits to test enumerateServices
Services.perms.addFromPrincipal(TEST_PRINCIPAL_2, type, TEST_PERMS[type]);
}
}
// open about:permissions
gBrowser.selectedTab = gBrowser.addTab("about:permissions");
});
function observer() {
Services.obs.removeObserver(observer, "browser-permissions-initialized");
runNextTest();
}
Services.obs.addObserver(observer, "browser-permissions-initialized", false);
}
function cleanUp() {
for (let type in TEST_PERMS) {
if (type != "password") {
Services.perms.removeFromPrincipal(TEST_PRINCIPAL_1, type);
Services.perms.removeFromPrincipal(TEST_PRINCIPAL_2, type);
}
}
gBrowser.removeTab(gBrowser.selectedTab);
}
function runNextTest() {
if (gTestIndex == tests.length) {
PlacesTestUtils.clearHistory().then(finish);
return;
}
let nextTest = tests[gTestIndex++];
info("[" + nextTest.name + "] running test");
nextTest();
}
var gSitesList;
var gHeaderDeck;
var gSiteLabel;
var gTestIndex = 0;
var tests = [
function test_page_load() {
is(gBrowser.currentURI.spec, ABOUT_PERMISSIONS_SPEC, "about:permissions loaded");
gSitesList = gBrowser.contentDocument.getElementById("sites-list");
ok(gSitesList, "got sites list");
gHeaderDeck = gBrowser.contentDocument.getElementById("header-deck");
ok(gHeaderDeck, "got header deck");
gSiteLabel = gBrowser.contentDocument.getElementById("site-label");
ok(gSiteLabel, "got site label");
runNextTest();
},
function test_sites_list() {
is(gSitesList.firstChild.id, "all-sites-item",
"all sites is the first item in the sites list");
ok(getSiteItem(TEST_URI_1.host), "site item from places db exists");
ok(getSiteItem(TEST_URI_2.host), "site item from enumerating services exists");
runNextTest();
},
function test_filter_sites_list() {
// set filter to test host
let sitesFilter = gBrowser.contentDocument.getElementById("sites-filter");
sitesFilter.value = TEST_URI_1.host;
sitesFilter.doCommand();
// make sure correct sites are collapsed/showing
let testSite1 = getSiteItem(TEST_URI_1.host);
ok(!testSite1.collapsed, "test site 1 is not collapsed");
let testSite2 = getSiteItem(TEST_URI_2.host);
ok(testSite2.collapsed, "test site 2 is collapsed");
// clear filter
sitesFilter.value = "";
sitesFilter.doCommand();
runNextTest();
},
function test_all_sites() {
// "All Sites" item should be selected when the page is first loaded
is(gSitesList.selectedItem, gBrowser.contentDocument.getElementById("all-sites-item"),
"all sites item is selected");
let defaultsHeader = gBrowser.contentDocument.getElementById("defaults-header");
is(defaultsHeader, gHeaderDeck.selectedPanel,
"correct header shown for all sites");
ok(gBrowser.contentDocument.getElementById("passwords-count").hidden,
"passwords count is hidden");
ok(gBrowser.contentDocument.getElementById("cookies-count").hidden,
"cookies count is hidden");
// Test to make sure "Allow" items hidden for certain permission types
NO_GLOBAL_ALLOW.forEach(function(aType) {
let menuitem = gBrowser.contentDocument.getElementById(aType + "-" + PERM_ALLOW);
ok(menuitem.hidden, aType + " allow menuitem hidden for all sites");
});
runNextTest();
},
function test_all_sites_permission() {
// apply the old default of allowing all cookies
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
// there should be no user-set pref for cookie behavior
is(Services.prefs.getIntPref("network.cookie.cookieBehavior"), PERM_UNKNOWN,
"network.cookie.cookieBehavior is expected default");
// the default behavior is to allow cookies
let cookieMenulist = getPermissionMenulist("cookie");
is(cookieMenulist.value, PERM_ALLOW,
"menulist correctly shows that cookies are allowed");
// set the pref to block cookies
Services.prefs.setIntPref("network.cookie.cookieBehavior", PERM_DENY);
// check to make sure this change is reflected in the UI
is(cookieMenulist.value, PERM_DENY, "menulist correctly shows that cookies are blocked");
// clear the pref
Services.prefs.clearUserPref("network.cookie.cookieBehavior");
runNextTest();
},
function test_manage_all_passwords() {
// make sure "Manage All Passwords..." button opens the correct dialog
addWindowListener("chrome://passwordmgr/content/passwordManager.xul", runNextTest);
gBrowser.contentDocument.getElementById("passwords-manage-all-button").doCommand();
},
function test_manage_all_cookies() {
// make sure "Manage All Cookies..." button opens the correct dialog
addWindowListener("chrome://browser/content/preferences/cookies.xul", runNextTest);
gBrowser.contentDocument.getElementById("cookies-manage-all-button").doCommand();
},
function test_select_site() {
// select the site that has the permissions we set at the beginning of the test
let testSiteItem = getSiteItem(TEST_URI_2.host);
gSitesList.selectedItem = testSiteItem;
let siteHeader = gBrowser.contentDocument.getElementById("site-header");
is(siteHeader, gHeaderDeck.selectedPanel,
"correct header shown for a specific site");
is(gSiteLabel.value, TEST_URI_2.host, "header updated for selected site");
ok(!gBrowser.contentDocument.getElementById("passwords-count").hidden,
"passwords count is not hidden");
ok(!gBrowser.contentDocument.getElementById("cookies-count").hidden,
"cookies count is not hidden");
// Test to make sure "Allow" items are *not* hidden for certain permission types
NO_GLOBAL_ALLOW.forEach(function(aType) {
let menuitem = gBrowser.contentDocument.getElementById(aType + "-" + PERM_ALLOW);
ok(!menuitem.hidden, aType + " allow menuitem not hidden for single site");
});
runNextTest();
},
function test_permissions() {
let menulists = gBrowser.contentDocument.getElementsByClassName("pref-menulist");
is(menulists.length, TEST_PERMS_COUNT, "got expected number of managed permissions");
for (let i = 0; i < menulists.length; i++) {
let permissionMenulist = menulists.item(i);
let permissionType = permissionMenulist.getAttribute("type");
// permissions should reflect what we set at the beginning of the test
is(permissionMenulist.value, TEST_PERMS[permissionType],
"got expected value for " + permissionType + " permission");
}
runNextTest();
},
function test_permission_change() {
let geoMenulist = getPermissionMenulist("geo");
is(geoMenulist.value, PERM_UNKNOWN, "menulist correctly shows that geolocation permission is unspecified");
// change a permission programatically
Services.perms.addFromPrincipal(TEST_PRINCIPAL_2, "geo", PERM_DENY);
// check to make sure this change is reflected in the UI
is(geoMenulist.value, PERM_DENY, "menulist shows that geolocation is blocked");
// change a permisssion in the UI
let geoAllowItem = gBrowser.contentDocument.getElementById("geo-" + PERM_ALLOW);
geoMenulist.selectedItem = geoAllowItem;
geoMenulist.doCommand();
// check to make sure this change is reflected in the permission manager
is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, "geo"), PERM_ALLOW,
"permission manager shows that geolocation is allowed");
// change a site-specific cookie permission, just for fun
let cookieMenuList = getPermissionMenulist("cookie");
let cookieItem = gBrowser.contentDocument.getElementById("cookie-" + PERM_FIRST_PARTY_ONLY);
cookieMenuList.selectedItem = cookieItem;
cookieMenuList.doCommand();
is(cookieMenuList.value, PERM_FIRST_PARTY_ONLY, "menulist correctly shows that " +
"first party only cookies are allowed");
is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, "cookie"),
PERM_FIRST_PARTY_ONLY, "permission manager shows that first party cookies " +
"are allowed");
runNextTest();
},
function test_forget_site() {
// click "Forget About This Site" button
gBrowser.contentDocument.getElementById("forget-site-button").doCommand();
PlacesTestUtils.clearHistory().then(() => {
is(gSiteLabel.value, "", "site label cleared");
let allSitesItem = gBrowser.contentDocument.getElementById("all-sites-item");
is(gSitesList.selectedItem, allSitesItem,
"all sites item selected after forgetting selected site");
// check to make sure site is gone from sites list
let testSiteItem = getSiteItem(TEST_URI_2.host);
ok(!testSiteItem, "site removed from sites list");
// check to make sure we forgot all permissions corresponding to site
for (let type in TEST_PERMS) {
if (type == "password") {
ok(Services.logins.getLoginSavingEnabled(TEST_URI_2.prePath),
"password saving should be enabled by default");
} else {
is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, type), PERM_UNKNOWN,
type + " permission should not be set for test site 2");
}
}
runNextTest();
});
}
];
function getPermissionMenulist(aType) {
return gBrowser.contentDocument.getElementById(aType + "-menulist");
}
function getSiteItem(aHost) {
return gBrowser.contentDocument.
querySelector(".site[value='" + aHost + "']");
}
function addWindowListener(aURL, aCallback) {
Services.wm.addListener({
onOpenWindow: function(aXULWindow) {
info("window opened, waiting for focus");
Services.wm.removeListener(this);
var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
waitForFocus(function() {
is(domwindow.document.location.href, aURL, "should have seen the right window open");
domwindow.close();
aCallback();
}, domwindow);
},
onCloseWindow: function(aXULWindow) { },
onWindowTitleChange: function(aXULWindow, aNewTitle) { }
});
}
@@ -656,3 +656,7 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY pluginActivateNow.label "Allow Now">
<!ENTITY pluginActivateAlways.label "Allow and Remember">
<!ENTITY pluginBlockNow.label "Block Plugin">
<!ENTITY tabCrashed.header "Tab crashed">
<!ENTITY tabCrashed.message "Well, this is embarrassing. We tried to display this Web page, but it's not responding.">
<!ENTITY tabCrashed.tryAgain "Try Again">
+210
View File
@@ -0,0 +1,210 @@
/* 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";
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
this.EXPORTED_SYMBOLS = [ "AboutHomeUtils", "AboutHome" ];
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
// Url to fetch snippets, in the urlFormatter service format.
const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
// Should be bumped up if the snippets content format changes.
const STARTPAGE_VERSION = 4;
this.AboutHomeUtils = {
get snippetsVersion() STARTPAGE_VERSION,
/*
* showKnowYourRights - Determines if the user should be shown the
* about:rights notification. The notification should *not* be shown if
* we've already shown the current version, or if the override pref says to
* never show it. The notification *should* be shown if it's never been seen
* before, if a newer version is available, or if the override pref says to
* always show it.
*/
get showKnowYourRights() {
// Look for an unconditional override pref. If set, do what it says.
// (true --> never show, false --> always show)
try {
return !Services.prefs.getBoolPref("browser.rights.override");
} catch (e) { }
// Ditto, for the legacy EULA pref.
try {
return !Services.prefs.getBoolPref("browser.EULA.override");
} catch (e) { }
#ifndef MOZILLA_OFFICIAL
// Non-official builds shouldn't show the notification.
return false;
#endif
// Look to see if the user has seen the current version or not.
var currentVersion = Services.prefs.getIntPref("browser.rights.version");
try {
return !Services.prefs.getBoolPref("browser.rights." + currentVersion + ".shown");
} catch (e) { }
// Legacy: If the user accepted a EULA, we won't annoy them with the
// equivalent about:rights page until the version changes.
try {
return !Services.prefs.getBoolPref("browser.EULA." + currentVersion + ".accepted");
} catch (e) { }
// We haven't shown the notification before, so do so now.
return true;
}
};
/**
* Returns the URL to fetch snippets from, in the urlFormatter service format.
*/
XPCOMUtils.defineLazyGetter(AboutHomeUtils, "snippetsURL", function() {
let updateURL = Services.prefs
.getCharPref(SNIPPETS_URL_PREF)
.replace("%STARTPAGE_VERSION%", STARTPAGE_VERSION);
return Services.urlFormatter.formatURL(updateURL);
});
/**
* This code provides services to the about:home page. Whenever
* about:home needs to do something chrome-privileged, it sends a
* message that's handled here.
*/
let AboutHome = {
MESSAGES: [
"AboutHome:RestorePreviousSession",
"AboutHome:Downloads",
"AboutHome:Bookmarks",
"AboutHome:History",
"AboutHome:Apps",
"AboutHome:Addons",
"AboutHome:Sync",
"AboutHome:Settings",
"AboutHome:RequestUpdate",
"AboutHome:Search",
],
init: function() {
let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
for (let msg of this.MESSAGES) {
mm.addMessageListener(msg, this);
}
Services.obs.addObserver(this, "browser-search-engine-modified", false);
},
observe: function(aEngine, aTopic, aVerb) {
switch (aTopic) {
case "browser-search-engine-modified":
this.sendAboutHomeData(null);
break;
}
},
receiveMessage: function(aMessage) {
let window = aMessage.target.ownerDocument.defaultView;
switch (aMessage.name) {
case "AboutHome:RestorePreviousSession":
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
if (ss.canRestoreLastSession) {
ss.restoreLastSession();
}
break;
case "AboutHome:Downloads":
window.BrowserDownloadsUI();
break;
case "AboutHome:Bookmarks":
window.PlacesCommandHook.showPlacesOrganizer("AllBookmarks");
break;
case "AboutHome:History":
window.PlacesCommandHook.showPlacesOrganizer("History");
break;
case "AboutHome:Apps":
window.openUILinkIn("https://marketplace.mozilla.org/", "tab");
break;
case "AboutHome:Addons":
window.BrowserOpenAddonsMgr();
break;
case "AboutHome:Sync":
window.openPreferences("paneSync");
break;
case "AboutHome:Settings":
window.openPreferences();
break;
case "AboutHome:RequestUpdate":
this.sendAboutHomeData(aMessage.target);
break;
case "AboutHome:Search":
let data;
try {
data = JSON.parse(aMessage.data.searchData);
} catch(ex) {
Cu.reportError(ex);
break;
}
#ifdef MOZ_SERVICES_HEALTHREPORT
window.BrowserSearch.recordSearchInHealthReport(data.engineName, "abouthome");
#endif
// Trigger a search through nsISearchEngine.getSubmission()
let submission = Services.search.currentEngine.getSubmission(data.searchTerms);
window.loadURI(submission.uri.spec, null, submission.postData);
break;
}
},
// Send all the chrome-privileged data needed by about:home. This
// gets re-sent when the search engine changes.
sendAboutHomeData: function(target) {
let wrapper = {};
Components.utils.import("resource:///modules/sessionstore/SessionStore.jsm",
wrapper);
let ss = wrapper.SessionStore;
ss.promiseInitialized.then(function() {
let data = {
showRestoreLastSession: ss.canRestoreLastSession,
snippetsURL: AboutHomeUtils.snippetsURL,
showKnowYourRights: AboutHomeUtils.showKnowYourRights,
snippetsVersion: AboutHomeUtils.snippetsVersion,
defaultEngineName: Services.search.defaultEngine.name
};
if (AboutHomeUtils.showKnowYourRights) {
// Set pref to indicate we've shown the notification.
let currentVersion = Services.prefs.getIntPref("browser.rights.version");
Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
}
if (target) {
target.messageManager.sendAsyncMessage("AboutHome:Update", data);
} else {
let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
mm.broadcastAsyncMessage("AboutHome:Update", data);
}
}).then(null, function onError(x) {
Cu.reportError("Error in AboutHome.sendAboutHomeData " + x);
});
},
};
+82
View File
@@ -0,0 +1,82 @@
/* 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";
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
this.EXPORTED_SYMBOLS = [ "ContentClick" ];
Cu.import("resource:///modules/PlacesUIUtils.jsm");
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
let ContentClick = {
init: function() {
let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
mm.addMessageListener("Content:Click", this);
},
receiveMessage: function (message) {
switch (message.name) {
case "Content:Click":
this.contentAreaClick(message.json, message.target)
break;
}
},
contentAreaClick: function (json, browser) {
// This is heavily based on contentAreaClick from browser.js (Bug 903016)
// The json is set up in a way to look like an Event.
let window = browser.ownerDocument.defaultView;
if (!json.href) {
// Might be middle mouse navigation.
if (Services.prefs.getBoolPref("middlemouse.contentLoadURL") &&
!Services.prefs.getBoolPref("general.autoScroll")) {
window.middleMousePaste(json);
}
return;
}
if (json.bookmark) {
// This is the Opera convention for a special link that, when clicked,
// allows to add a sidebar panel. The link's title attribute contains
// the title that should be used for the sidebar panel.
PlacesUIUtils.showBookmarkDialog({ action: "add"
, type: "bookmark"
, uri: Services.io.newURI(json.href, null, null)
, title: json.title
, loadBookmarkInSidebar: true
, hiddenRows: [ "description"
, "location"
, "keyword" ]
}, window);
return;
}
// Note: We don't need the sidebar code here.
// This part is based on handleLinkClick.
var where = window.whereToOpenLink(json);
if (where == "current")
return false;
// Todo(903022): code for where == save
window.openLinkIn(json.href, where, { referrerURI: browser.documentURI,
charset: browser.characterSet });
// Mark the page as a user followed link. This is done so that history can
// distinguish automatic embed visits from user activated ones. For example
// pages loaded in frames are embed visits and lost with the session, while
// visits across frames should be preserved.
try {
if (!PrivateBrowsingUtils.isWindowPrivate(window))
PlacesUIUtils.markPageAsFollowedLink(href);
} catch (ex) { /* Skip invalid URIs. */ }
}
};
+3 -2
View File
@@ -11,6 +11,7 @@ EXTRA_JS_MODULES += [ 'promise.js' ]
EXTRA_JS_MODULES += [
'BrowserNewTabPreloader.jsm',
'CharsetMenu.jsm',
'ContentClick.jsm',
'FormSubmitObserver.jsm',
'FormValidationHandler.jsm',
'NetworkPrioritizer.jsm',
@@ -31,9 +32,9 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
]
EXTRA_PP_JS_MODULES += [
'AboutHomeUtils.jsm',
'AboutHome.jsm',
'RecentWindow.jsm',
]
if CONFIG['MOZILLA_OFFICIAL']:
DEFINES['MOZILLA_OFFICIAL'] = 1
DEFINES['MOZILLA_OFFICIAL'] = 1
+98
View File
@@ -0,0 +1,98 @@
body {
background-color: rgb(241, 244, 248);
margin-top: 2em;
font: message-box;
font-size: 100%;
}
p {
font-size: .8em;
}
#error-box {
background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
-moz-padding-start: 30px;
}
#error-box:-moz-locale-dir(rtl) {
background-position: right 4px;
}
#main-error-msg {
color: #4b4b4b;
font-weight: bold;
}
#button-box {
text-align: center;
width: 75%;
margin: 0 auto;
}
@media all and (min-width: 300px) {
#error-box {
max-width: 50%;
margin: 0 auto;
background-image: url('chrome://global/skin/icons/information-32.png');
min-height: 36px;
-moz-padding-start: 38px;
}
button {
width: auto !important;
min-width: 150px;
}
}
@media all and (min-width: 780px) {
#error-box {
max-width: 30%;
}
}
button {
font: message-box;
font-size: 0.6875em;
-moz-appearance: none;
-moz-user-select: none;
width: 100%;
margin: 2px 0;
padding: 2px 6px;
line-height: 1.2;
background-color: hsla(210,30%,95%,.1);
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
background-clip: padding-box;
border: 1px solid hsla(210,15%,25%,.4);
border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4);
border-radius: 3px;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover {
background-color: hsla(210,30%,95%,.8);
border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55);
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1),
0 0 3px hsla(210,15%,25%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover:active {
background-color: hsla(210,15%,25%,.2);
box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset,
0 0 2px hsla(210,15%,25%,.4) inset;
transition-property: background-color, border-color, box-shadow;
transition-duration: 10ms;
transition-timing-function: linear;
}
+4
View File
@@ -1478,6 +1478,10 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
color: -moz-dialogtext;
}
.tabbrowser-tab[remote] {
text-decoration: underline;
}
#main-window[tabsontop=false]:not([disablechrome]) .tabbrowser-tab[selected=true]:not(:-moz-lwtheme) {
background-image: linear-gradient(to top, rgba(0,0,0,.3) 1px, transparent 1px),
linear-gradient(@selectedTabHighlight@, @toolbarHighlight@ 32%),
+1
View File
@@ -16,6 +16,7 @@ browser.jar:
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/aboutSyncTabs.css
#endif
skin/classic/browser/aboutTabCrashed.css
skin/classic/browser/actionicon-tab.png
* skin/classic/browser/browser.css
skin/classic/browser/click-to-play-warning-stripes.png
+98
View File
@@ -0,0 +1,98 @@
body {
background-color: rgb(241, 244, 248);
margin-top: 2em;
font: message-box;
font-size: 100%;
}
p {
font-size: .8em;
}
#error-box {
background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
-moz-padding-start: 30px;
}
#error-box:-moz-locale-dir(rtl) {
background-position: right 4px;
}
#main-error-msg {
color: #4b4b4b;
font-weight: bold;
}
#button-box {
text-align: center;
width: 75%;
margin: 0 auto;
}
@media all and (min-width: 300px) {
#error-box {
max-width: 50%;
margin: 0 auto;
background-image: url('chrome://global/skin/icons/information-32.png');
min-height: 36px;
-moz-padding-start: 38px;
}
button {
width: auto !important;
min-width: 150px;
}
}
@media all and (min-width: 780px) {
#error-box {
max-width: 30%;
}
}
button {
font: message-box;
font-size: 0.6875em;
-moz-appearance: none;
-moz-user-select: none;
width: 100%;
margin: 2px 0;
padding: 2px 6px;
line-height: 1.2;
background-color: hsla(210,30%,95%,.1);
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
background-clip: padding-box;
border: 1px solid hsla(210,15%,25%,.4);
border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4);
border-radius: 3px;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover {
background-color: hsla(210,30%,95%,.8);
border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55);
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1),
0 0 3px hsla(210,15%,25%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover:active {
background-color: hsla(210,15%,25%,.2);
box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset,
0 0 2px hsla(210,15%,25%,.4) inset;
transition-property: background-color, border-color, box-shadow;
transition-duration: 10ms;
transition-timing-function: linear;
}
+4
View File
@@ -1458,6 +1458,10 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-
min-width: 102px;
}
.tabbrowser-tab[remote] {
text-decoration: underline;
}
/* When the tabs are on top and the window is maximized or in full-
screen mode, unhide the transparent top border of the tabs so we
have a 1px gap between the tabs and the top edge of the screen */
+1
View File
@@ -15,6 +15,7 @@ browser.jar:
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/aboutSyncTabs.css
#endif
skin/classic/browser/aboutTabCrashed.css
skin/classic/browser/actionicon-tab.png
skin/classic/browser/appmenu-icons.png
skin/classic/browser/appmenu-dropmarker.png
@@ -0,0 +1,98 @@
body {
background-color: rgb(241, 244, 248);
margin-top: 2em;
font: message-box;
font-size: 100%;
}
p {
font-size: .8em;
}
#error-box {
background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
-moz-padding-start: 30px;
}
#error-box:-moz-locale-dir(rtl) {
background-position: right 4px;
}
#main-error-msg {
color: #4b4b4b;
font-weight: bold;
}
#button-box {
text-align: center;
width: 75%;
margin: 0 auto;
}
@media all and (min-width: 300px) {
#error-box {
max-width: 50%;
margin: 0 auto;
background-image: url('chrome://global/skin/icons/information-32.png');
min-height: 36px;
-moz-padding-start: 38px;
}
button {
width: auto !important;
min-width: 150px;
}
}
@media all and (min-width: 780px) {
#error-box {
max-width: 30%;
}
}
button {
font: message-box;
font-size: 0.6875em;
-moz-appearance: none;
-moz-user-select: none;
width: 100%;
margin: 2px 0;
padding: 2px 6px;
line-height: 1.2;
background-color: hsla(210,30%,95%,.1);
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
background-clip: padding-box;
border: 1px solid hsla(210,15%,25%,.4);
border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4);
border-radius: 3px;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover {
background-color: hsla(210,30%,95%,.8);
border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55);
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1),
0 0 3px hsla(210,15%,25%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover:active {
background-color: hsla(210,15%,25%,.2);
box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset,
0 0 2px hsla(210,15%,25%,.4) inset;
transition-property: background-color, border-color, box-shadow;
transition-duration: 10ms;
transition-timing-function: linear;
}
+4
View File
@@ -1929,6 +1929,10 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
}
}
.tabbrowser-tab[remote] {
text-decoration: underline;
}
.tabbrowser-tab:hover,
.tabs-newtab-button:hover {
background-image: @toolbarShadowOnTab@, var(--tab-background-hover),
+2
View File
@@ -15,6 +15,8 @@ browser.jar:
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/aboutSyncTabs.css
#endif
skin/classic/browser/aboutTabCrashed.css
skin/classic/browser/aboutTabCrashed.css
skin/classic/browser/actionicon-tab.png
skin/classic/browser/appmenu-icons.png
skin/classic/browser/appmenu-dropmarker.png
+12
View File
@@ -906,6 +906,18 @@ DOMInterfaces = {
'headerFile': 'HTMLPropertiesCollection.h',
},
'PushEvent': {
'headerFile': 'ServiceWorkerEvents.h',
'nativeType': 'mozilla::dom::workers::PushEvent',
'workers': True
},
'PushMessageData': {
'headerFile': 'ServiceWorkerEvents.h',
'nativeType': 'mozilla::dom::workers::PushMessageData',
'workers': True
},
'Range': {
'nativeType': 'nsRange',
'binaryNames': {
+4
View File
@@ -102,6 +102,10 @@ if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
# This is needed for Window.webidl
DEFINES['HAVE_SIDEBAR'] = True
if CONFIG['MOZ_SIMPLEPUSH']:
DEFINES['MOZ_SIMPLEPUSH'] = True
PYTHON_UNIT_TESTS += [
'mozwebidlcodegen/test/test_mozwebidlcodegen.py',
]
+2
View File
@@ -18,6 +18,7 @@ support-files =
test_cache_keys.js
test_cache_put.js
test_cache_requestCache.js
test_cache_delete.js
[test_cache.html]
[test_cache_add.html]
@@ -29,3 +30,4 @@ support-files =
[test_cache_keys.html]
[test_cache_put.html]
[test_cache_requestCache.html]
[test_cache_delete.html]
+20
View File
@@ -0,0 +1,20 @@
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE HTML>
<html>
<head>
<title>Validate the Cache.delete() method</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="driver.js"></script>
</head>
<body>
<iframe id="frame"></iframe>
<script class="testbody" type="text/javascript">
runTests("test_cache_delete.js")
.then(function() {
SimpleTest.finish();
});
</script>
</body>
</html>
+111
View File
@@ -0,0 +1,111 @@
var name = "delete" + context;
var c;
function setupTest(reqs) {
return new Promise(function(resolve, reject) {
var cache;
caches.open(name).then(function(c) {
cache = c;
return c.addAll(reqs);
}).then(function() {
resolve(cache);
}).catch(function(err) {
reject(err);
});
});
}
function testBasics() {
var tests = [
"//mochi.test:8888/?foo" + context,
"//mochi.test:8888/?bar" + context,
];
var cache;
return setupTest(tests)
.then(function(c) {
cache = c;
return cache.delete("//mochi.test:8888/?baz");
}).then(function(deleted) {
ok(!deleted, "Deleting a non-existing entry should fail");
return cache.keys();
}).then(function(keys) {
is(keys.length, 2, "No entries from the cache should be deleted");
return cache.delete(tests[0]);
}).then(function(deleted) {
ok(deleted, "Deleting an existing entry should succeed");
return cache.keys();
}).then(function(keys) {
is(keys.length, 1, "Only one entry should exist now");
ok(keys[0].url.indexOf(tests[1]) >= 0, "The correct entry must be deleted");
});
}
function testFragment() {
var tests = [
"//mochi.test:8888/?foo" + context,
"//mochi.test:8888/?bar" + context,
"//mochi.test:8888/?baz" + context + "#fragment",
];
var cache;
return setupTest(tests)
.then(function(c) {
cache = c;
return cache.delete(tests[0] + "#fragment");
}).then(function(deleted) {
ok(deleted, "Deleting an existing entry should succeed");
return cache.keys();
}).then(function(keys) {
is(keys.length, 2, "Only one entry should exist now");
ok(keys[0].url.indexOf(tests[1]) >= 0, "The correct entry must be deleted");
ok(keys[1].url.indexOf(tests[2].replace("#fragment", "")) >= 0, "The correct entry must be deleted");
// Now, delete a request that was added with a fragment
return cache.delete("//mochi.test:8888/?baz" + context);
}).then(function(deleted) {
ok(deleted, "Deleting an existing entry should succeed");
return cache.keys();
}).then(function(keys) {
is(keys.length, 1, "Only one entry should exist now");
ok(keys[0].url.indexOf(tests[1]) >= 0, "3The correct entry must be deleted");
});
}
function testInterleaved() {
var tests = [
"//mochi.test:8888/?foo" + context,
"//mochi.test:8888/?bar" + context,
];
var newURL = "//mochi.test:8888/?baz" + context;
var cache;
return setupTest(tests)
.then(function(c) {
cache = c;
// Simultaneously add and delete a request
return Promise.all([
cache.delete(newURL),
cache.add(newURL),
]);
}).then(function(result) {
ok(!result[1], "deletion should fail");
return cache.keys();
}).then(function(keys) {
is(keys.length, 3, "Tree entries should still exist");
ok(keys[0].url.indexOf(tests[0]) >= 0, "The correct entry must be deleted");
ok(keys[1].url.indexOf(tests[1]) >= 0, "The correct entry must be deleted");
ok(keys[2].url.indexOf(newURL) >= 0, "The new entry should be correctly inserted");
});
}
// Make sure to clean up after each test step.
function step(testPromise) {
return testPromise.then(function() {
caches.delete(name);
});
}
step(testBasics()).then(function() {
return step(testFragment());
}).then(function() {
return step(testInterleaved());
}).then(function() {
testDone();
});
@@ -33,7 +33,7 @@ interface nsIServiceWorkerInfo : nsISupports
readonly attribute DOMString waitingCacheName;
};
[scriptable, builtinclass, uuid(861b55e9-d6ac-47cf-a528-8590e9b44de6)]
[scriptable, builtinclass, uuid(3cd3acce-8c80-4fcc-9265-067ebe8cab92)]
interface nsIServiceWorkerManager : nsISupports
{
/**
@@ -121,6 +121,9 @@ interface nsIServiceWorkerManager : nsISupports
// This is meant to be used only by about:serviceworkers. It returns an array
// of nsIServiceWorkerInfo.
nsIArray getAllRegistrations();
void sendPushEvent(in ACString scope, in DOMString data);
void sendPushSubscriptionChangedEvent(in ACString scope);
};
%{ C++
+1
View File
@@ -8,5 +8,6 @@ toolkit.jar:
content/global/BrowserElementChild.js (../browser-element/BrowserElementChild.js)
content/global/BrowserElementChildPreload.js (../browser-element/BrowserElementChildPreload.js)
* content/global/BrowserElementPanning.js (../browser-element/BrowserElementPanning.js)
content/global/PushServiceChildPreload.js (../push/PushServiceChildPreload.js)
content/global/preload.js (preload.js)
content/global/post-fork-preload.js (post-fork-preload.js)
@@ -33,4 +33,6 @@ phishingBlocked=The website at %S has been reported as a web forgery designed to
cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected.
remoteXUL=This page uses an unsupported technology that is no longer available by default.
#LOCALIZATION NOTE (tabcrashed): The following string is shown in the tab title if a page with a blank title has crashed. Current UX says that the tab title should remain blank
tabcrashed=
sslv3Used=The safety of your data on %S could not be guaranteed because it uses SSLv3, a broken security protocol.
+14 -14
View File
@@ -609,22 +609,18 @@ MediaStreamGraphImpl::UpdateStreamOrder()
// If this is a AudioNodeStream, force a AudioCallbackDriver.
if (stream->AsAudioNodeStream()) {
audioTrackPresent = true;
}
for (StreamBuffer::TrackIter tracks(stream->GetStreamBuffer(), MediaSegment::AUDIO);
!tracks.IsEnded(); tracks.Next()) {
audioTrackPresent = true;
} else {
for (StreamBuffer::TrackIter tracks(stream->GetStreamBuffer(), MediaSegment::AUDIO);
!tracks.IsEnded(); tracks.Next()) {
audioTrackPresent = true;
}
}
}
if (!audioTrackPresent &&
CurrentDriver()->AsAudioCallbackDriver()) {
bool started;
{
MonitorAutoLock mon(mMonitor);
started = CurrentDriver()->AsAudioCallbackDriver()->IsStarted();
}
if (started) {
MonitorAutoLock mon(mMonitor);
MonitorAutoLock mon(mMonitor);
if (CurrentDriver()->AsAudioCallbackDriver()->IsStarted()) {
if (mLifecycleState == LIFECYCLE_RUNNING) {
SystemClockDriver* driver = new SystemClockDriver(this);
CurrentDriver()->SwitchAtNextIteration(driver);
@@ -644,6 +640,12 @@ MediaStreamGraphImpl::UpdateStreamOrder()
}
#endif
if (!mStreamOrderDirty) {
return;
}
mStreamOrderDirty = false;
// The algorithm for finding cycles is based on Tim Leslie's iterative
// implementation [1][2] of Pearce's variant [3] of Tarjan's strongly
// connected components (SCC) algorithm. There are variations (a) to
@@ -1374,9 +1376,7 @@ MediaStreamGraphImpl::UpdateGraph(GraphTime aEndBlockingDecision)
}
mFrontMessageQueue.Clear();
if (mStreamOrderDirty) {
UpdateStreamOrder();
}
UpdateStreamOrder();
bool ensureNextIteration = false;
+237 -64
View File
@@ -4,8 +4,8 @@
"use strict";
// Don't modify this, instead set services.push.debug.
let gDebuggingEnabled = false;
// Don't modify this, instead set dom.push.debug.
let gDebuggingEnabled = true;
function debug(s) {
if (gDebuggingEnabled)
@@ -21,6 +21,94 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
Cu.import("resource://gre/modules/AppsUtils.jsm");
const PUSH_SUBSCRIPTION_CID = Components.ID("{CA86B665-BEDA-4212-8D0F-5C9F65270B58}");
function PushSubscription(pushEndpoint, scope, pageURL) {
debug("PushSubscription Constructor");
this._pushEndpoint = pushEndpoint;
this._scope = scope;
this._pageURL = pageURL;
}
PushSubscription.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
contractID: "@mozilla.org/push/PushSubscription;1",
classID : PUSH_SUBSCRIPTION_CID,
QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
Ci.nsISupportsWeakReference,
Ci.nsIObserver]),
init: function(aWindow) {
debug("PushSubscription init()");
this.initDOMRequestHelper(aWindow, [
"PushService:Unregister:OK",
"PushService:Unregister:KO",
]);
this._cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Ci.nsISyncMessageSender);
},
__init: function(endpoint, scope, pageURL) {
this._pushEndpoint = endpoint;
this._scope = scope;
this._pageURL = pageURL;
},
get endpoint() {
return this._pushEndpoint;
},
get subscriptionId() {
// TODO bug 1149271. Not sure what this is about.
return "The twins of Mammon quarrelled.";
},
unsubscribe: function() {
debug("unsubscribe! ")
let promiseInit = function(resolve, reject) {
let resolverId = this.getPromiseResolverId({resolve: resolve,
reject: reject });
this._cpmm.sendAsyncMessage("Push:Unregister", {
pageURL: this._pageURL,
scope: this._scope,
pushEndpoint: this._pushEndpoint,
requestID: resolverId
});
}.bind(this);
return this.createPromise(promiseInit);
},
receiveMessage: function(aMessage) {
debug("push subscription receiveMessage(): " + JSON.stringify(aMessage))
let json = aMessage.data;
let resolver = this.takePromiseResolver(json.requestID);
if (resolver == null) {
return;
}
switch (aMessage.name) {
case "PushService:Unregister:OK":
resolver.resolve(false);
break;
case "PushService:Unregister:KO":
resolver.reject(true);
break;
default:
debug("NOT IMPLEMENTED! receiveMessage for " + aMessage.name);
}
},
};
const PUSH_CID = Components.ID("{cde1d019-fad8-4044-b141-65fb4fb7a245}");
/**
@@ -47,101 +135,186 @@ Push.prototype = {
// Set debug first so that all debugging actually works.
// NOTE: We don't add an observer here like in PushService. Flipping the
// pref will require a reload of the app/page, which seems acceptable.
gDebuggingEnabled = Services.prefs.getBoolPref("services.push.debug");
gDebuggingEnabled = Services.prefs.getBoolPref("dom.push.debug");
debug("init()");
let principal = aWindow.document.nodePrincipal;
let appsService = Cc["@mozilla.org/AppsService;1"]
.getService(Ci.nsIAppsService);
this._manifestURL = appsService.getManifestURLByLocalId(principal.appId);
this._pageURL = principal.URI;
this._pageURL = aWindow.document.nodePrincipal.URI;
this._window = aWindow;
this.initDOMRequestHelper(aWindow, [
"PushService:Register:OK",
"PushService:Register:KO",
"PushService:Unregister:OK",
"PushService:Unregister:KO",
"PushService:Registrations:OK",
"PushService:Registrations:KO"
"PushService:Registration:OK",
"PushService:Registration:KO"
]);
this._cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Ci.nsISyncMessageSender);
},
setScope: function(scope){
debug('setScope ' + scope);
this._scope = scope;
},
askPermission: function (aAllowCallback, aCancelCallback) {
debug("askPermission");
let principal = this._window.document.nodePrincipal;
let type = "push";
let permValue =
Services.perms.testExactPermissionFromPrincipal(principal, type);
debug("Existing permission " + permValue);
if (permValue == Ci.nsIPermissionManager.ALLOW_ACTION) {
aAllowCallback();
return;
}
if (permValue == Ci.nsIPermissionManager.DENY_ACTION) {
aCancelCallback();
return;
}
// Create an array with a single nsIContentPermissionType element.
type = {
type: "push",
access: null,
options: [],
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType])
};
let typeArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
typeArray.appendElement(type, false);
// create a nsIContentPermissionRequest
let request = {
types: typeArray,
principal: principal,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
allow: function() {
aAllowCallback();
},
cancel: function() {
aCancelCallback();
},
window: this._window
};
debug("asking the window utils about permission...")
// Using askPermission from nsIDOMWindowUtils that takes care of the
// remoting if needed.
let windowUtils = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.askPermission(request);
},
receiveMessage: function(aMessage) {
debug("receiveMessage()");
let request = this.getRequest(aMessage.data.requestID);
debug("push receiveMessage(): " + JSON.stringify(aMessage))
let json = aMessage.data;
if (!request) {
debug("No request " + json.requestID);
let resolver = this.takePromiseResolver(json.requestID);
if (!resolver) {
return;
}
switch (aMessage.name) {
case "PushService:Register:OK":
Services.DOMRequest.fireSuccess(request, json.pushEndpoint);
{
let subscription = new this._window.PushSubscription(json.pushEndpoint,
this._scope,
this._pageURL.spec);
resolver.resolve(subscription);
break;
}
case "PushService:Register:KO":
Services.DOMRequest.fireError(request, json.error);
resolver.reject(null);
break;
case "PushService:Unregister:OK":
Services.DOMRequest.fireSuccess(request, json.pushEndpoint);
case "PushService:Registration:OK":
{
let subscription = null;
try {
subscription = new this._window.PushSubscription(json.registration.pushEndpoint,
this._scope, this._pageURL.spec);
} catch(error) {
}
resolver.resolve(subscription);
break;
case "PushService:Unregister:KO":
Services.DOMRequest.fireError(request, json.error);
break;
case "PushService:Registrations:OK":
Services.DOMRequest.fireSuccess(request, json.registrations);
break;
case "PushService:Registrations:KO":
Services.DOMRequest.fireError(request, json.error);
}
case "PushService:Registration:KO":
resolver.reject(null);
break;
default:
debug("NOT IMPLEMENTED! receiveMessage for " + aMessage.name);
}
},
register: function() {
debug("register()");
let req = this.createRequest();
if (!Services.prefs.getBoolPref("services.push.connection.enabled")) {
// If push socket is disabled by the user, immediately error rather than
// timing out.
Services.DOMRequest.fireErrorAsync(req, "NetworkError");
return req;
}
subscribe: function() {
debug("subscribe()");
let p = this.createPromise(function(resolve, reject) {
let resolverId = this.getPromiseResolverId({ resolve: resolve, reject: reject });
this._cpmm.sendAsyncMessage("Push:Register", {
pageURL: this._pageURL.spec,
manifestURL: this._manifestURL,
requestID: this.getRequestId(req)
});
return req;
this.askPermission(
function() {
this._cpmm.sendAsyncMessage("Push:Register", {
pageURL: this._pageURL.spec,
scope: this._scope,
requestID: resolverId
});
}.bind(this),
function() {
reject("denied");
}
);
}.bind(this));
return p;
},
unregister: function(aPushEndpoint) {
debug("unregister(" + aPushEndpoint + ")");
let req = this.createRequest();
this._cpmm.sendAsyncMessage("Push:Unregister", {
pageURL: this._pageURL.spec,
manifestURL: this._manifestURL,
requestID: this.getRequestId(req),
pushEndpoint: aPushEndpoint
});
return req;
getSubscription: function() {
debug("getSubscription()" + this._scope);
let p = this.createPromise(function(resolve, reject) {
let resolverId = this.getPromiseResolverId({ resolve: resolve, reject: reject });
this.askPermission(
function() {
this._cpmm.sendAsyncMessage("Push:Registration", {
pageURL: this._pageURL.spec,
scope: this._scope,
requestID: resolverId
});
}.bind(this),
function() {
reject("denied");
}
);
}.bind(this));
return p;
},
registrations: function() {
debug("registrations()");
let req = this.createRequest();
this._cpmm.sendAsyncMessage("Push:Registrations", {
manifestURL: this._manifestURL,
requestID: this.getRequestId(req)
});
return req;
}
hasPermission: function() {
debug("getSubscription()" + this._scope);
let p = this.createPromise(function(resolve, reject) {
let permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
let permission = permissionManager.testExactPermission(this._pageURL, "push");
let pushPermissionStatus = "default";
if (permission == Ci.nsIPermissionManager.ALLOW_ACTION) {
pushPermissionStatus = "granted";
} else if (permission == Ci.nsIPermissionManager.DENY_ACTION) {
pushPermissionStatus = "denied";
}
resolve(pushPermissionStatus);
}.bind(this));
return p;
},
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Push]);
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Push, PushSubscription]);
+3
View File
@@ -2,6 +2,9 @@
component {cde1d019-fad8-4044-b141-65fb4fb7a245} Push.js
contract @mozilla.org/push/PushManager;1 {cde1d019-fad8-4044-b141-65fb4fb7a245}
component {CA86B665-BEDA-4212-8D0F-5C9F65270B58} Push.js
contract @mozilla.org/push/PushSubscription;1 {CA86B665-BEDA-4212-8D0F-5C9F65270B58}
# Component to initialize PushService on startup.
component {4b8caa3b-3c58-4f3c-a7f5-7bd9cb24c11d} PushServiceLauncher.js
contract @mozilla.org/push/ServiceLauncher;1 {4b8caa3b-3c58-4f3c-a7f5-7bd9cb24c11d}
+129 -112
View File
@@ -5,8 +5,8 @@
"use strict";
// Don't modify this, instead set services.push.debug.
let gDebuggingEnabled = false;
// Don't modify this, instead set dom.push.debug.
let gDebuggingEnabled = true;
function debug(s) {
if (gDebuggingEnabled)
@@ -41,7 +41,7 @@ var threadManager = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadM
this.EXPORTED_SYMBOLS = ["PushService"];
const prefs = new Preferences("services.push.");
const prefs = new Preferences("dom.push.");
// Set debug first so that all debugging actually works.
gDebuggingEnabled = prefs.get("debug");
@@ -54,7 +54,7 @@ const kUDP_WAKEUP_WS_STATUS_CODE = 4774; // WebSocket Close status code sent
// wake client up using UDP.
const kCHILD_PROCESS_MESSAGES = ["Push:Register", "Push:Unregister",
"Push:Registrations"];
"Push:Registration"];
const kWS_MAX_WENTDOWN = 2;
@@ -79,10 +79,9 @@ this.PushDB.prototype = {
// index to fetch records based on endpoints. used by unregister
objectStore.createIndex("pushEndpoint", "pushEndpoint", { unique: true });
// index to fetch records per manifest, so we can identify endpoints
// associated with an app. Since an app can have multiple endpoints
// uniqueness cannot be enforced
objectStore.createIndex("manifestURL", "manifestURL", { unique: false });
// index to fetch records per scope, so we can identify endpoints
// associated with an app.
objectStore.createIndex("scope", "scope", { unique: true });
},
/*
@@ -94,7 +93,7 @@ this.PushDB.prototype = {
* Callback function to invoke when there was an error.
*/
put: function(aChannelRecord, aSuccessCb, aErrorCb) {
debug("put()");
debug("put()" + JSON.stringify(aChannelRecord));
this.newTxn(
"readwrite",
@@ -173,30 +172,20 @@ this.PushDB.prototype = {
);
},
getAllByManifestURL: function(aManifestURL, aSuccessCb, aErrorCb) {
debug("getAllByManifestURL()");
if (!aManifestURL) {
if (typeof aErrorCb == "function") {
aErrorCb("PushDB.getAllByManifestURL: Got undefined aManifestURL");
}
return;
}
let self = this;
getByScope: function(aScope, aSuccessCb, aErrorCb) {
debug("getByScope() " + aScope);
this.newTxn(
"readonly",
kPUSHDB_STORE_NAME,
function txnCb(aTxn, aStore) {
let index = aStore.index("manifestURL");
let range = IDBKeyRange.only(aManifestURL);
aTxn.result = [];
index.openCursor(range).onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
debug(cursor.value.manifestURL + " " + cursor.value.channelID);
aTxn.result.push(cursor.value);
cursor.continue();
}
aTxn.result = undefined;
let index = aStore.index("scope");
index.get(aScope).onsuccess = function setTxnResult(aEvent) {
aTxn.result = aEvent.target.result;
debug("Fetch successful " + aEvent.target.result);
}
},
aSuccessCb,
@@ -325,17 +314,17 @@ this.PushService = {
}
break;
case "nsPref:changed":
if (aData == "services.push.serverURL") {
debug("services.push.serverURL changed! websocket. new value " +
if (aData == "dom.push.serverURL") {
debug("dom.push.serverURL changed! websocket. new value " +
prefs.get("serverURL"));
this._shutdownWS();
} else if (aData == "services.push.connection.enabled") {
} else if (aData == "dom.push.connection.enabled") {
if (prefs.get("connection.enabled")) {
this._startListeningIfChannelsPresent();
} else {
this._shutdownWS();
}
} else if (aData == "services.push.debug") {
} else if (aData == "dom.push.debug") {
gDebuggingEnabled = prefs.get("debug");
}
break;
@@ -382,35 +371,30 @@ this.PushService = {
return;
}
// Only remove push registrations for apps.
if (data.browserOnly) {
return;
}
// TODO 1149274. We should support site permissions as well as a way to go from manifest
// url to 'all scopes registered for push in this app'
let appsService = Cc["@mozilla.org/AppsService;1"]
.getService(Ci.nsIAppsService);
let manifestURL = appsService.getManifestURLByLocalId(data.appId);
if (!manifestURL) {
debug("webapps-clear-data: No manifest URL found for " + data.appId);
let scope = appsService.getScopeByLocalId(data.appId);
if (!scope) {
debug("webapps-clear-data: No scope found for " + data.appId);
return;
}
this._db.getAllByManifestURL(manifestURL, function(records) {
debug("Got " + records.length);
for (let i = 0; i < records.length; i++) {
this._db.delete(records[i].channelID, null, function() {
debug("webapps-clear-data: " + manifestURL +
" Could not delete entry " + records[i].channelID);
});
this._db.getByScope(scope, function(record) {
this._db.delete(records.channelID, null, function() {
debug("webapps-clear-data: " + scope +
" Could not delete entry " + records.channelID);
// courtesy, but don't establish a connection
// just for it
if (this._ws) {
debug("Had a connection, so telling the server");
this._send("unregister", {channelID: records[i].channelID});
this._send("unregister", {channelID: records.channelID});
}
}
}.bind(this), function() {
debug("webapps-clear-data: Error in getAllByManifestURL(" + manifestURL + ")");
}.bind(this), function() {
debug("webapps-clear-data: Error in getByScope(" + scope + ")");
});
});
break;
@@ -465,7 +449,7 @@ this.PushService = {
* 1) the gap between the maximum working ping and the first ping that
* gives an error (timeout) OR
* 2) we have reached the pref of the maximum value we allow for a ping
* (services.push.adaptive.upperLimit)
* (dom.push.adaptive.upperLimit)
*/
_recalculatePing: true,
@@ -512,6 +496,11 @@ this.PushService = {
if (!prefs.get("enabled"))
return null;
var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIFrameScriptLoader);
globalMM.loadFrameScript("chrome://global/content/PushServiceChildPreload.js", true);
this._db = new PushDB();
let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
@@ -672,7 +661,7 @@ this.PushService = {
* This algorithm tries to search the best value between a disconnection and a
* valid ping, to ensure better battery life and network resources usage.
*
* The value is saved in services.push.pingInterval
* The value is saved in dom.push.pingInterval
* @param wsWentDown [Boolean] if the WebSocket was closed or it is still alive
*
*/
@@ -842,7 +831,7 @@ this.PushService = {
let serverURL = prefs.get("serverURL");
if (!serverURL) {
debug("No services.push.serverURL found!");
debug("No dom.push.serverURL found!");
return;
}
@@ -850,7 +839,7 @@ this.PushService = {
try {
uri = Services.io.newURI(serverURL, null, null);
} catch(e) {
debug("Error creating valid URI from services.push.serverURL (" +
debug("Error creating valid URI from dom.push.serverURL (" +
serverURL + ")");
return;
}
@@ -1090,7 +1079,7 @@ this.PushService = {
debug("got new UAID: all re-register");
this._notifyAllAppsRegister()
.then(this._dropRegistrations.bind(this))
.then(this._dropRegistration.bind(this))
.then(finishHandshake.bind(this));
return;
@@ -1279,35 +1268,32 @@ this.PushService = {
},
// Fires a push-register system message to all applications that have
// registrations.
// registration.
_notifyAllAppsRegister: function() {
debug("notifyAllAppsRegister()");
let deferred = Promise.defer();
// records are objects describing the registrations as stored in IndexedDB.
// records are objects describing the registration as stored in IndexedDB.
function wakeupRegisteredApps(records) {
// Pages to be notified.
// wakeupTable[manifestURL] -> [ pageURL ]
// wakeupTable[scope] -> [ pageURL ]
let wakeupTable = {};
for (let i = 0; i < records.length; i++) {
let record = records[i];
if (!(record.manifestURL in wakeupTable))
wakeupTable[record.manifestURL] = [];
if (!(record.scope in wakeupTable))
wakeupTable[record.scope] = [];
wakeupTable[record.manifestURL].push(record.pageURL);
wakeupTable[record.scope].push(record.pageURL);
}
let messenger = Cc["@mozilla.org/system-message-internal;1"]
.getService(Ci.nsISystemMessagesInternal);
// TODO -- test needed. E10s support needed.
for (let manifestURL in wakeupTable) {
wakeupTable[manifestURL].forEach(function(pageURL) {
messenger.sendMessage('push-register', {},
Services.io.newURI(pageURL, null, null),
Services.io.newURI(manifestURL, null, null));
let globalMM = Cc['@mozilla.org/globalmessagemanager;1'].getService(Ci.nsIMessageListenerManager);
for (let scope in wakeupTable) {
wakeupTable[scope].forEach(function(pageURL) {
globalMM.broadcastAsyncMessage('pushsubscriptionchanged', aPushRecord.scope);
});
}
deferred.resolve();
}
@@ -1317,22 +1303,36 @@ this.PushService = {
},
_notifyApp: function(aPushRecord) {
if (!aPushRecord || !aPushRecord.pageURL || !aPushRecord.manifestURL) {
debug("notifyApp() something is undefined. Dropping notification");
if (!aPushRecord || !aPushRecord.pageURL || !aPushRecord.scope) {
debug("notifyApp() something is undefined. Dropping notification: "
+ JSON.stringify(aPushRecord) );
return;
}
debug("notifyApp() " + aPushRecord.pageURL +
" " + aPushRecord.manifestURL);
" " + aPushRecord.scope);
let pageURI = Services.io.newURI(aPushRecord.pageURL, null, null);
let manifestURI = Services.io.newURI(aPushRecord.manifestURL, null, null);
let scopeURI = Services.io.newURI(aPushRecord.scope, null, null);
let message = {
pushEndpoint: aPushRecord.pushEndpoint,
version: aPushRecord.version
};
let messenger = Cc["@mozilla.org/system-message-internal;1"]
.getService(Ci.nsISystemMessagesInternal);
messenger.sendMessage('push', message, pageURI, manifestURI);
// If permission has been revoked, trash the message.
if(Services.perms.testExactPermission(scopeURI, "push") != Ci.nsIPermissionManager.ALLOW_ACTION) {
debug("Does not have permission for push.")
return;
}
// TODO data.
let data = {
payload: "Short as life is, we make it still shorter by the careless waste of time.",
scope: aPushRecord.scope
};
let globalMM = Cc['@mozilla.org/globalmessagemanager;1']
.getService(Ci.nsIMessageListenerManager);
globalMM.broadcastAsyncMessage('push', data);
},
_updatePushRecord: function(aPushRecord) {
@@ -1342,7 +1342,7 @@ this.PushService = {
return deferred.promise;
},
_dropRegistrations: function() {
_dropRegistration: function() {
let deferred = Promise.defer();
this._db.drop(deferred.resolve, deferred.reject);
return deferred.promise;
@@ -1365,9 +1365,8 @@ this.PushService = {
* Called on message from the child process. aPageRecord is an object sent by
* navigator.push, identifying the sending page and other fields.
*/
register: function(aPageRecord, aMessageManager) {
debug("register()");
_registerWithServer: function(aPageRecord, aMessageManager) {
let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator);
// generateUUID() gives a UUID surrounded by {...}, slice them off.
@@ -1384,7 +1383,26 @@ this.PushService = {
},
function(message) {
aMessageManager.sendAsyncMessage("PushService:Register:KO", message);
});
}
);
},
register: function(aPageRecord, aMessageManager) {
debug("register(): " + JSON.stringify(aPageRecord));
this._db.getByScope(aPageRecord.scope,
function(aPageRecord, aMessageManager, pushRecord) {
if (pushRecord == null) {
this._registerWithServer(aPageRecord, aMessageManager);
}
else {
this._onRegistrationSuccess(aPageRecord, aMessageManager, pushRecord);
}
}.bind(this, aPageRecord, aMessageManager),
function () {
debug("getByScope failed");
}
);
},
/**
@@ -1421,10 +1439,12 @@ this.PushService = {
channelID: data.channelID,
pushEndpoint: data.pushEndpoint,
pageURL: aPageRecord.pageURL,
manifestURL: aPageRecord.manifestURL,
scope: aPageRecord.scope,
version: null
};
debug("scope in _onRegisterSuccess: " + aPageRecord.scope)
this._updatePushRecord(record)
.then(
function() {
@@ -1436,7 +1456,7 @@ this.PushService = {
this._send("unregister", {channelID: record.channelID});
message["error"] = error;
deferred.reject(message);
}
}.bind(this)
);
return deferred.promise;
@@ -1479,7 +1499,7 @@ this.PushService = {
* data is cheap, reliable notification is not.
*/
unregister: function(aPageRecord, aMessageManager) {
debug("unregister()");
debug("unregister() " + JSON.stringify(aPageRecord));
let fail = function(error) {
debug("unregister() fail() error " + error);
@@ -1495,7 +1515,7 @@ this.PushService = {
}
// Non-owner tried to unregister, say success, but don't do anything.
if (record.manifestURL !== aPageRecord.manifestURL) {
if (record.scope !== aPageRecord.scope) {
aMessageManager.sendAsyncMessage("PushService:Unregister:OK", {
requestID: aPageRecord.requestID,
pushEndpoint: aPageRecord.pushEndpoint
@@ -1518,38 +1538,35 @@ this.PushService = {
/**
* Called on message from the child process
*/
registrations: function(aPageRecord, aMessageManager) {
debug("registrations()");
if (aPageRecord.manifestURL) {
this._db.getAllByManifestURL(aPageRecord.manifestURL,
this._onRegistrationsSuccess.bind(this, aPageRecord, aMessageManager),
this._onRegistrationsError.bind(this, aPageRecord, aMessageManager));
}
else {
this._onRegistrationsError(aPageRecord, aMessageManager);
}
registration: function(aPageRecord, aMessageManager) {
debug("registration()");
this._db.getByScope(aPageRecord.scope,
this._onRegistrationSuccess.bind(this, aPageRecord, aMessageManager),
this._onRegistrationError.bind(this, aPageRecord, aMessageManager));
},
_onRegistrationsSuccess: function(aPageRecord,
aMessageManager,
pushRecords) {
let registrations = [];
pushRecords.forEach(function(pushRecord) {
registrations.push({
__exposedProps__: { pushEndpoint: 'r', version: 'r' },
pushEndpoint: pushRecord.pushEndpoint,
version: pushRecord.version
});
});
aMessageManager.sendAsyncMessage("PushService:Registrations:OK", {
_onRegistrationSuccess: function(aPageRecord,
aMessageManager,
pushRecord) {
let registration = null;
if (pushRecord) {
registration = {
pushEndpoint: pushRecord.pushEndpoint,
version: pushRecord.version
};
}
aMessageManager.sendAsyncMessage("PushService:Registration:OK", {
requestID: aPageRecord.requestID,
registrations: registrations
registration: registration
});
},
_onRegistrationsError: function(aPageRecord, aMessageManager) {
aMessageManager.sendAsyncMessage("PushService:Registrations:KO", {
_onRegistrationError: function(aPageRecord, aMessageManager) {
aMessageManager.sendAsyncMessage("PushService:Registration:KO", {
requestID: aPageRecord.requestID,
error: "Database error"
});
@@ -1733,7 +1750,7 @@ this.PushService = {
this._udpServer = Cc["@mozilla.org/network/udp-socket;1"]
.createInstance(Ci.nsIUDPSocket);
this._udpServer.init(-1, false, Services.scriptSecurityManager.getSystemPrincipal());
this._udpServer.init(-1, false);
this._udpServer.asyncListen(this);
debug("listenForUDPWakeup listening on " + this._udpServer.port);
+18
View File
@@ -0,0 +1,18 @@
/* 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";
XPCOMUtils.defineLazyServiceGetter(this,
"swm",
"@mozilla.org/serviceworkers/manager;1",
"nsIServiceWorkerManager");
addMessageListener("push", function (aMessage) {
swm.sendPushEvent(aMessage.data.scope, aMessage.data.payload);
});
addMessageListener("pushsubscriptionchanged", function (aMessage) {
swm.sendPushSubscriptionChangedEvent(aMessage.data);
});
+10 -3
View File
@@ -30,11 +30,18 @@ PushServiceLauncher.prototype = {
break;
case "final-ui-startup":
Services.obs.removeObserver(this, "final-ui-startup");
if (!Services.prefs.getBoolPref("services.push.enabled")) {
if (!Services.prefs.getBoolPref("dom.push.enabled")) {
return;
}
Cu.import("resource://gre/modules/PushService.jsm");
PushService.init();
let isParent = Cc["@mozilla.org/xre/runtime;1"]
.getService(Ci.nsIXULRuntime)
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
if (isParent) {
Cu.import("resource://gre/modules/PushService.jsm");
PushService.init();
}
break;
}
}
+4
View File
@@ -12,3 +12,7 @@ EXTRA_COMPONENTS += [
EXTRA_JS_MODULES += [
'PushService.jsm',
]
MOCHITEST_MANIFESTS += [
'test/mochitest.ini',
]
+27
View File
@@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<script>
function waitOnPushMessage(pushSubscription)
{
var p = new Promise(function(res, rej) {
navigator.serviceWorker.onmessage = function(e) {
if (e.data.type == "finished") {
parent.ok(e.data.okay == "yes", "Got a push message.");
res(pushSubscription);
}
};
});
return p;
}
</script>
</head>
<body>
</body>
</html>
+15
View File
@@ -0,0 +1,15 @@
[DEFAULT]
subsuite = push
support-files =
worker.js
push-server.sjs
frame.html
[test_has_permissions.html]
skip-if = os == "android" || toolkit == "gonk"
[test_permissions.html]
skip-if = os == "android" || toolkit == "gonk"
[test_register.html]
skip-if = os == "android" || toolkit == "gonk"
[test_multiple_register.html]
skip-if = os == "android" || toolkit == "gonk"
+26
View File
@@ -0,0 +1,26 @@
function debug(str) {
// dump("@@@ push-server " + str + "\n");
}
function handleRequest(request, response)
{
debug("handling request!");
const Cc = Components.classes;
const Ci = Components.interfaces;
let params = request.getHeader("X-Push-Server");
debug("params = " + params);
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
xhr.open("PUT", params);
xhr.send();
xhr.onload = function(e) {
debug("xhr : " + this.status);
}
xhr.onerror = function(e) {
debug("xhr error: " + e);
}
response.setStatusLine(request.httpVersion, "200", "OK");
}
+76
View File
@@ -0,0 +1,76 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 1038811: Push tests.
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<head>
<title>Test for Bug 1038811</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1038811">Mozilla Bug 1038811</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="text/javascript">
function debug(str) {
// console.log(str + "\n");
}
function start() {
return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: "."})
.then((swr) => registration = swr);
}
function unregister() {
return registration.unregister().then(function(result) {
ok(result, "Unregister should return true.");
});
}
function hasPermission(swr) {
var p = new Promise(function(res, rej) {
swr.pushManager.hasPermission().then(
function(status) {
debug("status: " + status);
ok(true, "hasPermission() returned a status");
res(swr);
}, function(error) {
ok(false, "hasPermission failed.");
res(swr);
}
);
});
return p;
}
function runTest() {
start()
.then(hasPermission)
.then(unregister)
.catch(function(e) {
ok(false, "Some test failed with error " + e);
}).then(SimpleTest.finish);
}
SpecialPowers.addPermission('push', false, document);
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, runTest);
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>
+128
View File
@@ -0,0 +1,128 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 1038811: Push tests.
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<head>
<title>Test for Bug 1038811</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1038811">Mozilla Bug 1038811</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="text/javascript">
function debug(str) {
// console.log(str + "\n");
}
function start() {
return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: "."})
.then((swr) => registration = swr);
}
function unregister() {
return registration.unregister().then(function(result) {
ok(result, "Unregister should return true.");
});
}
function setupPushNotification(swr) {
var p = new Promise(function(res, rej) {
swr.pushManager.subscribe().then(
function(pushSubscription) {
ok(true, "successful registered for push notification");
res({swr: swr, pushSubscription: pushSubscription});
}, function(error) {
ok(false, "could not register for push notification");
res(null);
}
);
});
return p;
}
function setupSecondEndpoint(result) {
var p = new Promise(function(res, rej) {
result.swr.pushManager.subscribe().then(
function(pushSubscription) {
ok(result.pushSubscription.endpoint == pushSubscription.endpoint, "setupSecondEndpoint - Got the same endpoint back.");
res(result);
}, function(error) {
ok(false, "could not register for push notification");
res(null);
}
);
});
return p;
}
function getEndpointExpectNull(swr) {
var p = new Promise(function(res, rej) {
swr.pushManager.getSubscription().then(
function(pushSubscription) {
ok(pushSubscription == null, "getEndpoint should return null when app not subscribed.");
res(swr);
}, function(error) {
ok(false, "could not register for push notification");
res(null);
}
);
});
return p;
}
function getEndpoint(result) {
var p = new Promise(function(res, rej) {
result.swr.pushManager.getSubscription().then(
function(pushSubscription) {
ok(result.pushSubscription.endpoint == pushSubscription.endpoint, "getEndpoint - Got the same endpoint back.");
res(pushSubscription);
}, function(error) {
ok(false, "could not register for push notification");
res(null);
}
);
});
return p;
}
function unregisterPushNotification(pushSubscription) {
return pushSubscription.unsubscribe();
}
function runTest() {
start()
.then(getEndpointExpectNull)
.then(setupPushNotification)
.then(setupSecondEndpoint)
.then(getEndpoint)
.then(unregisterPushNotification)
.then(unregister)
.catch(function(e) {
ok(false, "Some test failed with error " + e);
}).then(SimpleTest.finish);
}
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, runTest);
SpecialPowers.addPermission('push', true, document);
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>
+91
View File
@@ -0,0 +1,91 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 1038811: Push tests.
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<head>
<title>Test for Bug 1038811</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1038811">Mozilla Bug 1038811</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="text/javascript">
function debug(str) {
// console.log(str + "\n");
}
function start() {
return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: "."})
.then((swr) => registration = swr);
}
function unregister() {
return registration.unregister().then(function(result) {
ok(result, "Unregister should return true.");
});
}
function setupPushNotification(swr) {
var p = new Promise(function(res, rej) {
swr.pushManager.subscribe().then(
function(pushSubscription) {
ok(false, "subscribe() should fail because no permission for push");
res(swr);
}, function(error) {
ok(true, "subscribe() could not register for push notification");
res(swr);
}
);
});
return p;
}
function getEndpoint(swr) {
var p = new Promise(function(res, rej) {
swr.pushManager.getSubscription().then(
function(pushSubscription) {
ok(false, "getSubscription() should fail because no permission for push");
res(swr);
}, function(error) {
ok(true, "getSubscription() could not register for push notification");
res(swr);
}
);
});
return p;
}
function runTest() {
start()
.then(setupPushNotification)
.then(getEndpoint)
.then(unregister)
.catch(function(e) {
ok(false, "Some test failed with error " + e);
}).then(SimpleTest.finish);
}
SpecialPowers.addPermission('push', false, document);
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, runTest);
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>
+123
View File
@@ -0,0 +1,123 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 1038811: Push tests.
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<head>
<title>Test for Bug 1038811</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1038811">Mozilla Bug 1038811</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="text/javascript">
function debug(str) {
// console.log(str + "\n");
}
var controlledFrame;
function createControlledIFrame(swr) {
var p = new Promise(function(res, rej) {
var iframe = document.createElement('iframe');
iframe.id = "controlledFrame";
iframe.src = "http://mochi.test:8888/tests/dom/push/test/frame.html";
iframe.onload = function() {
res(swr)
}
controlledFrame = iframe;
document.body.appendChild(iframe);
});
return p;
}
function sendPushToPushServer(pushEndpoint) {
// Work around CORS for now.
var xhr = new XMLHttpRequest();
xhr.open('GET', "http://mochi.test:8888/tests/dom/push/test/push-server.sjs", true);
xhr.setRequestHeader("X-Push-Server", pushEndpoint);
xhr.onload = function(e) {
debug("xhr : " + this.status);
}
xhr.onerror = function(e) {
debug("xhr error: " + e);
}
xhr.send("version=24601");
}
var registration;
function start() {
return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: "."})
.then((swr) => registration = swr);
}
function unregister() {
return registration.unregister().then(function(result) {
ok(result, "Unregister should return true.");
});
}
function setupPushNotification(swr) {
var p = new Promise(function(res, rej) {
swr.pushManager.subscribe().then(
function(pushSubscription) {
ok(true, "successful registered for push notification");
res(pushSubscription);
}, function(error) {
ok(false, "could not register for push notification");
res(null);
}
);
});
return p;
}
function unregisterPushNotification(pushSubscription) {
controlledFrame.parentNode.removeChild(controlledFrame);
controlledFrame = null;
return pushSubscription.unsubscribe();
}
function waitForPushNotification(pushSubscription) {
var p = controlledFrame.contentWindow.waitOnPushMessage();
sendPushToPushServer(pushSubscription.endpoint);
return p.then(function() {
return pushSubscription;
});
}
function runTest() {
start()
.then(createControlledIFrame)
.then(setupPushNotification)
.then(waitForPushNotification)
.then(unregisterPushNotification)
.then(unregister)
.catch(function(e) {
ok(false, "Some test failed with error " + e);
}).then(SimpleTest.finish);
}
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, runTest);
SpecialPowers.addPermission('push', true, document);
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>
+16
View File
@@ -0,0 +1,16 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
addEventListener("push", function(event) {
self.clients.matchAll().then(function(result) {
if (event instanceof PushEvent &&
event.data instanceof PushMessageData &&
event.data.text().length > 0) {
result[0].postMessage({type: "finished", okay: "yes"});
return;
}
result[0].postMessage({type: "finished", okay: "no"});
});
});
@@ -864,6 +864,8 @@ var interfaceNamesInGlobalScope =
"Promise",
// IMPORTANT: Do not change this list without review from a DOM peer!
"PropertyNodeList",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "PushManager", pref: "dom.push.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"RadioNodeList",
// IMPORTANT: Do not change this list without review from a DOM peer!
@@ -900,6 +902,8 @@ var interfaceNamesInGlobalScope =
{name: "ServiceWorker", pref: "dom.serviceWorkers.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "ServiceWorkerContainer", pref: "dom.serviceWorkers.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "ServiceWorkerRegistration", pref: "dom.serviceWorkers.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"SettingsLock",
// IMPORTANT: Do not change this list without review from a DOM peer!
+19
View File
@@ -0,0 +1,19 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* https://w3c.github.io/push-api/
*/
[Constructor(DOMString type, optional PushEventInit eventInitDict), Exposed=ServiceWorker]
interface PushEvent : ExtendableEvent {
readonly attribute PushMessageData data;
};
typedef USVString PushMessageDataInit;
dictionary PushEventInit : ExtendableEventInit {
PushMessageDataInit data;
};
+20 -8
View File
@@ -2,15 +2,27 @@
/* 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/.
*
* The origin of this IDL file is
* https://w3c.github.io/push-api/
*/
[NoInterfaceObject,
NavigatorProperty="push",
JSImplementation="@mozilla.org/push/PushManager;1",
CheckPermissions="push",
Pref="services.push.enabled"]
[JSImplementation="@mozilla.org/push/PushManager;1",
Pref="dom.push.enabled"]
interface PushManager {
DOMRequest register();
DOMRequest unregister(DOMString pushEndpoint);
DOMRequest registrations();
Promise<PushSubscription> subscribe();
Promise<PushSubscription?> getSubscription();
Promise<PushPermissionStatus> hasPermission();
// We need a setter in the bindings so that the C++ can use it,
// but we don't want it exposed to client JS. WebPushMethodHider
// always returns false.
[Func="ServiceWorkerRegistration::WebPushMethodHider"] void setScope(DOMString scope);
};
enum PushPermissionStatus
{
"granted",
"denied",
"default"
};
+17
View File
@@ -0,0 +1,17 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* https://w3c.github.io/push-api/
*/
[Exposed=ServiceWorker]
interface PushMessageData
{
ArrayBuffer arrayBuffer();
Blob blob();
object json();
USVString text();
};
+17
View File
@@ -0,0 +1,17 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* https://w3c.github.io/push-api/
*/
[JSImplementation="@mozilla.org/push/PushSubscription;1",
Constructor(DOMString pushEndpoint, DOMString scope, DOMString pageURL), ChromeOnly]
interface PushSubscription
{
readonly attribute USVString endpoint;
readonly attribute DOMString subscriptionId;
Promise<boolean> unsubscribe();
};
+1 -1
View File
@@ -27,7 +27,7 @@ interface ServiceWorkerContainer : EventTarget {
Promise<ServiceWorkerRegistration> getRegistration(optional USVString documentURL = "");
[Throws]
Promise<sequence<ServiceWorkerRegistration>> getRegistrations();
Promise<sequence<ServiceWorkerRegistration>> getRegistrations();
attribute EventHandler oncontrollerchange;
attribute EventHandler onreloadpage;
@@ -23,3 +23,10 @@ interface ServiceWorkerRegistration : EventTarget {
// event
attribute EventHandler onupdatefound;
};
partial interface ServiceWorkerRegistration {
#ifndef MOZ_SIMPLEPUSH
[Throws, Pref="dom.push.enabled"]
readonly attribute PushManager pushManager;
#endif
};
+1 -1
View File
@@ -8,7 +8,7 @@
JSImplementation="@mozilla.org/push/PushManager;1",
CheckPermissions="push",
Pref="services.push.enabled"]
interface PushManager {
interface SimplePushManager {
DOMRequest register();
DOMRequest unregister(DOMString pushEndpoint);
DOMRequest registrations();
+9 -2
View File
@@ -11,6 +11,7 @@ GENERATED_WEBIDL_FILES = [
PREPROCESSED_WEBIDL_FILES = [
'HTMLMediaElement.webidl',
'Navigator.webidl',
'ServiceWorkerRegistration.webidl',
'Window.webidl',
]
@@ -364,7 +365,6 @@ WEBIDL_FILES = [
'ServiceWorker.webidl',
'ServiceWorkerContainer.webidl',
'ServiceWorkerGlobalScope.webidl',
'ServiceWorkerRegistration.webidl',
'SettingChangeNotification.webidl',
'SettingsManager.webidl',
'ShadowRoot.webidl',
@@ -658,9 +658,16 @@ if CONFIG['MOZ_B2G_RIL']:
]
if CONFIG['MOZ_SIMPLEPUSH']:
WEBIDL_FILES += SimplePushManager.webidl
WEBIDL_FILES += [
'SimplePushManager.webidl'
]
else:
WEBIDL_FILES += [
'PushEvent.webidl',
'PushManager.webidl',
'PushManager.webidl',
'PushMessageData.webidl',
'PushSubscription.webidl',
]
if CONFIG['MOZ_NFC']:
+55
View File
@@ -405,4 +405,59 @@ NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent)
NS_IMPL_CYCLE_COLLECTION_INHERITED(InstallEvent, ExtendableEvent, mActiveWorker)
#ifndef MOZ_SIMPLEPUSH
PushMessageData::PushMessageData(const nsAString& aData)
: mData(aData)
{
}
PushMessageData::~PushMessageData()
{
}
NS_IMPL_ISUPPORTS0(PushMessageData);
void
PushMessageData::Json(JSContext* cx, JS::MutableHandle<JSObject*> aRetval)
{
//todo bug 1149195. Don't be lazy.
NS_ABORT();
}
void
PushMessageData::Text(nsAString& aData)
{
aData = mData;
}
void
PushMessageData::ArrayBuffer(JSContext* cx, JS::MutableHandle<JSObject*> aRetval)
{
//todo bug 1149195. Don't be lazy.
NS_ABORT();
}
mozilla::dom::File*
PushMessageData::Blob()
{
//todo bug 1149195. Don't be lazy.
NS_ABORT();
return nullptr;
}
PushEvent::PushEvent(EventTarget* aOwner)
: ExtendableEvent(aOwner)
{
}
NS_INTERFACE_MAP_BEGIN(PushEvent)
NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent)
NS_IMPL_ADDREF_INHERITED(PushEvent, ExtendableEvent)
NS_IMPL_RELEASE_INHERITED(PushEvent, ExtendableEvent)
#endif /* ! MOZ_SIMPLEPUSH */
END_WORKERS_NAMESPACE
+92
View File
@@ -12,6 +12,12 @@
#include "mozilla/dom/InstallEventBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/Response.h"
#ifndef MOZ_SIMPLEPUSH
#include "mozilla/dom/PushEventBinding.h"
#include "mozilla/dom/PushMessageDataBinding.h"
#endif
#include "nsProxyRelease.h"
class nsIInterceptedChannel;
@@ -218,5 +224,91 @@ public:
}
};
#ifndef MOZ_SIMPLEPUSH
class PushMessageData final : public nsISupports,
public nsWrapperCache
{
nsString mData;
public:
NS_DECL_ISUPPORTS
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
{
return mozilla::dom::PushMessageDataBinding_workers::Wrap(aCx, this, aGivenProto);
}
nsISupports* GetParentObject() const {
return nullptr;
}
void Json(JSContext* cx, JS::MutableHandle<JSObject*> aRetval);
void Text(nsAString& aData);
void ArrayBuffer(JSContext* cx, JS::MutableHandle<JSObject*> aRetval);
mozilla::dom::File* Blob();
explicit PushMessageData(const nsAString& aData);
private:
~PushMessageData();
};
class PushEvent final : public ExtendableEvent
{
nsString mData;
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
protected:
explicit PushEvent(mozilla::dom::EventTarget* aOwner);
~PushEvent() {}
public:
NS_DECL_ISUPPORTS_INHERITED
NS_FORWARD_TO_EVENT
virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
{
return mozilla::dom::PushEventBinding_workers::Wrap(aCx, this, aGivenProto);
}
static already_AddRefed<PushEvent>
Constructor(mozilla::dom::EventTarget* aOwner,
const nsAString& aType,
const PushEventInit& aOptions)
{
nsRefPtr<PushEvent> e = new PushEvent(aOwner);
bool trusted = e->Init(aOwner);
e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
e->SetTrusted(trusted);
if(aOptions.mData.WasPassed()){
e->mData = aOptions.mData.Value();
}
return e.forget();
}
static already_AddRefed<PushEvent>
Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const PushEventInit& aOptions,
ErrorResult& aRv)
{
nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
return Constructor(owner, aType, aOptions);
}
void PostInit(nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
{
mServiceWorker = aServiceWorker;
}
already_AddRefed<PushMessageData> Data()
{
nsRefPtr<PushMessageData> data = new PushMessageData(mData);
return data.forget();
}
};
#endif /* ! MOZ_SIMPLEPUSH */
END_WORKERS_NAMESPACE
#endif /* mozilla_dom_workers_serviceworkerevents_h__ */
+172 -8
View File
@@ -1467,6 +1467,139 @@ public:
}
};
#ifndef MOZ_SIMPLEPUSH
class SendPushEventRunnable final : public WorkerRunnable
{
nsString mData;
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
public:
SendPushEventRunnable(
WorkerPrivate* aWorkerPrivate,
const nsAString& aData,
nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
, mData(aData)
, mServiceWorker(aServiceWorker)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
MOZ_ASSERT(aWorkerPrivate);
GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
PushEventInit pei;
pei.mData.Construct(mData);
pei.mBubbles = false;
pei.mCancelable = true;
ErrorResult result;
nsRefPtr<PushEvent> event =
PushEvent::Constructor(globalObj, NS_LITERAL_STRING("push"), pei, result);
if (NS_WARN_IF(result.Failed())) {
return false;
}
event->SetTrusted(true);
event->PostInit(mServiceWorker);
nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
return true;
}
};
class SendPushSubscriptionChangeEventRunnable final : public WorkerRunnable
{
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
public:
SendPushSubscriptionChangeEventRunnable(
WorkerPrivate* aWorkerPrivate,
nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
, mServiceWorker(aServiceWorker)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
MOZ_ASSERT(aWorkerPrivate);
nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
nsContentUtils::DispatchTrustedEvent(nullptr, target,
NS_LITERAL_STRING("pushsubscriptionchange"),
true, true);
return true;
}
};
#endif /* ! MOZ_SIMPLEPUSH */
NS_IMETHODIMP
ServiceWorkerManager::SendPushEvent(const nsACString& aScope, const nsAString& aData)
{
#ifdef MOZ_SIMPLEPUSH
return NS_ERROR_NOT_AVAILABLE;
#else
nsRefPtr<ServiceWorker> serviceWorker = CreateServiceWorkerForScope(aScope);
if (!serviceWorker) {
return NS_ERROR_FAILURE;
}
nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
nsRefPtr<SendPushEventRunnable> r =
new SendPushEventRunnable(serviceWorker->GetWorkerPrivate(), aData, serviceWorkerHandle);
AutoJSAPI jsapi;
jsapi.Init();
if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
#endif
}
NS_IMETHODIMP
ServiceWorkerManager::SendPushSubscriptionChangedEvent(const nsACString& aScope)
{
#ifdef MOZ_SIMPLEPUSH
return NS_ERROR_NOT_AVAILABLE;
#else
nsRefPtr<ServiceWorker> serviceWorker = CreateServiceWorkerForScope(aScope);
if (!serviceWorker) {
return NS_ERROR_FAILURE;
}
nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
nsRefPtr<SendPushSubscriptionChangeEventRunnable> r =
new SendPushSubscriptionChangeEventRunnable(serviceWorker->GetWorkerPrivate(), serviceWorkerHandle);
AutoJSAPI jsapi;
jsapi.Init();
if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
#endif
}
NS_IMETHODIMP
ServiceWorkerManager::GetReadyPromise(nsIDOMWindow* aWindow,
nsISupports** aPromise)
@@ -1568,6 +1701,37 @@ ServiceWorkerManager::CheckReadyPromise(nsPIDOMWindow* aWindow,
return false;
}
already_AddRefed<ServiceWorker>
ServiceWorkerManager::CreateServiceWorkerForScope(const nsACString& aScope)
{
AssertIsOnMainThread();
nsCOMPtr<nsIURI> scopeURI;
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
if (NS_FAILED(rv)) {
return nullptr;
}
nsRefPtr<ServiceWorkerRegistrationInfo> registration = GetServiceWorkerRegistrationInfo(scopeURI);
if (!registration) {
return nullptr;
}
if (!registration->mActiveWorker) {
return nullptr;
}
nsRefPtr<ServiceWorker> sw;
rv = CreateServiceWorker(registration->mPrincipal,
registration->mActiveWorker,
getter_AddRefs(sw));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return sw.forget();
}
class ServiceWorkerUnregisterJob final : public ServiceWorkerJob
{
nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
@@ -2320,9 +2484,9 @@ private:
nsRefPtr<InternalHeaders> internalHeaders = new InternalHeaders(HeadersGuardEnum::Request);
MOZ_ASSERT(mHeaderNames.Length() == mHeaderValues.Length());
for (uint32_t i = 0; i < mHeaderNames.Length(); i++) {
ErrorResult rv;
internalHeaders->Set(mHeaderNames[i], mHeaderValues[i], rv);
if (NS_WARN_IF(rv.Failed())) {
ErrorResult result;
internalHeaders->Set(mHeaderNames[i], mHeaderValues[i], result);
if (NS_WARN_IF(result.Failed())) {
return false;
}
}
@@ -2336,9 +2500,9 @@ private:
reqInit.mMode.Construct(mRequestMode);
reqInit.mCredentials.Construct(mRequestCredentials);
ErrorResult rv;
nsRefPtr<Request> request = Request::Constructor(globalObj, requestInfo, reqInit, rv);
if (NS_WARN_IF(rv.Failed())) {
ErrorResult result;
nsRefPtr<Request> request = Request::Constructor(globalObj, requestInfo, reqInit, result);
if (NS_WARN_IF(result.Failed())) {
return false;
}
@@ -2351,8 +2515,8 @@ private:
init.mCancelable = true;
init.mIsReload.Construct(mIsReload);
nsRefPtr<FetchEvent> event =
FetchEvent::Constructor(globalObj, NS_LITERAL_STRING("fetch"), init, rv);
if (NS_WARN_IF(rv.Failed())) {
FetchEvent::Constructor(globalObj, NS_LITERAL_STRING("fetch"), init, result);
if (NS_WARN_IF(result.Failed())) {
return false;
}
+3
View File
@@ -427,6 +427,9 @@ private:
WhichServiceWorker aWhichWorker,
nsISupports** aServiceWorker);
already_AddRefed<ServiceWorker>
CreateServiceWorkerForScope(const nsACString& aScope);
void
InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
WhichServiceWorker aWhichOnes);
+66
View File
@@ -19,6 +19,10 @@
#include "nsISupportsPrimitives.h"
#include "nsPIDOMWindow.h"
#ifndef MOZ_SIMPLEPUSH
#include "mozilla/dom/PushManagerBinding.h"
#endif
using namespace mozilla::dom::workers;
namespace mozilla {
@@ -30,12 +34,24 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
#ifdef MOZ_SIMPLEPUSH
NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistration,
DOMEventTargetHelper,
mInstallingWorker,
mWaitingWorker,
mActiveWorker)
#else
NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistration,
DOMEventTargetHelper,
mInstallingWorker,
mWaitingWorker,
mActiveWorker,
mPushManager)
#endif
ServiceWorkerRegistration::ServiceWorkerRegistration(nsPIDOMWindow* aWindow,
const nsAString& aScope)
: DOMEventTargetHelper(aWindow)
@@ -291,5 +307,55 @@ ServiceWorkerRegistration::StopListeningForEvents()
}
}
already_AddRefed<PushManager>
ServiceWorkerRegistration::GetPushManager(ErrorResult& aRv)
{
AssertIsOnMainThread();
#ifdef MOZ_SIMPLEPUSH
return nullptr;
#else
if (!mPushManager) {
nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(GetOwner());
if (!globalObject) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(globalObject))) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
JSContext* cx = jsapi.cx();
JS::RootedObject globalJs(cx, globalObject->GetGlobalJSObject());
GlobalObject global(cx, globalJs);
// TODO: bug 1148117. This will fail when swr is exposed on workers
JS::Rooted<JSObject*> jsImplObj(cx);
nsCOMPtr<nsIGlobalObject> unused = ConstructJSImplementation(cx, "@mozilla.org/push/PushManager;1",
global, &jsImplObj, aRv);
if (aRv.Failed()) {
return nullptr;
}
mPushManager = new PushManager(jsImplObj, globalObject);
mPushManager->SetScope(mScope, aRv);
if (aRv.Failed()) {
mPushManager = nullptr;
return nullptr;
}
}
nsRefPtr<PushManager> ret = mPushManager;
return ret.forget();
#endif /* ! MOZ_SIMPLEPUSH */
}
} // dom namespace
} // mozilla namespace
+16 -1
View File
@@ -17,6 +17,7 @@ namespace mozilla {
namespace dom {
class Promise;
class PushManager;
namespace workers {
class ServiceWorker;
@@ -62,6 +63,16 @@ public:
// DOMEventTargethelper
virtual void DisconnectFromOwner() override;
already_AddRefed<PushManager>
GetPushManager(ErrorResult& aRv);
// Something that we can feed into the Func webidl property to ensure that
// SetScope is never exposed to the user.
static bool
WebPushMethodHider(JSContext* unusedContext, JSObject* unusedObject) {
return false;
}
private:
~ServiceWorkerRegistration();
@@ -82,6 +93,10 @@ private:
nsRefPtr<workers::ServiceWorker> mWaitingWorker;
nsRefPtr<workers::ServiceWorker> mActiveWorker;
#ifndef MOZ_SIMPLEPUSH
nsRefPtr<PushManager> mPushManager;
#endif
const nsString mScope;
bool mListeningForEvents;
};
@@ -89,4 +104,4 @@ private:
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_ServiceWorkerRegistration_h */
#endif /* mozilla_dom_ServiceWorkerRegistration_h */
@@ -44,6 +44,8 @@ var ecmaGlobals =
"Number",
"Object",
"Proxy",
"PushEvent",
"PushMessageData",
"RangeError",
"ReferenceError",
"RegExp",
+8 -5
View File
@@ -660,14 +660,17 @@ nsPNGEncoder::ConvertHostARGBRow(const uint8_t* aSrc, uint8_t* aDest,
uint8_t* pixelOut = &aDest[x * pixelStride];
uint8_t alpha = (pixelIn & 0xff000000) >> 24;
if (alpha == 0) {
pixelOut[0] = pixelOut[1] = pixelOut[2] = pixelOut[3] = 0;
pixelOut[pixelStride - 1] = alpha; // overwritten below if pixelStride == 3
if (alpha == 255) {
pixelOut[0] = (pixelIn & 0xff0000) >> 16;
pixelOut[1] = (pixelIn & 0x00ff00) >> 8;
pixelOut[2] = (pixelIn & 0x0000ff) ;
} else if (alpha == 0) {
pixelOut[0] = pixelOut[1] = pixelOut[2] = 0;
} else {
pixelOut[0] = (((pixelIn & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
pixelOut[1] = (((pixelIn & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
pixelOut[2] = (((pixelIn & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
if (aUseTransparency)
pixelOut[3] = alpha;
pixelOut[2] = (((pixelIn & 0x0000ff) ) * 255 + alpha / 2) / alpha;
}
}
}
@@ -26,3 +26,31 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=691610
</script>
</body>
</html>
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=691610
-->
<head>
<title>Test for Bug 691610</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<script type="text/javascript">
SimpleTest.waitForExplicitFinish()
SpecialPowers.addChromeEventListener("ImageContentLoaded", function () {
ok(true, "chrome listener was invoked");
SimpleTest.finish();
}, true);
var iframe = document.createElement("iframe");
iframe.src = "damon.jpg"
document.body.appendChild(iframe);
iframe.contentDocument.defaultView.addEventListener("ImageContentLoaded", function () {
ok(false, "should not invoke event");
}, true);
</script>
</body>
</html>
+26 -14
View File
@@ -710,18 +710,22 @@ gc::MarkGCThingUnbarriered(JSTracer* trc, void** thingp, const char* name)
/*** ID Marking ***/
static inline void
MarkIdInternal(JSTracer* trc, jsid* id)
MarkIdInternal(JSTracer *trc, jsid *id)
{
if (JSID_IS_STRING(*id)) {
JSString* str = JSID_TO_STRING(*id);
trc->setTracingLocation((void*)id);
JSString *str = JSID_TO_STRING(*id);
JSString *prior = str;
trc->setTracingLocation((void *)id);
MarkInternal(trc, &str);
*id = NON_INTEGER_ATOM_TO_JSID(reinterpret_cast<JSAtom*>(str));
if (str != prior)
*id = NON_INTEGER_ATOM_TO_JSID(reinterpret_cast<JSAtom *>(str));
} else if (JSID_IS_SYMBOL(*id)) {
JS::Symbol* sym = JSID_TO_SYMBOL(*id);
trc->setTracingLocation((void*)id);
JS::Symbol *sym = JSID_TO_SYMBOL(*id);
JS::Symbol *prior = sym;
trc->setTracingLocation((void *)id);
MarkInternal(trc, &sym);
*id = SYMBOL_TO_JSID(sym);
if (sym != prior)
*id = SYMBOL_TO_JSID(sym);
} else {
/* Unset realLocation manually if we do not call MarkInternal. */
trc->unsetTracingLocation();
@@ -772,20 +776,28 @@ gc::MarkIdRootRange(JSTracer* trc, size_t len, jsid* vec, const char* name)
/*** Value Marking ***/
static inline void
MarkValueInternal(JSTracer* trc, Value* v)
MarkValueInternal(JSTracer *trc, Value *v)
{
if (v->isMarkable()) {
MOZ_ASSERT(v->toGCThing());
void* thing = v->toGCThing();
trc->setTracingLocation((void*)v);
MarkKind(trc, &thing, v->gcKind());
void *thing = v->toGCThing();
trc->setTracingLocation((void *)v);
if (v->isString()) {
v->setString((JSString*)thing);
JSString *str = static_cast<JSString*>(thing);
MarkInternal(trc, &str);
if (str != thing)
v->setString(str);
} else if (v->isObject()) {
v->setObjectOrNull((JSObject*)thing);
JSObject *obj = static_cast<JSObject*>(thing);
MarkInternal(trc, &obj);
if (obj != thing)
v->setObjectOrNull(obj);
} else {
MOZ_ASSERT(v->isSymbol());
v->setSymbol((JS::Symbol*)thing);
JS::Symbol *sym = static_cast<JS::Symbol*>(thing);
MarkInternal(trc, &sym);
if (sym != thing)
v->setSymbol(sym);
}
} else {
/* Unset realLocation manually if we do not call MarkInternal. */
+2 -2
View File
@@ -1356,8 +1356,8 @@ skip-if(B2G&&browserIsRemote) == 486848-1.xul 486848-1-ref.xul # bug 974780
== 490177-1.svg 490177-1-ref.svg
== 490182-1a.html 490182-1-ref.html
== 490182-1b.html 490182-1-ref.html
== 491180-1.html 491180-1-ref.html
== 491180-2.html 491180-2-ref.html
pref(browser.display.focus_ring_width,1) == 491180-1.html 491180-1-ref.html
pref(browser.display.focus_ring_width,1) == 491180-2.html 491180-2-ref.html
skip-if(B2G&&browserIsRemote) == 491323-1.xul 491323-1-ref.xul # bug 974780
skip-if(B2G&&browserIsRemote) == 492239-1.xul 492239-1-ref.xul # bug 974780
== 492661-1.html 492661-1-ref.html
+2 -2
View File
@@ -7,9 +7,9 @@
# on B2G, despite their "-moz-appearance: none; background: gray", so they
# don't quite match the reference case's normal <div>. That's why they're fuzzy.
fuzzy-if(B2G||Android,125,20) == percent-height-child-1.html percent-height-child-1-ref.html
fuzzy-if(B2G||Android,125,80) == percent-height-child-2.html percent-height-child-2-ref.html
pref(browser.display.focus_ring_width,1) fuzzy-if(B2G||Android,125,80) == percent-height-child-2.html percent-height-child-2-ref.html
fuzzy-if(B2G||Android,125,20) == percent-width-child-1.html percent-width-child-1-ref.html
fuzzy-if(B2G||Android,125,80) == percent-width-child-2.html percent-width-child-2-ref.html
pref(browser.display.focus_ring_width,1) fuzzy-if(B2G||Android,125,80) == percent-width-child-2.html percent-width-child-2-ref.html
== vertical-centering.html vertical-centering-ref.html
+1 -1
View File
@@ -551,7 +551,7 @@ CacheFileContextEvictor::EvictEntries()
mEntries[0]->mIterator.get(), mEntries[0]->mInfo.get()));
nsRefPtr<CacheFileHandle> handle;
CacheFileIOManager::gInstance->mHandles.GetHandle(&hash, false,
CacheFileIOManager::gInstance->mHandles.GetHandle(&hash,
getter_AddRefs(handle));
if (handle) {
// We doom any active handle in CacheFileIOManager::EvictByContext(), so
+13 -33
View File
@@ -309,7 +309,6 @@ CacheFileHandles::~CacheFileHandles()
nsresult
CacheFileHandles::GetHandle(const SHA1Sum::Hash *aHash,
bool aReturnDoomed,
CacheFileHandle **_retval)
{
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
@@ -343,16 +342,12 @@ CacheFileHandles::GetHandle(const SHA1Sum::Hash *aHash,
if (handle->IsDoomed()) {
LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
"found doomed handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
// If the consumer doesn't want doomed handles, exit with NOT_AVAIL.
if (!aReturnDoomed) {
return NS_ERROR_NOT_AVAILABLE;
}
} else {
LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
"found handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
return NS_ERROR_NOT_AVAILABLE;
}
LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
"found handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
handle.forget(_retval);
return NS_OK;
}
@@ -809,7 +804,7 @@ public:
if (!mIOMan) {
rv = NS_ERROR_NOT_INITIALIZED;
} else {
rv = mIOMan->DoomFileByKeyInternal(&mHash, false);
rv = mIOMan->DoomFileByKeyInternal(&mHash);
mIOMan = nullptr;
}
@@ -1571,7 +1566,7 @@ CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<CacheFileHandle> handle;
mHandles.GetHandle(aHash, false, getter_AddRefs(handle));
mHandles.GetHandle(aHash, getter_AddRefs(handle));
if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
if (handle) {
@@ -2070,11 +2065,10 @@ CacheFileIOManager::DoomFileByKey(const nsACString &aKey,
}
nsresult
CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash *aHash,
bool aFailIfAlreadyDoomed)
CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash *aHash)
{
LOG(("CacheFileIOManager::DoomFileByKeyInternal() [hash=%08x%08x%08x%08x%08x,"
" failIfAlreadyDoomed=%d]", LOGSHA1(aHash), aFailIfAlreadyDoomed));
LOG(("CacheFileIOManager::DoomFileByKeyInternal() [hash=%08x%08x%08x%08x%08x]"
, LOGSHA1(aHash)));
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
@@ -2090,15 +2084,11 @@ CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash *aHash,
// Find active handle
nsRefPtr<CacheFileHandle> handle;
mHandles.GetHandle(aHash, true, getter_AddRefs(handle));
mHandles.GetHandle(aHash, getter_AddRefs(handle));
if (handle) {
handle->Log();
if (handle->IsDoomed()) {
return aFailIfAlreadyDoomed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
}
return DoomFileInternal(handle);
}
@@ -2240,7 +2230,7 @@ CacheFileIOManager::GetEntryInfo(const SHA1Sum::Hash *aHash,
nsAutoCString uriSpec;
nsRefPtr<CacheFileHandle> handle;
ioMan->mHandles.GetHandle(aHash, false, getter_AddRefs(handle));
ioMan->mHandles.GetHandle(aHash, getter_AddRefs(handle));
if (handle) {
nsRefPtr<nsILoadContextInfo> info =
CacheFileUtils::ParseKey(handle->Key(), &enhanceId, &uriSpec);
@@ -2611,7 +2601,7 @@ CacheFileIOManager::OverLimitEvictionInternal()
rv = CacheIndex::GetEntryForEviction(false, &hash, &cnt);
NS_ENSURE_SUCCESS(rv, rv);
rv = DoomFileByKeyInternal(&hash, true);
rv = DoomFileByKeyInternal(&hash);
if (NS_SUCCEEDED(rv)) {
consecutiveFailures = 0;
} else if (rv == NS_ERROR_NOT_AVAILABLE) {
@@ -2619,16 +2609,6 @@ CacheFileIOManager::OverLimitEvictionInternal()
"DoomFileByKeyInternal() failed. [rv=0x%08x]", rv));
// TODO index is outdated, start update
#ifdef DEBUG
// Dooming should never fail due to already doomed handle, but bug 1028415
// shows that this unexpected state can happen. Assert in debug build so
// we can find the cause if we ever find a way to reproduce it with NSPR
// logging enabled.
nsRefPtr<CacheFileHandle> handle;
mHandles.GetHandle(&hash, true, getter_AddRefs(handle));
MOZ_ASSERT(!handle || !handle->IsDoomed());
#endif
// Make sure index won't return the same entry again
CacheIndex::RemoveEntry(&hash);
consecutiveFailures = 0;
@@ -3616,7 +3596,7 @@ CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate)
rv = CacheIndex::GetEntryForEviction(true, &hash, &cnt);
if (NS_SUCCEEDED(rv)) {
rv = DoomFileByKeyInternal(&hash, true);
rv = DoomFileByKeyInternal(&hash);
}
if (NS_SUCCEEDED(rv)) {
rv = aHandle->mFile->OpenNSPRFileDesc(
+2 -3
View File
@@ -90,7 +90,7 @@ public:
CacheFileHandles();
~CacheFileHandles();
nsresult GetHandle(const SHA1Sum::Hash *aHash, bool aReturnDoomed, CacheFileHandle **_retval);
nsresult GetHandle(const SHA1Sum::Hash *aHash, CacheFileHandle **_retval);
nsresult NewHandle(const SHA1Sum::Hash *aHash, bool aPriority, CacheFileHandle **_retval);
void RemoveHandle(CacheFileHandle *aHandlle);
void GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval);
@@ -330,8 +330,7 @@ private:
nsresult WriteInternal(CacheFileHandle *aHandle, int64_t aOffset,
const char *aBuf, int32_t aCount, bool aValidate);
nsresult DoomFileInternal(CacheFileHandle *aHandle);
nsresult DoomFileByKeyInternal(const SHA1Sum::Hash *aHash,
bool aFailIfAlreadyDoomed);
nsresult DoomFileByKeyInternal(const SHA1Sum::Hash *aHash);
nsresult ReleaseNSPRHandleInternal(CacheFileHandle *aHandle);
nsresult TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
int64_t aTruncatePos, int64_t aEOFPos);
+3 -3
View File
@@ -1277,7 +1277,7 @@ bool CacheIndex::IsForcedValidEntry(const SHA1Sum::Hash *aHash)
nsRefPtr<CacheFileHandle> handle;
CacheFileIOManager::gInstance->mHandles.GetHandle(
aHash, false, getter_AddRefs(handle));
aHash, getter_AddRefs(handle));
if (!handle)
return false;
@@ -2752,7 +2752,7 @@ CacheIndex::BuildIndex()
#ifdef DEBUG
nsRefPtr<CacheFileHandle> handle;
CacheFileIOManager::gInstance->mHandles.GetHandle(&hash, false,
CacheFileIOManager::gInstance->mHandles.GetHandle(&hash,
getter_AddRefs(handle));
#endif
@@ -2965,7 +2965,7 @@ CacheIndex::UpdateIndex()
#ifdef DEBUG
nsRefPtr<CacheFileHandle> handle;
CacheFileIOManager::gInstance->mHandles.GetHandle(&hash, false,
CacheFileIOManager::gInstance->mHandles.GetHandle(&hash,
getter_AddRefs(handle));
#endif
+7 -5
View File
@@ -4272,12 +4272,14 @@ nsHttpChannel::InstallCacheListener(int64_t offset)
do_CreateInstance(kStreamListenerTeeCID, &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsICacheStorageService> serv =
do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIEventTarget> cacheIOTarget;
serv->GetIoTarget(getter_AddRefs(cacheIOTarget));
if (!CacheObserver::UseNewCache()) {
nsCOMPtr<nsICacheStorageService> serv =
do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
serv->GetIoTarget(getter_AddRefs(cacheIOTarget));
}
if (!cacheIOTarget) {
LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%x "
@@ -2761,7 +2761,7 @@ nsHttpConnectionMgr::ActivateTimeoutTick()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG(("nsHttpConnectionMgr::ActivateTimeoutTick() "
"this=%p mTimeoutTick=%p\n"));
"this=%p mTimeoutTick=%p\n", this, mTimeoutTick.get()));
// The timer tick should be enabled if it is not already pending.
// Upon running the tick will rearm itself if there are active
+4 -5
View File
@@ -110,14 +110,13 @@ def rect_is_empty(valobj):
def summarize_region(valobj, internal_dict):
# This function makes use of the synthetic children generated for ns(Int)Regions.
bounds = valobj.GetChildMemberWithName("bounds")
num_rects = valobj.GetChildMemberWithName("numRects").GetValueAsUnsigned(0)
if num_rects == 0:
return "empty"
bounds_summary = summarize_rect(bounds, internal_dict)
if num_rects == 1:
num_rects = valobj.GetChildMemberWithName("numRects").GetValueAsUnsigned(0)
if num_rects <= 1:
if rect_is_empty(bounds):
return "empty"
return "one rect: " + bounds_summary
if num_rects == 1:
return "one rect: " + bounds_summary
return str(num_rects) + " rects, bounds: " + bounds_summary
def init(debugger):
+6 -6
View File
@@ -99,12 +99,12 @@ def match(path, pattern):
'''
if not pattern:
return True
if not pattern in re_cache:
pattern = re.escape(pattern)
pattern = re.sub(r'(^|\\\/)\\\*\\\*\\\/', r'\1(?:.+/)?', pattern)
pattern = re.sub(r'(^|\\\/)\\\*\\\*$', r'(?:\1.+)?', pattern)
pattern = pattern.replace(r'\*', '[^/]*') + '(?:/.*)?$'
re_cache[pattern] = re.compile(pattern)
if pattern not in re_cache:
p = re.escape(pattern)
p = re.sub(r'(^|\\\/)\\\*\\\*\\\/', r'\1(?:.+/)?', p)
p = re.sub(r'(^|\\\/)\\\*\\\*$', r'(?:\1.+)?', p)
p = p.replace(r'\*', '[^/]*') + '(?:/.*)?$'
re_cache[pattern] = re.compile(p)
return re_cache[pattern].match(path) is not None
+8 -3
View File
@@ -2078,12 +2078,17 @@ class Mochitest(MochitestUtilsMixin):
options,
debuggerInfo is not None)
# If there are any Mulet-specific tests doing remote network access,
# we will not be aware since we are explicitely allowing this, as for
# B2G
if mozinfo.info.get(
'buildapp') == 'mulet' and 'MOZ_DISABLE_NONLOCAL_CONNECTIONS' in self.browserEnv:
del self.browserEnv['MOZ_DISABLE_NONLOCAL_CONNECTIONS']
#
# In addition, the push subsuite directly accesses the production
# push service.
if 'MOZ_DISABLE_NONLOCAL_CONNECTIONS' in self.browserEnv:
if mozinfo.info.get('buildapp') == 'mulet' or options.subsuite == 'push':
del self.browserEnv['MOZ_DISABLE_NONLOCAL_CONNECTIONS']
os.environ["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "0"
if self.browserEnv is None:
return 1
+4 -1
View File
@@ -217,8 +217,11 @@ this.BrowserUtils = {
* @return a boolean indicating if linkNode has a rel="noreferrer" attribute.
*/
linkHasNoReferrer: function (linkNode) {
// A null linkNode typically means that we're checking a link that wasn't
// provided via an <a> link, like a text-selected URL. Don't leak
// referrer information in this case.
if (!linkNode)
return false;
return true;
let rel = linkNode.getAttribute("rel");
if (!rel)
+48
View File
@@ -1,3 +1,51 @@
// -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// 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/.
this.EXPORTED_SYMBOLS = ["RemoteSecurityUI"];
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function RemoteSecurityUI()
{
this._state = 0;
this._SSLStatus = null;
}
RemoteSecurityUI.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISSLStatusProvider, Ci.nsISecureBrowserUI]),
// nsISecureBrowserUI
get state() { return this._state; },
get tooltipText() { return ""; },
// nsISSLStatusProvider
get SSLStatus() { return this._SSLStatus; },
_update: function (state, status) {
let deserialized = null;
if (status) {
let helper = Cc["@mozilla.org/network/serialization-helper;1"]
.getService(Components.interfaces.nsISerializationHelper);
deserialized = helper.deserializeObject(status)
deserialized.QueryInterface(Ci.nsISSLStatus);
}
// We must check the Extended Validation (EV) state here, on the chrome
// process, because NSS is needed for that determination.
if (deserialized && deserialized.isExtendedValidation)
state |= Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL;
this._state = state;
this._SSLStatus = deserialized;
}
};
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
// 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
+2 -2
View File
@@ -289,8 +289,8 @@ private:
bool IsPageScroll(bool aForVertical) const
{
MOZ_ASSERT(mInitialized, "SystemSettings must be initialized");
return aForVertical ? (mScrollLines == WHEEL_PAGESCROLL) :
(mScrollChars == WHEEL_PAGESCROLL);
return aForVertical ? (uint32_t(mScrollLines) == WHEEL_PAGESCROLL) :
(uint32_t(mScrollChars) == WHEEL_PAGESCROLL);
}
private: