зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1301514 - Destroy browser API frame scripts during swap. r=kanru
When swapping content from <iframe mozbrowser> to <xul:browser>, we now stop the frame scripts that implement the content side of the browser API since they are no longer needed and can cause issues if they remain active. MozReview-Commit-ID: JrecxA4MI93 --HG-- extra : rebase_source : cc68b975c7d82035410a647ff66eab130055ed04
This commit is contained in:
Родитель
bcefcd65fe
Коммит
b7ca8b1d80
|
@ -1032,6 +1032,14 @@ nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// Destroy browser frame scripts for content leaving a frame with browser API
|
||||
if (OwnerIsMozBrowserOrAppFrame() && !aOther->OwnerIsMozBrowserOrAppFrame()) {
|
||||
DestroyBrowserFrameScripts();
|
||||
}
|
||||
if (!OwnerIsMozBrowserOrAppFrame() && aOther->OwnerIsMozBrowserOrAppFrame()) {
|
||||
aOther->DestroyBrowserFrameScripts();
|
||||
}
|
||||
|
||||
aOther->mRemoteBrowser->SetBrowserDOMWindow(browserDOMWindow);
|
||||
mRemoteBrowser->SetBrowserDOMWindow(otherBrowserDOMWindow);
|
||||
|
||||
|
@ -1406,6 +1414,14 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// Destroy browser frame scripts for content leaving a frame with browser API
|
||||
if (OwnerIsMozBrowserOrAppFrame() && !aOther->OwnerIsMozBrowserOrAppFrame()) {
|
||||
DestroyBrowserFrameScripts();
|
||||
}
|
||||
if (!OwnerIsMozBrowserOrAppFrame() && aOther->OwnerIsMozBrowserOrAppFrame()) {
|
||||
aOther->DestroyBrowserFrameScripts();
|
||||
}
|
||||
|
||||
// Now move the docshells to the right docshell trees. Note that this
|
||||
// resets their treeowners to null.
|
||||
ourParentItem->RemoveChild(ourDocshell);
|
||||
|
@ -3364,24 +3380,37 @@ nsFrameLoader::GetLoadContext(nsILoadContext** aLoadContext)
|
|||
void
|
||||
nsFrameLoader::InitializeBrowserAPI()
|
||||
{
|
||||
if (OwnerIsMozBrowserOrAppFrame()) {
|
||||
if (!IsRemoteFrame()) {
|
||||
nsresult rv = EnsureMessageManager();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
if (mMessageManager) {
|
||||
mMessageManager->LoadFrameScript(
|
||||
NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
|
||||
/* allowDelayedLoad = */ true,
|
||||
/* aRunInGlobalScope */ true);
|
||||
}
|
||||
if (!OwnerIsMozBrowserOrAppFrame()) {
|
||||
return;
|
||||
}
|
||||
if (!IsRemoteFrame()) {
|
||||
nsresult rv = EnsureMessageManager();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
|
||||
if (browserFrame) {
|
||||
browserFrame->InitializeBrowserAPI();
|
||||
if (mMessageManager) {
|
||||
mMessageManager->LoadFrameScript(
|
||||
NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
|
||||
/* allowDelayedLoad = */ true,
|
||||
/* aRunInGlobalScope */ true);
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
|
||||
if (browserFrame) {
|
||||
browserFrame->InitializeBrowserAPI();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsFrameLoader::DestroyBrowserFrameScripts()
|
||||
{
|
||||
if (!OwnerIsMozBrowserOrAppFrame()) {
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
|
||||
if (browserFrame) {
|
||||
browserFrame->DestroyBrowserFrameScripts();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -333,6 +333,7 @@ private:
|
|||
void ResetPermissionManagerStatus();
|
||||
|
||||
void InitializeBrowserAPI();
|
||||
void DestroyBrowserFrameScripts();
|
||||
|
||||
nsresult GetNewTabContext(mozilla::dom::MutableTabContext* aTabContext,
|
||||
nsIURI* aURI = nullptr,
|
||||
|
|
|
@ -10,7 +10,7 @@ Test swapFrameLoaders with different frame types and remoteness
|
|||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
|
||||
|
||||
<script type="application/javascript"><![CDATA[
|
||||
["SimpleTest", "SpecialPowers", "info", "is"].forEach(key => {
|
||||
["SimpleTest", "SpecialPowers", "info", "is", "ok"].forEach(key => {
|
||||
window[key] = window.opener[key];
|
||||
})
|
||||
const { interfaces: Ci } = Components;
|
||||
|
@ -117,6 +117,13 @@ Test swapFrameLoaders with different frame types and remoteness
|
|||
addMessageListener("ping", function() {
|
||||
sendAsyncMessage("pong", "${name}");
|
||||
});
|
||||
addMessageListener("check-browser-api", function() {
|
||||
let exists = "api" in this;
|
||||
sendAsyncMessage("check-browser-api", {
|
||||
exists,
|
||||
running: exists && !this.api._shuttingDown,
|
||||
});
|
||||
});
|
||||
}`;
|
||||
}
|
||||
|
||||
|
@ -213,6 +220,35 @@ Test swapFrameLoaders with different frame types and remoteness
|
|||
is(pongB, "A", "Frame B message manager acquired after swap gets reply A after swap");
|
||||
}
|
||||
|
||||
// Verify browser API frame scripts destroyed if swapped out of browser frame
|
||||
if (frameA.hasAttribute("mozbrowser") != frameB.hasAttribute("mozbrowser")) {
|
||||
let mmA = frameA.frameLoader.messageManager;
|
||||
let mmB = frameB.frameLoader.messageManager;
|
||||
|
||||
let inflightA = once(mmA, "check-browser-api");
|
||||
let inflightB = once(mmB, "check-browser-api");
|
||||
|
||||
info("Check browser API for frame A");
|
||||
mmA.sendAsyncMessage("check-browser-api");
|
||||
let [ { data: apiA } ] = yield inflightA;
|
||||
if (frameA.hasAttribute("mozbrowser")) {
|
||||
ok(apiA.exists && apiA.running, "Frame A browser API exists and is running");
|
||||
} else {
|
||||
ok(apiA.exists && !apiA.running, "Frame A browser API did exist but is now destroyed");
|
||||
}
|
||||
|
||||
info("Check browser API for frame B");
|
||||
mmB.sendAsyncMessage("check-browser-api");
|
||||
let [ { data: apiB } ] = yield inflightB;
|
||||
if (frameB.hasAttribute("mozbrowser")) {
|
||||
ok(apiB.exists && apiB.running, "Frame B browser API exists and is running");
|
||||
} else {
|
||||
ok(apiB.exists && !apiB.running, "Frame B browser API did exist but is now destroyed");
|
||||
}
|
||||
} else {
|
||||
info("Frames have matching mozbrowser state, skipping browser API destruction check");
|
||||
}
|
||||
|
||||
frameA.remove();
|
||||
frameB.remove();
|
||||
}
|
||||
|
|
|
@ -77,6 +77,27 @@ if (!BrowserElementIsReady) {
|
|||
ContentPanning.init();
|
||||
}
|
||||
}
|
||||
|
||||
function onDestroy() {
|
||||
removeMessageListener("browser-element-api:destroy", onDestroy);
|
||||
|
||||
if (api) {
|
||||
api.destroy();
|
||||
}
|
||||
if ("ContentPanning" in this) {
|
||||
ContentPanning.destroy();
|
||||
}
|
||||
if ("ContentPanningAPZDisabled" in this) {
|
||||
ContentPanningAPZDisabled.destroy();
|
||||
}
|
||||
if ("CopyPasteAssistent" in this) {
|
||||
CopyPasteAssistent.destroy();
|
||||
}
|
||||
|
||||
BrowserElementIsReady = false;
|
||||
}
|
||||
addMessageListener("browser-element-api:destroy", onDestroy);
|
||||
|
||||
BrowserElementIsReady = true;
|
||||
} else {
|
||||
debug("BE already loaded, abort");
|
||||
|
|
|
@ -30,9 +30,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "ManifestObtainer",
|
|||
|
||||
var kLongestReturnedString = 128;
|
||||
|
||||
const Timer = Components.Constructor("@mozilla.org/timer;1",
|
||||
"nsITimer",
|
||||
"initWithCallback");
|
||||
var Timer = Components.Constructor("@mozilla.org/timer;1",
|
||||
"nsITimer",
|
||||
"initWithCallback");
|
||||
|
||||
function sendAsyncMsg(msg, data) {
|
||||
// Ensure that we don't send any messages before BrowserElementChild.js
|
||||
|
@ -66,7 +66,7 @@ function sendSyncMsg(msg, data) {
|
|||
|
||||
var CERTIFICATE_ERROR_PAGE_PREF = 'security.alternate_certificate_error_page';
|
||||
|
||||
const OBSERVED_EVENTS = [
|
||||
var OBSERVED_EVENTS = [
|
||||
'xpcom-shutdown',
|
||||
'audio-playback',
|
||||
'activity-done',
|
||||
|
@ -74,6 +74,36 @@ const OBSERVED_EVENTS = [
|
|||
'will-launch-app'
|
||||
];
|
||||
|
||||
var LISTENED_EVENTS = [
|
||||
{ type: "DOMTitleChanged", useCapture: true, wantsUntrusted: false },
|
||||
{ type: "DOMLinkAdded", useCapture: true, wantsUntrusted: false },
|
||||
{ type: "MozScrolledAreaChanged", useCapture: true, wantsUntrusted: false },
|
||||
{ type: "MozDOMFullscreen:Request", useCapture: true, wantsUntrusted: false },
|
||||
{ type: "MozDOMFullscreen:NewOrigin", useCapture: true, wantsUntrusted: false },
|
||||
{ type: "MozDOMFullscreen:Exit", useCapture: true, wantsUntrusted: false },
|
||||
{ type: "DOMMetaAdded", useCapture: true, wantsUntrusted: false },
|
||||
{ type: "DOMMetaChanged", useCapture: true, wantsUntrusted: false },
|
||||
{ type: "DOMMetaRemoved", useCapture: true, wantsUntrusted: false },
|
||||
{ type: "scrollviewchange", useCapture: true, wantsUntrusted: false },
|
||||
{ type: "click", useCapture: false, wantsUntrusted: false },
|
||||
// This listens to unload events from our message manager, but /not/ from
|
||||
// the |content| window. That's because the window's unload event doesn't
|
||||
// bubble, and we're not using a capturing listener. If we'd used
|
||||
// useCapture == true, we /would/ hear unload events from the window, which
|
||||
// is not what we want!
|
||||
{ type: "unload", useCapture: false, wantsUntrusted: false },
|
||||
];
|
||||
|
||||
// We are using the system group for those events so if something in the
|
||||
// content called .stopPropagation() this will still be called.
|
||||
var LISTENED_SYSTEM_EVENTS = [
|
||||
{ type: "DOMWindowClose", useCapture: false },
|
||||
{ type: "DOMWindowCreated", useCapture: false },
|
||||
{ type: "DOMWindowResize", useCapture: false },
|
||||
{ type: "contextmenu", useCapture: false },
|
||||
{ type: "scroll", useCapture: false },
|
||||
];
|
||||
|
||||
/**
|
||||
* The BrowserElementChild implements one half of <iframe mozbrowser>.
|
||||
* (The other half is, unsurprisingly, BrowserElementParent.)
|
||||
|
@ -177,76 +207,124 @@ BrowserElementChild.prototype = {
|
|||
|
||||
this._shuttingDown = false;
|
||||
|
||||
addEventListener('DOMTitleChanged',
|
||||
this._titleChangedHandler.bind(this),
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addEventListener('DOMLinkAdded',
|
||||
this._linkAddedHandler.bind(this),
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addEventListener('MozScrolledAreaChanged',
|
||||
this._mozScrollAreaChanged.bind(this),
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addEventListener("MozDOMFullscreen:Request",
|
||||
this._mozRequestedDOMFullscreen.bind(this),
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addEventListener("MozDOMFullscreen:NewOrigin",
|
||||
this._mozFullscreenOriginChange.bind(this),
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addEventListener("MozDOMFullscreen:Exit",
|
||||
this._mozExitDomFullscreen.bind(this),
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addEventListener('DOMMetaAdded',
|
||||
this._metaChangedHandler.bind(this),
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addEventListener('DOMMetaChanged',
|
||||
this._metaChangedHandler.bind(this),
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addEventListener('DOMMetaRemoved',
|
||||
this._metaChangedHandler.bind(this),
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addEventListener('scrollviewchange',
|
||||
this._ScrollViewChangeHandler.bind(this),
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addEventListener('click',
|
||||
this._ClickHandler.bind(this),
|
||||
/* useCapture = */ false,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
// This listens to unload events from our message manager, but /not/ from
|
||||
// the |content| window. That's because the window's unload event doesn't
|
||||
// bubble, and we're not using a capturing listener. If we'd used
|
||||
// useCapture == true, we /would/ hear unload events from the window, which
|
||||
// is not what we want!
|
||||
addEventListener('unload',
|
||||
this._unloadHandler.bind(this),
|
||||
/* useCapture = */ false,
|
||||
/* wantsUntrusted = */ false);
|
||||
LISTENED_EVENTS.forEach(event => {
|
||||
addEventListener(event.type, this, event.useCapture, event.wantsUntrusted);
|
||||
});
|
||||
|
||||
// Registers a MozAfterPaint handler for the very first paint.
|
||||
this._addMozAfterPaintHandler(function () {
|
||||
sendAsyncMsg('firstpaint');
|
||||
});
|
||||
|
||||
addMessageListener("browser-element-api:call", this);
|
||||
|
||||
let els = Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(Ci.nsIEventListenerService);
|
||||
LISTENED_SYSTEM_EVENTS.forEach(event => {
|
||||
els.addSystemEventListener(global, event.type, this, event.useCapture);
|
||||
});
|
||||
|
||||
OBSERVED_EVENTS.forEach((aTopic) => {
|
||||
Services.obs.addObserver(this, aTopic, false);
|
||||
});
|
||||
|
||||
this.forwarder.init();
|
||||
},
|
||||
|
||||
/**
|
||||
* Shut down the frame's side of the browser API. This is called when:
|
||||
* - our TabChildGlobal starts to die
|
||||
* - the content is moved to frame without the browser API
|
||||
* This is not called when the page inside |content| unloads.
|
||||
*/
|
||||
destroy: function() {
|
||||
debug("Destroying");
|
||||
this._shuttingDown = true;
|
||||
|
||||
BrowserElementPromptService.unmapWindowToBrowserElementChild(content);
|
||||
|
||||
docShell.QueryInterface(Ci.nsIWebProgress)
|
||||
.removeProgressListener(this._progressListener);
|
||||
|
||||
LISTENED_EVENTS.forEach(event => {
|
||||
removeEventListener(event.type, this, event.useCapture, event.wantsUntrusted);
|
||||
});
|
||||
|
||||
this._deactivateNextPaintListener();
|
||||
|
||||
removeMessageListener("browser-element-api:call", this);
|
||||
|
||||
let els = Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(Ci.nsIEventListenerService);
|
||||
LISTENED_SYSTEM_EVENTS.forEach(event => {
|
||||
els.removeSystemEventListener(global, event.type, this, event.useCapture);
|
||||
});
|
||||
|
||||
OBSERVED_EVENTS.forEach((aTopic) => {
|
||||
Services.obs.removeObserver(this, aTopic);
|
||||
});
|
||||
|
||||
this.forwarder.uninit();
|
||||
this.forwarder = null;
|
||||
},
|
||||
|
||||
handleEvent: function(event) {
|
||||
switch (event.type) {
|
||||
case "DOMTitleChanged":
|
||||
this._titleChangedHandler(event);
|
||||
break;
|
||||
case "DOMLinkAdded":
|
||||
this._linkAddedHandler(event);
|
||||
break;
|
||||
case "MozScrolledAreaChanged":
|
||||
this._mozScrollAreaChanged(event);
|
||||
break;
|
||||
case "MozDOMFullscreen:Request":
|
||||
this._mozRequestedDOMFullscreen(event);
|
||||
break;
|
||||
case "MozDOMFullscreen:NewOrigin":
|
||||
this._mozFullscreenOriginChange(event);
|
||||
break;
|
||||
case "MozDOMFullscreen:Exit":
|
||||
this._mozExitDomFullscreen(event);
|
||||
break;
|
||||
case "DOMMetaAdded":
|
||||
this._metaChangedHandler(event);
|
||||
break;
|
||||
case "DOMMetaChanged":
|
||||
this._metaChangedHandler(event);
|
||||
break;
|
||||
case "DOMMetaRemoved":
|
||||
this._metaChangedHandler(event);
|
||||
break;
|
||||
case "scrollviewchange":
|
||||
this._ScrollViewChangeHandler(event);
|
||||
break;
|
||||
case "click":
|
||||
this._ClickHandler(event);
|
||||
break;
|
||||
case "unload":
|
||||
this.destroy(event);
|
||||
break;
|
||||
case "DOMWindowClose":
|
||||
this._windowCloseHandler(event);
|
||||
break;
|
||||
case "DOMWindowCreated":
|
||||
this._windowCreatedHandler(event);
|
||||
break;
|
||||
case "DOMWindowResize":
|
||||
this._windowResizeHandler(event);
|
||||
break;
|
||||
case "contextmenu":
|
||||
this._contextmenuHandler(event);
|
||||
break;
|
||||
case "scroll":
|
||||
this._scrollEventHandler(event);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
let self = this;
|
||||
|
||||
let mmCalls = {
|
||||
|
@ -259,11 +337,11 @@ BrowserElementChild.prototype = {
|
|||
"send-touch-event": this._recvSendTouchEvent,
|
||||
"get-can-go-back": this._recvCanGoBack,
|
||||
"get-can-go-forward": this._recvCanGoForward,
|
||||
"mute": this._recvMute.bind(this),
|
||||
"unmute": this._recvUnmute.bind(this),
|
||||
"get-muted": this._recvGetMuted.bind(this),
|
||||
"set-volume": this._recvSetVolume.bind(this),
|
||||
"get-volume": this._recvGetVolume.bind(this),
|
||||
"mute": this._recvMute,
|
||||
"unmute": this._recvUnmute,
|
||||
"get-muted": this._recvGetMuted,
|
||||
"set-volume": this._recvSetVolume,
|
||||
"get-volume": this._recvGetVolume,
|
||||
"go-back": this._recvGoBack,
|
||||
"go-forward": this._recvGoForward,
|
||||
"reload": this._recvReload,
|
||||
|
@ -273,13 +351,13 @@ BrowserElementChild.prototype = {
|
|||
"fire-ctx-callback": this._recvFireCtxCallback,
|
||||
"owner-visibility-change": this._recvOwnerVisibilityChange,
|
||||
"entered-fullscreen": this._recvEnteredFullscreen,
|
||||
"exit-fullscreen": this._recvExitFullscreen.bind(this),
|
||||
"activate-next-paint-listener": this._activateNextPaintListener.bind(this),
|
||||
"set-input-method-active": this._recvSetInputMethodActive.bind(this),
|
||||
"deactivate-next-paint-listener": this._deactivateNextPaintListener.bind(this),
|
||||
"find-all": this._recvFindAll.bind(this),
|
||||
"find-next": this._recvFindNext.bind(this),
|
||||
"clear-match": this._recvClearMatch.bind(this),
|
||||
"exit-fullscreen": this._recvExitFullscreen,
|
||||
"activate-next-paint-listener": this._activateNextPaintListener,
|
||||
"set-input-method-active": this._recvSetInputMethodActive,
|
||||
"deactivate-next-paint-listener": this._deactivateNextPaintListener,
|
||||
"find-all": this._recvFindAll,
|
||||
"find-next": this._recvFindNext,
|
||||
"clear-match": this._recvClearMatch,
|
||||
"execute-script": this._recvExecuteScript,
|
||||
"get-audio-channel-volume": this._recvGetAudioChannelVolume,
|
||||
"set-audio-channel-volume": this._recvSetAudioChannelVolume,
|
||||
|
@ -289,38 +367,9 @@ BrowserElementChild.prototype = {
|
|||
"get-web-manifest": this._recvGetWebManifest,
|
||||
}
|
||||
|
||||
addMessageListener("browser-element-api:call", function(aMessage) {
|
||||
if (aMessage.data.msg_name in mmCalls) {
|
||||
return mmCalls[aMessage.data.msg_name].apply(self, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
let els = Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(Ci.nsIEventListenerService);
|
||||
|
||||
// We are using the system group for those events so if something in the
|
||||
// content called .stopPropagation() this will still be called.
|
||||
els.addSystemEventListener(global, 'DOMWindowClose',
|
||||
this._windowCloseHandler.bind(this),
|
||||
/* useCapture = */ false);
|
||||
els.addSystemEventListener(global, 'DOMWindowCreated',
|
||||
this._windowCreatedHandler.bind(this),
|
||||
/* useCapture = */ true);
|
||||
els.addSystemEventListener(global, 'DOMWindowResize',
|
||||
this._windowResizeHandler.bind(this),
|
||||
/* useCapture = */ false);
|
||||
els.addSystemEventListener(global, 'contextmenu',
|
||||
this._contextmenuHandler.bind(this),
|
||||
/* useCapture = */ false);
|
||||
els.addSystemEventListener(global, 'scroll',
|
||||
this._scrollEventHandler.bind(this),
|
||||
/* useCapture = */ false);
|
||||
|
||||
OBSERVED_EVENTS.forEach((aTopic) => {
|
||||
Services.obs.addObserver(this, aTopic, false);
|
||||
});
|
||||
|
||||
this.forwarder.init();
|
||||
if (message.data.msg_name in mmCalls) {
|
||||
return mmCalls[message.data.msg_name].apply(self, arguments);
|
||||
}
|
||||
},
|
||||
|
||||
_paintFrozenTimer: null,
|
||||
|
@ -376,20 +425,6 @@ BrowserElementChild.prototype = {
|
|||
this._paintFrozenTimer = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when our TabChildGlobal starts to die. This is not called when the
|
||||
* page inside |content| unloads.
|
||||
*/
|
||||
_unloadHandler: function() {
|
||||
this._shuttingDown = true;
|
||||
OBSERVED_EVENTS.forEach((aTopic) => {
|
||||
Services.obs.removeObserver(this, aTopic);
|
||||
});
|
||||
|
||||
this.forwarder.uninit();
|
||||
this.forwarder = null;
|
||||
},
|
||||
|
||||
get _windowUtils() {
|
||||
return content.document.defaultView
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
|
|
|
@ -23,11 +23,31 @@ var CopyPasteAssistent = {
|
|||
},
|
||||
|
||||
init: function() {
|
||||
addEventListener('mozcaretstatechanged',
|
||||
this._caretStateChangedHandler.bind(this),
|
||||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
addMessageListener('browser-element-api:call', this._browserAPIHandler.bind(this));
|
||||
addEventListener("mozcaretstatechanged", this,
|
||||
/* useCapture = */ true, /* wantsUntrusted = */ false);
|
||||
addMessageListener("browser-element-api:call", this);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
removeEventListener("mozcaretstatechanged", this,
|
||||
/* useCapture = */ true, /* wantsUntrusted = */ false);
|
||||
removeMessageListener("browser-element-api:call", this);
|
||||
},
|
||||
|
||||
handleEvent: function(event) {
|
||||
switch (event.type) {
|
||||
case "mozcaretstatechanged":
|
||||
this._caretStateChangedHandler(event);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
switch (message.name) {
|
||||
case "browser-element-api:call":
|
||||
this._browserAPIHandler(message);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_browserAPIHandler: function(e) {
|
||||
|
|
|
@ -17,27 +17,57 @@ var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
|||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Geometry.jsm");
|
||||
|
||||
const kObservedEvents = [
|
||||
var kObservedEvents = [
|
||||
"BEC:ShownModalPrompt",
|
||||
"Activity:Success",
|
||||
"Activity:Error"
|
||||
];
|
||||
|
||||
const ContentPanning = {
|
||||
var ContentPanning = {
|
||||
init: function cp_init() {
|
||||
addEventListener("unload",
|
||||
this._unloadHandler.bind(this),
|
||||
/* useCapture = */ false,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addMessageListener("Viewport:Change", this._recvViewportChange.bind(this));
|
||||
addMessageListener("Gesture:DoubleTap", this._recvDoubleTap.bind(this));
|
||||
addEventListener("visibilitychange", this._handleVisibilityChange.bind(this));
|
||||
addEventListener("unload", this,
|
||||
/* useCapture = */ false, /* wantsUntrusted = */ false);
|
||||
addMessageListener("Viewport:Change", this);
|
||||
addMessageListener("Gesture:DoubleTap", this);
|
||||
addEventListener("visibilitychange", this);
|
||||
kObservedEvents.forEach((topic) => {
|
||||
Services.obs.addObserver(this, topic, false);
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
removeEventListener("unload", this,
|
||||
/* useCapture = */ false, /* wantsUntrusted = */ false);
|
||||
removeMessageListener("Viewport:Change", this);
|
||||
removeMessageListener("Gesture:DoubleTap", this);
|
||||
removeEventListener("visibilitychange", this);
|
||||
kObservedEvents.forEach((topic) => {
|
||||
Services.obs.removeObserver(this, topic, false);
|
||||
});
|
||||
},
|
||||
|
||||
handleEvent: function(event) {
|
||||
switch (event.type) {
|
||||
case "unload":
|
||||
this._unloadHandler(event);
|
||||
break;
|
||||
case "visibilitychange":
|
||||
this._handleVisibilityChange(event);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
switch (message.name) {
|
||||
case "Viewport:Change":
|
||||
this._recvViewportChange(message);
|
||||
break;
|
||||
case "Gesture:DoubleTap":
|
||||
this._recvDoubleTap(message);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
observe: function cp_observe(subject, topic, data) {
|
||||
this._resetHover();
|
||||
},
|
||||
|
@ -176,7 +206,7 @@ const ContentPanning = {
|
|||
}
|
||||
};
|
||||
|
||||
const ElementTouchHelper = {
|
||||
var ElementTouchHelper = {
|
||||
anyElementFromPoint: function(aWindow, aX, aY) {
|
||||
let cwu = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
let elem = cwu.elementFromPoint(aX, aY, true, true);
|
||||
|
|
|
@ -14,7 +14,7 @@ Cu.import("resource://gre/modules/Geometry.jsm");
|
|||
|
||||
var global = this;
|
||||
|
||||
const ContentPanningAPZDisabled = {
|
||||
var ContentPanningAPZDisabled = {
|
||||
// Are we listening to touch or mouse events?
|
||||
watchedEventsType: '',
|
||||
|
||||
|
@ -23,10 +23,26 @@ const ContentPanningAPZDisabled = {
|
|||
hybridEvents: false,
|
||||
|
||||
init: function cp_init() {
|
||||
this._setupListenersForPanning();
|
||||
let events = this._getEventsList();
|
||||
let els = Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(Ci.nsIEventListenerService);
|
||||
events.forEach(type => {
|
||||
// Using the system group for mouse/touch events to avoid
|
||||
// missing events if .stopPropagation() has been called.
|
||||
els.addSystemEventListener(global, type, this, /* useCapture = */ false);
|
||||
});
|
||||
},
|
||||
|
||||
_setupListenersForPanning: function cp_setupListenersForPanning() {
|
||||
destroy: function () {
|
||||
let events = this._getEventsList();
|
||||
let els = Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(Ci.nsIEventListenerService);
|
||||
events.forEach(type => {
|
||||
els.removeSystemEventListener(global, type, this, /* useCapture = */ false);
|
||||
});
|
||||
},
|
||||
|
||||
_getEventsList: function () {
|
||||
let events;
|
||||
|
||||
if (content.TouchEvent) {
|
||||
|
@ -48,16 +64,7 @@ const ContentPanningAPZDisabled = {
|
|||
this.watchedEventsType = 'mouse';
|
||||
}
|
||||
|
||||
let els = Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(Ci.nsIEventListenerService);
|
||||
|
||||
events.forEach(function(type) {
|
||||
// Using the system group for mouse/touch events to avoid
|
||||
// missing events if .stopPropagation() has been called.
|
||||
els.addSystemEventListener(global, type,
|
||||
this.handleEvent.bind(this),
|
||||
/* useCapture = */ false);
|
||||
}.bind(this));
|
||||
return events;
|
||||
},
|
||||
|
||||
handleEvent: function cp_handleEvent(evt) {
|
||||
|
@ -478,23 +485,23 @@ const ContentPanningAPZDisabled = {
|
|||
};
|
||||
|
||||
// Min/max velocity of kinetic panning. This is in pixels/millisecond.
|
||||
const kMinVelocity = 0.2;
|
||||
const kMaxVelocity = 6;
|
||||
var kMinVelocity = 0.2;
|
||||
var kMaxVelocity = 6;
|
||||
|
||||
// Constants that affect the "friction" of the scroll pane.
|
||||
const kExponentialC = 1000;
|
||||
const kPolynomialC = 100 / 1000000;
|
||||
var kExponentialC = 1000;
|
||||
var kPolynomialC = 100 / 1000000;
|
||||
|
||||
// How often do we change the position of the scroll pane?
|
||||
// Too often and panning may jerk near the end.
|
||||
// Too little and panning will be choppy. In milliseconds.
|
||||
const kUpdateInterval = 16;
|
||||
var kUpdateInterval = 16;
|
||||
|
||||
// The numbers of momentums to use for calculating the velocity of the pan.
|
||||
// Those are taken from the end of the action
|
||||
const kSamples = 5;
|
||||
var kSamples = 5;
|
||||
|
||||
const KineticPanning = {
|
||||
var KineticPanning = {
|
||||
_position: new Point(0, 0),
|
||||
_velocity: new Point(0, 0),
|
||||
_acceleration: new Point(0, 0),
|
||||
|
|
|
@ -263,6 +263,7 @@ BrowserElementParent.prototype = {
|
|||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
setFrameLoader: function(frameLoader) {
|
||||
debug("Setting frameLoader");
|
||||
this._frameLoader = frameLoader;
|
||||
this._frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
|
||||
if (!this._frameElement) {
|
||||
|
@ -302,6 +303,11 @@ BrowserElementParent.prototype = {
|
|||
this._frameElement, this._frameLoader.messageManager);
|
||||
},
|
||||
|
||||
destroyFrameScripts() {
|
||||
debug("Destroying frame scripts");
|
||||
this._mm.sendAsyncMessage("browser-element-api:destroy");
|
||||
},
|
||||
|
||||
_runPendingAPICall: function() {
|
||||
if (!this._pendingAPICalls) {
|
||||
return;
|
||||
|
|
|
@ -54,7 +54,7 @@ BrowserElementPrompt.prototype = {
|
|||
// Each button is described by an object with the following schema
|
||||
// {
|
||||
// string messageType, // 'builtin' or 'custom'
|
||||
// string message, // 'ok', 'cancel', 'yes', 'no', 'save', 'dontsave',
|
||||
// string message, // 'ok', 'cancel', 'yes', 'no', 'save', 'dontsave',
|
||||
// // 'revert' or a string from caller if messageType was 'custom'.
|
||||
// }
|
||||
//
|
||||
|
@ -631,6 +631,9 @@ this.BrowserElementPromptService = {
|
|||
mapWindowToBrowserElementChild: function(win, browserElementChild) {
|
||||
this._browserElementChildMap[this._getOuterWindowID(win)] = browserElementChild;
|
||||
},
|
||||
unmapWindowToBrowserElementChild: function(win) {
|
||||
delete this._browserElementChildMap[this._getOuterWindowID(win)];
|
||||
},
|
||||
|
||||
getBrowserElementChildForWindow: function(win) {
|
||||
// We only have a mapping for <iframe mozbrowser>s, not their inner
|
||||
|
|
|
@ -35,6 +35,11 @@ interface nsIBrowserElementAPI : nsISupports
|
|||
const long FIND_FORWARD = 0;
|
||||
const long FIND_BACKWARD = 1;
|
||||
|
||||
/**
|
||||
* Notify frame scripts that support the API to destroy.
|
||||
*/
|
||||
void destroyFrameScripts();
|
||||
|
||||
void setFrameLoader(in nsIFrameLoader frameLoader);
|
||||
|
||||
void setVisible(in boolean visible);
|
||||
|
|
|
@ -74,6 +74,15 @@ nsBrowserElement::InitBrowserElementAPI()
|
|||
mBrowserElementAPI->SetFrameLoader(frameLoader);
|
||||
}
|
||||
|
||||
void
|
||||
nsBrowserElement::DestroyBrowserElementFrameScripts()
|
||||
{
|
||||
if (!mBrowserElementAPI) {
|
||||
return;
|
||||
}
|
||||
mBrowserElementAPI->DestroyFrameScripts();
|
||||
}
|
||||
|
||||
void
|
||||
nsBrowserElement::SetVisible(bool aVisible, ErrorResult& aRv)
|
||||
{
|
||||
|
|
|
@ -130,6 +130,7 @@ protected:
|
|||
NS_IMETHOD GetParentApplication(mozIApplication** aApplication) = 0;
|
||||
|
||||
void InitBrowserElementAPI();
|
||||
void DestroyBrowserElementFrameScripts();
|
||||
nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
|
||||
nsTArray<RefPtr<dom::BrowserElementAudioChannel>> mBrowserElementAudioChannels;
|
||||
|
||||
|
|
|
@ -726,3 +726,11 @@ nsGenericHTMLFrameElement::InitializeBrowserAPI()
|
|||
InitBrowserElementAPI();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGenericHTMLFrameElement::DestroyBrowserFrameScripts()
|
||||
{
|
||||
MOZ_ASSERT(mFrameLoader);
|
||||
DestroyBrowserElementFrameScripts();
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -91,8 +91,13 @@ interface nsIMozBrowserFrame : nsIDOMMozBrowserFrame
|
|||
void createRemoteFrameLoader(in nsITabParent aTabParent);
|
||||
|
||||
/**
|
||||
* Initialize the API, and add frame message listener to listen to API
|
||||
* Initialize the API, and add frame message listener that supports API
|
||||
* invocations.
|
||||
*/
|
||||
[noscript] void initializeBrowserAPI();
|
||||
|
||||
/**
|
||||
* Notify frame scripts that support the API to destroy.
|
||||
*/
|
||||
[noscript] void destroyBrowserFrameScripts();
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче