gecko-dev/toolkit/modules/RemoteWebProgress.jsm

242 строки
6.5 KiB
JavaScript

// -*- 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
var EXPORTED_SYMBOLS = ["RemoteWebProgressManager"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const RemoteWebProgress = Components.Constructor(
"@mozilla.org/dom/remote-web-progress;1",
"nsIRemoteWebProgress",
"init"
);
const RemoteWebProgressRequest = Components.Constructor(
"@mozilla.org/dom/remote-web-progress-request;1",
"nsIRemoteWebProgressRequest",
"init"
);
class RemoteWebProgressManager {
constructor(aBrowser) {
this._topLevelWebProgress = new RemoteWebProgress(
this.QueryInterface(Ci.nsIWebProgress),
/* aIsTopLevel = */ true
);
this._progressListeners = [];
this.swapBrowser(aBrowser);
}
swapBrowser(aBrowser) {
if (this._messageManager) {
this._messageManager.removeMessageListener(
"Content:SecurityChange",
this
);
}
this._browser = aBrowser;
this._messageManager = aBrowser.messageManager;
this._messageManager.addMessageListener("Content:SecurityChange", this);
}
swapListeners(aOtherRemoteWebProgressManager) {
let temp = aOtherRemoteWebProgressManager.progressListeners;
aOtherRemoteWebProgressManager._progressListeners = this._progressListeners;
this._progressListeners = temp;
}
get progressListeners() {
return this._progressListeners;
}
get topLevelWebProgress() {
return this._topLevelWebProgress;
}
addProgressListener(aListener, aNotifyMask) {
let listener = aListener.QueryInterface(Ci.nsIWebProgressListener);
this._progressListeners.push({
listener,
mask: aNotifyMask || Ci.nsIWebProgress.NOTIFY_ALL,
});
}
removeProgressListener(aListener) {
this._progressListeners = this._progressListeners.filter(
l => l.listener != aListener
);
}
_fixSecInfo(aSecInfo) {
let deserialized = null;
if (aSecInfo) {
let helper = Cc["@mozilla.org/network/serialization-helper;1"].getService(
Ci.nsISerializationHelper
);
deserialized = helper.deserializeObject(aSecInfo);
deserialized.QueryInterface(Ci.nsITransportSecurityInfo);
}
return deserialized;
}
setCurrentURI(aURI) {
// This function is simpler than nsDocShell::SetCurrentURI since
// it doesn't have to deal with child docshells.
let remoteWebNav = this._browser._remoteWebNavigationImpl;
remoteWebNav._currentURI = aURI;
let webProgress = this.topLevelWebProgress;
for (let { listener, mask } of this._progressListeners) {
if (mask & Ci.nsIWebProgress.NOTIFY_LOCATION) {
listener.onLocationChange(webProgress, null, aURI);
}
}
}
_callProgressListeners(type, methodName, ...args) {
for (let { listener, mask } of this._progressListeners) {
if (mask & type && listener[methodName]) {
try {
listener[methodName].apply(listener, args);
} catch (ex) {
Cu.reportError(
"RemoteWebProgress failed to call " + methodName + ": " + ex + "\n"
);
}
}
}
}
onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
this._callProgressListeners(
Ci.nsIWebProgress.NOTIFY_STATE_ALL,
"onStateChange",
aWebProgress,
aRequest,
aStateFlags,
aStatus
);
}
onProgressChange(
aWebProgress,
aRequest,
aCurSelfProgress,
aMaxSelfProgress,
aCurTotalProgress,
aMaxTotalProgress
) {
this._callProgressListeners(
Ci.nsIWebProgress.NOTIFY_PROGRESS,
"onProgressChange",
aWebProgress,
aRequest,
aCurSelfProgress,
aMaxSelfProgress,
aCurTotalProgress,
aMaxTotalProgress
);
}
onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
this._callProgressListeners(
Ci.nsIWebProgress.NOTIFY_LOCATION,
"onLocationChange",
aWebProgress,
aRequest,
aLocation,
aFlags
);
}
onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
this._callProgressListeners(
Ci.nsIWebProgress.NOTIFY_STATUS,
"onStatusChange",
aWebProgress,
aRequest,
aStatus,
aMessage
);
}
onSecurityChange(aWebProgress, aRequest, aState) {
this._callProgressListeners(
Ci.nsIWebProgress.NOTIFY_SECURITY,
"onSecurityChange",
aWebProgress,
aRequest,
aState
);
}
onContentBlockingEvent(aWebProgress, aRequest, aEvent) {
this._callProgressListeners(
Ci.nsIWebProgress.NOTIFY_CONTENT_BLOCKING,
"onContentBlockingEvent",
aWebProgress,
aRequest,
aEvent
);
}
receiveMessage(aMessage) {
let json = aMessage.json;
let webProgress = null;
let isTopLevel = json.webProgress && json.webProgress.isTopLevel;
// The top-level WebProgress is always the same, but because we don't
// really have a concept of subframes/content we always create a new object
// for those.
if (json.webProgress) {
webProgress = isTopLevel
? this._topLevelWebProgress
: new RemoteWebProgress(this, isTopLevel);
webProgress.update(
json.webProgress.DOMWindowID,
0,
json.webProgress.loadType,
json.webProgress.isLoadingDocument
);
webProgress.QueryInterface(Ci.nsIWebProgress);
}
// The WebProgressRequest object however is always dynamic.
let request = null;
if (json.requestURI) {
request = new RemoteWebProgressRequest(
Services.io.newURI(json.requestURI),
Services.io.newURI(json.originalRequestURI)
);
request = request.QueryInterface(Ci.nsIRequest);
}
switch (aMessage.name) {
case "Content:SecurityChange":
let state = json.state;
if (isTopLevel) {
let secInfo = this._fixSecInfo(json.secInfo);
let isSecureContext = json.isSecureContext;
// Invoking this getter triggers the generation of the underlying object,
// which we need to access with ._securityUI, because .securityUI returns
// a wrapper that makes _update inaccessible.
void this._browser.securityUI;
this._browser._securityUI._update(secInfo, state, isSecureContext);
}
this.onSecurityChange(webProgress, request, state);
break;
}
}
}
RemoteWebProgressManager.prototype.QueryInterface = ChromeUtils.generateQI([
"nsIWebProgress",
"nsIWebProgressListener",
]);