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:
J. Ryan Stinnett 2016-09-08 16:00:12 -05:00
Родитель bcefcd65fe
Коммит b7ca8b1d80
15 изменённых файлов: 396 добавлений и 180 удалений

Просмотреть файл

@ -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();
};