зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1614761, move refresh blocking handling from framescript to an actor, r=Gijs
Differential Revision: https://phabricator.services.mozilla.com/D87206
This commit is contained in:
Родитель
7a092a0059
Коммит
3bcbd15d51
|
@ -2,14 +2,20 @@
|
|||
* 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/. */
|
||||
|
||||
/* eslint-env mozilla/frame-script */
|
||||
/**
|
||||
* This file has two actors, RefreshBlockerChild js a window actor which
|
||||
* handles the refresh notifications. RefreshBlockerObserverChild is a process
|
||||
* actor that enables refresh blocking on each docshell that is created.
|
||||
*/
|
||||
|
||||
var EXPORTED_SYMBOLS = ["RefreshBlockerChild", "RefreshBlockerObserverChild"];
|
||||
|
||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
var RefreshBlocker = {
|
||||
PREF: "accessibility.blockautorefresh",
|
||||
const REFRESHBLOCKING_PREF = "accessibility.blockautorefresh";
|
||||
|
||||
var progressListener = {
|
||||
// Bug 1247100 - When a refresh is caused by an HTTP header,
|
||||
// onRefreshAttempted will be fired before onLocationChange.
|
||||
// When a refresh is caused by a <meta> tag in the document,
|
||||
|
@ -42,68 +48,6 @@ var RefreshBlocker = {
|
|||
// otherwise, null is set as the value of the mapping.
|
||||
blockedWindows: new WeakMap(),
|
||||
|
||||
init() {
|
||||
if (Services.prefs.getBoolPref(this.PREF)) {
|
||||
this.enable();
|
||||
}
|
||||
|
||||
Services.prefs.addObserver(this.PREF, this);
|
||||
},
|
||||
|
||||
uninit() {
|
||||
if (Services.prefs.getBoolPref(this.PREF)) {
|
||||
this.disable();
|
||||
}
|
||||
|
||||
Services.prefs.removeObserver(this.PREF, this);
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
if (topic == "nsPref:changed" && data == this.PREF) {
|
||||
if (Services.prefs.getBoolPref(this.PREF)) {
|
||||
this.enable();
|
||||
} else {
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
enable() {
|
||||
this._filter = Cc[
|
||||
"@mozilla.org/appshell/component/browser-status-filter;1"
|
||||
].createInstance(Ci.nsIWebProgress);
|
||||
this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
|
||||
this._filter.target = tabEventTarget;
|
||||
|
||||
let webProgress = docShell
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);
|
||||
|
||||
addMessageListener("RefreshBlocker:Refresh", this);
|
||||
},
|
||||
|
||||
disable() {
|
||||
let webProgress = docShell
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
webProgress.removeProgressListener(this._filter);
|
||||
|
||||
this._filter.removeProgressListener(this);
|
||||
this._filter = null;
|
||||
|
||||
removeMessageListener("RefreshBlocker:Refresh", this);
|
||||
},
|
||||
|
||||
send(data) {
|
||||
// Due to the |nsDocLoader| calling its |nsIWebProgressListener|s in
|
||||
// reverse order, this will occur *before* the |BrowserChild| can send its
|
||||
// |OnLocationChange| event to the parent, but we need this message to
|
||||
// arrive after to ensure that the refresh blocker notification is not
|
||||
// immediately cleared by the |OnLocationChange| from |BrowserChild|.
|
||||
setTimeout(() => sendAsyncMessage("RefreshBlocker:Blocked", data), 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Notices when the nsIWebProgress transitions to STATE_STOP for
|
||||
* the STATE_IS_WINDOW case, which will clear any mappings from
|
||||
|
@ -130,7 +74,7 @@ var RefreshBlocker = {
|
|||
if (data) {
|
||||
// We saw onRefreshAttempted before onLocationChange, so
|
||||
// send the message to the parent to show the notification.
|
||||
this.send(data);
|
||||
this.send(win, data);
|
||||
}
|
||||
} else {
|
||||
this.blockedWindows.set(win, null);
|
||||
|
@ -144,19 +88,18 @@ var RefreshBlocker = {
|
|||
*/
|
||||
onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
|
||||
let win = aWebProgress.DOMWindow;
|
||||
let outerWindowID = win.docShell.outerWindowID;
|
||||
|
||||
let data = {
|
||||
browsingContext: win.browsingContext,
|
||||
URI: aURI.spec,
|
||||
delay: aDelay,
|
||||
sameURI: aSameURI,
|
||||
outerWindowID,
|
||||
};
|
||||
|
||||
if (this.blockedWindows.has(win)) {
|
||||
// onLocationChange must have fired before, so we can tell the
|
||||
// parent to show the notification.
|
||||
this.send(data);
|
||||
this.send(win, data);
|
||||
} else {
|
||||
// onLocationChange hasn't fired yet, so stash the data in the
|
||||
// map so that onLocationChange can send it when it fires.
|
||||
|
@ -166,17 +109,22 @@ var RefreshBlocker = {
|
|||
return false;
|
||||
},
|
||||
|
||||
receiveMessage(message) {
|
||||
let data = message.data;
|
||||
|
||||
if (message.name == "RefreshBlocker:Refresh") {
|
||||
let win = Services.wm.getOuterWindowWithId(data.outerWindowID);
|
||||
let refreshURI = win.docShell.QueryInterface(Ci.nsIRefreshURI);
|
||||
|
||||
let URI = Services.io.newURI(data.URI);
|
||||
|
||||
refreshURI.forceRefreshURI(URI, null, data.delay, true);
|
||||
}
|
||||
send(win, data) {
|
||||
// Due to the |nsDocLoader| calling its |nsIWebProgressListener|s in
|
||||
// reverse order, this will occur *before* the |BrowserChild| can send its
|
||||
// |OnLocationChange| event to the parent, but we need this message to
|
||||
// arrive after to ensure that the refresh blocker notification is not
|
||||
// immediately cleared by the |OnLocationChange| from |BrowserChild|.
|
||||
setTimeout(() => {
|
||||
// An exception can occur if refresh blocking was turned off
|
||||
// during a pageload.
|
||||
try {
|
||||
let actor = win.windowGlobalChild.getActor("RefreshBlocker");
|
||||
if (actor) {
|
||||
actor.sendAsyncMessage("RefreshBlocker:Blocked", data);
|
||||
}
|
||||
} catch (ex) {}
|
||||
}, 0);
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([
|
||||
|
@ -186,8 +134,104 @@ var RefreshBlocker = {
|
|||
]),
|
||||
};
|
||||
|
||||
RefreshBlocker.init();
|
||||
class RefreshBlockerChild extends JSWindowActorChild {
|
||||
didDestroy() {
|
||||
// If the refresh blocking preference is turned off, all of the
|
||||
// RefreshBlockerChild actors will get destroyed, so disable
|
||||
// refresh blocking only in this case.
|
||||
if (!Services.prefs.getBoolPref(REFRESHBLOCKING_PREF)) {
|
||||
this.disable(this.docShell);
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener("unload", () => {
|
||||
RefreshBlocker.uninit();
|
||||
});
|
||||
enable() {
|
||||
ChromeUtils.domProcessChild
|
||||
.getActor("RefreshBlockerObserver")
|
||||
.enable(this.docShell);
|
||||
}
|
||||
|
||||
disable() {
|
||||
ChromeUtils.domProcessChild
|
||||
.getActor("RefreshBlockerObserver")
|
||||
.disable(this.docShell);
|
||||
}
|
||||
|
||||
receiveMessage(message) {
|
||||
let data = message.data;
|
||||
|
||||
switch (message.name) {
|
||||
case "RefreshBlocker:Refresh":
|
||||
let docShell = data.browsingContext.docShell;
|
||||
let refreshURI = docShell.QueryInterface(Ci.nsIRefreshURI);
|
||||
let URI = Services.io.newURI(data.URI);
|
||||
refreshURI.forceRefreshURI(URI, null, data.delay, true);
|
||||
break;
|
||||
|
||||
case "PreferenceChanged":
|
||||
if (data.isEnabled) {
|
||||
this.enable(this.docShell);
|
||||
} else {
|
||||
this.disable(this.docShell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RefreshBlockerObserverChild extends JSProcessActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
this.filtersMap = new Map();
|
||||
}
|
||||
|
||||
observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "webnavigation-create":
|
||||
case "chrome-webnavigation-create":
|
||||
if (Services.prefs.getBoolPref(REFRESHBLOCKING_PREF)) {
|
||||
this.enable(subject.QueryInterface(Ci.nsIDocShell));
|
||||
}
|
||||
break;
|
||||
|
||||
case "webnavigation-destroy":
|
||||
case "chrome-webnavigation-destroy":
|
||||
if (Services.prefs.getBoolPref(REFRESHBLOCKING_PREF)) {
|
||||
this.disable(subject.QueryInterface(Ci.nsIDocShell));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
enable(docShell) {
|
||||
if (this.filtersMap.has(docShell)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let filter = Cc[
|
||||
"@mozilla.org/appshell/component/browser-status-filter;1"
|
||||
].createInstance(Ci.nsIWebProgress);
|
||||
|
||||
filter.addProgressListener(progressListener, Ci.nsIWebProgress.NOTIFY_ALL);
|
||||
|
||||
this.filtersMap.set(docShell, filter);
|
||||
|
||||
let webProgress = docShell
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
|
||||
}
|
||||
|
||||
disable(docShell) {
|
||||
let filter = this.filtersMap.get(docShell);
|
||||
if (!filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
let webProgress = docShell
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
webProgress.removeProgressListener(filter);
|
||||
|
||||
filter.removeProgressListener(progressListener);
|
||||
this.filtersMap.delete(docShell);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/* 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";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["RefreshBlockerParent"];
|
||||
|
||||
class RefreshBlockerParent extends JSWindowActorParent {
|
||||
receiveMessage(message) {
|
||||
if (message.name == "RefreshBlocker:Blocked") {
|
||||
let browser = this.browsingContext.top.embedderElement;
|
||||
if (browser) {
|
||||
let gBrowser = browser.ownerGlobal.gBrowser;
|
||||
if (gBrowser) {
|
||||
gBrowser.refreshBlocked(this, browser, message.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -72,6 +72,8 @@ FINAL_TARGET_FILES.actors += [
|
|||
'PluginChild.jsm',
|
||||
'PluginParent.jsm',
|
||||
'PromptParent.jsm',
|
||||
'RefreshBlockerChild.jsm',
|
||||
'RefreshBlockerParent.jsm',
|
||||
'RFPHelperChild.jsm',
|
||||
'RFPHelperParent.jsm',
|
||||
'SearchTelemetryChild.jsm',
|
||||
|
|
|
@ -1871,7 +1871,6 @@ var gBrowserInit = {
|
|||
BrowserSearch.init();
|
||||
BrowserPageActions.init();
|
||||
gAccessibilityServiceIndicator.init();
|
||||
AccessibilityRefreshBlocker.init();
|
||||
if (gToolbarKeyNavEnabled) {
|
||||
ToolbarKeyboardNavigator.init();
|
||||
}
|
||||
|
@ -2457,8 +2456,6 @@ var gBrowserInit = {
|
|||
|
||||
gAccessibilityServiceIndicator.uninit();
|
||||
|
||||
AccessibilityRefreshBlocker.uninit();
|
||||
|
||||
if (gToolbarKeyNavEnabled) {
|
||||
ToolbarKeyboardNavigator.uninit();
|
||||
}
|
||||
|
@ -5796,50 +5793,6 @@ var CombinedStopReload = {
|
|||
},
|
||||
};
|
||||
|
||||
// This helper only cares about loading the frame
|
||||
// script if the pref is seen as true.
|
||||
// After the frame script is loaded, it takes over
|
||||
// the responsibility of watching the pref and
|
||||
// enabling/disabling itself.
|
||||
const AccessibilityRefreshBlocker = {
|
||||
PREF: "accessibility.blockautorefresh",
|
||||
|
||||
init() {
|
||||
if (Services.prefs.getBoolPref(this.PREF)) {
|
||||
this.loadFrameScript();
|
||||
} else {
|
||||
Services.prefs.addObserver(this.PREF, this);
|
||||
}
|
||||
},
|
||||
|
||||
uninit() {
|
||||
Services.prefs.removeObserver(this.PREF, this);
|
||||
},
|
||||
|
||||
observe(aSubject, aTopic, aPrefName) {
|
||||
if (
|
||||
aTopic == "nsPref:changed" &&
|
||||
aPrefName == this.PREF &&
|
||||
Services.prefs.getBoolPref(this.PREF)
|
||||
) {
|
||||
this.loadFrameScript();
|
||||
Services.prefs.removeObserver(this.PREF, this);
|
||||
}
|
||||
},
|
||||
|
||||
loadFrameScript() {
|
||||
if (!this._loaded) {
|
||||
this._loaded = true;
|
||||
let mm = window.getGroupMessageManager("browsers");
|
||||
mm.loadFrameScript(
|
||||
"chrome://browser/content/content-refreshblocker.js",
|
||||
true,
|
||||
true
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var TabsProgressListener = {
|
||||
onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
// Collect telemetry data about tab load times.
|
||||
|
|
|
@ -57,9 +57,6 @@
|
|||
);
|
||||
}
|
||||
|
||||
let messageManager = window.getGroupMessageManager("browsers");
|
||||
messageManager.addMessageListener("RefreshBlocker:Blocked", this);
|
||||
|
||||
this._setFindbarData();
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
|
@ -5125,76 +5122,6 @@
|
|||
}
|
||||
},
|
||||
|
||||
receiveMessage(aMessage) {
|
||||
let data = aMessage.data;
|
||||
let browser = aMessage.target;
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "RefreshBlocker:Blocked": {
|
||||
// The data object is expected to contain the following properties:
|
||||
// - URI (string)
|
||||
// The URI that a page is attempting to refresh or redirect to.
|
||||
// - delay (int)
|
||||
// The delay (in milliseconds) before the page was going to
|
||||
// reload or redirect.
|
||||
// - sameURI (bool)
|
||||
// true if we're refreshing the page. false if we're redirecting.
|
||||
// - outerWindowID (int)
|
||||
// The outerWindowID of the frame that requested the refresh or
|
||||
// redirect.
|
||||
|
||||
let brandBundle = document.getElementById("bundle_brand");
|
||||
let brandShortName = brandBundle.getString("brandShortName");
|
||||
let message = gNavigatorBundle.getFormattedString(
|
||||
"refreshBlocked." +
|
||||
(data.sameURI ? "refreshLabel" : "redirectLabel"),
|
||||
[brandShortName]
|
||||
);
|
||||
|
||||
let notificationBox = this.getNotificationBox(browser);
|
||||
let notification = notificationBox.getNotificationWithValue(
|
||||
"refresh-blocked"
|
||||
);
|
||||
|
||||
if (notification) {
|
||||
notification.label = message;
|
||||
} else {
|
||||
let refreshButtonText = gNavigatorBundle.getString(
|
||||
"refreshBlocked.goButton"
|
||||
);
|
||||
let refreshButtonAccesskey = gNavigatorBundle.getString(
|
||||
"refreshBlocked.goButton.accesskey"
|
||||
);
|
||||
|
||||
let buttons = [
|
||||
{
|
||||
label: refreshButtonText,
|
||||
accessKey: refreshButtonAccesskey,
|
||||
callback() {
|
||||
if (browser.messageManager) {
|
||||
browser.messageManager.sendAsyncMessage(
|
||||
"RefreshBlocker:Refresh",
|
||||
data
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
notificationBox.appendNotification(
|
||||
message,
|
||||
"refresh-blocked",
|
||||
"chrome://browser/skin/notification-icons/popup.svg",
|
||||
notificationBox.PRIORITY_INFO_MEDIUM,
|
||||
buttons
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
observe(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "contextual-identity-updated": {
|
||||
|
@ -5209,6 +5136,58 @@
|
|||
}
|
||||
},
|
||||
|
||||
refreshBlocked(actor, browser, data) {
|
||||
// The data object is expected to contain the following properties:
|
||||
// - URI (string)
|
||||
// The URI that a page is attempting to refresh or redirect to.
|
||||
// - delay (int)
|
||||
// The delay (in milliseconds) before the page was going to
|
||||
// reload or redirect.
|
||||
// - sameURI (bool)
|
||||
// true if we're refreshing the page. false if we're redirecting.
|
||||
|
||||
let brandBundle = document.getElementById("bundle_brand");
|
||||
let brandShortName = brandBundle.getString("brandShortName");
|
||||
let message = gNavigatorBundle.getFormattedString(
|
||||
"refreshBlocked." + (data.sameURI ? "refreshLabel" : "redirectLabel"),
|
||||
[brandShortName]
|
||||
);
|
||||
|
||||
let notificationBox = this.getNotificationBox(browser);
|
||||
let notification = notificationBox.getNotificationWithValue(
|
||||
"refresh-blocked"
|
||||
);
|
||||
|
||||
if (notification) {
|
||||
notification.label = message;
|
||||
} else {
|
||||
let refreshButtonText = gNavigatorBundle.getString(
|
||||
"refreshBlocked.goButton"
|
||||
);
|
||||
let refreshButtonAccesskey = gNavigatorBundle.getString(
|
||||
"refreshBlocked.goButton.accesskey"
|
||||
);
|
||||
|
||||
let buttons = [
|
||||
{
|
||||
label: refreshButtonText,
|
||||
accessKey: refreshButtonAccesskey,
|
||||
callback() {
|
||||
actor.sendAsyncMessage("RefreshBlocker:Refresh", data);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
notificationBox.appendNotification(
|
||||
message,
|
||||
"refresh-blocked",
|
||||
"chrome://browser/skin/notification-icons/popup.svg",
|
||||
notificationBox.PRIORITY_INFO_MEDIUM,
|
||||
buttons
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_generateUniquePanelID() {
|
||||
if (!this._uniquePanelIDCounter) {
|
||||
this._uniquePanelIDCounter = 0;
|
||||
|
|
|
@ -85,7 +85,6 @@ browser.jar:
|
|||
content/browser/pageinfo/pageInfo.css (content/pageinfo/pageInfo.css)
|
||||
content/browser/pageinfo/permissions.js (content/pageinfo/permissions.js)
|
||||
content/browser/pageinfo/security.js (content/pageinfo/security.js)
|
||||
content/browser/content-refreshblocker.js (content/content-refreshblocker.js)
|
||||
content/browser/robot.ico (content/robot.ico)
|
||||
content/browser/static-robot.png (content/static-robot.png)
|
||||
content/browser/safeMode.css (content/safeMode.css)
|
||||
|
|
|
@ -92,6 +92,34 @@ let JSPROCESSACTORS = {
|
|||
],
|
||||
},
|
||||
},
|
||||
|
||||
RefreshBlockerObserver: {
|
||||
child: {
|
||||
moduleURI: "resource:///actors/RefreshBlockerChild.jsm",
|
||||
observers: [
|
||||
"webnavigation-create",
|
||||
"chrome-webnavigation-create",
|
||||
"webnavigation-destroy",
|
||||
"chrome-webnavigation-destroy",
|
||||
],
|
||||
},
|
||||
|
||||
enablePreference: "accessibility.blockautorefresh",
|
||||
onPreferenceChanged: (prefName, prevValue, isEnabled) => {
|
||||
BrowserWindowTracker.orderedWindows.forEach(win => {
|
||||
for (let browser of win.gBrowser.browsers) {
|
||||
try {
|
||||
browser.sendMessageToActor(
|
||||
"PreferenceChanged",
|
||||
{ isEnabled },
|
||||
"RefreshBlocker",
|
||||
"all"
|
||||
);
|
||||
} catch (ex) {}
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -514,6 +542,18 @@ let JSWINDOWACTORS = {
|
|||
allFrames: true,
|
||||
},
|
||||
|
||||
RefreshBlocker: {
|
||||
parent: {
|
||||
moduleURI: "resource:///actors/RefreshBlockerParent.jsm",
|
||||
},
|
||||
child: {
|
||||
moduleURI: "resource:///actors/RefreshBlockerChild.jsm",
|
||||
},
|
||||
|
||||
messageManagerGroups: ["browsers"],
|
||||
enablePreference: "accessibility.blockautorefresh",
|
||||
},
|
||||
|
||||
SearchTelemetry: {
|
||||
parent: {
|
||||
moduleURI: "resource:///actors/SearchTelemetryParent.jsm",
|
||||
|
|
|
@ -651,6 +651,9 @@ var ActorManagerParent = {
|
|||
} else {
|
||||
unregister(actorName, actor);
|
||||
}
|
||||
if (actor.onPreferenceChanged) {
|
||||
actor.onPreferenceChanged(prefName, prevValue, isEnabled);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (!this[actorNameProp]) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче