mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 13:23:07 +00:00
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:
@@ -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>
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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", {});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -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. */ }
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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%),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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': {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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++
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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]);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,3 +12,7 @@ EXTRA_COMPONENTS += [
|
||||
EXTRA_JS_MODULES += [
|
||||
'PushService.jsm',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'test/mochitest.ini',
|
||||
]
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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!
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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"
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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']:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -427,6 +427,9 @@ private:
|
||||
WhichServiceWorker aWhichWorker,
|
||||
nsISupports** aServiceWorker);
|
||||
|
||||
already_AddRefed<ServiceWorker>
|
||||
CreateServiceWorkerForScope(const nsACString& aScope);
|
||||
|
||||
void
|
||||
InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
|
||||
WhichServiceWorker aWhichOnes);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user