gecko-dev/toolkit/modules/WebProgressChild.jsm

238 строки
8.4 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["WebProgressChild"];
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
"@mozilla.org/xre/app-info;1",
"nsICrashReporter");
XPCOMUtils.defineLazyServiceGetter(this, "serializationHelper",
"@mozilla.org/network/serialization-helper;1",
"nsISerializationHelper");
class WebProgressChild {
constructor(mm) {
this.mm = mm;
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 = this.mm.tabEventTarget;
let webProgress = this.mm.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);
}
_requestSpec(aRequest, aPropertyName) {
if (!aRequest || !(aRequest instanceof Ci.nsIChannel))
return null;
return aRequest[aPropertyName].spec;
}
_setupJSON(aWebProgress, aRequest, aStateFlags) {
// Avoid accessing this.mm.content.document when being called from onStateChange
// unless if we are in STATE_STOP, because otherwise the getter will
// instantiate an about:blank document for us.
let contentDocument = null;
if (aStateFlags) {
// We're being called from onStateChange
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
contentDocument = this.mm.content.document;
}
} else {
contentDocument = this.mm.content.document;
}
let innerWindowID = null;
if (aWebProgress) {
let domWindowID = null;
try {
domWindowID = aWebProgress.DOMWindowID;
innerWindowID = aWebProgress.innerDOMWindowID;
} catch (e) {
// The DOM Window ID getters above may throw if the inner or outer
// windows aren't created yet or are destroyed at the time we're making
// this call but that isn't fatal so ignore the exceptions here.
}
aWebProgress = {
isTopLevel: aWebProgress.isTopLevel,
isLoadingDocument: aWebProgress.isLoadingDocument,
loadType: aWebProgress.loadType,
DOMWindowID: domWindowID
};
}
return {
webProgress: aWebProgress || null,
requestURI: this._requestSpec(aRequest, "URI"),
originalRequestURI: this._requestSpec(aRequest, "originalURI"),
documentContentType: contentDocument ? contentDocument.contentType : null,
innerWindowID,
};
}
_setupObjects(aWebProgress, aRequest) {
let domWindow;
try {
domWindow = aWebProgress && aWebProgress.DOMWindow;
} catch (e) {
// If nsDocShell::Destroy has already been called, then we'll
// get NS_NOINTERFACE when trying to get the DOM window. Ignore
// that here.
domWindow = null;
}
return {
contentWindow: this.mm.content,
contentDocument: this.mm.content.document,
// DOMWindow is not necessarily the this.mm.content-window with subframes.
DOMWindow: domWindow,
webProgress: aWebProgress,
request: aRequest,
};
}
_send(name, data, objects) {
this.mm.sendAsyncMessage(name, data, objects);
}
onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
let json = this._setupJSON(aWebProgress, aRequest, aStateFlags);
let objects = this._setupObjects(aWebProgress, aRequest);
json.stateFlags = aStateFlags;
json.status = aStatus;
// It's possible that this state change was triggered by
// loading an internal error page, for which the parent
// will want to know some details, so we'll update it with
// the documentURI.
if (aWebProgress && aWebProgress.isTopLevel) {
json.documentURI = this.mm.content.document.documentURIObject.spec;
json.charset = this.mm.content.document.characterSet;
json.mayEnableCharacterEncodingMenu = this.mm.docShell.mayEnableCharacterEncodingMenu;
json.inLoadURI = this.mm.WebNavigation.inLoadURI;
}
this._send("Content:StateChange", json, objects);
}
// Note: Because the nsBrowserStatusFilter timeout runnable is
// SystemGroup-labeled, this method should not modify this.mm.content DOM or
// run this.mm.content JS.
onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
let json = this._setupJSON(aWebProgress, aRequest);
let objects = this._setupObjects(aWebProgress, aRequest);
json.curSelf = aCurSelf;
json.maxSelf = aMaxSelf;
json.curTotal = aCurTotal;
json.maxTotal = aMaxTotal;
this._send("Content:ProgressChange", json, objects);
}
onProgressChange64(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
this.onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal);
}
onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
let json = this._setupJSON(aWebProgress, aRequest);
let objects = this._setupObjects(aWebProgress, aRequest);
json.location = aLocationURI ? aLocationURI.spec : "";
json.flags = aFlags;
// These properties can change even for a sub-frame navigation.
let webNav = this.mm.docShell.QueryInterface(Ci.nsIWebNavigation);
json.canGoBack = webNav.canGoBack;
json.canGoForward = webNav.canGoForward;
if (aWebProgress && aWebProgress.isTopLevel) {
json.documentURI = this.mm.content.document.documentURIObject.spec;
json.title = this.mm.content.document.title;
json.charset = this.mm.content.document.characterSet;
json.mayEnableCharacterEncodingMenu = this.mm.docShell.mayEnableCharacterEncodingMenu;
json.principal = this.mm.content.document.nodePrincipal;
json.synthetic = this.mm.content.document.mozSyntheticDocument;
json.inLoadURI = this.mm.WebNavigation.inLoadURI;
json.requestContextID = this.mm.content.document.documentLoadGroup
? this.mm.content.document.documentLoadGroup.requestContextID
: null;
if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
let uri = aLocationURI;
try {
// If the current URI contains a username/password, remove it.
uri = uri.mutate()
.setUserPass("")
.finalize();
} catch (ex) { /* Ignore failures on about: URIs. */ }
CrashReporter.annotateCrashReport("URL", uri.spec);
}
}
this._send("Content:LocationChange", json, objects);
}
// Note: Because the nsBrowserStatusFilter timeout runnable is
// SystemGroup-labeled, this method should not modify this.mm.content DOM or
// run this.mm.content JS.
onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
let json = this._setupJSON(aWebProgress, aRequest);
let objects = this._setupObjects(aWebProgress, aRequest);
json.status = aStatus;
json.message = aMessage;
this._send("Content:StatusChange", json, objects);
}
getSecInfoAsString() {
let secInfo = this.mm.docShell.securityUI.secInfo;
if (secInfo) {
return serializationHelper.serializeToString(secInfo);
}
return null;
}
onSecurityChange(aWebProgress, aRequest, aState) {
let json = this._setupJSON(aWebProgress, aRequest);
let objects = this._setupObjects(aWebProgress, aRequest);
json.state = aState;
json.secInfo = this.getSecInfoAsString();
json.matchedList = null;
if (aRequest && aRequest instanceof Ci.nsIClassifiedChannel) {
json.matchedList = aRequest.matchedList;
}
this._send("Content:SecurityChange", json, objects);
}
onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
return true;
}
sendLoadCallResult() {
this.mm.sendAsyncMessage("Content:LoadURIResult");
}
}
WebProgressChild.prototype.QueryInterface =
ChromeUtils.generateQI(["nsIWebProgressListener",
"nsIWebProgressListener2",
"nsISupportsWeakReference"]);