From 139dd41f0fda172c1ef849319e69cc5a3b7cca06 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Wed, 9 Nov 2022 15:10:01 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1203802 - Websocket Frame Listener API for devtool Network Inspector - part 1 - WindowID added into WebSocketChannel, r=michal (80cae04416) - Bug 1203802 - Websocket Frame Listener API for devtool Network Inspector - part 2 - WebSocketFrameService, r=michal (66e1935806) - Bug 1203802 - Websocket Frame Listener API for devtool Network Inspector - part 3 - timestamp, r=michal (1ee78cbab5) - Bug 1203802 - Websocket Frame Listener API for devtool Network Inspector - part 4 - IPC, r=michal (651d908780) - Bug 1215092 - WebSocketEventService and WebSocket discovering - part 1 - Renaming WebSocketFrameService, r=michal (c215f04303) - Bug 1215092 - WebSocketEventService and WebSocket discovering - part 2 - Unique Serial number for WebSocketChannel in IPC, r=michal (9a42b6b898) - Bug 1215092 - WebSocketEventService and WebSocket discovering - part 3 - Events, r=michal (3e22a9b8a9) - Bug 1215092 - WebSocketEventService and WebSocket discovering - part 4 - MessageAvailable event, r=michal (03606add66) - Bug 1185351 - Don't force inline style CSP checks on native anonymous content; r=ckerschb (51deea3648) - Bug 1212477 - Needs a way to access to 's context (2d, webgl) from Anonymous Content API; r=roc;r=smaug (ed3335513d) - Bug 1119692 - Part 1: Get cached jar file fd if it exists instead of always openning it. r=smaug, r=jduell (399a3701dd) - Bug 1119692 - Part 2: Always use scheme jar:remoteopenfile: for out-of-process apps to ease sandboxing code. r=fabrice (d3783120a9) - Bug 1212244 - Same-process sendAsyncMessage can now throw instead of OOM. r=smaug (f22bbeb173) - Bug 1207752 - Increase how long we let incremental GC run before forcing it to finish. r=smaug (ef731501a0) - Bug 1213019. Get UnmapBuffer as part of MapBufferRanges features. r=jgilbert (0e1a1d7ffd) - Bug 941858 - Do not scale down elements passed to setDragImage. r=roc (7c1f18056e) - Bug 1212027 - part 1 - rename ipdl lowering helpers to reflect intent, not function; r=jld (f2c80853a7) - Bug 1212027 - part 2 - add a C++ AST type for 'auto'; r=jld (480120b163) - Bug 1212027 - part 3 - use class interfaces when cloning managees; r=jld (98d8aa9322) - Bug 1212027 - part 4 - use class interfaces when destroying managees; r=jld (2ed9d48242) - Add default initializers for primitive fields in IPDL structs. (bug 1154522, r=billm) (981f0e0d82) - Bug 1158905 - remove dead code from protocol Transition functions; r=bent (e4a43b880a) - Bug 1207921 - Call makeReply before dtorEpilogue so that we don't end up with a nullptr deref r=billm (f7d4530a72) - Bug 1212248 - Align the creation of cpow in ContentBridgeParent to ContentParent. r=khuey (ace596456a) - Bug 1212027 - part 5 - add LoneManagedOrNull for simplifying a lot of upcoming code; r=jld (6aec494580) - Bug 1212027 - part 6 - add an IsEmpty method to nsTHashtable; r=erahm (cabe26f3d6) - bug 1209615 - use TabParent::GetTopLevelDocAccessible() in OuterDocAccessible::RemoteChildDoc() r=davidb (0765f27bb5) - Bug 1214316 - Improve assertions on top level remote accessible doc handling, r=tbsaunde (257c6c8ef6) - Bug 1212027 - part 7 - modify IPDL codegen to store sub-protocols in a hashtable rather than an array; r=jld,nical,cpearce,billm (99dd4fa79c) - Bug 1212027 - followup - qualify PBrowserParent in GonkPermission.cpp; r=me (051e7e1bde) - Bug 1217250 - Fix some IPDL tests that were broken by bug 1212027; r=froydnj (d368b3aa02) - Bug 1202634 - Make sure TabParent LoadContext for pop-ups shares private browsing state of opener. r=billm (b347018487) - Bug 1191740 - Add originAttributes in TabContext. r=bholley (43ea8ca527) - Bug 1191740 - Factor out nsFrameLoader::GetNewTabContext. r=bholley (3311ab7287) - Bug 1180088 - Use origin-based permission check on parent side for signed packaged web app. r=kanru. (01659e2906) - Bug 1205399 - Backend for disabling of notifications for a site from the UI. r=nsm (6380757077) - Bug 1208295 - Dispatch notifications-open-settings upon receiving alertsettingscallback so UI can open notification settings. r=nsm (776ab25c91) - Bug 1212129 - e10s support for disabling site notifications. r=wchen (e896f87940) - Bug 1208622 - Separate API entry points. r=bz (a324f95322) --- accessible/generic/OuterDocAccessible.cpp | 13 +- accessible/generic/RootAccessible.cpp | 6 +- .../loop/modules/MozLoopPushHandler.jsm | 873 ++++++++++++++++++ browser/components/loop/test/xpcshell/head.js | 225 +++++ dom/base/AnonymousContent.cpp | 25 + dom/base/AnonymousContent.h | 4 + dom/base/WebSocket.cpp | 87 +- dom/base/nsFrameLoader.cpp | 126 ++- dom/base/nsFrameLoader.h | 15 +- dom/base/nsFrameMessageManager.cpp | 128 +-- dom/base/nsFrameMessageManager.h | 34 +- dom/base/nsGlobalWindow.cpp | 17 +- dom/base/nsGlobalWindow.h | 15 +- dom/base/nsIFrameLoader.idl | 7 +- dom/base/nsInProcessTabChildGlobal.cpp | 26 +- dom/base/nsInProcessTabChildGlobal.h | 10 +- dom/base/nsJSEnvironment.cpp | 2 +- dom/base/nsStyledElement.cpp | 7 +- dom/base/test/chrome.ini | 1 + dom/base/test/mochitest.ini | 3 + dom/base/test/test_anonymousContent_api.html | 2 +- .../test/test_anonymousContent_canvas.html | 57 ++ .../test/test_anonymousContent_style_csp.html | 28 + ...t_anonymousContent_style_csp.html^headers^ | 1 + .../test/test_frameLoader_switchProcess.html | 2 +- dom/base/test/test_websocket_frame.html | 166 ++++ dom/canvas/CanvasRenderingContext2D.h | 4 + dom/canvas/WebGLContext.cpp | 7 +- dom/events/EventStateManager.cpp | 5 +- dom/indexedDB/ActorsParent.cpp | 8 +- dom/interfaces/base/nsIDOMWindow.idl | 5 +- dom/ipc/AppProcessChecker.cpp | 47 + dom/ipc/ContentBridgeChild.cpp | 4 +- dom/ipc/ContentBridgeParent.cpp | 8 +- dom/ipc/ContentChild.cpp | 14 +- dom/ipc/ContentParent.cpp | 57 +- dom/ipc/ContentParent.h | 12 +- dom/ipc/ContentProcessManager.cpp | 13 +- dom/ipc/CrashReporterChild.cpp | 8 +- dom/ipc/PContent.ipdl | 8 +- dom/ipc/PContentBridge.ipdl | 4 +- dom/ipc/PTabContext.ipdlh | 36 +- dom/ipc/ProcessPriorityManager.cpp | 18 +- dom/ipc/StructuredCloneData.cpp | 3 +- dom/ipc/TabChild.cpp | 13 +- dom/ipc/TabChild.h | 10 +- dom/ipc/TabContext.cpp | 139 ++- dom/ipc/TabContext.h | 79 +- dom/ipc/TabParent.cpp | 50 +- dom/ipc/TabParent.h | 3 +- dom/ipc/moz.build | 5 + dom/ipc/nsIContentParent.cpp | 35 +- dom/media/gmp/GMPContentChild.cpp | 24 +- dom/media/gmp/GMPParent.cpp | 6 +- dom/network/TCPServerSocketParent.cpp | 22 +- dom/network/TCPSocketParent.cpp | 27 +- dom/notification/Notification.cpp | 97 +- dom/notification/Notification.h | 4 +- dom/plugins/ipc/PluginModuleParent.cpp | 2 +- dom/push/PushServiceWebSocket.jsm | 2 +- dom/push/test/xpcshell/head.js | 2 +- dom/simplepush/PushService.jsm | 2 +- dom/webidl/AnonymousContent.webidl | 8 + gfx/gl/GLContext.cpp | 1 + gfx/layers/ipc/CompositorChild.cpp | 8 +- gfx/layers/ipc/CompositorParent.cpp | 2 +- gfx/layers/ipc/LayerTransactionChild.cpp | 7 +- gfx/layers/ipc/LayerTransactionParent.cpp | 5 +- ipc/glue/IPCMessageUtils.h | 17 + ipc/glue/ProtocolUtils.h | 18 + ipc/ipdl/ipdl/builtin.py | 1 + ipc/ipdl/ipdl/cxx/ast.py | 1 + ipc/ipdl/ipdl/lower.py | 140 +-- ipc/ipdl/test/cxx/TestManyChildAllocs.cpp | 2 +- ipc/ipdl/test/cxx/TestMultiMgrs.cpp | 9 +- ipc/ipdl/test/cxx/TestSelfManageRoot.cpp | 18 +- ipc/ipdl/test/cxx/TestShutdown.cpp | 12 +- layout/base/nsIPresShell.h | 29 +- layout/base/nsPresShell.cpp | 16 +- layout/base/nsPresShell.h | 11 +- layout/build/nsLayoutModule.cpp | 9 + netwerk/ipc/NeckoChild.cpp | 21 +- netwerk/ipc/NeckoChild.h | 6 +- netwerk/ipc/NeckoParent.cpp | 24 +- netwerk/ipc/NeckoParent.h | 6 +- netwerk/ipc/PNecko.ipdl | 6 +- netwerk/ipc/RemoteOpenFileChild.cpp | 3 +- netwerk/protocol/app/AppProtocolHandler.cpp | 11 +- netwerk/protocol/http/HttpChannelParent.cpp | 2 +- .../websocket/BaseWebSocketChannel.cpp | 49 + .../protocol/websocket/BaseWebSocketChannel.h | 4 + netwerk/protocol/websocket/PWebSocket.ipdl | 1 + .../websocket/PWebSocketEventListener.ipdl | 51 + .../protocol/websocket/WebSocketChannel.cpp | 144 ++- netwerk/protocol/websocket/WebSocketChannel.h | 38 +- .../websocket/WebSocketChannelChild.cpp | 8 +- .../websocket/WebSocketChannelChild.h | 4 +- .../websocket/WebSocketChannelParent.cpp | 12 +- .../websocket/WebSocketChannelParent.h | 5 +- .../websocket/WebSocketEventListenerChild.cpp | 117 +++ .../websocket/WebSocketEventListenerChild.h | 62 ++ .../WebSocketEventListenerParent.cpp | 128 +++ .../websocket/WebSocketEventListenerParent.h | 43 + .../websocket/WebSocketEventService.cpp | 579 ++++++++++++ .../websocket/WebSocketEventService.h | 113 +++ netwerk/protocol/websocket/WebSocketFrame.cpp | 168 ++++ netwerk/protocol/websocket/WebSocketFrame.h | 79 ++ netwerk/protocol/websocket/moz.build | 10 + .../websocket/nsIWebSocketChannel.idl | 21 +- .../websocket/nsIWebSocketEventService.idl | 79 ++ netwerk/test/unit/test_dns_proxy_bypass.js | 2 +- netwerk/test/unit/test_websocket_offline.js | 2 +- toolkit/components/telemetry/Histograms.json | 8 + toolkit/xre/nsNativeAppSupportWin.cpp | 26 +- widget/gonk/GonkPermission.cpp | 6 +- widget/nsBaseDragService.cpp | 6 +- xpcom/glue/nsTHashtable.h | 5 + 117 files changed, 4118 insertions(+), 708 deletions(-) create mode 100644 browser/components/loop/modules/MozLoopPushHandler.jsm create mode 100644 browser/components/loop/test/xpcshell/head.js create mode 100644 dom/base/test/test_anonymousContent_canvas.html create mode 100644 dom/base/test/test_anonymousContent_style_csp.html create mode 100644 dom/base/test/test_anonymousContent_style_csp.html^headers^ create mode 100644 dom/base/test/test_websocket_frame.html create mode 100644 netwerk/protocol/websocket/PWebSocketEventListener.ipdl create mode 100644 netwerk/protocol/websocket/WebSocketEventListenerChild.cpp create mode 100644 netwerk/protocol/websocket/WebSocketEventListenerChild.h create mode 100644 netwerk/protocol/websocket/WebSocketEventListenerParent.cpp create mode 100644 netwerk/protocol/websocket/WebSocketEventListenerParent.h create mode 100644 netwerk/protocol/websocket/WebSocketEventService.cpp create mode 100644 netwerk/protocol/websocket/WebSocketEventService.h create mode 100644 netwerk/protocol/websocket/WebSocketFrame.cpp create mode 100644 netwerk/protocol/websocket/WebSocketFrame.h create mode 100644 netwerk/protocol/websocket/nsIWebSocketEventService.idl diff --git a/accessible/generic/OuterDocAccessible.cpp b/accessible/generic/OuterDocAccessible.cpp index a780b96a39..daf7b77de8 100644 --- a/accessible/generic/OuterDocAccessible.cpp +++ b/accessible/generic/OuterDocAccessible.cpp @@ -192,16 +192,5 @@ OuterDocAccessible::RemoteChildDoc() const if (!tab) return nullptr; - // XXX Consider managing non top level remote documents with there parent - // document. - const nsTArray& docs = tab->ManagedPDocAccessibleParent(); - size_t docCount = docs.Length(); - for (size_t i = 0; i < docCount; i++) { - auto doc = static_cast(docs[i]); - if (!doc->ParentDoc()) - return doc; - } - - MOZ_ASSERT(false, "no top level tab document?"); - return nullptr; + return tab->GetTopLevelDocAccessible(); } diff --git a/accessible/generic/RootAccessible.cpp b/accessible/generic/RootAccessible.cpp index 92f14b99cf..6f16826ef7 100644 --- a/accessible/generic/RootAccessible.cpp +++ b/accessible/generic/RootAccessible.cpp @@ -39,6 +39,7 @@ #include "nsIWebBrowserChrome.h" #include "nsReadableUtils.h" #include "nsFocusManager.h" +#include "nsGlobalWindow.h" #ifdef MOZ_XUL #include "nsIXULDocument.h" @@ -484,10 +485,9 @@ RootAccessible::RelationByType(RelationType aType) if (!mDocumentNode || aType != RelationType::EMBEDS) return DocAccessibleWrap::RelationByType(aType); - nsIDOMWindow* rootWindow = mDocumentNode->GetWindow(); + nsPIDOMWindow* rootWindow = mDocumentNode->GetWindow(); if (rootWindow) { - nsCOMPtr contentWindow; - rootWindow->GetContent(getter_AddRefs(contentWindow)); + nsCOMPtr contentWindow = nsGlobalWindow::Cast(rootWindow)->GetContent(); if (contentWindow) { nsCOMPtr contentDOMDocument; contentWindow->GetDocument(getter_AddRefs(contentDOMDocument)); diff --git a/browser/components/loop/modules/MozLoopPushHandler.jsm b/browser/components/loop/modules/MozLoopPushHandler.jsm new file mode 100644 index 0000000000..8be93ff80f --- /dev/null +++ b/browser/components/loop/modules/MozLoopPushHandler.jsm @@ -0,0 +1,873 @@ +/* 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"; + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Timer.jsm"); + +const { MozLoopService } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {}); +const consoleLog = MozLoopService.log; + +this.EXPORTED_SYMBOLS = ["MozLoopPushHandler"]; + +const CONNECTION_STATE_CLOSED = 0; +const CONNECTION_STATE_CONNECTING = 1; +const CONNECTION_STATE_OPEN = 2; + +const SERVICE_STATE_OFFLINE = 0; +const SERVICE_STATE_PENDING = 1; +const SERVICE_STATE_ACTIVE = 2; + +function PushSocket(webSocket = null) { + this._websocket = webSocket; +} + +PushSocket.prototype = { + + /** + * Open push-notification websocket. + * + * @param {String} pushUri + * @param {Function} onMsg(aMsg) callback receives any incoming messages + * aMsg is constructed from the json payload; both + * text and binary message reception are mapped to this + * callback. + * @param {Function} onStart called when the socket is connected + * @param {Function} onClose(aCode, aReason) called when the socket closes; + * both near and far side close events map to this + * callback. + * aCode is any status code returned on close + * aReason is any string returned on close + */ + + connect: function(pushUri, onMsg, onStart, onClose) { + if (!pushUri || !onMsg || !onStart || !onClose) { + throw new Error("PushSocket: missing required parameter(s):" + + (pushUri ? "" : " pushUri") + + (onMsg ? "" : " onMsg") + + (onStart ? "" : " onStart") + + (onClose ? "" : " onClose")); + } + + this._onMsg = onMsg; + this._onStart = onStart; + this._onClose = onClose; + + if (!this._websocket) { + this._websocket = Cc["@mozilla.org/network/protocol;1?name=wss"] + .createInstance(Ci.nsIWebSocketChannel); + this._websocket.initLoadInfo(null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_WEBSOCKET); + } + + let uri = Services.io.newURI(pushUri, null, null); + this._websocket.protocol = "push-notification"; + this._websocket.asyncOpen(uri, pushUri, 0, this, null); + }, + + /** + * nsIWebSocketListener method, handles the start of the websocket stream. + * + * @param {nsISupports} aContext Not used + */ + onStart: function() { + this._socketOpen = true; + this._onStart(); + }, + + /** + * nsIWebSocketListener method, called when the websocket is closed locally. + * + * @param {nsISupports} aContext Not used + * @param {nsresult} aStatusCode + */ + onStop: function(aContext, aStatusCode) { + this._socketOpen = false; + this._onClose(aStatusCode, "websocket onStop"); + }, + + /** + * nsIWebSocketListener method, called when the websocket is closed + * by the far end. + * + * @param {nsISupports} aContext Not used + * @param {integer} aCode the websocket closing handshake close code + * @param {String} aReason the websocket closing handshake close reason + */ + onServerClose: function(aContext, aCode, aReason) { + this._socketOpen = false; + this._onClose(aCode, aReason); + }, + + /** + * nsIWebSocketListener method, called when the websocket receives + * a text message (normally json encoded). + * + * @param {nsISupports} aContext Not used + * @param {String} aMsg The message data + */ + onMessageAvailable: function(aContext, aMsg) { + consoleLog.log("PushSocket: Message received: ", aMsg); + if (!this._socketOpen) { + consoleLog.error("Message received in Winsocket closed state"); + return; + } + + try { + this._onMsg(JSON.parse(aMsg)); + } + catch (error) { + consoleLog.error("PushSocket: error parsing message payload - ", error); + } + }, + + /** + * nsIWebSocketListener method, called when the websocket receives a binary message. + * This class assumes that it is connected to a SimplePushServer and therefore treats + * the message payload as json encoded. + * + * @param {nsISupports} aContext Not used + * @param {String} aMsg The message data + */ + onBinaryMessageAvailable: function(aContext, aMsg) { + consoleLog.log("PushSocket: Binary message received: ", aMsg); + if (!this._socketOpen) { + consoleLog.error("PushSocket: message receive in Winsocket closed state"); + return; + } + + try { + this._onMsg(JSON.parse(aMsg)); + } + catch (error) { + consoleLog.error("PushSocket: error parsing message payload - ", error); + } + }, + + /** + * Create a JSON encoded message payload and send via websocket. + * + * @param {Object} aMsg Message to send. + * + * @returns {Boolean} true if message has been sent, false otherwise + */ + send: function(aMsg) { + if (!this._socketOpen) { + consoleLog.error("PushSocket: attempt to send before websocket is open"); + return false; + } + + let msg; + try { + msg = JSON.stringify(aMsg); + } + catch (error) { + consoleLog.error("PushSocket: JSON generation error - ", error); + return false; + } + + try { + this._websocket.sendMsg(msg); + consoleLog.log("PushSocket: Message sent: ", msg); + } + // guard against the case that the websocket has closed before this call. + catch (e) { + consoleLog.warn("PushSocket: websocket send error", e); + return false; + } + + return true; + }, + + /** + * Close the websocket. + */ + close: function() { + if (!this._socketOpen) { + return; + } + + this._socketOpen = false; + consoleLog.info("PushSocket: websocket closing"); + + // Do not pass through any callbacks after this point. + this._onStart = function() {}; + this._onMsg = this._onStart; + this._onClose = this._onStart; + + try { + this._websocket.close(this._websocket.CLOSE_NORMAL); + } + catch (e) { + // Do nothing + } + } +}; + + +/** + * Create a RetryManager object. Class to handle retrying a UserAgent + * to PushServer request following a retry back-off scheme managed by + * this class. The current delay mechanism is to double the delay + * each time an operation to be retried until a maximum is met. + * + * @param {Integer} startDelay The initial delay interval in milliseconds. + * @param {Integer} maxDelay Maximum time delay value in milliseconds. + */ +function RetryManager(startDelay, maxDelay) { + if (!startDelay || !maxDelay) { + throw new Error("RetryManager: missing required parameters(s)" + + (startDelay ? "" : " startDelay") + + (maxDelay ? "" : " maxDelay")); + } + + this._startDelay = startDelay; + // The maximum delay cannot be less than the starting delay. + this._maxDelay = maxDelay > startDelay ? maxDelay : startDelay; +} + +RetryManager.prototype = { + /** + * Method to handle retrying a UserAgent to PushServer request. + * + * @param {Function} delayedOp Function to call after current delay is satisfied + */ + retry: function(delayedOp) { + if (!this._timeoutID) { + this._retryDelay = this._startDelay; + } else { + clearTimeout(this._timeoutID); + let nextDelay = this._retryDelay * 2; + this._retryDelay = nextDelay > this._maxDelay ? this._maxDelay : nextDelay; + } + + this._timeoutID = setTimeout(delayedOp, this._retryDelay); + consoleLog.log("PushHandler: retry delay set for ", this._retryDelay); + }, + + /** + * Method used to reset the delay back-off logic and clear any currently + * running delay timeout. + */ + reset: function() { + if (this._timeoutID) { + clearTimeout(this._timeoutID); + this._timeoutID = null; + } + } +}; + +/** + * Create a PingMonitor object. An object instance will periodically execute + * a ping send function and if not reset, will then execute an error function. + * + * @param {Function} pingFunc Function that is called after a ping interval + * has expired without being restart. + * @param {Function} onTimeout Function that is called after a ping timeout + * interval has expired without restart being called. + * @param {Integer} interval Timeout value in milliseconds between successive + * pings or between the last restart call and a ping. + * When this interval expires, pingFunc is called and the + * timeout interval is started. + * @param {Integer} timeout Timeout value in milliseconds between a call to + * pingFunc and a call to onTimeout unless restart is called. + * Restart will begin the ping timeout interval again. + */ +function PingMonitor(pingFunc, onTimeout, interval, timeout) { + if (!pingFunc || !onTimeout || !interval || !timeout) { + throw new Error("PingMonitor: missing required parameters"); + } + this._onTimeout = onTimeout; + this._pingFunc = pingFunc; + this._pingInterval = interval; + this._pingTimeout = timeout; +} + +PingMonitor.prototype = { + /** + * Function to restart the ping timeout and cancel any current timeout operation. + */ + restart: function() { + consoleLog.info("PushHandler: ping timeout restart"); + this.stop(); + this._pingTimerID = setTimeout(() => { this._pingSend(); }, this._pingInterval); + }, + + /** + * Function to stop the PingMonitor. + */ + stop: function() { + if (this._pingTimerID) { + clearTimeout(this._pingTimerID); + this._pingTimerID = undefined; + } + }, + + _pingSend: function() { + consoleLog.info("PushHandler: ping sent"); + this._pingTimerID = setTimeout(this._onTimeout, this._pingTimeout); + this._pingFunc(); + } +}; + + +/** + * We don't have push notifications on desktop currently, so this is a + * workaround to get them going for us. + */ +var MozLoopPushHandler = { + // This is the uri of the push server. + pushServerUri: undefined, + // Records containing the registration and notification callbacks indexed by channelID. + // Each channel will be registered with the PushServer. + channels: new Map(), + // This is the UserAgent UUID assigned by the PushServer + uaID: undefined, + // Each successfully registered channelID is used as a key to hold its pushEndpoint URL. + registeredChannels: {}, + // Push protocol state variable + serviceState: SERVICE_STATE_OFFLINE, + // Websocket connection state variable + connectionState: CONNECTION_STATE_CLOSED, + // Contains channels that need to be registered with the PushServer + _channelsToRegister: [], + + get _startRetryDelay_ms() { + try { + return Services.prefs.getIntPref("loop.retry_delay.start"); + } + catch (e) { + return 60000; // 1 minute + } + }, + + get _maxRetryDelay_ms() { + try { + return Services.prefs.getIntPref("loop.retry_delay.limit"); + } + catch (e) { + return 300000; // 5 minutes + } + }, + + get _pingInterval_ms() { + try { + return Services.prefs.getIntPref("loop.ping.interval"); + } + catch (e) { + return 18000000; // 30 minutes + } + }, + + get _pingTimeout_ms() { + try { + return Services.prefs.getIntPref("loop.ping.timeout"); + } + catch (e) { + return 10000; // 10 seconds + } + }, + + /** + * Inializes the PushHandler and opens a socket with the PushServer. + * It will automatically say hello and register any channels + * that are found in the work queue at that point. + * + * @param {Object} options Set of configuration options. Currently, + * the only option is mocketWebSocket which will be + * used for testing. + */ + initialize: function(options = {}) { + consoleLog.info("PushHandler: initialize options = ", options); + + if (this._initDone) { + return; + } + + this._initDone = true; + this._retryManager = new RetryManager(this._startRetryDelay_ms, + this._maxRetryDelay_ms); + // Send an empty json payload as a ping. + // Close the websocket and re-open if a timeout occurs. + this._pingMonitor = new PingMonitor(() => this._pushSocket.send({}), + () => this._restartConnection(), + this._pingInterval_ms, + this._pingTimeout_ms); + + if ("mockWebSocket" in options) { + this._mockWebSocket = options.mockWebSocket; + } + + this._openSocket(); + }, + + /** + * Reset and clear PushServer connection. + * Returns MozLoopPushHandler to pre-initialized state. + */ + shutdown: function() { + consoleLog.info("PushHandler: shutdown"); + if (!this._initDone) { + return; + } + + this._initDone = false; + this._retryManager.reset(); + this._pingMonitor.stop(); + + // Un-register each active notification channel + if (this.connectionState === CONNECTION_STATE_OPEN) { + Object.keys(this.registeredChannels).forEach((id) => { + let unRegMsg = { messageType: "unregister", + channelID: id }; + this._pushSocket.send(unRegMsg); + }); + this.registeredChannels = {}; + } + + this.connectionState = CONNECTION_STATE_CLOSED; + this.serviceState = SERVICE_STATE_OFFLINE; + this._pushSocket.close(); + this._pushSocket = undefined; + // NOTE: this PushSocket instance will not be released until at least + // the websocket referencing it as an nsIWebSocketListener is released. + this.channels.clear(); + this.uaID = undefined; + this.pushUrl = undefined; + this.pushServerUri = undefined; + }, + + /** + * Assign a channel to be registered with the PushServer + * This channel will be registered when a connection to the PushServer + * has been established or re-registered after a connection has been lost + * and re-established. Calling this more than once for the same channel + * has no additional effect. + * + * onRegistered callback parameters: + * - {String|null} err: Encountered error, if any + * - {String} url: The push url obtained from the server + * - {String} channelID The channelID on which the notification was sent. + * + * onNotification parameters: + * - {String} version The version string received from the push server for + * the notification. + * - {String} channelID The channelID on which the notification was sent. + * + * @param {String} channelID Channel ID to use in registration. + * + * @param {Function} onRegistered Callback to be called once we are + * registered. + * NOTE: This function can be called multiple times if + * the PushServer generates new pushURLs due to + * re-registration due to network loss or PushServer + * initiated re-assignment. + * @param {Function} onNotification Callback to be called when a + * push notification is received (may be called multiple + * times). + */ + register: function(channelID, onRegistered, onNotification) { + if (!channelID || !onRegistered || !onNotification) { + throw new Error("missing required parameter(s):" + + (channelID ? "" : " channelID") + + (onRegistered ? "" : " onRegistered") + + (onNotification ? "" : " onNotification")); + } + + consoleLog.info("PushHandler: channel registration: ", channelID); + if (this.channels.has(channelID)) { + // If this channel has an active registration with the PushServer + // call the onRegister callback with the URL. + if (this.registeredChannels[channelID]) { + onRegistered(null, this.registeredChannels[channelID], channelID); + } + // Update the channel record. + this.channels.set(channelID, { onRegistered: onRegistered, + onNotification: onNotification }); + return; + } + + this.channels.set(channelID, { onRegistered: onRegistered, + onNotification: onNotification }); + this._channelsToRegister.push(channelID); + this._registerChannels(); + }, + + /** + * Un-register a notification channel. + * + * @param {String} channelID Notification channel ID. + */ + unregister: function(channelID) { + consoleLog.info("MozLoopPushHandler: un-register channel ", channelID); + if (!this.channels.has(channelID)) { + return; + } + + this.channels.delete(channelID); + + if (this.registeredChannels[channelID]) { + delete this.registeredChannels[channelID]; + if (this.connectionState === CONNECTION_STATE_OPEN) { + this._pushSocket.send({ messageType: "unregister", + channelID: channelID }); + } + } + }, + + /** + * Handles the start of the websocket stream. + * Sends a hello message to the server. + * + */ + _onStart: function() { + consoleLog.info("PushHandler: websocket open, sending 'hello' to PushServer"); + this.connectionState = CONNECTION_STATE_OPEN; + // If a uaID has already been assigned, assume this is a re-connect; + // send the uaID and channelIDs in order to re-synch with the + // PushServer. The PushServer does not need to accept the existing channelIDs + // and may issue new channelIDs along with new pushURLs. + this.serviceState = SERVICE_STATE_PENDING; + let helloMsg = { + messageType: "hello", + uaid: this.uaID || "", + channelIDs: this.uaID ? Object.keys(this.registeredChannels) : [] + }; + // The Simple PushServer spec does not allow a retry of the Hello handshake but requires that the socket + // be closed and another socket openned in order to re-attempt the handshake. + // Here, the retryManager is not set up to retry the sending another 'hello' message: the timeout will + // trigger closing the websocket and starting the connection again from the start. + this._retryManager.reset(); + this._retryManager.retry(() => this._restartConnection()); + this._pushSocket.send(helloMsg); + }, + + /** + * Handles websocket close callbacks. + * + * This method will continually try to re-establish a connection + * to the PushServer unless shutdown has been called. + */ + _onClose: function(aCode, aReason) { + this._pingMonitor.stop(); + + switch (this.connectionState) { + case CONNECTION_STATE_OPEN: + this.connectionState = CONNECTION_STATE_CLOSED; + consoleLog.info("PushHandler: websocket closed: begin reconnect - ", aCode); + // The first retry is immediate + this._retryManager.reset(); + this._openSocket(); + break; + + case CONNECTION_STATE_CONNECTING: + // Wait before re-attempting to open the websocket. + consoleLog.info("PushHandler: websocket closed: delay and retry - ", aCode); + this._retryManager.retry(() => this._openSocket()); + break; + } + }, + + /** + * Listener method, called when the websocket receives a message. + * + * @param {Object} aMsg The message data + */ + _onMsg: function(aMsg) { + // If an error property exists in the message object ignore the other + // properties. + if (aMsg.error) { + consoleLog.error("PushHandler: received error response msg: ", aMsg.error); + return; + } + + // The recommended response to a ping message when the push server has nothing + // else to send is a blank JSON message body: {} + if (!aMsg.messageType && this.serviceState === SERVICE_STATE_ACTIVE) { + // Treat this as a ping response + this._pingMonitor.restart(); + return; + } + + switch (aMsg.messageType) { + case "hello": + this._onHello(aMsg); + break; + + case "register": + this._onRegister(aMsg); + break; + + case "notification": + this._onNotification(aMsg); + break; + + default: + consoleLog.warn("PushHandler: unknown message type = ", aMsg.messageType); + if (this.serviceState === SERVICE_STATE_ACTIVE) { + // Treat this as a ping response + this._pingMonitor.restart(); + } + break; + } + }, + + /** + * Handles hello message. + * + * This method will parse the hello response from the PushServer + * and determine whether registration is necessary. + * + * @param {aMsg} hello message body + */ + _onHello: function(aMsg) { + if (this.serviceState !== SERVICE_STATE_PENDING) { + consoleLog.error("PushHandler: extra 'hello' response received from PushServer"); + return; + } + + // Clear any pending timeout that will restart the connection. + this._retryManager.reset(); + this.serviceState = SERVICE_STATE_ACTIVE; + consoleLog.info("PushHandler: 'hello' handshake complete"); + // Start the PushServer ping monitor + this._pingMonitor.restart(); + // If a new uaID is received, then any previous channel registrations + // are no longer valid and a Registration request is generated. + if (this.uaID !== aMsg.uaid) { + consoleLog.log("PushHandler: registering all channels"); + this.uaID = aMsg.uaid; + // Re-register all channels. + this._channelsToRegister = [...this.channels.keys()]; + this.registeredChannels = {}; + } + // Allow queued registrations to start (or all if cleared above). + this._registerChannels(); + }, + + /** + * Handles notification message. + * + * This method will parse the Array of updates and trigger + * the callback of any registered channel. + * This method will construct an ack message containing + * a set of channel version update notifications. + * + * @param {aMsg} notification message body + */ + _onNotification: function(aMsg) { + if (this.serviceState !== SERVICE_STATE_ACTIVE || + this.registeredChannels.length === 0) { + // Treat reception of a notification before handshake and registration + // are complete as a fatal error. + consoleLog.error("PushHandler: protocol error - notification received in wrong state"); + this._restartConnection(); + return; + } + + this._pingMonitor.restart(); + if (Array.isArray(aMsg.updates) && aMsg.updates.length > 0) { + let ackChannels = []; + aMsg.updates.forEach(update => { + if (update.channelID in this.registeredChannels) { + consoleLog.log("PushHandler: notification: version = ", update.version, + ", channelID = ", update.channelID); + this.channels.get(update.channelID) + .onNotification(update.version, update.channelID); + ackChannels.push(update); + } else { + consoleLog.error("PushHandler: notification received for unknown channelID: ", + update.channelID); + } + }); + + consoleLog.log("PushHandler: PusherServer 'ack': ", ackChannels); + this._pushSocket.send({ messageType: "ack", + updates: ackChannels }); + } + }, + + /** + * Handles the PushServer registration response. + * + * @param {Object} msg PushServer to UserAgent registration response (parsed from JSON). + */ + _onRegister: function(msg) { + if (this.serviceState !== SERVICE_STATE_ACTIVE || + msg.channelID != this._pendingChannelID) { + // Treat reception of a register response outside of a completed handshake + // or for a channelID not currently pending a response + // as an indication that the connections should be reset. + consoleLog.error("PushHandler: registration protocol error"); + this._restartConnection(); + return; + } + + this._retryManager.reset(); + this._pingMonitor.restart(); + + switch (msg.status) { + case 200: + consoleLog.info("PushHandler: channel registered: ", msg.channelID); + this.registeredChannels[msg.channelID] = msg.pushEndpoint; + this.channels.get(msg.channelID) + .onRegistered(null, msg.pushEndpoint, msg.channelID); + this._registerNext(); + break; + + case 500: + consoleLog.info("PushHandler: eeceived a 500 retry response from the PushServer: ", + msg.channelID); + // retry the registration request after a suitable delay + this._retryManager.retry(() => this._sendRegistration(msg.channelID)); + break; + + case 409: + consoleLog.error("PushHandler: received a 409 response from the PushServer: ", + msg.channelID); + this.channels.get(this._pendingChannelID).onRegistered("409"); + // Remove this channel from the channel list. + this.channels.delete(this._pendingChannelID); + this._registerNext(); + break; + + default: + consoleLog.error("PushHandler: received error ", msg.status, + " from the PushServer: ", msg.channelID); + this.channels.get(this._pendingChannelID).onRegistered(msg.status); + this.channels.delete(this._pendingChannelID); + this._registerNext(); + break; + } + }, + + /** + * Attempts to open a websocket. + * + * A new websocket interface is used each time. If an onStop callback + * was received, calling asyncOpen() on the same interface will + * trigger an "already open socket" exception even though the channel + * is logically closed. + */ + _openSocket: function() { + this.connectionState = CONNECTION_STATE_CONNECTING; + // For tests, use the mock instance. + this._pushSocket = new PushSocket(this._mockWebSocket); + + let performOpen = () => { + consoleLog.info("PushHandler: attempt to open websocket to PushServer: ", this.pushServerUri); + this._pushSocket.connect(this.pushServerUri, + (aMsg) => this._onMsg(aMsg), + () => this._onStart(), + (aCode, aReason) => this._onClose(aCode, aReason)); + }; + + let pushServerURLFetchError = () => { + consoleLog.warn("PushHandler: Could not retrieve push server URL from Loop server, will retry"); + this._pushSocket = undefined; + this._retryManager.retry(() => this._openSocket()); + return; + }; + + try { + this.pushServerUri = Services.prefs.getCharPref("loop.debug.pushserver"); + } + catch (e) { + // Do nothing + } + + if (!this.pushServerUri) { + // Get push server to use from the Loop server + let pushUrlEndpoint = Services.prefs.getCharPref("loop.server") + "/push-server-config"; + let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance( + Ci.nsIXMLHttpRequest); + req.open("GET", pushUrlEndpoint); + req.onload = () => { + if (req.status >= 200 && req.status < 300) { + let pushServerConfig; + try { + pushServerConfig = JSON.parse(req.responseText); + } catch (e) { + consoleLog.warn("PushHandler: Error parsing JSON response for push server URL"); + pushServerURLFetchError(); + } + if (pushServerConfig.pushServerURI) { + this._retryManager.reset(); + this.pushServerUri = pushServerConfig.pushServerURI; + performOpen(); + } else { + consoleLog.warn("PushHandler: push server URL config lacks pushServerURI parameter"); + pushServerURLFetchError(); + } + } else { + consoleLog.warn("PushHandler: push server URL retrieve error: " + req.status); + pushServerURLFetchError(); + } + }; + req.onerror = pushServerURLFetchError; + req.send(); + } else { + // this.pushServerUri already set -- just open the channel + performOpen(); + } + }, + + /** + * Closes websocket and begins re-establishing a connection with the PushServer + */ + _restartConnection: function() { + this._retryManager.reset(); + this._pingMonitor.stop(); + this.serviceState = SERVICE_STATE_OFFLINE; + this._pendingChannelID = null; + + if (this.connectionState === CONNECTION_STATE_OPEN) { + // Close the current PushSocket and start the operation to open a new one. + this.connectionState = CONNECTION_STATE_CLOSED; + this._pushSocket.close(); + consoleLog.warn("PushHandler: connection error: re-establishing connection to PushServer"); + this._openSocket(); + } + }, + + /** + * Begins registering the channelIDs with the PushServer + */ + _registerChannels: function() { + // Hold off registration operation until handshake is complete. + // If a registration cycle is in progress, do nothing. + if (this.serviceState !== SERVICE_STATE_ACTIVE || + this._pendingChannelID) { + return; + } + this._registerNext(); + }, + + /** + * Gets the next channel to register from the worklist and kicks of its registration + */ + _registerNext: function() { + this._pendingChannelID = this._channelsToRegister.pop(); + this._sendRegistration(this._pendingChannelID); + }, + + /** + * Handles registering a service + * + * @param {string} channelID - identification token to use in registration for this channel. + */ + _sendRegistration: function(channelID) { + if (channelID) { + this._pushSocket.send({ messageType: "register", + channelID: channelID }); + } + } +}; diff --git a/browser/components/loop/test/xpcshell/head.js b/browser/components/loop/test/xpcshell/head.js new file mode 100644 index 0000000000..820510ce0d --- /dev/null +++ b/browser/components/loop/test/xpcshell/head.js @@ -0,0 +1,225 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +// Initialize this before the imports, as some of them need it. +do_get_profile(); + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Http.jsm"); +Cu.import("resource://testing-common/httpd.js"); +Cu.import("resource:///modules/loop/MozLoopService.jsm"); +Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource:///modules/loop/LoopRooms.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); +const { MozLoopServiceInternal } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {}); +const { LoopRoomsInternal, timerHandlers } = Cu.import("resource:///modules/loop/LoopRooms.jsm", {}); + +XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler", + "resource:///modules/loop/MozLoopPushHandler.jsm"); + +const kMockWebSocketChannelName = "Mock WebSocket Channel"; +const kWebSocketChannelContractID = "@mozilla.org/network/protocol;1?name=wss"; + +const kServerPushUrl = "ws://localhost"; +const kLoopServerUrl = "http://localhost:3465"; +const kEndPointUrl = "http://example.com/fake"; +const kUAID = "f47ac11b-58ca-4372-9567-0e02b2c3d479"; + +// Fake loop server +var loopServer; + +// Ensure loop is always enabled for tests +Services.prefs.setBoolPref("loop.enabled", true); + +// Cleanup function for all tests +do_register_cleanup(() => { + Services.prefs.clearUserPref("loop.enabled"); + MozLoopService.errors.clear(); +}); + +function setupFakeLoopServer() { + loopServer = new HttpServer(); + loopServer.start(-1); + + Services.prefs.setCharPref("loop.server", + "http://localhost:" + loopServer.identity.primaryPort); + + MozLoopServiceInternal.mocks.pushHandler = mockPushHandler; + + do_register_cleanup(function() { + loopServer.stop(function() {}); + MozLoopServiceInternal.mocks.pushHandler = undefined; + }); +} + +/** + * Sets up the userProfile to make the service think we're logged into FxA. + */ +function setupFakeFxAUserProfile() { + MozLoopServiceInternal.fxAOAuthTokenData = { token_type: "bearer" }; + MozLoopServiceInternal.fxAOAuthProfile = { email: "fake@invalid.com" }; + + do_register_cleanup(function() { + MozLoopServiceInternal.fxAOAuthTokenData = null; + MozLoopServiceInternal.fxAOAuthProfile = null; + }); +} + +function waitForCondition(aConditionFn, aMaxTries = 50, aCheckInterval = 100) { + function tryAgain() { + function tryNow() { + tries++; + if (aConditionFn()) { + deferred.resolve(); + } else if (tries < aMaxTries) { + tryAgain(); + } else { + deferred.reject("Condition timed out: " + aConditionFn.toSource()); + } + } + do_timeout(aCheckInterval, tryNow); + } + let deferred = Promise.defer(); + let tries = 0; + tryAgain(); + return deferred.promise; +} + +function getLoopString(stringID) { + return MozLoopServiceInternal.localizedStrings.get(stringID); +} + +/** + * This is used to fake push registration and notifications for + * MozLoopService tests. There is only one object created per test instance, as + * once registration has taken place, the object cannot currently be changed. + */ +var mockPushHandler = { + // This sets the registration result to be returned when initialize + // is called. By default, it is equivalent to success. + registrationResult: null, + registrationPushURL: null, + notificationCallback: {}, + registeredChannels: {}, + + /** + * MozLoopPushHandler API + */ + initialize: function(options = {}) { + if ("mockWebSocket" in options) { + this._mockWebSocket = options.mockWebSocket; + } + }, + + register: function(channelId, registerCallback, notificationCallback) { + this.notificationCallback[channelId] = notificationCallback; + this.registeredChannels[channelId] = this.registrationPushURL; + registerCallback(this.registrationResult, this.registrationPushURL, channelId); + }, + + unregister: function(channelID) { + return; + }, + + /** + * Test-only API to simplify notifying a push notification result. + */ + notify: function(version, chanId) { + this.notificationCallback[chanId](version, chanId); + } +}; + +/** + * Mock nsIWebSocketChannel for tests. This mocks the WebSocketChannel, and + * enables us to check parameters and return messages similar to the push + * server. + */ +function MockWebSocketChannel() {} + +MockWebSocketChannel.prototype = { + QueryInterface: XPCOMUtils.generateQI(Ci.nsIWebSocketChannel), + + initRegStatus: 0, + + defaultMsgHandler: function(msg) { + // Treat as a ping + this.listener.onMessageAvailable(this.context, + JSON.stringify({})); + return; + }, + + /** + * nsIWebSocketChannel implementations. + * See nsIWebSocketChannel.idl for API details. + */ + asyncOpen: function(aURI, aOrigin, aWindowId, aListener, aContext) { + this.uri = aURI; + this.origin = aOrigin; + this.listener = aListener; + this.context = aContext; + this.windowId = aWindowId; + + this.listener.onStart(this.context); + }, + + sendMsg: function(aMsg) { + var message = JSON.parse(aMsg); + + switch (message.messageType) { + case "hello": + this.listener.onMessageAvailable(this.context, + JSON.stringify({ messageType: "hello", + uaid: kUAID })); + break; + case "register": + this.channelID = message.channelID; + let statusCode = 200; + if (this.initRegStatus) { + statusCode = this.initRegStatus; + this.initRegStatus = 0; + } + this.listener.onMessageAvailable(this.context, + JSON.stringify({ messageType: "register", + status: statusCode, + channelID: this.channelID, + pushEndpoint: kEndPointUrl })); + break; + default: + this.defaultMsgHandler && this.defaultMsgHandler(message); + } + }, + + close: function(aCode, aReason) { + this.stop(aCode); + }, + + notify: function(version) { + this.listener.onMessageAvailable(this.context, + JSON.stringify({ + messageType: "notification", updates: [{ + channelID: this.channelID, + version: version + }] + })); + }, + + stop: function(err) { + this.listener.onStop(this.context, err || -1); + }, + + serverClose: function(err) { + this.listener.onServerClose(this.context, err || -1); + } +}; + +const extend = function(target, source) { + for (let key of Object.getOwnPropertyNames(source)) { + target[key] = source[key]; + } + return target; +}; diff --git a/dom/base/AnonymousContent.cpp b/dom/base/AnonymousContent.cpp index 6ef54a8096..90aeab6bca 100644 --- a/dom/base/AnonymousContent.cpp +++ b/dom/base/AnonymousContent.cpp @@ -11,6 +11,7 @@ #include "nsIDocument.h" #include "nsIDOMHTMLCollection.h" #include "nsStyledElement.h" +#include "HTMLCanvasElement.h" namespace mozilla { namespace dom { @@ -112,6 +113,30 @@ AnonymousContent::RemoveAttributeForElement(const nsAString& aElementId, element->RemoveAttribute(aName, aRv); } +already_AddRefed +AnonymousContent::GetCanvasContext(const nsAString& aElementId, + const nsAString& aContextId, + ErrorResult& aRv) +{ + Element* element = GetElementById(aElementId); + + if (!element) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return nullptr; + } + + if (!element->IsHTMLElement(nsGkAtoms::canvas)) { + return nullptr; + } + + nsCOMPtr context; + + HTMLCanvasElement* canvas = static_cast(element); + canvas->GetContext(aContextId, getter_AddRefs(context)); + + return context.forget(); +} + Element* AnonymousContent::GetElementById(const nsAString& aElementId) { diff --git a/dom/base/AnonymousContent.h b/dom/base/AnonymousContent.h index 38b50f0424..6d6b1e2571 100644 --- a/dom/base/AnonymousContent.h +++ b/dom/base/AnonymousContent.h @@ -52,6 +52,10 @@ public: const nsAString& aName, ErrorResult& aRv); + already_AddRefed GetCanvasContext(const nsAString& aElementId, + const nsAString& aContextId, + ErrorResult& aRv); + private: ~AnonymousContent(); Element* GetElementById(const nsAString& aElementId); diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index 16950e3ea9..7396fdfee5 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -38,6 +38,7 @@ #include "nsIStringBundle.h" #include "nsIConsoleService.h" #include "mozilla/dom/CloseEvent.h" +#include "mozilla/net/WebSocketEventService.h" #include "nsICryptoHash.h" #include "nsJSUtils.h" #include "nsIScriptError.h" @@ -128,7 +129,8 @@ public: ErrorResult& aRv, bool* aConnectionFailed); - void AsyncOpen(nsIPrincipal* aPrincipal, ErrorResult& aRv); + void AsyncOpen(nsIPrincipal* aPrincipal, uint64_t aInnerWindowID, + ErrorResult& aRv); nsresult ParseURL(const nsAString& aURL); nsresult InitializeConnection(nsIPrincipal* aPrincipal); @@ -241,6 +243,8 @@ public: mozilla::Mutex mMutex; bool mWorkerShuttingDown; + RefPtr mService; + private: ~WebSocketImpl() { @@ -631,11 +635,8 @@ WebSocketImpl::Disconnect() // until the end of the method. RefPtr kungfuDeathGrip = this; - nsCOMPtr mainThread; - if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) || - NS_FAILED(NS_ProxyRelease(mainThread, mChannel))) { - NS_WARNING("Failed to proxy release of channel, leaking instead!"); - } + NS_ReleaseOnMainThread(mChannel); + NS_ReleaseOnMainThread(static_cast(mService.forget().take())); mWebSocket->DontKeepAliveAnyMore(); mWebSocket->mImpl = nullptr; @@ -770,6 +771,11 @@ WebSocketImpl::OnStart(nsISupports* aContext) mWebSocket->SetReadyState(WebSocket::OPEN); + mService->WebSocketOpened(mChannel->Serial(),mInnerWindowID, + mWebSocket->mEffectiveURL, + mWebSocket->mEstablishedProtocol, + mWebSocket->mEstablishedExtensions); + // Let's keep the object alive because the webSocket can be CCed in the // onopen callback. RefPtr webSocket = mWebSocket; @@ -1126,6 +1132,7 @@ protected: virtual bool InitWithWindow(nsPIDOMWindow* aWindow) override { AssertIsOnMainThread(); + MOZ_ASSERT(aWindow); nsIDocument* doc = aWindow->GetExtantDoc(); if (!doc) { @@ -1139,7 +1146,23 @@ protected: return true; } - mImpl->AsyncOpen(principal, mRv); + if (aWindow->IsOuterWindow()) { + aWindow = aWindow->GetCurrentInnerWindow(); + } + + MOZ_ASSERT(aWindow); + + uint64_t windowID = 0; + nsCOMPtr topWindow = aWindow->GetScriptableTop(); + if (topWindow) { + topWindow = topWindow->GetCurrentInnerWindow(); + } + + if (topWindow) { + windowID = topWindow->WindowID(); + } + + mImpl->AsyncOpen(principal, windowID, mRv); return true; } @@ -1148,7 +1171,7 @@ protected: MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aTopLevelWorkerPrivate && !aTopLevelWorkerPrivate->GetWindow()); - mImpl->AsyncOpen(aTopLevelWorkerPrivate->GetPrincipal(), mRv); + mImpl->AsyncOpen(aTopLevelWorkerPrivate->GetPrincipal(), 0, mRv); return true; } @@ -1198,6 +1221,8 @@ WebSocket::Constructor(const GlobalObject& aGlobal, } } + MOZ_ASSERT_IF(ownerWindow, ownerWindow->IsInnerWindow()); + nsTArray protocolArray; for (uint32_t index = 0, len = aProtocols.Length(); index < len; ++index) { @@ -1310,7 +1335,20 @@ WebSocket::Constructor(const GlobalObject& aGlobal, if (NS_IsMainThread()) { MOZ_ASSERT(principal); - webSocket->mImpl->AsyncOpen(principal, aRv); + + nsPIDOMWindow* outerWindow = ownerWindow->GetOuterWindow(); + + uint64_t windowID = 0; + nsCOMPtr topWindow = outerWindow->GetScriptableTop(); + if (topWindow) { + topWindow = topWindow->GetCurrentInnerWindow(); + } + + if (topWindow) { + windowID = topWindow->WindowID(); + } + + webSocket->mImpl->AsyncOpen(principal, windowID, aRv); } else { RefPtr runnable = new AsyncOpenRunnable(webSocket->mImpl, aRv); @@ -1328,6 +1366,12 @@ WebSocket::Constructor(const GlobalObject& aGlobal, return nullptr; } + // Let's inform devtools about this new active WebSocket. + webSocket->mImpl->mService->WebSocketCreated(webSocket->mImpl->mChannel->Serial(), + webSocket->mImpl->mInnerWindowID, + webSocket->mURI, + webSocket->mImpl->mRequestedProtocolList); + cws.Done(); return webSocket.forget(); } @@ -1413,6 +1457,8 @@ WebSocketImpl::Init(JSContext* aCx, AssertIsOnMainThread(); MOZ_ASSERT(aPrincipal); + mService = WebSocketEventService::GetOrCreate(); + // We need to keep the implementation alive in case the init disconnects it // because of some error. RefPtr kungfuDeathGrip = this; @@ -1606,7 +1652,8 @@ WebSocketImpl::Init(JSContext* aCx, } void -WebSocketImpl::AsyncOpen(nsIPrincipal* aPrincipal, ErrorResult& aRv) +WebSocketImpl::AsyncOpen(nsIPrincipal* aPrincipal, uint64_t aInnerWindowID, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread"); @@ -1622,10 +1669,12 @@ WebSocketImpl::AsyncOpen(nsIPrincipal* aPrincipal, ErrorResult& aRv) aRv = NS_NewURI(getter_AddRefs(uri), mURI); MOZ_ASSERT(!aRv.Failed()); - aRv = mChannel->AsyncOpen(uri, asciiOrigin, this, nullptr); + aRv = mChannel->AsyncOpen(uri, asciiOrigin, aInnerWindowID, this, nullptr); if (NS_WARN_IF(aRv.Failed())) { return; } + + mInnerWindowID = aInnerWindowID; } //----------------------------------------------------------------------------- @@ -1805,14 +1854,20 @@ WebSocket::CreateAndDispatchMessageEvent(JSContext* aCx, return NS_OK; } + uint16_t messageType = nsIWebSocketEventListener::TYPE_STRING; + // Create appropriate JS object for message JS::Rooted jsData(aCx); if (aIsBinary) { if (mBinaryType == dom::BinaryType::Blob) { + messageType = nsIWebSocketEventListener::TYPE_BLOB; + nsresult rv = nsContentUtils::CreateBlobBuffer(aCx, GetOwner(), aData, &jsData); NS_ENSURE_SUCCESS(rv, rv); } else if (mBinaryType == dom::BinaryType::Arraybuffer) { + messageType = nsIWebSocketEventListener::TYPE_ARRAYBUFFER; + JS::Rooted arrayBuf(aCx); nsresult rv = nsContentUtils::CreateArrayBuffer(aCx, aData, arrayBuf.address()); @@ -1832,6 +1887,10 @@ WebSocket::CreateAndDispatchMessageEvent(JSContext* aCx, jsData.setString(jsString); } + mImpl->mService->WebSocketMessageAvailable(mImpl->mChannel->Serial(), + mImpl->mInnerWindowID, + aData, messageType); + // create an event that uses the MessageEvent interface, // which does not bubble, is not cancelable, and has no default action @@ -1851,11 +1910,15 @@ WebSocket::CreateAndDispatchMessageEvent(JSContext* aCx, nsresult WebSocket::CreateAndDispatchCloseEvent(bool aWasClean, uint16_t aCode, - const nsAString &aReason) + const nsAString& aReason) { MOZ_ASSERT(mImpl); AssertIsOnTargetThread(); + mImpl->mService->WebSocketClosed(mImpl->mChannel->Serial(), + mImpl->mInnerWindowID, + aWasClean, aCode, aReason); + nsresult rv = CheckInnerWindowCorrectness(); if (NS_FAILED(rv)) { return NS_OK; diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 70c1c6ccb3..21dbaebea1 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -79,6 +79,7 @@ #include "mozilla/plugins/PPluginWidgetParent.h" #include "../plugins/ipc/PluginWidgetParent.h" #include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/BasePrincipal.h" #include "mozilla/GuardObjects.h" #include "mozilla/Preferences.h" #include "mozilla/unused.h" @@ -95,6 +96,8 @@ #include "mozilla/dom/ipc/StructuredCloneData.h" +#include "nsPrincipal.h" + #ifdef MOZ_XUL #include "nsXULPopupManager.h" #endif @@ -275,25 +278,21 @@ nsFrameLoader::LoadURI(nsIURI* aURI) } NS_IMETHODIMP -nsFrameLoader::SwitchProcessAndLoadURI(nsIURI* aURI) +nsFrameLoader::SwitchProcessAndLoadURI(nsIURI* aURI, const nsACString& aPackageId) { nsCOMPtr URIToLoad = aURI; RefPtr tp = nullptr; - MutableTabContext context; - nsCOMPtr ownApp = GetOwnApp(); - nsCOMPtr containingApp = GetContainingApp(); - - bool tabContextUpdated = true; - if (ownApp) { - tabContextUpdated = context.SetTabContextForAppFrame(ownApp, containingApp); - } else if (OwnerIsBrowserFrame()) { - // The |else| above is unnecessary; OwnerIsBrowserFrame() implies !ownApp. - tabContextUpdated = context.SetTabContextForBrowserFrame(containingApp); - } else { - tabContextUpdated = context.SetTabContextForNormalFrame(); + nsCString signedPkgOrigin; + if (!aPackageId.IsEmpty()) { + // Only when aPackageId is not empty would signed package origin + // be meaningful. + nsPrincipal::GetOriginForURI(aURI, signedPkgOrigin); } - NS_ENSURE_STATE(tabContextUpdated); + + MutableTabContext context; + nsresult rv = GetNewTabContext(&context, signedPkgOrigin, aPackageId); + NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr ownerElement = mOwnerContent; tp = ContentParent::CreateBrowserOrApp(context, ownerElement, nullptr); @@ -302,7 +301,7 @@ nsFrameLoader::SwitchProcessAndLoadURI(nsIURI* aURI) } mRemoteBrowserShown = false; - nsresult rv = SwapRemoteBrowser(tp); + rv = SwapRemoteBrowser(tp); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -944,13 +943,13 @@ nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther, mRemoteBrowser->SetBrowserDOMWindow(otherBrowserDOMWindow); // Native plugin windows used by this remote content need to be reparented. - const nsTArray& plugins = - aOther->mRemoteBrowser->ManagedPPluginWidgetParent(); nsPIDOMWindow* newWin = ourDoc->GetWindow(); if (newWin) { RefPtr newParent = ((nsGlobalWindow*)newWin)->GetMainWidget(); - for (uint32_t idx = 0; idx < plugins.Length(); ++idx) { - static_cast(plugins[idx])->SetParent(newParent); + const ManagedContainer& plugins = + aOther->mRemoteBrowser->ManagedPPluginWidgetParent(); + for (auto iter = plugins.ConstIter(); !iter.Done(); iter.Next()) { + static_cast(iter.Get()->GetKey())->SetParent(newParent); } } @@ -2271,19 +2270,8 @@ nsFrameLoader::TryRemoteBrowser() js::ProfileEntry::Category::OTHER); MutableTabContext context; - nsCOMPtr ownApp = GetOwnApp(); - nsCOMPtr containingApp = GetContainingApp(); - - bool rv = true; - if (ownApp) { - rv = context.SetTabContextForAppFrame(ownApp, containingApp); - } else if (OwnerIsBrowserFrame()) { - // The |else| above is unnecessary; OwnerIsBrowserFrame() implies !ownApp. - rv = context.SetTabContextForBrowserFrame(containingApp); - } else { - rv = context.SetTabContextForNormalFrame(); - } - NS_ENSURE_TRUE(rv, false); + nsresult rv = GetNewTabContext(&context); + NS_ENSURE_SUCCESS(rv, false); nsCOMPtr ownerElement = mOwnerContent; mRemoteBrowser = ContentParent::CreateBrowserOrApp(context, ownerElement, openerContentParent); @@ -2455,13 +2443,8 @@ class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase, public nsRunnable { public: - nsAsyncMessageToChild(JSContext* aCx, - nsFrameLoader* aFrameLoader, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) - : nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal) + nsAsyncMessageToChild(JSContext* aCx, JS::Handle aCpows, nsFrameLoader* aFrameLoader) + : nsSameProcessAsyncMessageBase(aCx, aCpows) , mFrameLoader(aFrameLoader) { } @@ -2482,7 +2465,7 @@ public: RefPtr mFrameLoader; }; -bool +nsresult nsFrameLoader::DoSendAsyncMessage(JSContext* aCx, const nsAString& aMessage, StructuredCloneData& aData, @@ -2494,27 +2477,37 @@ nsFrameLoader::DoSendAsyncMessage(JSContext* aCx, ClonedMessageData data; nsIContentParent* cp = tabParent->Manager(); if (!BuildClonedMessageDataForParent(cp, aData, data)) { - return false; + MOZ_CRASH(); + return NS_ERROR_DOM_DATA_CLONE_ERR; } InfallibleTArray cpows; jsipc::CPOWManager* mgr = cp->GetCPOWManager(); if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) { - return false; + return NS_ERROR_UNEXPECTED; + } + if (tabParent->SendAsyncMessage(nsString(aMessage), data, cpows, + IPC::Principal(aPrincipal))) { + return NS_OK; + } else { + return NS_ERROR_UNEXPECTED; } - return tabParent->SendAsyncMessage(nsString(aMessage), data, cpows, - IPC::Principal(aPrincipal)); } if (mChildMessageManager) { - nsCOMPtr ev = new nsAsyncMessageToChild(aCx, this, aMessage, - aData, aCpows, - aPrincipal); - NS_DispatchToCurrentThread(ev); - return true; + RefPtr ev = new nsAsyncMessageToChild(aCx, aCpows, this); + nsresult rv = ev->Init(aCx, aMessage, aData, aPrincipal); + if (NS_FAILED(rv)) { + return rv; + } + rv = NS_DispatchToCurrentThread(ev); + if (NS_FAILED(rv)) { + return rv; + } + return rv; } // We don't have any targets to send our asynchronous message to. - return false; + return NS_ERROR_UNEXPECTED; } bool @@ -3019,3 +3012,36 @@ nsFrameLoader::MaybeUpdatePrimaryTabParent(TabParentChange aChange) } } } + +nsresult +nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext, + const nsACString& aSignedPkgOriginNoSuffix, + const nsACString& aPackageId) +{ + nsCOMPtr ownApp = GetOwnApp(); + nsCOMPtr containingApp = GetContainingApp(); + OriginAttributes attrs = OriginAttributes(); + attrs.mInBrowser = OwnerIsBrowserFrame(); + + // Get the AppId from ownApp + uint32_t appId = nsIScriptSecurityManager::NO_APP_ID; + if (ownApp) { + nsresult rv = ownApp->GetLocalId(&appId); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_STATE(appId != nsIScriptSecurityManager::NO_APP_ID); + } else if (containingApp) { + nsresult rv = containingApp->GetLocalId(&appId); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_STATE(appId != nsIScriptSecurityManager::NO_APP_ID); + } + attrs.mAppId = appId; + + // Populate packageId to signedPkg. + attrs.mSignedPkg = NS_ConvertUTF8toUTF16(aPackageId); + + bool tabContextUpdated = aTabContext->SetTabContext(ownApp, containingApp, + attrs, aSignedPkgOriginNoSuffix); + NS_ENSURE_STATE(tabContextUpdated); + + return NS_OK; +} diff --git a/dom/base/nsFrameLoader.h b/dom/base/nsFrameLoader.h index afd866fdfa..e1d5e82712 100644 --- a/dom/base/nsFrameLoader.h +++ b/dom/base/nsFrameLoader.h @@ -42,6 +42,7 @@ namespace dom { class ContentParent; class PBrowserParent; class TabParent; +class MutableTabContext; } // namespace dom namespace ipc { @@ -89,11 +90,11 @@ public: */ virtual bool DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope) override; - virtual bool DoSendAsyncMessage(JSContext* aCx, - const nsAString& aMessage, - mozilla::dom::ipc::StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) override; + virtual nsresult DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + mozilla::dom::ipc::StructuredCloneData& aData, + JS::Handle aCpows, + nsIPrincipal* aPrincipal) override; virtual bool CheckPermission(const nsAString& aPermission) override; virtual bool CheckManifestURL(const nsAString& aManifestURL) override; virtual bool CheckAppHasPermission(const nsAString& aPermission) override; @@ -315,6 +316,10 @@ private: void InitializeBrowserAPI(); + nsresult GetNewTabContext(mozilla::dom::MutableTabContext* aTabContext, + const nsACString& aSignedPkgNoSuffix = EmptyCString(), + const nsACString& aPackageId = EmptyCString()); + enum TabParentChange { eTabParentRemoved, eTabParentChanged diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index e7a31e78cd..19df14811e 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -31,6 +31,7 @@ #include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/IntentionalCrash.h" #include "mozilla/Preferences.h" +#include "mozilla/Telemetry.h" #include "mozilla/dom/File.h" #include "mozilla/dom/nsIContentParent.h" #include "mozilla/dom/PermissionMessageUtils.h" @@ -809,8 +810,9 @@ nsFrameMessageManager::DispatchAsyncMessageInternal(JSContext* aCx, return NS_ERROR_NOT_INITIALIZED; } - if (!mCallback->DoSendAsyncMessage(aCx, aMessage, aData, aCpows, aPrincipal)) { - return NS_ERROR_FAILURE; + nsresult rv = mCallback->DoSendAsyncMessage(aCx, aMessage, aData, aCpows, aPrincipal); + if (NS_FAILED(rv)) { + return rv; } return NS_OK; } @@ -1893,15 +1895,9 @@ class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase, public nsRunnable { public: - nsAsyncMessageToSameProcessChild(JSContext* aCx, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) - : nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal) - { - } - + nsAsyncMessageToSameProcessChild(JSContext* aCx, JS::Handle aCpows) + : nsSameProcessAsyncMessageBase(aCx, aCpows) + { } NS_IMETHOD Run() { nsFrameMessageManager* ppm = nsFrameMessageManager::GetChildProcessManager(); @@ -1935,17 +1931,24 @@ public: return true; } - virtual bool DoSendAsyncMessage(JSContext* aCx, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) override + virtual nsresult DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle aCpows, + nsIPrincipal* aPrincipal) override { - nsCOMPtr ev = - new nsAsyncMessageToSameProcessChild(aCx, aMessage, aData, aCpows, - aPrincipal); - NS_DispatchToCurrentThread(ev); - return true; + RefPtr ev = + new nsAsyncMessageToSameProcessChild(aCx, aCpows); + + nsresult rv = ev->Init(aCx, aMessage, aData, aPrincipal); + if (NS_FAILED(rv)) { + return rv; + } + rv = NS_DispatchToCurrentThread(ev); + if (NS_FAILED(rv)) { + return rv; + } + return NS_OK; } bool CheckPermission(const nsAString& aPermission) override @@ -2018,29 +2021,32 @@ public: IPC::Principal(aPrincipal), aRetVal); } - virtual bool DoSendAsyncMessage(JSContext* aCx, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) override + virtual nsresult DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle aCpows, + nsIPrincipal* aPrincipal) override { mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); if (!cc) { - return true; + return NS_OK; } ClonedMessageData data; if (!BuildClonedMessageDataForChild(cc, aData, data)) { - return false; + return NS_ERROR_DOM_DATA_CLONE_ERR; } InfallibleTArray cpows; if (aCpows && !cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { - return false; + return NS_ERROR_UNEXPECTED; + } + if (!cc->SendAsyncMessage(PromiseFlatString(aMessage), data, cpows, + IPC::Principal(aPrincipal))) { + return NS_ERROR_UNEXPECTED; } - return cc->SendAsyncMessage(PromiseFlatString(aMessage), data, cpows, - IPC::Principal(aPrincipal)); - } + return NS_OK; + } }; @@ -2048,15 +2054,9 @@ class nsAsyncMessageToSameProcessParent : public nsSameProcessAsyncMessageBase, public SameProcessMessageQueue::Runnable { public: - nsAsyncMessageToSameProcessParent(JSContext* aCx, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) - : nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal) - { - } - + nsAsyncMessageToSameProcessParent(JSContext* aCx, JS::Handle aCpows) + : nsSameProcessAsyncMessageBase(aCx, aCpows) + { } virtual nsresult HandleMessage() override { nsFrameMessageManager* ppm = nsFrameMessageManager::sSameProcessParentManager; @@ -2100,7 +2100,7 @@ public: return true; } - virtual bool DoSendAsyncMessage(JSContext* aCx, + virtual nsresult DoSendAsyncMessage(JSContext* aCx, const nsAString& aMessage, StructuredCloneData& aData, JS::Handle aCpows, @@ -2108,9 +2108,14 @@ public: { SameProcessMessageQueue* queue = SameProcessMessageQueue::Get(); RefPtr ev = - new nsAsyncMessageToSameProcessParent(aCx, aMessage, aData, aCpows, aPrincipal); + new nsAsyncMessageToSameProcessParent(aCx, aCpows); + nsresult rv = ev->Init(aCx, aMessage, aData, aPrincipal); + + if (NS_FAILED(rv)) { + return rv; + } queue->Push(ev); - return true; + return NS_OK; } }; @@ -2206,23 +2211,28 @@ nsFrameMessageManager::MarkForCC() return true; } -nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase(JSContext* aCx, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) - : mRuntime(js::GetRuntime(aCx)), - mMessage(aMessage), - mCpows(aCx, aCpows), - mPrincipal(aPrincipal) +nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase(JSContext* aCx, JS::Handle aCpows) + : mRuntime(nullptr) + , mCpows(aCx, aCpows) +{ } + + +nsresult +nsSameProcessAsyncMessageBase::Init(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + nsIPrincipal* aPrincipal) { if (!mData.Copy(aData)) { -#ifdef MOZ_CRASHREPORTER - CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AsyncMessageOOM"), - NS_ConvertUTF16toUTF8(aMessage)); -#endif - NS_ABORT_OOM(aData.DataLength()); + Telemetry::Accumulate(Telemetry::IPC_SAME_PROCESS_MESSAGE_COPY_OOM_KB, aData.DataLength()); + return NS_ERROR_OUT_OF_MEMORY; } + + mRuntime = js::GetRuntime(aCx); + mMessage = aMessage; + mPrincipal = aPrincipal; + + return NS_OK; } void @@ -2230,6 +2240,8 @@ nsSameProcessAsyncMessageBase::ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, nsFrameMessageManager* aManager) { + // Make sure that we have called Init() and it has succeeded. + MOZ_ASSERT(mRuntime); if (aManager) { SameProcessCpowHolder cpows(mRuntime, mCpows); diff --git a/dom/base/nsFrameMessageManager.h b/dom/base/nsFrameMessageManager.h index 00d06cbe28..b0215ef32c 100644 --- a/dom/base/nsFrameMessageManager.h +++ b/dom/base/nsFrameMessageManager.h @@ -73,13 +73,13 @@ public: return true; } - virtual bool DoSendAsyncMessage(JSContext* aCx, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) + virtual nsresult DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle aCpows, + nsIPrincipal* aPrincipal) { - return true; + return NS_OK; } virtual bool CheckPermission(const nsAString& aPermission) @@ -317,28 +317,32 @@ private: class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public nsRunnable { - // Initialize nsSameProcessAsyncMessageBase... - NS_IMETHOD Run() { ReceiveMessage(..., ...); return NS_OK; } }; - */ + + + RefPtr ev = new MyAsyncMessage(); + nsresult rv = ev->Init(...); + if (NS_SUCCEEDED(rv)) { + NS_DispatchToMainThread(ev); + } +*/ class nsSameProcessAsyncMessageBase { public: typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData; - nsSameProcessAsyncMessageBase(JSContext* aCx, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal); + nsSameProcessAsyncMessageBase(JSContext* aCx, JS::Handle aCpows); + nsresult Init(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + nsIPrincipal* aPrincipal); void ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, nsFrameMessageManager* aManager); - private: nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase&); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 40a5de47d2..248ac3956b 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3887,7 +3887,7 @@ nsGlobalWindow::GetContentOuter(JSContext* aCx, { MOZ_RELEASE_ASSERT(IsOuterWindow()); - nsCOMPtr content = GetContentInternal(aError); + nsCOMPtr content = GetContentInternal(aError, !nsContentUtils::IsCallerChrome()); if (aError.Failed()) { return; } @@ -3916,7 +3916,7 @@ nsGlobalWindow::GetContent(JSContext* aCx, } already_AddRefed -nsGlobalWindow::GetContentInternal(ErrorResult& aError) +nsGlobalWindow::GetContentInternal(ErrorResult& aError, bool aUnprivilegedCaller) { MOZ_ASSERT(IsOuterWindow()); @@ -3934,7 +3934,7 @@ nsGlobalWindow::GetContentInternal(ErrorResult& aError) } nsCOMPtr primaryContent; - if (!nsContentUtils::IsCallerChrome()) { + if (aUnprivilegedCaller) { // If we're called by non-chrome code, make sure we don't return // the primary content window if the calling tab is hidden. In // such a case we return the same-type root in the hidden tab, @@ -3969,17 +3969,6 @@ nsGlobalWindow::GetContentInternal(ErrorResult& aError) return domWindow.forget(); } -NS_IMETHODIMP -nsGlobalWindow::GetContent(nsIDOMWindow** aContent) -{ - FORWARD_TO_OUTER(GetContent, (aContent), NS_ERROR_UNEXPECTED); - - ErrorResult rv; - *aContent = GetContentInternal(rv).take(); - - return rv.StealNSResult(); -} - MozSelfSupport* nsGlobalWindow::GetMozSelfSupport(ErrorResult& aError) { diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 4f15d6ee4e..cadaa2e46d 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -40,6 +40,7 @@ #include "mozilla/dom/StorageEvent.h" #include "mozilla/dom/StorageEventBinding.h" #include "mozilla/dom/UnionTypes.h" +#include "mozilla/ErrorResult.h" #include "nsFrameMessageManager.h" #include "mozilla/LinkedList.h" #include "mozilla/TimeStamp.h" @@ -338,6 +339,8 @@ public: { } #endif + static nsGlobalWindow* Cast(nsPIDOMWindow* aPIWin) { return static_cast(aPIWin); } + // public methods nsPIDOMWindow* GetPrivateParent(); @@ -1106,12 +1109,21 @@ public: OpenDialog(const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions, nsISupports* aExtraArgument, nsIDOMWindow** _retval) override; + already_AddRefed + GetContentInternal(mozilla::ErrorResult& aError, bool aUnprivilegedCaller); void GetContentOuter(JSContext* aCx, JS::MutableHandle aRetval, mozilla::ErrorResult& aError); void GetContent(JSContext* aCx, JS::MutableHandle aRetval, mozilla::ErrorResult& aError); + already_AddRefed GetContent() + { + MOZ_ASSERT(IsOuterWindow()); + mozilla::ErrorResult ignored; + return GetContentInternal(ignored, /* aUnprivilegedCaller = */ false); + } + void Get_content(JSContext* aCx, JS::MutableHandle aRetval, mozilla::ErrorResult& aError) @@ -1609,9 +1621,6 @@ protected: ShowModalDialog(const nsAString& aUrl, nsIVariant* aArgument, const nsAString& aOptions, mozilla::ErrorResult& aError); - already_AddRefed - GetContentInternal(mozilla::ErrorResult& aError); - // Ask the user if further dialogs should be blocked, if dialogs are currently // being abused. This is used in the cases where we have no modifiable UI to // show, in that case we show a separate dialog to ask this question. diff --git a/dom/base/nsIFrameLoader.idl b/dom/base/nsIFrameLoader.idl index f993b8f07a..ffd743fa8c 100644 --- a/dom/base/nsIFrameLoader.idl +++ b/dom/base/nsIFrameLoader.idl @@ -16,7 +16,7 @@ interface nsIDOMElement; interface nsITabParent; interface nsILoadContext; -[scriptable, builtinclass, uuid(c6e00815-b7a1-4544-b309-a85b86cb1747)] +[scriptable, builtinclass, uuid(1645af04-1bc7-4363-8f2c-eb9679220ab1)] interface nsIFrameLoader : nsISupports { /** @@ -52,10 +52,11 @@ interface nsIFrameLoader : nsISupports /** * Loads the specified URI in this frame but using a different process. * Behaves identically to loadURI, except that this method only works - * with remote frame. + * with remote frame. For a signed package, we need to specifiy the + * package identifier. * Throws an exception with non-remote frames. */ - void switchProcessAndLoadURI(in nsIURI aURI); + void switchProcessAndLoadURI(in nsIURI aURI, in ACString aPackageId); /** * Puts the frameloader in prerendering mode. diff --git a/dom/base/nsInProcessTabChildGlobal.cpp b/dom/base/nsInProcessTabChildGlobal.cpp index dc9a277704..d18e3926bb 100644 --- a/dom/base/nsInProcessTabChildGlobal.cpp +++ b/dom/base/nsInProcessTabChildGlobal.cpp @@ -49,16 +49,10 @@ class nsAsyncMessageToParent : public nsSameProcessAsyncMessageBase, public SameProcessMessageQueue::Runnable { public: - nsAsyncMessageToParent(JSContext* aCx, - nsInProcessTabChildGlobal* aTabChild, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) - : nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal), - mTabChild(aTabChild) - { - } + nsAsyncMessageToParent(JSContext* aCx, JS::Handle aCpows, nsInProcessTabChildGlobal* aTabChild) + : nsSameProcessAsyncMessageBase(aCx, aCpows) + , mTabChild(aTabChild) + { } virtual nsresult HandleMessage() override { @@ -69,7 +63,7 @@ public: RefPtr mTabChild; }; -bool +nsresult nsInProcessTabChildGlobal::DoSendAsyncMessage(JSContext* aCx, const nsAString& aMessage, StructuredCloneData& aData, @@ -78,9 +72,15 @@ nsInProcessTabChildGlobal::DoSendAsyncMessage(JSContext* aCx, { SameProcessMessageQueue* queue = SameProcessMessageQueue::Get(); RefPtr ev = - new nsAsyncMessageToParent(aCx, this, aMessage, aData, aCpows, aPrincipal); + new nsAsyncMessageToParent(aCx, aCpows, this); + + nsresult rv = ev->Init(aCx, aMessage, aData, aPrincipal); + if (NS_FAILED(rv)) { + return rv; + } + queue->Push(ev); - return true; + return NS_OK; } nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell, diff --git a/dom/base/nsInProcessTabChildGlobal.h b/dom/base/nsInProcessTabChildGlobal.h index b861329f62..36d046eada 100644 --- a/dom/base/nsInProcessTabChildGlobal.h +++ b/dom/base/nsInProcessTabChildGlobal.h @@ -88,11 +88,11 @@ public: nsIPrincipal* aPrincipal, nsTArray* aRetVal, bool aIsSync) override; - virtual bool DoSendAsyncMessage(JSContext* aCx, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) override; + virtual nsresult DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle aCpows, + nsIPrincipal* aPrincipal) override; virtual nsresult PreHandleEvent( mozilla::EventChainPreVisitor& aVisitor) override; diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 39ea784404..a5b3b1f717 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -128,7 +128,7 @@ static const uint32_t kMaxICCDuration = 2000; // ms #define NS_CC_FORCED_PURPLE_LIMIT 10 // Don't allow an incremental GC to lock out the CC for too long. -#define NS_MAX_CC_LOCKEDOUT_TIME (15 * PR_USEC_PER_SEC) // 15 seconds +#define NS_MAX_CC_LOCKEDOUT_TIME (30 * PR_USEC_PER_SEC) // 30 seconds // Trigger a CC if the purple buffer exceeds this size when we check it. #define NS_CC_PURPLE_LIMIT 200 diff --git a/dom/base/nsStyledElement.cpp b/dom/base/nsStyledElement.cpp index a8d4d984ae..fd0d1df8dd 100644 --- a/dom/base/nsStyledElement.cpp +++ b/dom/base/nsStyledElement.cpp @@ -151,8 +151,10 @@ nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aV bool aForceInDataDoc) { nsIDocument* doc = OwnerDoc(); + bool isNativeAnon = IsInNativeAnonymousSubtree(); - if (!nsStyleUtil::CSPAllowsInlineStyle(nullptr, NodePrincipal(), + if (!isNativeAnon && + !nsStyleUtil::CSPAllowsInlineStyle(nullptr, NodePrincipal(), doc->GetDocumentURI(), 0, aValue, nullptr)) return; @@ -162,8 +164,7 @@ nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aV doc->IsStaticDocument()) { bool isCSS = true; // assume CSS until proven otherwise - if (!IsInNativeAnonymousSubtree()) { // native anonymous content - // always assumes CSS + if (!isNativeAnon) { // native anonymous content always assumes CSS nsAutoString styleType; doc->GetHeaderData(nsGkAtoms::headerContentStyleType, styleType); if (!styleType.IsEmpty()) { diff --git a/dom/base/test/chrome.ini b/dom/base/test/chrome.ini index 9684ee8dba..8cb7ddb6bb 100644 --- a/dom/base/test/chrome.ini +++ b/dom/base/test/chrome.ini @@ -26,3 +26,4 @@ run-if = os == 'linux' [test_bug1008126.html] run-if = os == 'linux' [test_sandboxed_blob_uri.html] +[test_websocket_frame.html] diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 406edacd2d..5e5f4aa578 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -252,12 +252,15 @@ support-files = iframe_postMessages.html test_performance_observer.js performance_observer.html + test_anonymousContent_style_csp.html^headers^ file_explicit_user_agent.sjs [test_anonymousContent_api.html] [test_anonymousContent_append_after_reflow.html] +[test_anonymousContent_canvas.html] [test_anonymousContent_insert.html] [test_anonymousContent_manipulate_content.html] +[test_anonymousContent_style_csp.html] [test_appname_override.html] [test_async_setTimeout_stack.html] [test_async_setTimeout_stack_across_globals.html] diff --git a/dom/base/test/test_anonymousContent_api.html b/dom/base/test/test_anonymousContent_api.html index 7f5423c275..bcb2b28770 100644 --- a/dom/base/test/test_anonymousContent_api.html +++ b/dom/base/test/test_anonymousContent_api.html @@ -45,7 +45,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1020244 let members = ["getTextContentForElement", "setTextContentForElement", "getAttributeForElement", "setAttributeForElement", - "removeAttributeForElement"]; + "removeAttributeForElement", "getCanvasContext"]; for (let member of members) { ok(member in anonymousContent, "AnonymousContent object defines " + member); } diff --git a/dom/base/test/test_anonymousContent_canvas.html b/dom/base/test/test_anonymousContent_canvas.html new file mode 100644 index 0000000000..9ee1eca200 --- /dev/null +++ b/dom/base/test/test_anonymousContent_canvas.html @@ -0,0 +1,57 @@ + + + + + + Test for Bug 1212477 - Needs a way to access to <canvas>'s context (2d, webgl) from Anonymous Content API + + + + + Mozilla Bug 1212477 +
+
text content
+ + + +
+ + + diff --git a/dom/base/test/test_anonymousContent_style_csp.html b/dom/base/test/test_anonymousContent_style_csp.html new file mode 100644 index 0000000000..69d133aad2 --- /dev/null +++ b/dom/base/test/test_anonymousContent_style_csp.html @@ -0,0 +1,28 @@ + + + + + + Test for Bug 1185351 - Make sure that we don't enforce CSP on styles for AnonymousContent + + + + +
+
text content
+
+ + + diff --git a/dom/base/test/test_anonymousContent_style_csp.html^headers^ b/dom/base/test/test_anonymousContent_style_csp.html^headers^ new file mode 100644 index 0000000000..b7b3c8a4f9 --- /dev/null +++ b/dom/base/test/test_anonymousContent_style_csp.html^headers^ @@ -0,0 +1 @@ +Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' diff --git a/dom/base/test/test_frameLoader_switchProcess.html b/dom/base/test/test_frameLoader_switchProcess.html index 9c5d845730..92b25cb06f 100644 --- a/dom/base/test/test_frameLoader_switchProcess.html +++ b/dom/base/test/test_frameLoader_switchProcess.html @@ -30,7 +30,7 @@ .QueryInterface(Ci.nsIFrameLoaderOwner) .frameLoader; var uri = SpecialPowers.Services.io.newURI(url, null, null); - fl.switchProcessAndLoadURI(uri); + fl.switchProcessAndLoadURI(uri, ""); } function runTest() { diff --git a/dom/base/test/test_websocket_frame.html b/dom/base/test/test_websocket_frame.html new file mode 100644 index 0000000000..59ab859077 --- /dev/null +++ b/dom/base/test/test_websocket_frame.html @@ -0,0 +1,166 @@ + + + + + Basic websocket frame interception test + + + + + + + diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index 8a2922058e..85b7d783c9 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -70,6 +70,10 @@ public: HTMLCanvasElement* GetCanvas() const { + if (mCanvasElement->IsInNativeAnonymousSubtree()) { + return nullptr; + } + // corresponds to changes to the old bindings made in bug 745025 return mCanvasElement->GetOriginalCanvas(); } diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index ffce92c2f8..20f7f89ac9 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -1215,7 +1215,12 @@ WebGLContext::GetCanvas(Nullable& { if (mCanvasElement) { MOZ_RELEASE_ASSERT(!mOffscreenCanvas); - retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement; + + if (mCanvasElement->IsInNativeAnonymousSubtree()) { + retval.SetNull(); + } else { + retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement; + } } else if (mOffscreenCanvas) { retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas; } else { diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 3f134de782..a0fe410889 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -1980,11 +1980,10 @@ EventStateManager::GetContentViewer(nsIContentViewer** aCv) nsCOMPtr ourWindow = do_QueryInterface(focusedWindow); if(!ourWindow) return NS_ERROR_FAILURE; - nsIDOMWindow *rootWindow = ourWindow->GetPrivateRoot(); + nsCOMPtr rootWindow = do_QueryInterface(ourWindow->GetPrivateRoot()); if(!rootWindow) return NS_ERROR_FAILURE; - nsCOMPtr contentWindow; - rootWindow->GetContent(getter_AddRefs(contentWindow)); + nsCOMPtr contentWindow = nsGlobalWindow::Cast(rootWindow)->GetContent(); if(!contentWindow) return NS_ERROR_FAILURE; nsIDocument *doc = GetDocumentFromWindow(contentWindow); diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index fed54581fc..6987a411f0 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -19852,7 +19852,7 @@ FactoryOp::CheckAtLeastOneAppHasPermission(ContentParent* aContentParent, MOZ_ASSERT(!aPermissionString.IsEmpty()); #ifdef MOZ_CHILD_PERMISSIONS - const nsTArray& browsers = + const ManagedContainer& browsers = aContentParent->ManagedPBrowserParent(); if (!browsers.IsEmpty()) { @@ -19876,11 +19876,9 @@ FactoryOp::CheckAtLeastOneAppHasPermission(ContentParent* aContentParent, const nsPromiseFlatCString permissionString = PromiseFlatCString(aPermissionString); - for (uint32_t index = 0, count = browsers.Length(); - index < count; - index++) { + for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) { uint32_t appId = - TabParent::GetFrom(browsers[index])->OwnOrContainingAppId(); + TabParent::GetFrom(iter.Get()->GetKey())->OwnOrContainingAppId(); MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID && appId != nsIScriptSecurityManager::NO_APP_ID); diff --git a/dom/interfaces/base/nsIDOMWindow.idl b/dom/interfaces/base/nsIDOMWindow.idl index b583adc043..ee1000e0e7 100644 --- a/dom/interfaces/base/nsIDOMWindow.idl +++ b/dom/interfaces/base/nsIDOMWindow.idl @@ -22,7 +22,7 @@ interface nsIVariant; * @see */ -[scriptable, uuid(7e26f1bd-a694-4b65-8d9d-9eead0645fe3)] +[scriptable, uuid(ab30b7cc-f7f9-4b9b-befb-7dbf6cf86d46)] interface nsIDOMWindow : nsISupports { // the current browsing context @@ -308,9 +308,6 @@ interface nsIDOMWindow : nsISupports */ void sizeToContent(); - /* [replaceable] content */ - [noscript] readonly attribute nsIDOMWindow content; - /* [replaceable] prompter */ [noscript] readonly attribute nsIPrompt prompter; diff --git a/dom/ipc/AppProcessChecker.cpp b/dom/ipc/AppProcessChecker.cpp index 44a57576a9..b57f280bd8 100644 --- a/dom/ipc/AppProcessChecker.cpp +++ b/dom/ipc/AppProcessChecker.cpp @@ -35,6 +35,12 @@ class nsIPrincipal; namespace mozilla { +#if DEUBG + #define LOG(args...) printf_stderr(args) +#else + #define LOG(...) +#endif + #ifdef MOZ_CHILD_PERMISSIONS static bool @@ -118,11 +124,52 @@ AssertAppStatus(PBrowserParent* aActor, return CheckAppStatusHelper(app, aStatus); } +// A general purpose helper function to check permission against the origin +// rather than mozIApplication. +static bool +CheckOriginPermission(const nsACString& aOrigin, const char* aPermission) +{ + LOG("CheckOriginPermission: %s, %s\n", nsCString(aOrigin).get(), aPermission); + + nsIScriptSecurityManager *securityManager = + nsContentUtils::GetSecurityManager(); + + nsCOMPtr principal; + securityManager->CreateCodebasePrincipalFromOrigin(aOrigin, + getter_AddRefs(principal)); + + nsCOMPtr permMgr = services::GetPermissionManager(); + NS_ENSURE_TRUE(permMgr, false); + + uint32_t perm; + nsresult rv = permMgr->TestExactPermissionFromPrincipal(principal, aPermission, &perm); + NS_ENSURE_SUCCESS(rv, false); + + LOG("Permission %s for %s: %d\n", aPermission, nsCString(aOrigin).get(), perm); + return nsIPermissionManager::ALLOW_ACTION == perm; +} + bool AssertAppProcess(TabContext& aContext, AssertAppProcessType aType, const char* aCapability) { + const mozilla::OriginAttributes& attr = aContext.OriginAttributesRef(); + nsCString suffix; + attr.CreateSuffix(suffix); + + if (!aContext.SignedPkgOriginNoSuffix().IsEmpty()) { + LOG("TabContext owning signed package origin: %s, originAttr; %s\n", + nsCString(aContext.SignedPkgOriginNoSuffix()).get(), + suffix.get()); + } + + // Do a origin-based permission check if the TabContext owns a signed package. + if (!aContext.SignedPkgOriginNoSuffix().IsEmpty() && + (ASSERT_APP_HAS_PERMISSION == aType || ASSERT_APP_PROCESS_PERMISSION == aType)) { + nsCString origin = aContext.SignedPkgOriginNoSuffix() + suffix; + return CheckOriginPermission(origin, aCapability); + } nsCOMPtr app = aContext.GetOwnOrContainingApp(); return CheckAppTypeHelper(app, aType, aCapability, aContext.IsBrowserElement()); diff --git a/dom/ipc/ContentBridgeChild.cpp b/dom/ipc/ContentBridgeChild.cpp index 31cf09d2a1..3684e88e96 100644 --- a/dom/ipc/ContentBridgeChild.cpp +++ b/dom/ipc/ContentBridgeChild.cpp @@ -98,8 +98,8 @@ ContentBridgeChild::SendPBrowserConstructor(PBrowserChild* aActor, jsipc::CPOWManager* ContentBridgeChild::GetCPOWManager() { - if (ManagedPJavaScriptChild().Length()) { - return CPOWManagerFor(ManagedPJavaScriptChild()[0]); + if (PJavaScriptChild* c = LoneManagedOrNull(ManagedPJavaScriptChild())) { + return CPOWManagerFor(c); } return CPOWManagerFor(SendPJavaScriptConstructor()); } diff --git a/dom/ipc/ContentBridgeParent.cpp b/dom/ipc/ContentBridgeParent.cpp index b1d0badfe6..2b5de0629a 100644 --- a/dom/ipc/ContentBridgeParent.cpp +++ b/dom/ipc/ContentBridgeParent.cpp @@ -166,7 +166,7 @@ ContentBridgeParent::DeallocPBrowserParent(PBrowserParent* aParent) void ContentBridgeParent::NotifyTabDestroyed() { - int32_t numLiveTabs = ManagedPBrowserParent().Length(); + int32_t numLiveTabs = ManagedPBrowserParent().Count(); if (numLiveTabs == 1) { MessageLoop::current()->PostTask( FROM_HERE, @@ -180,10 +180,10 @@ ContentBridgeParent::NotifyTabDestroyed() jsipc::CPOWManager* ContentBridgeParent::GetCPOWManager() { - if (ManagedPJavaScriptParent().Length()) { - return CPOWManagerFor(ManagedPJavaScriptParent()[0]); + if (PJavaScriptParent* p = LoneManagedOrNull(ManagedPJavaScriptParent())) { + return CPOWManagerFor(p); } - return CPOWManagerFor(SendPJavaScriptConstructor()); + return nullptr; } NS_IMETHODIMP diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 6afdc92957..ee128382cd 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1265,7 +1265,7 @@ static void FirstIdle(void) mozilla::jsipc::PJavaScriptChild * ContentChild::AllocPJavaScriptChild() { - MOZ_ASSERT(!ManagedPJavaScriptChild().Length()); + MOZ_ASSERT(ManagedPJavaScriptChild().IsEmpty()); return nsIContentChild::AllocPJavaScriptChild(); } @@ -1521,8 +1521,8 @@ ContentChild::DeallocPTestShellChild(PTestShellChild* shell) jsipc::CPOWManager* ContentChild::GetCPOWManager() { - if (ManagedPJavaScriptChild().Length()) { - return CPOWManagerFor(ManagedPJavaScriptChild()[0]); + if (PJavaScriptChild* c = LoneManagedOrNull(ManagedPJavaScriptChild())) { + return CPOWManagerFor(c); } return CPOWManagerFor(SendPJavaScriptConstructor()); } @@ -2010,11 +2010,11 @@ ContentChild::ProcessingError(Result aCode, const char* aReason) } #if defined(MOZ_CRASHREPORTER) && !defined(MOZ_B2G) - if (ManagedPCrashReporterChild().Length() > 0) { + if (PCrashReporterChild* c = LoneManagedOrNull(ManagedPCrashReporterChild())) { CrashReporterChild* crashReporter = - static_cast(ManagedPCrashReporterChild()[0]); - nsDependentCString reason(aReason); - crashReporter->SendAnnotateCrashReport( + static_cast(c); + nsDependentCString reason(aReason); + crashReporter->SendAnnotateCrashReport( NS_LITERAL_CSTRING("ipc_channel_error"), reason); } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 87e923e5f4..4502f9f107 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -42,6 +42,7 @@ #include "mozilla/dom/ExternalHelperAppParent.h" #include "mozilla/dom/FileSystemRequestParent.h" #include "mozilla/dom/GeolocationBinding.h" +#include "mozilla/dom/Notification.h" #include "mozilla/dom/NuwaParent.h" #include "mozilla/dom/PContentBridgeParent.h" #include "mozilla/dom/PContentPermissionRequestParent.h" @@ -1745,11 +1746,11 @@ ContentParent::ShutDownProcess(ShutDownMethod aMethod) } } - const InfallibleTArray& ocuParents = + const ManagedContainer& ocuParents = ManagedPOfflineCacheUpdateParent(); - for (uint32_t i = 0; i < ocuParents.Length(); ++i) { + for (auto iter = ocuParents.ConstIter(); !iter.Done(); iter.Next()) { RefPtr ocuParent = - static_cast(ocuParents[i]); + static_cast(iter.Get()->GetKey()); ocuParent->StopSendingMessagesToChild(); } @@ -2007,9 +2008,9 @@ ContentParent::ActorDestroy(ActorDestroyReason why) // There's a window in which child processes can crash // after IPC is established, but before a crash reporter // is created. - if (ManagedPCrashReporterParent().Length() > 0) { + if (PCrashReporterParent* p = LoneManagedOrNull(ManagedPCrashReporterParent())) { CrashReporterParent* crashReporter = - static_cast(ManagedPCrashReporterParent()[0]); + static_cast(p); // If we're an app process, always stomp the latest URI // loaded in the child process with our manifest URL. We @@ -2191,8 +2192,8 @@ ContentParent::NotifyTabDestroyed(const TabId& aTabId, jsipc::CPOWManager* ContentParent::GetCPOWManager() { - if (ManagedPJavaScriptParent().Length()) { - return CPOWManagerFor(ManagedPJavaScriptParent()[0]); + if (PJavaScriptParent* p = LoneManagedOrNull(ManagedPJavaScriptParent())) { + return CPOWManagerFor(p); } return nullptr; } @@ -2212,9 +2213,8 @@ ContentParent::DestroyTestShell(TestShellParent* aTestShell) TestShellParent* ContentParent::GetTestShellSingleton() { - if (!ManagedPTestShellParent().Length()) - return nullptr; - return static_cast(ManagedPTestShellParent()[0]); + PTestShellParent* p = LoneManagedOrNull(ManagedPTestShellParent()); + return static_cast(p); } void @@ -3090,7 +3090,8 @@ ContentParent::Observe(nsISupports* aSubject, // listening for alert notifications else if (!strcmp(aTopic, "alertfinished") || !strcmp(aTopic, "alertclickcallback") || - !strcmp(aTopic, "alertshow") ) { + !strcmp(aTopic, "alertshow") || + !strcmp(aTopic, "alertdisablecallback")) { if (!SendNotifyAlertsObserver(nsDependentCString(aTopic), nsDependentString(aData))) return NS_ERROR_NOT_AVAILABLE; @@ -3327,7 +3328,7 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline, mozilla::jsipc::PJavaScriptParent * ContentParent::AllocPJavaScriptParent() { - MOZ_ASSERT(!ManagedPJavaScriptParent().Length()); + MOZ_ASSERT(ManagedPJavaScriptParent().IsEmpty()); return nsIContentParent::AllocPJavaScriptParent(); } @@ -3445,9 +3446,9 @@ ContentParent::KillHard(const char* aReason) // We're about to kill the child process associated with this content. // Something has gone wrong to get us here, so we generate a minidump // of the parent and child for submission to the crash server. - if (ManagedPCrashReporterParent().Length() > 0) { + if (PCrashReporterParent* p = LoneManagedOrNull(ManagedPCrashReporterParent())) { CrashReporterParent* crashReporter = - static_cast(ManagedPCrashReporterParent()[0]); + static_cast(p); // GeneratePairedMinidump creates two minidumps for us - the main // one is for the content process we're about to kill, and the other // one is for the main browser process. That second one is the extra @@ -4203,6 +4204,21 @@ ContentParent::RecvCloseAlert(const nsString& aName, return true; } +bool +ContentParent::RecvDisableNotifications(const IPC::Principal& aPrincipal) +{ +#ifdef MOZ_CHILD_PERMISSIONS + uint32_t permission = mozilla::CheckPermission(this, aPrincipal, + "desktop-notification"); + if (permission != nsIPermissionManager::ALLOW_ACTION) { + return true; + } +#endif + + unused << Notification::RemovePermission(aPrincipal); + return true; +} + bool ContentParent::RecvSyncMessage(const nsString& aMsg, const ClonedMessageData& aData, @@ -4454,7 +4470,7 @@ ContentParent::DoLoadMessageManagerScript(const nsAString& aURL, return SendLoadProcessScript(nsString(aURL)); } -bool +nsresult ContentParent::DoSendAsyncMessage(JSContext* aCx, const nsAString& aMessage, StructuredCloneData& aHelper, @@ -4463,18 +4479,21 @@ ContentParent::DoSendAsyncMessage(JSContext* aCx, { ClonedMessageData data; if (!BuildClonedMessageDataForParent(this, aHelper, data)) { - return false; + return NS_ERROR_DOM_DATA_CLONE_ERR; } InfallibleTArray cpows; jsipc::CPOWManager* mgr = GetCPOWManager(); if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) { - return false; + return NS_ERROR_UNEXPECTED; } if (IsReadyNuwaProcess()) { // Nuwa won't receive frame messages after it is frozen. - return true; + return NS_OK; } - return SendAsyncMessage(nsString(aMessage), data, cpows, Principal(aPrincipal)); + if (!SendAsyncMessage(nsString(aMessage), data, cpows, Principal(aPrincipal))) { + return NS_ERROR_UNEXPECTED; + } + return NS_OK; } bool diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index dbd1eadee2..0d1b35f54d 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -195,11 +195,11 @@ public: */ virtual bool DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope) override; - virtual bool DoSendAsyncMessage(JSContext* aCx, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) override; + virtual nsresult DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle aCpows, + nsIPrincipal* aPrincipal) override; virtual bool CheckPermission(const nsAString& aPermission) override; virtual bool CheckManifestURL(const nsAString& aManifestURL) override; virtual bool CheckAppHasPermission(const nsAString& aPermission) override; @@ -742,6 +742,8 @@ private: virtual bool RecvCloseAlert(const nsString& aName, const IPC::Principal& aPrincipal) override; + virtual bool RecvDisableNotifications(const IPC::Principal& aPrincipal) override; + virtual bool RecvLoadURIExternal(const URIParams& uri) override; virtual bool RecvSyncMessage(const nsString& aMsg, diff --git a/dom/ipc/ContentProcessManager.cpp b/dom/ipc/ContentProcessManager.cpp index 93e2ede9ca..c862ba4488 100644 --- a/dom/ipc/ContentProcessManager.cpp +++ b/dom/ipc/ContentProcessManager.cpp @@ -150,10 +150,10 @@ ContentProcessManager::AllocateTabId(const TabId& aOpenerTabId, struct RemoteFrameInfo info; - const IPCTabAppBrowserContext& appBrowser = aContext.appBrowserContext(); + const IPCTabContextUnion& contextUnion = aContext.contextUnion(); // If it's a PopupIPCTabContext, it's the case that a TabChild want to // open a new tab. aOpenerTabId has to be it's parent frame's opener id. - if (appBrowser.type() == IPCTabAppBrowserContext::TPopupIPCTabContext) { + if (contextUnion.type() == IPCTabContextUnion::TPopupIPCTabContext) { auto remoteFrameIter = iter->second.mRemoteFrames.find(aOpenerTabId); if (remoteFrameIter == iter->second.mRemoteFrames.end()) { ASSERT_UNLESS_FUZZING("Failed to find parent frame's opener id."); @@ -162,7 +162,7 @@ ContentProcessManager::AllocateTabId(const TabId& aOpenerTabId, info.mOpenerTabId = remoteFrameIter->second.mOpenerTabId; - const PopupIPCTabContext &ipcContext = appBrowser.get_PopupIPCTabContext(); + const PopupIPCTabContext &ipcContext = contextUnion.get_PopupIPCTabContext(); MOZ_ASSERT(ipcContext.opener().type() == PBrowserOrId::TTabId); remoteFrameIter = iter->second.mRemoteFrames.find(ipcContext.opener().get_TabId()); @@ -290,10 +290,9 @@ ContentProcessManager::GetTabParentByProcessAndTabId(const ContentParentId& aChi return nullptr; } - const InfallibleTArray& browsers = - iter->second.mCp->ManagedPBrowserParent(); - for (uint32_t i = 0; i < browsers.Length(); i++) { - RefPtr tab = TabParent::GetFrom(browsers[i]); + const ManagedContainer& browsers = iter->second.mCp->ManagedPBrowserParent(); + for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) { + RefPtr tab = TabParent::GetFrom(iter.Get()->GetKey()); if (tab->GetTabId() == aChildTabId) { return tab.forget(); } diff --git a/dom/ipc/CrashReporterChild.cpp b/dom/ipc/CrashReporterChild.cpp index eddf384453..27d3dee2dd 100644 --- a/dom/ipc/CrashReporterChild.cpp +++ b/dom/ipc/CrashReporterChild.cpp @@ -17,7 +17,7 @@ namespace dom { PCrashReporterChild* CrashReporterChild::GetCrashReporter() { - const InfallibleTArray* reporters = nullptr; + const ManagedContainer* reporters = nullptr; switch (XRE_GetProcessType()) { case GeckoProcessType_Content: { ContentChild* child = ContentChild::GetSingleton(); @@ -32,10 +32,10 @@ CrashReporterChild::GetCrashReporter() default: break; } - if (reporters && reporters->Length() > 0) { - return reporters->ElementAt(0); + if (!reporters) { + return nullptr; } - return nullptr; + return LoneManagedOrNull(*reporters); } } // namespace dom diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index ec8eda5073..a77784e6ea 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -461,9 +461,9 @@ both: // content calls window.open()), and the parent creates the PBrowser as part // of ContentParent::CreateBrowserOrApp. // - // When the parent constructs a PBrowser, the child trusts the app token it - // receives from the parent. In that case, context can be any of the - // IPCTabContext subtypes. + // When the parent constructs a PBrowser, the child trusts the app token and + // other attributes it receives from the parent. In that case, the + // context should be FrameIPCTabContext. // // When the child constructs a PBrowser, the parent doesn't trust the app // token it receives from the child. In this case, context must have type @@ -827,6 +827,8 @@ parent: CloseAlert(nsString name, Principal principal); + DisableNotifications(Principal principal); + PPSMContentDownloader(uint32_t aCertType); PExternalHelperApp(OptionalURIParams uri, diff --git a/dom/ipc/PContentBridge.ipdl b/dom/ipc/PContentBridge.ipdl index 502f1fa066..982e5adefb 100644 --- a/dom/ipc/PContentBridge.ipdl +++ b/dom/ipc/PContentBridge.ipdl @@ -41,6 +41,8 @@ parent: sync SyncMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows, Principal aPrincipal) returns (StructuredCloneData[] retval); + + async PJavaScript(); both: // Both the parent and the child can construct the PBrowser. // See the comment in PContent::PBrowser(). @@ -49,8 +51,6 @@ both: async PBlob(BlobConstructorParams params); - async PJavaScript(); - AsyncMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows, Principal aPrincipal); }; diff --git a/dom/ipc/PTabContext.ipdlh b/dom/ipc/PTabContext.ipdlh index d9fff07d65..89ef5c6ebb 100644 --- a/dom/ipc/PTabContext.ipdlh +++ b/dom/ipc/PTabContext.ipdlh @@ -29,27 +29,21 @@ struct PopupIPCTabContext bool isBrowserElement; }; -// An IPCTabContext which corresponds to an app frame. -struct AppFrameIPCTabContext +// An IPCTabContext which corresponds to an app, browser, or normal frame. +struct FrameIPCTabContext { - // The ID of the app this frame corresponds to. May be NO_APP_ID. - uint32_t ownAppId; + // The stringified originAttributes dictionary. + nsCString originSuffix; - // The ID of the app containing this frame. May be NO_APP_ID. - uint32_t appFrameOwnerAppId; + // The ID of the app containing this app/browser frame, if applicable. + uint32_t frameOwnerAppId; + + // The origin without originAttribute suffix for a signed package. + // This value would be empty if the TabContext doesn't own a signed + // package. + nsCString signedPkgOriginNoSuffix; }; -// An IPCTabContext which corresponds to a browser frame. -struct BrowserFrameIPCTabContext -{ - // The ID of the app which contains this browser frame. May be NO_APP_ID. - uint32_t browserFrameOwnerAppId; -}; - -// This is equivalent to AppFrameIPCTabContext with all fields set to NO_APP_ID. -struct VanillaFrameIPCTabContext -{}; - // IPCTabContext is an analog to mozilla::dom::TabContext. Both specify an // iframe/PBrowser's own and containing app-ids and tell you whether the // iframe/PBrowser is a browser frame. But only IPCTabContext is allowed to @@ -58,16 +52,14 @@ struct VanillaFrameIPCTabContext // We need IPCTabContext (specifically, PopupIPCTabContext) to prevent a // privilege escalation attack by a compromised child process. See the comment // on AllocPBrowser for details. -union IPCTabAppBrowserContext +union IPCTabContextUnion { PopupIPCTabContext; - AppFrameIPCTabContext; - BrowserFrameIPCTabContext; - VanillaFrameIPCTabContext; + FrameIPCTabContext; }; struct IPCTabContext { - IPCTabAppBrowserContext appBrowserContext; + IPCTabContextUnion contextUnion; }; } diff --git a/dom/ipc/ProcessPriorityManager.cpp b/dom/ipc/ProcessPriorityManager.cpp index cb90355e62..cede2ca4bf 100644 --- a/dom/ipc/ProcessPriorityManager.cpp +++ b/dom/ipc/ProcessPriorityManager.cpp @@ -971,11 +971,11 @@ ParticularProcessPriorityManager::Notify(nsITimer* aTimer) bool ParticularProcessPriorityManager::HasAppType(const char* aAppType) { - const InfallibleTArray& browsers = + const ManagedContainer& browsers = mContentParent->ManagedPBrowserParent(); - for (uint32_t i = 0; i < browsers.Length(); i++) { + for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) { nsAutoString appType; - TabParent::GetFrom(browsers[i])->GetAppType(appType); + TabParent::GetFrom(iter.Get()->GetKey())->GetAppType(appType); if (appType.EqualsASCII(aAppType)) { return true; } @@ -987,10 +987,10 @@ ParticularProcessPriorityManager::HasAppType(const char* aAppType) bool ParticularProcessPriorityManager::IsExpectingSystemMessage() { - const InfallibleTArray& browsers = + const ManagedContainer& browsers = mContentParent->ManagedPBrowserParent(); - for (uint32_t i = 0; i < browsers.Length(); i++) { - TabParent* tp = TabParent::GetFrom(browsers[i]); + for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) { + TabParent* tp = TabParent::GetFrom(iter.Get()->GetKey()); nsCOMPtr bf = do_QueryInterface(tp->GetOwnerElement()); if (!bf) { continue; @@ -1019,10 +1019,10 @@ ParticularProcessPriorityManager::ComputePriority() } bool isVisible = false; - const InfallibleTArray& browsers = + const ManagedContainer& browsers = mContentParent->ManagedPBrowserParent(); - for (uint32_t i = 0; i < browsers.Length(); i++) { - if (TabParent::GetFrom(browsers[i])->IsVisible()) { + for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) { + if (TabParent::GetFrom(iter.Get()->GetKey())->IsVisible()) { isVisible = true; break; } diff --git a/dom/ipc/StructuredCloneData.cpp b/dom/ipc/StructuredCloneData.cpp index 9bc28520f9..aa6e224ddf 100644 --- a/dom/ipc/StructuredCloneData.cpp +++ b/dom/ipc/StructuredCloneData.cpp @@ -10,6 +10,7 @@ #include "nsIMutable.h" #include "nsIXPConnect.h" +#include "ipc/IPCMessageUtils.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/File.h" @@ -81,7 +82,7 @@ StructuredCloneData::Write(JSContext* aCx, } void -StructuredCloneData::WriteIPCParams(Message* aMsg) const +StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const { WriteParam(aMsg, mDataLength); diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index df51808494..b25070ac26 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -2898,7 +2898,7 @@ TabChild::DoSendBlockingMessage(JSContext* aCx, Principal(aPrincipal), aRetVal); } -bool +nsresult TabChild::DoSendAsyncMessage(JSContext* aCx, const nsAString& aMessage, StructuredCloneData& aData, @@ -2907,14 +2907,17 @@ TabChild::DoSendAsyncMessage(JSContext* aCx, { ClonedMessageData data; if (!BuildClonedMessageDataForChild(Manager(), aData, data)) { - return false; + return NS_ERROR_DOM_DATA_CLONE_ERR; } InfallibleTArray cpows; if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { - return false; + return NS_ERROR_UNEXPECTED; } - return SendAsyncMessage(PromiseFlatString(aMessage), data, cpows, - Principal(aPrincipal)); + if (!SendAsyncMessage(PromiseFlatString(aMessage), data, cpows, + Principal(aPrincipal))) { + return NS_ERROR_UNEXPECTED; + } + return NS_OK; } TabChild* diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 4fe586a639..6d91b50658 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -279,11 +279,11 @@ public: nsIPrincipal* aPrincipal, nsTArray* aRetVal, bool aIsSync) override; - virtual bool DoSendAsyncMessage(JSContext* aCx, - const nsAString& aMessage, - StructuredCloneData& aData, - JS::Handle aCpows, - nsIPrincipal* aPrincipal) override; + virtual nsresult DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle aCpows, + nsIPrincipal* aPrincipal) override; virtual bool DoUpdateZoomConstraints(const uint32_t& aPresShellId, const ViewID& aViewId, const Maybe& aConstraints) override; diff --git a/dom/ipc/TabContext.cpp b/dom/ipc/TabContext.cpp index 3f6fd54400..f88846db0f 100644 --- a/dom/ipc/TabContext.cpp +++ b/dom/ipc/TabContext.cpp @@ -22,16 +22,15 @@ namespace dom { TabContext::TabContext() : mInitialized(false) - , mOwnAppId(NO_APP_ID) , mContainingAppId(NO_APP_ID) - , mIsBrowser(false) + , mOriginAttributes() { } bool TabContext::IsBrowserElement() const { - return mIsBrowser; + return mOriginAttributes.mInBrowser; } bool @@ -43,7 +42,7 @@ TabContext::IsBrowserOrApp() const uint32_t TabContext::OwnAppId() const { - return mOwnAppId; + return mOriginAttributes.mAppId; } already_AddRefed @@ -116,7 +115,7 @@ uint32_t TabContext::OwnOrContainingAppId() const { if (HasOwnApp()) { - return mOwnAppId; + return mOriginAttributes.mAppId; } return mContainingAppId; @@ -153,9 +152,23 @@ TabContext::SetTabContext(const TabContext& aContext) return true; } +const OriginAttributes& +TabContext::OriginAttributesRef() const +{ + return mOriginAttributes; +} + +const nsACString& +TabContext::SignedPkgOriginNoSuffix() const +{ + return mSignedPkgOriginNoSuffix; +} + bool -TabContext::SetTabContextForAppFrame(mozIApplication* aOwnApp, - mozIApplication* aAppFrameOwnerApp) +TabContext::SetTabContext(mozIApplication* aOwnApp, + mozIApplication* aAppFrameOwnerApp, + const OriginAttributes& aOriginAttributes, + const nsACString& aSignedPkgOriginNoSuffix) { NS_ENSURE_FALSE(mInitialized, false); @@ -175,52 +188,28 @@ TabContext::SetTabContextForAppFrame(mozIApplication* aOwnApp, NS_ENSURE_TRUE(containingAppId != NO_APP_ID, false); } + // Veryify that app id matches mAppId passed in originAttributes + MOZ_RELEASE_ASSERT((aOwnApp && aOriginAttributes.mAppId == ownAppId) || + (aAppFrameOwnerApp && aOriginAttributes.mAppId == containingAppId) || + aOriginAttributes.mAppId == NO_APP_ID); + mInitialized = true; - mIsBrowser = false; - mOwnAppId = ownAppId; + mOriginAttributes = aOriginAttributes; mContainingAppId = containingAppId; mOwnApp = aOwnApp; mContainingApp = aAppFrameOwnerApp; - return true; -} - -bool -TabContext::SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp) -{ - NS_ENSURE_FALSE(mInitialized, false); - - uint32_t containingAppId = NO_APP_ID; - if (aBrowserFrameOwnerApp) { - nsresult rv = aBrowserFrameOwnerApp->GetLocalId(&containingAppId); - NS_ENSURE_SUCCESS(rv, false); - NS_ENSURE_TRUE(containingAppId != NO_APP_ID, false); - } - - mInitialized = true; - mIsBrowser = true; - mOwnAppId = NO_APP_ID; - mContainingAppId = containingAppId; - mContainingApp = aBrowserFrameOwnerApp; - return true; -} - -bool -TabContext::SetTabContextForNormalFrame() -{ - NS_ENSURE_FALSE(mInitialized, false); - - mInitialized = true; + mSignedPkgOriginNoSuffix = aSignedPkgOriginNoSuffix; return true; } IPCTabContext TabContext::AsIPCTabContext() const { - if (mIsBrowser) { - return IPCTabContext(BrowserFrameIPCTabContext(mContainingAppId)); - } - - return IPCTabContext(AppFrameIPCTabContext(mOwnAppId, mContainingAppId)); + nsAutoCString originSuffix; + mOriginAttributes.CreateSuffix(originSuffix); + return IPCTabContext(FrameIPCTabContext(originSuffix, + mContainingAppId, + mSignedPkgOriginNoSuffix)); } static already_AddRefed @@ -238,14 +227,15 @@ GetAppForId(uint32_t aAppId) MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams) : mInvalidReason(nullptr) { - bool isBrowser = false; - uint32_t ownAppId = NO_APP_ID; uint32_t containingAppId = NO_APP_ID; + OriginAttributes originAttributes = OriginAttributes(); + nsAutoCString originSuffix; + nsAutoCString signedPkgOriginNoSuffix; - const IPCTabAppBrowserContext& appBrowser = aParams.appBrowserContext(); - switch(appBrowser.type()) { - case IPCTabAppBrowserContext::TPopupIPCTabContext: { - const PopupIPCTabContext &ipcContext = appBrowser.get_PopupIPCTabContext(); + const IPCTabContextUnion& contextUnion = aParams.contextUnion(); + switch(contextUnion.type()) { + case IPCTabContextUnion::TPopupIPCTabContext: { + const PopupIPCTabContext &ipcContext = contextUnion.get_PopupIPCTabContext(); TabContext *context; if (ipcContext.opener().type() == PBrowserOrId::TPBrowserParent) { @@ -280,39 +270,25 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams) // // Otherwise, we're a new app window and we inherit from our // opener app. + + // FIXME bug 1212250 - use InheritFromDocToChildDocshell instead + // of copying attributes directly. + originAttributes = context->mOriginAttributes; if (ipcContext.isBrowserElement()) { - isBrowser = true; - ownAppId = NO_APP_ID; containingAppId = context->OwnOrContainingAppId(); } else { - isBrowser = false; - ownAppId = context->mOwnAppId; containingAppId = context->mContainingAppId; } break; } - case IPCTabAppBrowserContext::TAppFrameIPCTabContext: { - const AppFrameIPCTabContext &ipcContext = - appBrowser.get_AppFrameIPCTabContext(); + case IPCTabContextUnion::TFrameIPCTabContext: { + const FrameIPCTabContext &ipcContext = + contextUnion.get_FrameIPCTabContext(); - isBrowser = false; - ownAppId = ipcContext.ownAppId(); - containingAppId = ipcContext.appFrameOwnerAppId(); - break; - } - case IPCTabAppBrowserContext::TBrowserFrameIPCTabContext: { - const BrowserFrameIPCTabContext &ipcContext = - appBrowser.get_BrowserFrameIPCTabContext(); - - isBrowser = true; - ownAppId = NO_APP_ID; - containingAppId = ipcContext.browserFrameOwnerAppId(); - break; - } - case IPCTabAppBrowserContext::TVanillaFrameIPCTabContext: { - isBrowser = false; - ownAppId = NO_APP_ID; - containingAppId = NO_APP_ID; + containingAppId = ipcContext.frameOwnerAppId(); + signedPkgOriginNoSuffix = ipcContext.signedPkgOriginNoSuffix(); + originSuffix = ipcContext.originSuffix(); + originAttributes.PopulateFromSuffix(originSuffix); break; } default: { @@ -320,8 +296,8 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams) } } - nsCOMPtr ownApp = GetAppForId(ownAppId); - if ((ownApp == nullptr) != (ownAppId == NO_APP_ID)) { + nsCOMPtr ownApp = GetAppForId(originAttributes.mAppId); + if ((ownApp == nullptr) != (originAttributes.mAppId == NO_APP_ID)) { mInvalidReason = "Got an ownAppId that didn't correspond to an app."; return; } @@ -333,13 +309,10 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams) } bool rv; - if (isBrowser) { - rv = mTabContext.SetTabContextForBrowserFrame(containingApp); - } else { - rv = mTabContext.SetTabContextForAppFrame(ownApp, - containingApp); - } - + rv = mTabContext.SetTabContext(ownApp, + containingApp, + originAttributes, + signedPkgOriginNoSuffix); if (!rv) { mInvalidReason = "Couldn't initialize TabContext."; } diff --git a/dom/ipc/TabContext.h b/dom/ipc/TabContext.h index 00ecec4237..adead6dc19 100644 --- a/dom/ipc/TabContext.h +++ b/dom/ipc/TabContext.h @@ -9,6 +9,7 @@ #include "mozIApplication.h" #include "nsCOMPtr.h" +#include "mozilla/BasePrincipal.h" namespace mozilla { namespace dom { @@ -102,6 +103,19 @@ public: already_AddRefed GetOwnOrContainingApp() const; bool HasOwnOrContainingApp() const; + /** + * OriginAttributesRef() returns the OriginAttributes of this frame to the + * caller. This is used to store any attribute associated with the frame's + * docshell, such as the AppId. + */ + const OriginAttributes& OriginAttributesRef() const; + + /** + * Returns the origin associated with the tab (w/o suffix) if this tab owns + * a signed packaged content. + */ + const nsACString& SignedPkgOriginNoSuffix() const; + protected: friend class MaybeInvalidTabContext; @@ -120,22 +134,16 @@ protected: bool SetTabContext(const TabContext& aContext); /** - * Set this TabContext to be an app frame (with the given own app) inside the - * given app. Either or both apps may be null. + * Set the TabContext for this frame. This can either be: + * - an app frame (with the given own app) inside the given owner app. Either + * apps can be null. + * - a browser frame inside the given owner app (which may be null). + * - a non-browser, non-app frame. Both own app and owner app should be null. */ - bool SetTabContextForAppFrame(mozIApplication* aOwnApp, - mozIApplication* aAppFrameOwnerApp); - - /** - * Set this TabContext to be a browser frame inside the given app (which may - * be null). - */ - bool SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp); - - /** - * Set this TabContext to be a normal non-browser non-app frame. - */ - bool SetTabContextForNormalFrame(); + bool SetTabContext(mozIApplication* aOwnApp, + mozIApplication* aAppFrameOwnerApp, + const OriginAttributes& aOriginAttributes, + const nsACString& aSignedPkgOriginNoSuffix); private: /** @@ -149,12 +157,6 @@ private: */ nsCOMPtr mOwnApp; - /** - * A cache of mOwnApp->GetLocalId(). Speed really does matter here, since we - * read this ID often during process startup. - */ - uint32_t mOwnAppId; - /** * This TabContext's containing app. If mIsBrowser, this corresponds to the * app which contains the browser frame; otherwise, this corresponds to the @@ -168,11 +170,17 @@ private: uint32_t mContainingAppId; /** - * Does this TabContext correspond to a browser element? - * - * If this is true, mOwnApp must be null. + * OriginAttributes of the top level tab docShell */ - bool mIsBrowser; + OriginAttributes mOriginAttributes; + + /** + * The signed package origin without suffix. Since the signed packaged + * web content is always loaded in a separate process, it makes sense + * that we store this immutable value in TabContext. If the TabContext + * doesn't own a signed package, this value would be empty. + */ + nsCString mSignedPkgOriginNoSuffix; }; /** @@ -188,20 +196,15 @@ public: return TabContext::SetTabContext(aContext); } - bool SetTabContextForAppFrame(mozIApplication* aOwnApp, - mozIApplication* aAppFrameOwnerApp) + bool SetTabContext(mozIApplication* aOwnApp, + mozIApplication* aAppFrameOwnerApp, + const OriginAttributes& aOriginAttributes, + const nsACString& aSignedPkgOriginNoSuffix = EmptyCString()) { - return TabContext::SetTabContextForAppFrame(aOwnApp, aAppFrameOwnerApp); - } - - bool SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp) - { - return TabContext::SetTabContextForBrowserFrame(aBrowserFrameOwnerApp); - } - - bool SetTabContextForNormalFrame() - { - return TabContext::SetTabContextForNormalFrame(); + return TabContext::SetTabContext(aOwnApp, + aAppFrameOwnerApp, + aOriginAttributes, + aSignedPkgOriginNoSuffix); } }; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 015f23d776..ea5ef68ed6 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -70,6 +70,9 @@ #include "nsIWidget.h" #include "nsIWindowMediator.h" #include "nsIWindowWatcher.h" +#ifndef XP_WIN +#include "nsJARProtocolHandler.h" +#endif #include "nsOpenURIInFrameParams.h" #include "nsPIDOMWindow.h" #include "nsPIWindowWatcher.h" @@ -514,7 +517,8 @@ TabParent::ShouldSwitchProcess(nsIChannel* aChannel) } void -TabParent::OnStartSignedPackageRequest(nsIChannel* aChannel) +TabParent::OnStartSignedPackageRequest(nsIChannel* aChannel, + const nsACString& aPackageId) { if (!ShouldSwitchProcess(aChannel)) { return; @@ -533,7 +537,7 @@ TabParent::OnStartSignedPackageRequest(nsIChannel* aChannel) RefPtr frameLoader = GetFrameLoader(); NS_ENSURE_TRUE_VOID(frameLoader); - nsresult rv = frameLoader->SwitchProcessAndLoadURI(uri); + nsresult rv = frameLoader->SwitchProcessAndLoadURI(uri, aPackageId); if (NS_FAILED(rv)) { NS_WARNING("Failed to switch process."); } @@ -559,9 +563,11 @@ TabParent::DestroyInternal() // Let all PluginWidgets know we are tearing down. Prevents // these objects from sending async events after the child side // is shut down. - const nsTArray& kids = ManagedPPluginWidgetParent(); - for (uint32_t idx = 0; idx < kids.Length(); ++idx) { - static_cast(kids[idx])->ParentDestroy(); + const ManagedContainer& kids = + ManagedPPluginWidgetParent(); + for (auto iter = kids.ConstIter(); !iter.Done(); iter.Next()) { + static_cast( + iter.Get()->GetKey())->ParentDestroy(); } } @@ -1062,9 +1068,21 @@ TabParent::LoadURL(nsIURI* aURI) rv = packageFile->GetPath(path); NS_ENSURE_SUCCESS_VOID(rv); - RefPtr openFileRunnable = - new OpenFileAndSendFDRunnable(path, this); - openFileRunnable->Dispatch(); +#ifndef XP_WIN + PRFileDesc* cachedFd = nullptr; + gJarHandler->JarCache()->GetFd(packageFile, &cachedFd); + + if (cachedFd) { + FileDescriptor::PlatformHandleType handle = + FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(cachedFd)); + unused << SendCacheFileDescriptor(path, FileDescriptor(handle)); + } else +#endif + { + RefPtr openFileRunnable = + new OpenFileAndSendFDRunnable(path, this); + openFileRunnable->Dispatch(); + } } } } @@ -1431,14 +1449,16 @@ TabParent::GetTopLevelDocAccessible() const #ifdef ACCESSIBILITY // XXX Consider managing non top level PDocAccessibles with their parent // document accessible. - const nsTArray& docs = ManagedPDocAccessibleParent(); - size_t docCount = docs.Length(); - for (size_t i = 0; i < docCount; i++) { - auto doc = static_cast(docs[i]); + const ManagedContainer& docs = ManagedPDocAccessibleParent(); + for (auto iter = docs.ConstIter(); !iter.Done(); iter.Next()) { + auto doc = static_cast(iter.Get()->GetKey()); if (!doc->ParentDoc()) { return doc; } } + + MOZ_ASSERT(docs.Count() == 0, "If there isn't a top level accessible doc " + "there shouldn't be an accessible doc at all!"); #endif return nullptr; } @@ -2573,10 +2593,8 @@ TabParent::GetTabIdFrom(nsIDocShell *docShell) RenderFrameParent* TabParent::GetRenderFrame() { - if (ManagedPRenderFrameParent().IsEmpty()) { - return nullptr; - } - return static_cast(ManagedPRenderFrameParent()[0]); + PRenderFrameParent* p = LoneManagedOrNull(ManagedPRenderFrameParent()); + return static_cast(p); } bool diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 46a925e495..111577c100 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -451,7 +451,8 @@ public: // Called by HttpChannelParent. The function may use a new process to // reload the URI associated with the given channel. - void OnStartSignedPackageRequest(nsIChannel* aChannel); + void OnStartSignedPackageRequest(nsIChannel* aChannel, + const nsACString& aPackageId); protected: bool ReceiveMessage(const nsString& aMessage, diff --git a/dom/ipc/moz.build b/dom/ipc/moz.build index 74a8e5b566..a53ef761a2 100644 --- a/dom/ipc/moz.build +++ b/dom/ipc/moz.build @@ -160,6 +160,11 @@ if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT': '/security/sandbox/chromium-shim', ] +if CONFIG['OS_ARCH'] != 'WINNT': + LOCAL_INCLUDES += [ + '/modules/libjar', + ] + DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG['BIN_SUFFIX'] if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gonk', 'qt'): diff --git a/dom/ipc/nsIContentParent.cpp b/dom/ipc/nsIContentParent.cpp index 52af902b03..bbf8f931cd 100644 --- a/dom/ipc/nsIContentParent.cpp +++ b/dom/ipc/nsIContentParent.cpp @@ -20,6 +20,7 @@ #include "mozilla/unused.h" #include "nsFrameMessageManager.h" +#include "nsIWebBrowserChrome.h" #include "nsPrintfCString.h" #include "xpcpublic.h" @@ -70,18 +71,18 @@ nsIContentParent::DeallocPJavaScriptParent(PJavaScriptParent* aParent) bool nsIContentParent::CanOpenBrowser(const IPCTabContext& aContext) { - const IPCTabAppBrowserContext& appBrowser = aContext.appBrowserContext(); + const IPCTabContextUnion& contextUnion = aContext.contextUnion(); // We don't trust the IPCTabContext we receive from the child, so we'll bail // if we receive an IPCTabContext that's not a PopupIPCTabContext. // (PopupIPCTabContext lets the child process prove that it has access to // the app it's trying to open.) - if (appBrowser.type() != IPCTabAppBrowserContext::TPopupIPCTabContext) { + if (contextUnion.type() != IPCTabContextUnion::TPopupIPCTabContext) { ASSERT_UNLESS_FUZZING("Unexpected IPCTabContext type. Aborting AllocPBrowserParent."); return false; } - const PopupIPCTabContext& popupContext = appBrowser.get_PopupIPCTabContext(); + const PopupIPCTabContext& popupContext = contextUnion.get_PopupIPCTabContext(); if (popupContext.opener().type() != PBrowserOrId::TPBrowserParent) { ASSERT_UNLESS_FUZZING("Unexpected PopupIPCTabContext type. Aborting AllocPBrowserParent."); return false; @@ -128,9 +129,35 @@ nsIContentParent::AllocPBrowserParent(const TabId& aTabId, return nullptr; } + const IPCTabContextUnion& contextUnion = aContext.contextUnion(); + const PopupIPCTabContext& popupContext = contextUnion.get_PopupIPCTabContext(); + + uint32_t chromeFlags = aChromeFlags; + + // CanOpenBrowser has ensured that the IPCTabContext is of + // type PopupIPCTabContext, and that the opener TabParent is + // reachable. + auto opener = TabParent::GetFrom(popupContext.opener().get_PBrowserParent()); + // We must ensure that the private browsing and remoteness flags + // match those of the opener. + nsCOMPtr loadContext = opener->GetLoadContext(); + if (!loadContext) { + return nullptr; + } + + bool isPrivate; + loadContext->GetUsePrivateBrowsing(&isPrivate); + if (isPrivate) { + chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; + } + + // And because we're allocating a remote browser, of course the + // window is remote. + chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW; + MaybeInvalidTabContext tc(aContext); MOZ_ASSERT(tc.IsValid()); - TabParent* parent = new TabParent(this, aTabId, tc.GetTabContext(), aChromeFlags); + TabParent* parent = new TabParent(this, aTabId, tc.GetTabContext(), chromeFlags); // We release this ref in DeallocPBrowserParent() NS_ADDREF(parent); diff --git a/dom/media/gmp/GMPContentChild.cpp b/dom/media/gmp/GMPContentChild.cpp index 1e8fc58e0d..0d7b29b181 100644 --- a/dom/media/gmp/GMPContentChild.cpp +++ b/dom/media/gmp/GMPContentChild.cpp @@ -182,28 +182,28 @@ void GMPContentChild::CloseActive() { // Invalidate and remove any remaining API objects. - const nsTArray& audioDecoders = + const ManagedContainer& audioDecoders = ManagedPGMPAudioDecoderChild(); - for (uint32_t i = audioDecoders.Length(); i > 0; i--) { - audioDecoders[i - 1]->SendShutdown(); + for (auto iter = audioDecoders.ConstIter(); !iter.Done(); iter.Next()) { + iter.Get()->GetKey()->SendShutdown(); } - const nsTArray& decryptors = + const ManagedContainer& decryptors = ManagedPGMPDecryptorChild(); - for (uint32_t i = decryptors.Length(); i > 0; i--) { - decryptors[i - 1]->SendShutdown(); + for (auto iter = decryptors.ConstIter(); !iter.Done(); iter.Next()) { + iter.Get()->GetKey()->SendShutdown(); } - const nsTArray& videoDecoders = + const ManagedContainer& videoDecoders = ManagedPGMPVideoDecoderChild(); - for (uint32_t i = videoDecoders.Length(); i > 0; i--) { - videoDecoders[i - 1]->SendShutdown(); + for (auto iter = videoDecoders.ConstIter(); !iter.Done(); iter.Next()) { + iter.Get()->GetKey()->SendShutdown(); } - const nsTArray& videoEncoders = + const ManagedContainer& videoEncoders = ManagedPGMPVideoEncoderChild(); - for (uint32_t i = videoEncoders.Length(); i > 0; i--) { - videoEncoders[i - 1]->SendShutdown(); + for (auto iter = videoEncoders.ConstIter(); !iter.Done(); iter.Next()) { + iter.Get()->GetKey()->SendShutdown(); } } diff --git a/dom/media/gmp/GMPParent.cpp b/dom/media/gmp/GMPParent.cpp index 05ad933b80..1713a1b7d3 100644 --- a/dom/media/gmp/GMPParent.cpp +++ b/dom/media/gmp/GMPParent.cpp @@ -580,10 +580,8 @@ GMPParent::WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes) void GMPParent::GetCrashID(nsString& aResult) { - CrashReporterParent* cr = nullptr; - if (ManagedPCrashReporterParent().Length() > 0) { - cr = static_cast(ManagedPCrashReporterParent()[0]); - } + CrashReporterParent* cr = + static_cast(LoneManagedOrNull(ManagedPCrashReporterParent())); if (NS_WARN_IF(!cr)) { return; } diff --git a/dom/network/TCPServerSocketParent.cpp b/dom/network/TCPServerSocketParent.cpp index c46bb74411..7c78bed15c 100644 --- a/dom/network/TCPServerSocketParent.cpp +++ b/dom/network/TCPServerSocketParent.cpp @@ -65,27 +65,25 @@ TCPServerSocketParent::Init() uint32_t TCPServerSocketParent::GetAppId() { - uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID; const PContentParent *content = Manager()->Manager(); - const InfallibleTArray& browsers = content->ManagedPBrowserParent(); - if (browsers.Length() > 0) { - TabParent *tab = TabParent::GetFrom(browsers[0]); - appId = tab->OwnAppId(); + if (PBrowserParent* browser = LoneManagedOrNull(content->ManagedPBrowserParent())) { + TabParent *tab = TabParent::GetFrom(browser); + return tab->OwnAppId(); + } else { + return nsIScriptSecurityManager::UNKNOWN_APP_ID; } - return appId; }; bool TCPServerSocketParent::GetInBrowser() { - bool inBrowser = false; const PContentParent *content = Manager()->Manager(); - const InfallibleTArray& browsers = content->ManagedPBrowserParent(); - if (browsers.Length() > 0) { - TabParent *tab = TabParent::GetFrom(browsers[0]); - inBrowser = tab->IsBrowserElement(); + if (PBrowserParent* browser = LoneManagedOrNull(content->ManagedPBrowserParent())) { + TabParent *tab = TabParent::GetFrom(browser); + return tab->IsBrowserElement(); + } else { + return false; } - return inBrowser; } nsresult diff --git a/dom/network/TCPSocketParent.cpp b/dom/network/TCPSocketParent.cpp index fc3d659139..74cac2785f 100644 --- a/dom/network/TCPSocketParent.cpp +++ b/dom/network/TCPSocketParent.cpp @@ -67,27 +67,25 @@ TCPSocketParentBase::~TCPSocketParentBase() uint32_t TCPSocketParent::GetAppId() { - uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID; const PContentParent *content = Manager()->Manager(); - const InfallibleTArray& browsers = content->ManagedPBrowserParent(); - if (browsers.Length() > 0) { - TabParent *tab = TabParent::GetFrom(browsers[0]); - appId = tab->OwnAppId(); + if (PBrowserParent* browser = LoneManagedOrNull(content->ManagedPBrowserParent())) { + TabParent *tab = TabParent::GetFrom(browser); + return tab->OwnAppId(); + } else { + return nsIScriptSecurityManager::UNKNOWN_APP_ID; } - return appId; }; bool TCPSocketParent::GetInBrowser() { - bool inBrowser = false; const PContentParent *content = Manager()->Manager(); - const InfallibleTArray& browsers = content->ManagedPBrowserParent(); - if (browsers.Length() > 0) { - TabParent *tab = TabParent::GetFrom(browsers[0]); - inBrowser = tab->IsBrowserElement(); + if (PBrowserParent* browser = LoneManagedOrNull(content->ManagedPBrowserParent())) { + TabParent *tab = TabParent::GetFrom(browser); + return tab->IsBrowserElement(); + } else { + return false; } - return inBrowser; } nsresult @@ -225,9 +223,8 @@ TCPSocketParent::RecvOpenBind(const nsCString& aRemoteHost, uint32_t appId = nsIScriptSecurityManager::NO_APP_ID; bool inBrowser = false; const PContentParent *content = Manager()->Manager(); - const InfallibleTArray& browsers = content->ManagedPBrowserParent(); - if (browsers.Length() > 0) { - TabParent *tab = static_cast(browsers[0]); + if (PBrowserParent* browser = LoneManagedOrNull(content->ManagedPBrowserParent())) { + TabParent *tab = TabParent::GetFrom(browser); appId = tab->OwnAppId(); inBrowser = tab->IsBrowserElement(); } diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 6ecb1501b0..e2f530c7a6 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -14,6 +14,7 @@ #include "mozilla/dom/AppNotificationServiceOptionsBinding.h" #include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/NotificationEvent.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/Promise.h" @@ -40,6 +41,7 @@ #include "nsServiceManagerUtils.h" #include "nsStructuredCloneContainer.h" #include "nsToolkitCompsCID.h" +#include "nsXULAppAPI.h" #include "ServiceWorkerManager.h" #include "WorkerPrivate.h" #include "WorkerRunnable.h" @@ -626,17 +628,22 @@ NotificationPermissionRequest::GetTypes(nsIArray** aTypes) aTypes); } -class NotificationObserver : public nsIObserver +// Observer that the alert service calls to do common tasks and/or dispatch to the +// specific observer for the context e.g. main thread, worker, or service worker. +class NotificationObserver final : public nsIObserver { public: - UniquePtr mNotificationRef; + nsCOMPtr mObserver; + nsCOMPtr mPrincipal; NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER - explicit NotificationObserver(UniquePtr aRef) - : mNotificationRef(Move(aRef)) + NotificationObserver(nsIObserver* aObserver, nsIPrincipal* aPrincipal) + : mObserver(aObserver), mPrincipal(aPrincipal) { AssertIsOnMainThread(); + MOZ_ASSERT(mObserver); + MOZ_ASSERT(mPrincipal); } protected: @@ -648,6 +655,28 @@ protected: NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver) +class MainThreadNotificationObserver : public nsIObserver +{ +public: + UniquePtr mNotificationRef; + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + explicit MainThreadNotificationObserver(UniquePtr aRef) + : mNotificationRef(Move(aRef)) + { + AssertIsOnMainThread(); + } + +protected: + virtual ~MainThreadNotificationObserver() + { + AssertIsOnMainThread(); + } +}; + +NS_IMPL_ISUPPORTS(MainThreadNotificationObserver, nsIObserver) + NS_IMETHODIMP NotificationTask::Run() { @@ -949,14 +978,14 @@ Notification::GetPrincipal() } } -class WorkerNotificationObserver final : public NotificationObserver +class WorkerNotificationObserver final : public MainThreadNotificationObserver { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIOBSERVER explicit WorkerNotificationObserver(UniquePtr aRef) - : NotificationObserver(Move(aRef)) + : MainThreadNotificationObserver(Move(aRef)) { AssertIsOnMainThread(); MOZ_ASSERT(mNotificationRef->GetNotification()->mWorkerPrivate); @@ -1016,7 +1045,7 @@ protected: } }; -NS_IMPL_ISUPPORTS_INHERITED0(WorkerNotificationObserver, NotificationObserver) +NS_IMPL_ISUPPORTS_INHERITED0(WorkerNotificationObserver, MainThreadNotificationObserver) class ServiceWorkerNotificationObserver final : public nsIObserver { @@ -1124,6 +1153,36 @@ NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { AssertIsOnMainThread(); + + if (!strcmp("alertdisablecallback", aTopic)) { + if (XRE_IsParentProcess()) { + return Notification::RemovePermission(mPrincipal); + } + // Permissions can't be removed from the content process. Send a message + // to the parent; `ContentParent::RecvDisableNotifications` will call + // `RemovePermission`. + ContentChild::GetSingleton()->SendDisableNotifications( + IPC::Principal(mPrincipal)); + return NS_OK; + } else if (!strcmp("alertsettingscallback", aTopic)) { + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (!obs) { + return NS_ERROR_FAILURE; + } + + // Notify other observers so they can show settings UI. + obs->NotifyObservers(mPrincipal, "notifications-open-settings", nullptr); + return NS_OK; + } + + return mObserver->Observe(aSubject, aTopic, aData); +} + +NS_IMETHODIMP +MainThreadNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) +{ + AssertIsOnMainThread(); MOZ_ASSERT(mNotificationRef); Notification* notification = mNotificationRef->GetNotification(); MOZ_ASSERT(notification); @@ -1374,16 +1433,19 @@ Notification::ShowInternal() mObserver = new WorkerNotificationObserver(Move(ownership)); observer = mObserver; } else { - observer = new NotificationObserver(Move(ownership)); + observer = new MainThreadNotificationObserver(Move(ownership)); } } else { // This observer does not care about the Notification. It will be released // at the end of this function. // - // The observer is wholly owned by the alerts service. + // The observer is wholly owned by the NotificationObserver passed to the alert service. observer = new ServiceWorkerNotificationObserver(mScope, GetPrincipal(), mID); } MOZ_ASSERT(observer); + nsCOMPtr alertObserver = new NotificationObserver(observer, + GetPrincipal()); + #ifdef MOZ_B2G nsCOMPtr appNotifier = @@ -1422,7 +1484,7 @@ Notification::ShowInternal() } appNotifier->ShowAppNotification(iconUrl, mTitle, mBody, - observer, val); + alertObserver, val); return; } } @@ -1453,7 +1515,7 @@ Notification::ShowInternal() nsAutoString alertName; GetAlertName(alertName); alertService->ShowAlertNotification(iconUrl, mTitle, mBody, true, - uniqueCookie, observer, alertName, + uniqueCookie, alertObserver, alertName, DirectionToString(mDir), mLang, mDataAsBase64, GetPrincipal(), inPrivateBrowsing); @@ -2302,6 +2364,19 @@ Notification::CreateAndShow(nsIGlobalObject* aGlobal, return notification.forget(); } +/* static */ nsresult +Notification::RemovePermission(nsIPrincipal* aPrincipal) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + nsCOMPtr permissionManager = + mozilla::services::GetPermissionManager(); + if (!permissionManager) { + return NS_ERROR_FAILURE; + } + permissionManager->RemoveFromPrincipal(aPrincipal, "desktop-notification"); + return NS_OK; +} + } // namespace dom } // namespace mozilla diff --git a/dom/notification/Notification.h b/dom/notification/Notification.h index dd2825d349..76f06cbbf6 100644 --- a/dom/notification/Notification.h +++ b/dom/notification/Notification.h @@ -112,7 +112,7 @@ class Notification : public DOMEventTargetHelper friend class CloseNotificationRunnable; friend class NotificationTask; friend class NotificationPermissionRequest; - friend class NotificationObserver; + friend class MainThreadNotificationObserver; friend class NotificationStorageCallback; friend class ServiceWorkerNotificationObserver; friend class WorkerGetRunnable; @@ -285,6 +285,8 @@ public: bool DispatchClickEvent(); bool DispatchNotificationClickEvent(); + + static nsresult RemovePermission(nsIPrincipal* aPrincipal); protected: Notification(nsIGlobalObject* aGlobal, const nsAString& aID, const nsAString& aTitle, const nsAString& aBody, diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index fffdfc950c..20bc44e61f 100755 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -1494,7 +1494,7 @@ PluginModuleChromeParent::OnHangUIContinue() CrashReporterParent* PluginModuleChromeParent::CrashReporter() { - return static_cast(ManagedPCrashReporterParent()[0]); + return static_cast(LoneManagedOrNull(ManagedPCrashReporterParent())); } #ifdef MOZ_CRASHREPORTER_INJECTOR diff --git a/dom/push/PushServiceWebSocket.jsm b/dom/push/PushServiceWebSocket.jsm index 94d377b84c..68fe4b4c5e 100644 --- a/dom/push/PushServiceWebSocket.jsm +++ b/dom/push/PushServiceWebSocket.jsm @@ -625,7 +625,7 @@ this.PushServiceWebSocket = { try { // Grab a wakelock before we open the socket to ensure we don't go to // sleep before connection the is opened. - this._ws.asyncOpen(uri, uri.spec, this._wsListener, null); + this._ws.asyncOpen(uri, uri.spec, 0, this._wsListener, null); this._acquireWakeLock(); this._currentState = STATE_WAITING_FOR_WS_START; } catch(e) { diff --git a/dom/push/test/xpcshell/head.js b/dom/push/test/xpcshell/head.js index aa1ce9b2c7..b6b9af91ad 100644 --- a/dom/push/test/xpcshell/head.js +++ b/dom/push/test/xpcshell/head.js @@ -279,7 +279,7 @@ MockWebSocket.prototype = { return this._originalURI; }, - asyncOpen(uri, origin, listener, context) { + asyncOpen(uri, origin, windowId, listener, context) { this._listener = listener; this._context = context; waterfall(() => this._listener.onStart(this._context)); diff --git a/dom/simplepush/PushService.jsm b/dom/simplepush/PushService.jsm index c31babfca1..cdbc22b071 100644 --- a/dom/simplepush/PushService.jsm +++ b/dom/simplepush/PushService.jsm @@ -881,7 +881,7 @@ this.PushService = { try { // Grab a wakelock before we open the socket to ensure we don't go to sleep // before connection the is opened. - this._ws.asyncOpen(uri, serverURL, this._wsListener, null); + this._ws.asyncOpen(uri, serverURL, 0, this._wsListener, null); this._acquireWakeLock(); this._currentState = STATE_WAITING_FOR_WS_START; } catch(e) { diff --git a/dom/webidl/AnonymousContent.webidl b/dom/webidl/AnonymousContent.webidl index 8aa44e19df..65b4d79f1f 100644 --- a/dom/webidl/AnonymousContent.webidl +++ b/dom/webidl/AnonymousContent.webidl @@ -53,4 +53,12 @@ interface AnonymousContent { [Throws] void removeAttributeForElement(DOMString elementId, DOMString attributeName); + + /** + * Get the canvas' context for the element specified if it's a + * node, `null` otherwise. + */ + [Throws] + nsISupports? getCanvasContext(DOMString elementId, + DOMString contextId); }; diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index 4abcdc4187..35c8155ffb 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -1348,6 +1348,7 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl) SymLoadStruct mapBufferRangeSymbols[] = { { (PRFuncPtr*) &mSymbols.fMapBufferRange, { "MapBufferRange", nullptr } }, { (PRFuncPtr*) &mSymbols.fFlushMappedBufferRange, { "FlushMappedBufferRange", nullptr } }, + { (PRFuncPtr*) &mSymbols.fUnmapBuffer, { "UnmapBuffer", nullptr } }, END_SYMBOLS }; diff --git a/gfx/layers/ipc/CompositorChild.cpp b/gfx/layers/ipc/CompositorChild.cpp index d7cadb2bca..e6adc33657 100644 --- a/gfx/layers/ipc/CompositorChild.cpp +++ b/gfx/layers/ipc/CompositorChild.cpp @@ -92,11 +92,11 @@ CompositorChild::Destroy() mLayerManager = nullptr; } - // start from the end of the array because Destroy() can cause the - // LayerTransactionChild to be removed from the array. - for (int i = ManagedPLayerTransactionChild().Length() - 1; i >= 0; --i) { + nsAutoTArray transactions; + ManagedPLayerTransactionChild(transactions); + for (int i = transactions.Length() - 1; i >= 0; --i) { RefPtr layers = - static_cast(ManagedPLayerTransactionChild()[i]); + static_cast(transactions[i]); layers->Destroy(); } diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index d9ef5330e3..c5e6e69995 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -604,7 +604,7 @@ CompositorParent::~CompositorParent() void CompositorParent::Destroy() { - MOZ_ASSERT(ManagedPLayerTransactionParent().Length() == 0, + MOZ_ASSERT(ManagedPLayerTransactionParent().Count() == 0, "CompositorParent destroyed before managed PLayerTransactionParent"); MOZ_ASSERT(mPaused); // Ensure RecvWillStop was called diff --git a/gfx/layers/ipc/LayerTransactionChild.cpp b/gfx/layers/ipc/LayerTransactionChild.cpp index c61c9c94e6..1e456c4a22 100644 --- a/gfx/layers/ipc/LayerTransactionChild.cpp +++ b/gfx/layers/ipc/LayerTransactionChild.cpp @@ -33,11 +33,12 @@ LayerTransactionChild::Destroy() // When it happens, IPCOpen() is still true. // See bug 1004191. mDestroyed = true; - MOZ_ASSERT(0 == ManagedPLayerChild().Length(), + MOZ_ASSERT(0 == ManagedPLayerChild().Count(), "layers should have been cleaned up by now"); - for (size_t i = 0; i < ManagedPTextureChild().Length(); ++i) { - TextureClient* texture = TextureClient::AsTextureClient(ManagedPTextureChild()[i]); + const ManagedContainer& textures = ManagedPTextureChild(); + for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) { + TextureClient* texture = TextureClient::AsTextureClient(iter.Get()->GetKey()); if (texture) { texture->ForceRemove(); } diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp index 11af5864f3..f9609ea1ca 100644 --- a/gfx/layers/ipc/LayerTransactionParent.cpp +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -175,9 +175,10 @@ void LayerTransactionParent::Destroy() { mDestroyed = true; - for (size_t i = 0; i < ManagedPLayerParent().Length(); ++i) { + const ManagedContainer& layers = ManagedPLayerParent(); + for (auto iter = layers.ConstIter(); !iter.Done(); iter.Next()) { ShadowLayerParent* slp = - static_cast(ManagedPLayerParent()[i]); + static_cast(iter.Get()->GetKey()); slp->Destroy(); } } diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index b550c8d2eb..a591f4f3ab 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -14,6 +14,7 @@ #include "mozilla/DebugOnly.h" #include "mozilla/dom/ipc/StructuredCloneData.h" #include "mozilla/Maybe.h" +#include "mozilla/net/WebSocketFrame.h" #include "mozilla/TimeStamp.h" #ifdef XP_WIN #include "mozilla/TimeStamp_windows.h" @@ -713,6 +714,22 @@ struct ParamTraits } }; +template <> +struct ParamTraits +{ + typedef mozilla::net::WebSocketFrameData paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + aParam.WriteIPCParams(aMsg); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return aResult->ReadIPCParams(aMsg, aIter); + } +}; + template <> struct ParamTraits { diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index ab93ff9a5a..4a26031799 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -28,6 +28,9 @@ #include #endif +template class nsTHashtable; +template class nsPtrHashKey; + // WARNING: this takes into account the private, special-message-type // enum in ipc_channel.h. They need to be kept in sync. namespace { @@ -331,6 +334,21 @@ DuplicateHandle(HANDLE aSourceHandle, #endif } // namespace ipc + +template +using ManagedContainer = nsTHashtable>; + +template +Protocol* +LoneManagedOrNull(const ManagedContainer& aManagees) +{ + if (aManagees.IsEmpty()) { + return nullptr; + } + MOZ_ASSERT(aManagees.Count() == 1); + return aManagees.ConstIter().Get()->GetKey(); +} + } // namespace mozilla diff --git a/ipc/ipdl/ipdl/builtin.py b/ipc/ipdl/ipdl/builtin.py index b505d27688..0b23d045ae 100644 --- a/ipc/ipdl/ipdl/builtin.py +++ b/ipc/ipdl/ipdl/builtin.py @@ -50,6 +50,7 @@ HeaderIncludes = ( 'nsStringGlue.h', 'nsTArray.h', 'mozilla/ipc/ProtocolUtils.h', + 'nsTHashtable.h', ) CppIncludes = ( diff --git a/ipc/ipdl/ipdl/cxx/ast.py b/ipc/ipdl/ipdl/cxx/ast.py index 3c770b4950..1044d959d2 100644 --- a/ipc/ipdl/ipdl/cxx/ast.py +++ b/ipc/ipdl/ipdl/cxx/ast.py @@ -337,6 +337,7 @@ Type.UINT32PTR = Type('uint32_t', ptr=1) Type.SIZE = Type('size_t') Type.VOID = Type('void') Type.VOIDPTR = Type('void', ptr=1) +Type.AUTO = Type('auto') class TypeArray(Node): def __init__(self, basetype, nmemb): diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index 0495fe44f8..f4bfa2ed43 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -335,6 +335,10 @@ def _refptrTake(expr): def _cxxArrayType(basetype, const=0, ref=0): return Type('nsTArray', T=basetype, const=const, ref=ref, hasimplicitcopyctor=False) +def _cxxManagedContainerType(basetype, const=0, ref=0): + return Type('ManagedContainer', T=basetype, + const=const, ref=ref, hasimplicitcopyctor=False) + def _cxxFallibleArrayType(basetype, const=0, ref=0): return Type('FallibleTArray', T=basetype, const=const, ref=ref) @@ -352,21 +356,19 @@ def _callCxxSwapArrayElements(arr1, arr2, sel='.'): return ExprCall(ExprSelect(arr1, sel, 'SwapElements'), args=[ arr2 ]) -def _callCxxArrayInsertSorted(arr, elt): - return ExprCall(ExprSelect(arr, '.', 'InsertElementSorted'), - args=[ elt ]) +def _callInsertManagedActor(managees, actor): + return ExprCall(ExprSelect(managees, '.', 'PutEntry'), + args=[ actor ]) -def _callCxxArrayRemoveSorted(arr, elt): - return ExprCall(ExprSelect(arr, '.', 'RemoveElementSorted'), - args=[ elt ]) +def _callRemoveManagedActor(managees, actor): + return ExprCall(ExprSelect(managees, '.', 'RemoveEntry'), + args=[ actor ]) -def _callCxxArrayClear(arr): - return ExprCall(ExprSelect(arr, '.', 'Clear')) +def _callClearManagedActors(managees): + return ExprCall(ExprSelect(managees, '.', 'Clear')) -def _cxxArrayHasElementSorted(arr, elt): - return ExprBinary( - ExprSelect(arr, '.', 'NoIndex'), '!=', - ExprCall(ExprSelect(arr, '.', 'BinaryIndexOf'), args=[ elt ])) +def _callHasManagedActor(managees, actor): + return ExprCall(ExprSelect(managees, '.', 'Contains'), args=[ actor ]) def _otherSide(side): if side == 'child': return 'parent' @@ -1266,8 +1268,8 @@ class Protocol(ipdl.ast.Protocol): def managedVarType(self, actortype, side, const=0, ref=0): assert self.decl.type.isManagerOf(actortype) - return _cxxArrayType(self.managedCxxType(actortype, side), - const=const, ref=ref) + return _cxxManagedContainerType(Type(_actorName(actortype.name(), side)), + const=const, ref=ref) def managerArrayExpr(self, thisvar, side): """The member var my manager keeps of actors of my type.""" @@ -1838,12 +1840,15 @@ class _GenerateProtocolCode(ipdl.ast.Visitor): if usesend or userecv: transitionfunc.addstmt(Whitespace.NL) - transitionfunc.addstmts([ - fromswitch, - # all --> Error transitions break to here - StmtExpr(ExprAssn(ExprDeref(nextvar), _errorState())), - StmtReturn(ExprLiteral.FALSE) - ]) + transitionfunc.addstmt(fromswitch) + # all --> Error transitions break to here. But only insert this + # block if there is any possibility of such transitions. + if self.protocol.transitionStmts: + transitionfunc.addstmts([ + StmtExpr(ExprAssn(ExprDeref(nextvar), _errorState())), + StmtReturn(ExprLiteral.FALSE), + ]) + return transitionfunc ##-------------------------------------------------- @@ -2054,6 +2059,12 @@ def _generateCxxStruct(sd): # Struct() defctor = ConstructorDefn(ConstructorDecl(sd.name)) defctor.addstmt(StmtExpr(callinit)) + defctor.memberinits = [] + for f in sd.fields: + # Only generate default values for primitives. + if not (f.ipdltype.isCxx() and f.ipdltype.isAtom()): + continue + defctor.memberinits.append(ExprMemberInit(f.memberVar())) struct.addstmts([ defctor, Whitespace.NL ]) # Struct(const field1& _f1, ...) @@ -3045,17 +3056,41 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): self.cls.addstmts([ managermeth, Whitespace.NL ]) + def actorFromIter(itervar): + return ExprCall(ExprSelect(ExprCall(ExprSelect(itervar, '.', 'Get')), + '->', 'GetKey')) + def forLoopOverHashtable(hashtable, itervar, const=False): + return StmtFor( + init=Param(Type.AUTO, itervar.name, + ExprCall(ExprSelect(hashtable, '.', 'ConstIter' if const else 'Iter'))), + cond=ExprNot(ExprCall(ExprSelect(itervar, '.', 'Done'))), + update=ExprCall(ExprSelect(itervar, '.', 'Next'))) + ## Managed[T](Array& inout) const ## const Array& Managed() const for managed in ptype.manages: arrvar = ExprVar('aArr') meth = MethodDefn(MethodDecl( p.managedMethod(managed, self.side).name, - params=[ Decl(p.managedVarType(managed, self.side, ref=1), + params=[ Decl(_cxxArrayType(p.managedCxxType(managed, self.side), ref=1), arrvar.name) ], const=1)) - meth.addstmt(StmtExpr(ExprAssn( - arrvar, p.managedVar(managed, self.side)))) + ivar = ExprVar('i') + elementsvar = ExprVar('elements') + itervar = ExprVar('iter') + meth.addstmt(StmtDecl(Decl(Type.UINT32, ivar.name), + init=ExprLiteral.ZERO)) + meth.addstmt(StmtDecl(Decl(Type(_actorName(managed.name(), self.side), ptrptr=1), elementsvar.name), + init=ExprCall(ExprSelect(arrvar, '.', 'AppendElements'), + args=[ ExprCall(ExprSelect(p.managedVar(managed, self.side), + '.', 'Count')) ]))) + foreachaccumulate = forLoopOverHashtable(p.managedVar(managed, self.side), + itervar, const=True) + foreachaccumulate.addstmt(StmtExpr( + ExprAssn(ExprIndex(elementsvar, ivar), + actorFromIter(itervar)))) + foreachaccumulate.addstmt(StmtExpr(ExprPrefixUnop(ivar, '++'))) + meth.addstmt(foreachaccumulate) refmeth = MethodDefn(MethodDecl( p.managedMethod(managed, self.side).name, @@ -3395,6 +3430,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): subtreewhyvar = ExprVar('subtreewhy') kidsvar = ExprVar('kids') ivar = ExprVar('i') + itervar = ExprVar('iter') ithkid = ExprIndex(kidsvar, ivar) destroysubtree = MethodDefn(MethodDecl( @@ -3424,6 +3460,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ]) for managed in ptype.manages: + managedVar = p.managedVar(managed, self.side) + foreachdestroy = StmtFor( init=Param(Type.UINT32, ivar.name, ExprLiteral.ZERO), cond=ExprBinary(ivar, '<', _callCxxArrayLength(kidsvar)), @@ -3438,8 +3476,13 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): '// Recursively shutting down %s kids\n'% (managed.name()), indent=1), StmtDecl( - Decl(p.managedVarType(managed, self.side), kidsvar.name), - initargs=[ p.managedVar(managed, self.side) ]), + Decl(_cxxArrayType(p.managedCxxType(managed, self.side)), kidsvar.name), + initargs=[ ExprCall(ExprSelect(managedVar, '.', 'Count')) ]), + Whitespace( + '// Accumulate kids into a stable structure to iterate over\n', + indent=1), + StmtExpr(ExprCall(p.managedMethod(managed, self.side), + args=[ kidsvar ])), foreachdestroy, ]) destroysubtree.addstmt(block) @@ -3457,20 +3500,16 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ## DeallocSubtree() deallocsubtree = MethodDefn(MethodDecl(deallocsubtreevar.name)) for managed in ptype.manages: - foreachrecurse = StmtFor( - init=Param(Type.UINT32, ivar.name, ExprLiteral.ZERO), - cond=ExprBinary(ivar, '<', _callCxxArrayLength(kidsvar)), - update=ExprPrefixUnop(ivar, '++')) - foreachrecurse.addstmt(StmtExpr(ExprCall( - ExprSelect(ithkid, '->', deallocsubtreevar.name)))) + managedVar = p.managedVar(managed, self.side) - foreachdealloc = StmtFor( - init=Param(Type.UINT32, ivar.name, ExprLiteral.ZERO), - cond=ExprBinary(ivar, '<', _callCxxArrayLength(kidsvar)), - update=ExprPrefixUnop(ivar, '++')) + foreachrecurse = forLoopOverHashtable(managedVar, itervar) + foreachrecurse.addstmt(StmtExpr(ExprCall( + ExprSelect(actorFromIter(itervar), '->', deallocsubtreevar.name)))) + + foreachdealloc = forLoopOverHashtable(managedVar, itervar) foreachdealloc.addstmts([ StmtExpr(ExprCall(_deallocMethod(managed, self.side), - args=[ ithkid ])) + args=[ actorFromIter(itervar) ])) ]) block = StmtBlock() @@ -3478,17 +3517,10 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): Whitespace( '// Recursively deleting %s kids\n'% (managed.name()), indent=1), - StmtDecl( - Decl(p.managedVarType(managed, self.side, ref=1), - kidsvar.name), - init=p.managedVar(managed, self.side)), foreachrecurse, Whitespace.NL, - # no need to copy |kids| here; we're the ones deleting - # stragglers, no outside C++ is being invoked (except - # Dealloc(subactor)) foreachdealloc, - StmtExpr(_callCxxArrayClear(p.managedVar(managed, self.side))), + StmtExpr(_callClearManagedActors(managedVar)), ]) deallocsubtree.addstmt(block) @@ -3547,7 +3579,6 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): for managed in ptype.manages: self.cls.addstmts([ - Whitespace('// Sorted by pointer value\n', indent=1), StmtDecl(Decl( p.managedVarType(managed, self.side), p.managedVar(managed, self.side).name)) ]) @@ -3949,7 +3980,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): _actorChannel(actorvar), p.channelForSubactor())), StmtExpr(ExprAssn(_actorState(actorvar), _actorState(ithkid))), - StmtExpr(_callCxxArrayInsertSorted(manageearray, actorvar)), + StmtExpr(_callInsertManagedActor(manageearray, actorvar)), registerstmt, StmtExpr(ExprCall( ExprSelect(actorvar, @@ -3960,10 +3991,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): block.addstmts([ StmtDecl(Decl(_cxxArrayType(manageecxxtype, ref=0), kidsvar.name)), - StmtExpr(ExprAssn(kidsvar, - ExprSelect(othervar, - '->', - manageearray.name))), + StmtExpr(ExprCall(ExprSelect(othervar, '->', + p.managedMethod(manageeipdltype, self.side).name), + args=[ kidsvar ])), StmtDecl(Decl(manageecxxtype, actorvar.name)), forstmt]) clonemanagees.addstmt(block) @@ -3993,10 +4023,10 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): StmtDecl(Decl(manageecxxtype, actorvar.name), ExprCast(listenervar, manageecxxtype, static=1)), _abortIfFalse( - _cxxArrayHasElementSorted(manageearray, actorvar), + _callHasManagedActor(manageearray, actorvar), "actor not managed by this!"), Whitespace.NL, - StmtExpr(_callCxxArrayRemoveSorted(manageearray, actorvar)), + StmtExpr(_callRemoveManagedActor(manageearray, actorvar)), StmtExpr(ExprCall(_deallocMethod(manageeipdltype, self.side), args=[ actorvar ])), StmtReturn() @@ -4936,7 +4966,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): StmtExpr(ExprAssn(_actorManager(actorvar), ExprVar.THIS)), StmtExpr(ExprAssn(_actorChannel(actorvar), self.protocol.channelForSubactor())), - StmtExpr(_callCxxArrayInsertSorted( + StmtExpr(_callInsertManagedActor( self.protocol.managedVar(md.decl.type.constructedType(), self.side), actorvar)), @@ -5132,9 +5162,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): + self.invokeRecvHandler(md, implicit=0) + [ Whitespace.NL ] + saveIdStmts - + self.dtorEpilogue(md, md.actorDecl().var()) - + [ Whitespace.NL ] + self.makeReply(md, errfnRecv, routingId=idvar) + + [ Whitespace.NL ] + + self.dtorEpilogue(md, md.actorDecl().var()) + [ Whitespace.NL, StmtReturn(_Result.Processed) ]) diff --git a/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp b/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp index 95c97e7f2f..7dee30e632 100644 --- a/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp +++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp @@ -76,7 +76,7 @@ bool TestManyChildAllocsChild::RecvGo() fail("can't send Hello()"); } - size_t len = ManagedPTestManyChildAllocsSubChild().Length(); + size_t len = ManagedPTestManyChildAllocsSubChild().Count(); if (NALLOCS != len) fail("expected %lu kids, got %lu", NALLOCS, len); diff --git a/ipc/ipdl/test/cxx/TestMultiMgrs.cpp b/ipc/ipdl/test/cxx/TestMultiMgrs.cpp index 2f54f4b2fb..ab9daac173 100644 --- a/ipc/ipdl/test/cxx/TestMultiMgrs.cpp +++ b/ipc/ipdl/test/cxx/TestMultiMgrs.cpp @@ -1,6 +1,7 @@ #include "TestMultiMgrs.h" #include "IPDLUnitTests.h" // fail etc. +#include "mozilla/ipc/ProtocolUtils.h" namespace mozilla { namespace _ipdltest { @@ -70,17 +71,17 @@ TestMultiMgrsRightChild::RecvPTestMultiMgrsBottomConstructor( bool TestMultiMgrsChild::RecvCheck() { - if (1 != ManagedPTestMultiMgrsLeftChild().Length()) + if (1 != ManagedPTestMultiMgrsLeftChild().Count()) fail("where's leftie?"); - if (1 != ManagedPTestMultiMgrsRightChild().Length()) + if (1 != ManagedPTestMultiMgrsRightChild().Count()) fail("where's rightie?"); TestMultiMgrsLeftChild* leftie = static_cast( - ManagedPTestMultiMgrsLeftChild()[0]); + LoneManagedOrNull(ManagedPTestMultiMgrsLeftChild())); TestMultiMgrsRightChild* rightie = static_cast( - ManagedPTestMultiMgrsRightChild()[0]); + LoneManagedOrNull(ManagedPTestMultiMgrsRightChild())); if (!leftie->HasChild(mBottomL)) fail("leftie didn't have a child it was supposed to!"); diff --git a/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp b/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp index 564bc814df..e8b2330ba1 100644 --- a/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp +++ b/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp @@ -22,21 +22,21 @@ TestSelfManageRootParent::Main() if (!a) fail("constructing PTestSelfManage"); - ASSERT(1 == ManagedPTestSelfManageParent().Length()); + ASSERT(1 == ManagedPTestSelfManageParent().Count()); TestSelfManageParent* aa = static_cast(a->SendPTestSelfManageConstructor()); if (!aa) fail("constructing PTestSelfManage"); - ASSERT(1 == ManagedPTestSelfManageParent().Length() && - 1 == a->ManagedPTestSelfManageParent().Length()); + ASSERT(1 == ManagedPTestSelfManageParent().Count() && + 1 == a->ManagedPTestSelfManageParent().Count()); if (!PTestSelfManageParent::Send__delete__(aa)) fail("destroying PTestSelfManage"); ASSERT(Deletion == aa->mWhy && - 1 == ManagedPTestSelfManageParent().Length() && - 0 == a->ManagedPTestSelfManageParent().Length()); + 1 == ManagedPTestSelfManageParent().Count() && + 0 == a->ManagedPTestSelfManageParent().Count()); delete aa; aa = @@ -44,15 +44,15 @@ TestSelfManageRootParent::Main() if (!aa) fail("constructing PTestSelfManage"); - ASSERT(1 == ManagedPTestSelfManageParent().Length() && - 1 == a->ManagedPTestSelfManageParent().Length()); + ASSERT(1 == ManagedPTestSelfManageParent().Count() && + 1 == a->ManagedPTestSelfManageParent().Count()); if (!PTestSelfManageParent::Send__delete__(a)) fail("destroying PTestSelfManage"); ASSERT(Deletion == a->mWhy && AncestorDeletion == aa->mWhy && - 0 == ManagedPTestSelfManageParent().Length() && - 0 == a->ManagedPTestSelfManageParent().Length()); + 0 == ManagedPTestSelfManageParent().Count() && + 0 == a->ManagedPTestSelfManageParent().Count()); delete a; delete aa; diff --git a/ipc/ipdl/test/cxx/TestShutdown.cpp b/ipc/ipdl/test/cxx/TestShutdown.cpp index 217aabc851..95a242bff3 100644 --- a/ipc/ipdl/test/cxx/TestShutdown.cpp +++ b/ipc/ipdl/test/cxx/TestShutdown.cpp @@ -26,7 +26,7 @@ TestShutdownParent::ActorDestroy(ActorDestroyReason why) void TestShutdownSubParent::ActorDestroy(ActorDestroyReason why) { - if (Manager()->ManagedPTestShutdownSubParent().Length() == 0) + if (Manager()->ManagedPTestShutdownSubParent().Count() == 0) fail("manager should still have managees!"); if (mExpectCrash && AbnormalShutdown != why) @@ -34,14 +34,14 @@ TestShutdownSubParent::ActorDestroy(ActorDestroyReason why) else if (!mExpectCrash && AbnormalShutdown == why) fail("wasn't expecting crash!"); - if (mExpectCrash && 0 == ManagedPTestShutdownSubsubParent().Length()) + if (mExpectCrash && 0 == ManagedPTestShutdownSubsubParent().Count()) fail("expected to *still* have kids"); } void TestShutdownSubsubParent::ActorDestroy(ActorDestroyReason why) { - if (Manager()->ManagedPTestShutdownSubsubParent().Length() == 0) + if (Manager()->ManagedPTestShutdownSubsubParent().Count() == 0) fail("manager should still have managees!"); if (mExpectParentDeleted && AncestorDeletion != why) @@ -206,7 +206,7 @@ TestShutdownSubChild::AnswerStackFrame() void TestShutdownSubChild::ActorDestroy(ActorDestroyReason why) { - if (Manager()->ManagedPTestShutdownSubChild().Length() == 0) + if (Manager()->ManagedPTestShutdownSubChild().Count() == 0) fail("manager should still have managees!"); if (mExpectCrash && AbnormalShutdown != why) @@ -214,14 +214,14 @@ TestShutdownSubChild::ActorDestroy(ActorDestroyReason why) else if (!mExpectCrash && AbnormalShutdown == why) fail("wasn't expecting crash!"); - if (mExpectCrash && 0 == ManagedPTestShutdownSubsubChild().Length()) + if (mExpectCrash && 0 == ManagedPTestShutdownSubsubChild().Count()) fail("expected to *still* have kids"); } void TestShutdownSubsubChild::ActorDestroy(ActorDestroyReason why) { - if (Manager()->ManagedPTestShutdownSubsubChild().Length() == 0) + if (Manager()->ManagedPTestShutdownSubsubChild().Count() == 0) fail("manager should still have managees!"); if (mExpectParentDeleted && AncestorDeletion != why) diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 084a7fda28..d46c208330 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -1122,17 +1122,22 @@ public: nscolor aBackgroundColor, gfxContext* aRenderedContext) = 0; + enum { + RENDER_AUTO_SCALE = 0x80 + }; + /** * Renders a node aNode to a surface and returns it. The aRegion may be used * to clip the rendering. This region is measured in CSS pixels from the - * edge of the presshell area. The aPoint, aScreenRect and aSurface - * arguments function in a similar manner as RenderSelection. + * edge of the presshell area. The aPoint, aScreenRect and aFlags arguments + * function in a similar manner as RenderSelection. */ virtual already_AddRefed RenderNode(nsIDOMNode* aNode, nsIntRegion* aRegion, nsIntPoint& aPoint, - nsIntRect* aScreenRect) = 0; + nsIntRect* aScreenRect, + uint32_t aFlags) = 0; /** * Renders a selection to a surface and returns it. This method is primarily @@ -1141,18 +1146,20 @@ public: * aScreenRect will be filled in with the bounding rectangle of the * selection area on screen. * - * If the area of the selection is large, the image will be scaled down. - * The argument aPoint is used in this case as a reference point when - * determining the new screen rectangle after scaling. Typically, this - * will be the mouse position, so that the screen rectangle is positioned - * such that the mouse is over the same point in the scaled image as in - * the original. When scaling does not occur, the mouse point isn't used - * as the position can be determined from the displayed frames. + * If the area of the selection is large and the RENDER_AUTO_SCALE flag is + * set, the image will be scaled down. The argument aPoint is used in this + * case as a reference point when determining the new screen rectangle after + * scaling. Typically, this will be the mouse position, so that the screen + * rectangle is positioned such that the mouse is over the same point in the + * scaled image as in the original. When scaling does not occur, the mouse + * point isn't used because the position can be determined from the displayed + * frames. */ virtual already_AddRefed RenderSelection(nsISelection* aSelection, nsIntPoint& aPoint, - nsIntRect* aScreenRect) = 0; + nsIntRect* aScreenRect, + uint32_t aFlags) = 0; void AddWeakFrameInternal(nsWeakFrame* aWeakFrame); virtual void AddWeakFrameExternal(nsWeakFrame* aWeakFrame); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index ecce837a53..276e0fb25a 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -4987,7 +4987,8 @@ PresShell::PaintRangePaintInfo(nsTArray >* aItems, nsIntRegion* aRegion, nsRect aArea, nsIntPoint& aPoint, - nsIntRect* aScreenRect) + nsIntRect* aScreenRect, + uint32_t aFlags) { nsPresContext* pc = GetPresContext(); if (!pc || aArea.width == 0 || aArea.height == 0) @@ -5008,7 +5009,8 @@ PresShell::PaintRangePaintInfo(nsTArray >* aItems, pc->DeviceContext()->GetClientRect(maxSize); nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1); nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1); - bool resize = (pixelArea.width > maxWidth || pixelArea.height > maxHeight); + bool resize = (aFlags & RENDER_AUTO_SCALE) && + (pixelArea.width > maxWidth || pixelArea.height > maxHeight); if (resize) { scale = 1.0; // divide the maximum size by the image size in both directions. Whichever @@ -5108,7 +5110,8 @@ already_AddRefed PresShell::RenderNode(nsIDOMNode* aNode, nsIntRegion* aRegion, nsIntPoint& aPoint, - nsIntRect* aScreenRect) + nsIntRect* aScreenRect, + uint32_t aFlags) { // area will hold the size of the surface needed to draw the node, measured // from the root frame. @@ -5147,13 +5150,14 @@ PresShell::RenderNode(nsIDOMNode* aNode, } return PaintRangePaintInfo(&rangeItems, nullptr, aRegion, area, aPoint, - aScreenRect); + aScreenRect, aFlags); } already_AddRefed PresShell::RenderSelection(nsISelection* aSelection, nsIntPoint& aPoint, - nsIntRect* aScreenRect) + nsIntRect* aScreenRect, + uint32_t aFlags) { // area will hold the size of the surface needed to draw the selection, // measured from the root frame. @@ -5180,7 +5184,7 @@ PresShell::RenderSelection(nsISelection* aSelection, } return PaintRangePaintInfo(&rangeItems, aSelection, nullptr, area, aPoint, - aScreenRect); + aScreenRect, aFlags); } void diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index 8517541ec3..052f1a90f2 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -191,12 +191,14 @@ public: RenderNode(nsIDOMNode* aNode, nsIntRegion* aRegion, nsIntPoint& aPoint, - nsIntRect* aScreenRect) override; + nsIntRect* aScreenRect, + uint32_t aFlags) override; virtual already_AddRefed RenderSelection(nsISelection* aSelection, nsIntPoint& aPoint, - nsIntRect* aScreenRect) override; + nsIntRect* aScreenRect, + uint32_t aFlags) override; virtual already_AddRefed GetRootWindow() override; @@ -543,6 +545,8 @@ protected: * aPoint - reference point, typically the mouse position * aScreenRect - [out] set to the area of the screen the painted area should * be displayed at + * aFlags - set RENDER_AUTO_SCALE to scale down large images, but it must not + * be set if a custom image was specified */ already_AddRefed PaintRangePaintInfo(nsTArray >* aItems, @@ -550,7 +554,8 @@ protected: nsIntRegion* aRegion, nsRect aArea, nsIntPoint& aPoint, - nsIntRect* aScreenRect); + nsIntRect* aScreenRect, + uint32_t aFlags); /** * Methods to handle changes to user and UA sheet lists that we get diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 8e068b191b..f15347f014 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -230,6 +230,7 @@ static void Shutdown(); #include "mozilla/dom/nsMixedContentBlocker.h" #include "AudioChannelService.h" +#include "mozilla/net/WebSocketEventService.h" #include "mozilla/dom/DataStoreService.h" @@ -631,6 +632,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(Geolocation, Init) #define NS_AUDIOCHANNEL_SERVICE_CID \ { 0xf712e983, 0x048a, 0x443f, { 0x88, 0x02, 0xfc, 0xc3, 0xd9, 0x27, 0xce, 0xac }} +#define NS_WEBSOCKETEVENT_SERVICE_CID \ + { 0x31689828, 0xda66, 0x49a6, { 0x87, 0x0c, 0xdf, 0x62, 0xb8, 0x3f, 0xe7, 0x89 }} + #define NS_DATASTORE_SERVICE_CID \ { 0x0d4285fe, 0xf1b3, 0x49fa, { 0xbc, 0x51, 0xa4, 0xa8, 0x3f, 0x0a, 0xaf, 0x85 }} @@ -638,6 +642,8 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsGeolocationService, nsGeolocationServ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AudioChannelService, AudioChannelService::GetOrCreate) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WebSocketEventService, WebSocketEventService::GetOrCreate) + NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DataStoreService, DataStoreService::GetOrCreate) #ifdef MOZ_WEBSPEECH_TEST_BACKEND @@ -788,6 +794,7 @@ NS_DEFINE_NAMED_CID(NS_TEXTSERVICESDOCUMENT_CID); NS_DEFINE_NAMED_CID(NS_GEOLOCATION_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_GEOLOCATION_CID); NS_DEFINE_NAMED_CID(NS_AUDIOCHANNEL_SERVICE_CID); +NS_DEFINE_NAMED_CID(NS_WEBSOCKETEVENT_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_DATASTORE_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_FOCUSMANAGER_CID); NS_DEFINE_NAMED_CID(NS_CONTENTSECURITYMANAGER_CID); @@ -1095,6 +1102,7 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { { &kNS_GEOLOCATION_SERVICE_CID, false, nullptr, nsGeolocationServiceConstructor }, { &kNS_GEOLOCATION_CID, false, nullptr, GeolocationConstructor }, { &kNS_AUDIOCHANNEL_SERVICE_CID, false, nullptr, AudioChannelServiceConstructor }, + { &kNS_WEBSOCKETEVENT_SERVICE_CID, false, nullptr, WebSocketEventServiceConstructor }, { &kNS_DATASTORE_SERVICE_CID, false, nullptr, DataStoreServiceConstructor }, { &kNS_FOCUSMANAGER_CID, false, nullptr, CreateFocusManager }, #ifdef MOZ_WEBSPEECH_TEST_BACKEND @@ -1262,6 +1270,7 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { "@mozilla.org/geolocation/service;1", &kNS_GEOLOCATION_SERVICE_CID }, { "@mozilla.org/geolocation;1", &kNS_GEOLOCATION_CID }, { "@mozilla.org/audiochannel/service;1", &kNS_AUDIOCHANNEL_SERVICE_CID }, + { "@mozilla.org/websocketevent/service;1", &kNS_WEBSOCKETEVENT_SERVICE_CID }, { "@mozilla.org/datastore-service;1", &kNS_DATASTORE_SERVICE_CID }, { "@mozilla.org/focus-manager;1", &kNS_FOCUSMANAGER_CID }, #ifdef MOZ_WEBSPEECH_TEST_BACKEND diff --git a/netwerk/ipc/NeckoChild.cpp b/netwerk/ipc/NeckoChild.cpp index cd4fff277e..3ceb8bea58 100644 --- a/netwerk/ipc/NeckoChild.cpp +++ b/netwerk/ipc/NeckoChild.cpp @@ -15,6 +15,7 @@ #include "mozilla/net/WyciwygChannelChild.h" #include "mozilla/net/FTPChannelChild.h" #include "mozilla/net/WebSocketChannelChild.h" +#include "mozilla/net/WebSocketEventListenerChild.h" #include "mozilla/net/DNSRequestChild.h" #include "mozilla/net/RemoteOpenFileChild.h" #include "mozilla/net/ChannelDiverterChild.h" @@ -144,7 +145,8 @@ NeckoChild::DeallocPWyciwygChannelChild(PWyciwygChannelChild* channel) PWebSocketChild* NeckoChild::AllocPWebSocketChild(const PBrowserOrId& browser, - const SerializedLoadContext& aSerialized) + const SerializedLoadContext& aSerialized, + const uint32_t& aSerial) { NS_NOTREACHED("AllocPWebSocketChild should not be called"); return nullptr; @@ -158,6 +160,23 @@ NeckoChild::DeallocPWebSocketChild(PWebSocketChild* child) return true; } +PWebSocketEventListenerChild* +NeckoChild::AllocPWebSocketEventListenerChild(const uint64_t& aInnerWindowID) +{ + RefPtr c = + new WebSocketEventListenerChild(aInnerWindowID); + return c.forget().take(); +} + +bool +NeckoChild::DeallocPWebSocketEventListenerChild(PWebSocketEventListenerChild* aActor) +{ + RefPtr c = + dont_AddRef(static_cast(aActor)); + MOZ_ASSERT(c); + return true; +} + PDataChannelChild* NeckoChild::AllocPDataChannelChild(const uint32_t& channelId) { diff --git a/netwerk/ipc/NeckoChild.h b/netwerk/ipc/NeckoChild.h index 20e66ab94c..19f481dfe1 100644 --- a/netwerk/ipc/NeckoChild.h +++ b/netwerk/ipc/NeckoChild.h @@ -40,7 +40,8 @@ protected: virtual bool DeallocPFTPChannelChild(PFTPChannelChild*) override; virtual PWebSocketChild* AllocPWebSocketChild(const PBrowserOrId&, - const SerializedLoadContext&) override; + const SerializedLoadContext&, + const uint32_t&) override; virtual bool DeallocPWebSocketChild(PWebSocketChild*) override; virtual PTCPSocketChild* AllocPTCPSocketChild(const nsString& host, const uint16_t& port) override; @@ -79,6 +80,9 @@ protected: const nsString& aRealm, const uint64_t& aCallbackId) override; virtual bool RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline) override; + virtual PWebSocketEventListenerChild* + AllocPWebSocketEventListenerChild(const uint64_t& aInnerWindowID) override; + virtual bool DeallocPWebSocketEventListenerChild(PWebSocketEventListenerChild*) override; /* Predictor Messsages */ virtual bool RecvPredOnPredictPreconnect(const URIParams& aURI) override; diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index df951fe850..a2ccafafb3 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -13,6 +13,7 @@ #include "mozilla/net/WyciwygChannelParent.h" #include "mozilla/net/FTPChannelParent.h" #include "mozilla/net/WebSocketChannelParent.h" +#include "mozilla/net/WebSocketEventListenerParent.h" #include "mozilla/net/DataChannelParent.h" #ifdef NECKO_PROTOCOL_rtsp #include "mozilla/net/RtspControllerParent.h" @@ -319,7 +320,8 @@ NeckoParent::DeallocPWyciwygChannelParent(PWyciwygChannelParent* channel) PWebSocketParent* NeckoParent::AllocPWebSocketParent(const PBrowserOrId& browser, - const SerializedLoadContext& serialized) + const SerializedLoadContext& serialized, + const uint32_t& aSerial) { nsCOMPtr loadContext; const char *error = CreateChannelLoadContext(browser, Manager(), @@ -334,7 +336,8 @@ NeckoParent::AllocPWebSocketParent(const PBrowserOrId& browser, RefPtr tabParent = TabParent::GetFrom(browser.get_PBrowserParent()); PBOverrideStatus overrideStatus = PBOverrideStatusFromLoadContext(serialized); WebSocketChannelParent* p = new WebSocketChannelParent(tabParent, loadContext, - overrideStatus); + overrideStatus, + aSerial); p->AddRef(); return p; } @@ -347,6 +350,23 @@ NeckoParent::DeallocPWebSocketParent(PWebSocketParent* actor) return true; } +PWebSocketEventListenerParent* +NeckoParent::AllocPWebSocketEventListenerParent(const uint64_t& aInnerWindowID) +{ + RefPtr c = + new WebSocketEventListenerParent(aInnerWindowID); + return c.forget().take(); +} + +bool +NeckoParent::DeallocPWebSocketEventListenerParent(PWebSocketEventListenerParent* aActor) +{ + RefPtr c = + dont_AddRef(static_cast(aActor)); + MOZ_ASSERT(c); + return true; +} + PDataChannelParent* NeckoParent::AllocPDataChannelParent(const uint32_t &channelId) { diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h index 8b0720b3dd..c548b1b6e0 100644 --- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -124,7 +124,8 @@ protected: virtual bool DeallocPFTPChannelParent(PFTPChannelParent*) override; virtual PWebSocketParent* AllocPWebSocketParent(const PBrowserOrId& browser, - const SerializedLoadContext& aSerialized) override; + const SerializedLoadContext& aSerialized, + const uint32_t& aSerial) override; virtual bool DeallocPWebSocketParent(PWebSocketParent*) override; virtual PTCPSocketParent* AllocPTCPSocketParent(const nsString& host, const uint16_t& port) override; @@ -172,6 +173,9 @@ protected: virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname, const uint16_t& flags, const nsresult& reason) override; + virtual PWebSocketEventListenerParent* + AllocPWebSocketEventListenerParent(const uint64_t& aInnerWindowID) override; + virtual bool DeallocPWebSocketEventListenerParent(PWebSocketEventListenerParent*) override; virtual mozilla::ipc::IProtocol* CloneProtocol(Channel* aChannel, diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index b317e6091a..2e1f2f5182 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -12,6 +12,7 @@ include protocol PBrowser; include protocol PWyciwygChannel; include protocol PFTPChannel; include protocol PWebSocket; +include protocol PWebSocketEventListener; include protocol PTCPSocket; include protocol PTCPServerSocket; include protocol PUDPSocket; @@ -45,6 +46,7 @@ prio(normal upto urgent) sync protocol PNecko manages PWyciwygChannel; manages PFTPChannel; manages PWebSocket; + manages PWebSocketEventListener; manages PTCPSocket; manages PTCPServerSocket; manages PUDPSocket; @@ -66,12 +68,14 @@ parent: PFTPChannel(PBrowserOrId browser, SerializedLoadContext loadContext, FTPChannelCreationArgs args); - PWebSocket(PBrowserOrId browser, SerializedLoadContext loadContext); + PWebSocket(PBrowserOrId browser, SerializedLoadContext loadContext, + uint32_t aSerialID); PTCPServerSocket(uint16_t localPort, uint16_t backlog, bool useArrayBuffers); PUDPSocket(Principal principal, nsCString filter); PDNSRequest(nsCString hostName, uint32_t flags, nsCString networkInterface); + PWebSocketEventListener(uint64_t aInnerWindowID); /* Predictor Methods */ PredPredict(OptionalURIParams targetURI, OptionalURIParams sourceURI, diff --git a/netwerk/ipc/RemoteOpenFileChild.cpp b/netwerk/ipc/RemoteOpenFileChild.cpp index 293b08bf3f..b55c0a04d3 100644 --- a/netwerk/ipc/RemoteOpenFileChild.cpp +++ b/netwerk/ipc/RemoteOpenFileChild.cpp @@ -229,6 +229,8 @@ RemoteOpenFileChild::AsyncRemoteFileOpen(int32_t aFlags, MOZ_CRASH("Couldn't get path from file!"); } + mListener = aListener; + if (mTabChild) { if (mTabChild->GetCachedFileDescriptor(path, this)) { // The file descriptor was found in the cache and OnCachedFileDescriptor() @@ -248,7 +250,6 @@ RemoteOpenFileChild::AsyncRemoteFileOpen(int32_t aFlags, // The chrome process now has a logical ref to us until it calls Send__delete. AddIPDLReference(); - mListener = aListener; mAsyncOpenCalled = true; return NS_OK; #endif diff --git a/netwerk/protocol/app/AppProtocolHandler.cpp b/netwerk/protocol/app/AppProtocolHandler.cpp index 77d9804708..d4ca57f4ea 100644 --- a/netwerk/protocol/app/AppProtocolHandler.cpp +++ b/netwerk/protocol/app/AppProtocolHandler.cpp @@ -460,12 +460,11 @@ AppProtocolHandler::NewChannel2(nsIURI* aUri, mAppInfoCache.Put(host, appInfo); } - bool noRemote = (appInfo->mIsCoreApp || - XRE_IsParentProcess()); - - // In-parent and CoreApps can directly access files, so use jar:file:// - nsAutoCString jarSpec(noRemote ? "jar:file://" - : "jar:remoteopenfile://"); + // Even core apps are on /system partition and can be accessed directly, but + // to ease sandboxing code not to handle the special case of core apps, only + // use scheme jar:file in parent, see bug 1119692 comment 20. + nsAutoCString jarSpec(XRE_IsParentProcess() ? "jar:file://" + : "jar:remoteopenfile://"); jarSpec += NS_ConvertUTF16toUTF8(appInfo->mPath) + NS_LITERAL_CSTRING("/application.zip!") + fileSpec; diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index 636aa34e64..28e280c5ef 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -1027,7 +1027,7 @@ NS_IMETHODIMP HttpChannelParent::OnStartSignedPackageRequest(const nsACString& aPackageId) { if (mTabParent) { - mTabParent->OnStartSignedPackageRequest(mChannel); + mTabParent->OnStartSignedPackageRequest(mChannel, aPackageId); } return NS_OK; } diff --git a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp index d863a7b2c2..f48911d150 100644 --- a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp +++ b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp @@ -15,12 +15,25 @@ #include "nsStandardURL.h" #include "LoadInfo.h" #include "nsIDOMNode.h" +#include "mozilla/dom/ContentChild.h" + +using mozilla::dom::ContentChild; PRLogModuleInfo *webSocketLog = nullptr; namespace mozilla { namespace net { +static uint64_t gNextWebSocketID = 0; + +// We use only 53 bits for the WebSocket serial ID so that it can be converted +// to and from a JS value without loss of precision. The upper bits of the +// WebSocket serial ID hold the process ID. The lower bits identify the +// WebSocket. +static const uint64_t kWebSocketIDTotalBits = 53; +static const uint64_t kWebSocketIDProcessBits = 22; +static const uint64_t kWebSocketIDWebSocketBits = kWebSocketIDTotalBits - kWebSocketIDProcessBits; + BaseWebSocketChannel::BaseWebSocketChannel() : mWasOpened(0) , mClientSetPingInterval(0) @@ -32,6 +45,24 @@ BaseWebSocketChannel::BaseWebSocketChannel() { if (!webSocketLog) webSocketLog = PR_NewLogModule("nsWebSocket"); + + // Generation of a unique serial ID. + uint64_t processID = 0; + if (XRE_IsContentProcess()) { + ContentChild* cc = ContentChild::GetSingleton(); + processID = cc->GetID(); + } + + uint64_t processBits = processID & ((uint64_t(1) << kWebSocketIDProcessBits) - 1); + + // Make sure no actual webSocket ends up with mWebSocketID == 0 but less then + // what the kWebSocketIDProcessBits allows. + if (++gNextWebSocketID >= (uint64_t(1) << kWebSocketIDWebSocketBits)) { + gNextWebSocketID = 1; + } + + uint64_t webSocketBits = gNextWebSocketID & ((uint64_t(1) << kWebSocketIDWebSocketBits) - 1); + mSerial = (processBits << kWebSocketIDWebSocketBits) | webSocketBits; } //----------------------------------------------------------------------------- @@ -198,6 +229,24 @@ BaseWebSocketChannel::InitLoadInfo(nsIDOMNode* aLoadingNode, return NS_OK; } +NS_IMETHODIMP +BaseWebSocketChannel::GetSerial(uint32_t* aSerial) +{ + if (!aSerial) { + return NS_ERROR_FAILURE; + } + + *aSerial = mSerial; + return NS_OK; +} + +NS_IMETHODIMP +BaseWebSocketChannel::SetSerial(uint32_t aSerial) +{ + mSerial = aSerial; + return NS_OK; +} + //----------------------------------------------------------------------------- // BaseWebSocketChannel::nsIProtocolHandler //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/websocket/BaseWebSocketChannel.h b/netwerk/protocol/websocket/BaseWebSocketChannel.h index d9f52864e6..34757d85bb 100644 --- a/netwerk/protocol/websocket/BaseWebSocketChannel.h +++ b/netwerk/protocol/websocket/BaseWebSocketChannel.h @@ -55,6 +55,8 @@ class BaseWebSocketChannel : public nsIWebSocketChannel, NS_IMETHOD InitLoadInfo(nsIDOMNode* aLoadingNode, nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal, uint32_t aSecurityFlags, uint32_t aContentPolicyType) override; + NS_IMETHOD GetSerial(uint32_t* aSerial) override; + NS_IMETHOD SetSerial(uint32_t aSerial) override; // Off main thread URI access. virtual void GetEffectiveURL(nsAString& aEffectiveURL) const = 0; @@ -98,6 +100,8 @@ class BaseWebSocketChannel : public nsIWebSocketChannel, uint32_t mPingInterval; /* milliseconds */ uint32_t mPingResponseTimeout; /* milliseconds */ + + uint32_t mSerial; }; } // namespace net diff --git a/netwerk/protocol/websocket/PWebSocket.ipdl b/netwerk/protocol/websocket/PWebSocket.ipdl index 16e4f7684e..82a451b1a1 100644 --- a/netwerk/protocol/websocket/PWebSocket.ipdl +++ b/netwerk/protocol/websocket/PWebSocket.ipdl @@ -26,6 +26,7 @@ parent: // Forwarded methods corresponding to methods on nsIWebSocketChannel AsyncOpen(URIParams aURI, nsCString aOrigin, + uint64_t aInnerWindowID, nsCString aProtocol, bool aSecure, // ping values only meaningful if client set them diff --git a/netwerk/protocol/websocket/PWebSocketEventListener.ipdl b/netwerk/protocol/websocket/PWebSocketEventListener.ipdl new file mode 100644 index 0000000000..ef0310d7cb --- /dev/null +++ b/netwerk/protocol/websocket/PWebSocketEventListener.ipdl @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ + +/* 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/. */ + +include protocol PNecko; + +using mozilla::net::WebSocketFrameData from "ipc/IPCMessageUtils.h"; + +namespace mozilla { +namespace net { + +async protocol PWebSocketEventListener +{ + manager PNecko; + +child: + WebSocketCreated(uint32_t awebSocketSerialID, + nsString aURI, + nsCString aProtocols); + + WebSocketOpened(uint32_t awebSocketSerialID, + nsString aEffectiveURI, + nsCString aProtocols, + nsCString aExtensions); + + WebSocketMessageAvailable(uint32_t awebSocketSerialID, + nsCString aData, + uint16_t aMessageType); + + WebSocketClosed(uint32_t awebSocketSerialID, + bool aWasClean, + uint16_t aCode, + nsString aReason); + + FrameReceived(uint32_t aWebSocketSerialID, + WebSocketFrameData aFrameData); + + FrameSent(uint32_t aWebSocketSerialID, + WebSocketFrameData aFrameData); + + __delete__(); + +parent: + Close(); +}; + +} //namespace net +} //namespace mozilla diff --git a/netwerk/protocol/websocket/WebSocketChannel.cpp b/netwerk/protocol/websocket/WebSocketChannel.cpp index 11953fa6cc..0b9eeefce3 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.cpp +++ b/netwerk/protocol/websocket/WebSocketChannel.cpp @@ -4,6 +4,7 @@ * 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/. */ +#include "WebSocketFrame.h" #include "WebSocketLog.h" #include "WebSocketChannel.h" @@ -11,6 +12,7 @@ #include "mozilla/Attributes.h" #include "mozilla/Endian.h" #include "mozilla/MathAlgorithms.h" +#include "mozilla/net/WebSocketEventService.h" #include "nsIURI.h" #include "nsIChannel.h" @@ -963,7 +965,8 @@ public: : mMsgType(type), mDeflated(false), mOrigLength(0) { MOZ_COUNT_CTOR(OutboundMessage); - mMsg.pString = str; + mMsg.pString.mValue = str; + mMsg.pString.mOrigValue = nullptr; mLength = str ? str->Length() : 0; } @@ -983,7 +986,9 @@ public: case kMsgTypeBinaryString: case kMsgTypePing: case kMsgTypePong: - delete mMsg.pString; + delete mMsg.pString.mValue; + if (mMsg.pString.mOrigValue) + delete mMsg.pString.mOrigValue; break; case kMsgTypeStream: // for now this only gets hit if msg deleted w/o being sent @@ -1004,13 +1009,21 @@ public: uint8_t* BeginWriting() { MOZ_ASSERT(mMsgType != kMsgTypeStream, "Stream should have been converted to string by now"); - return (uint8_t *)(mMsg.pString ? mMsg.pString->BeginWriting() : nullptr); + return (uint8_t *)(mMsg.pString.mValue ? mMsg.pString.mValue->BeginWriting() : nullptr); } uint8_t* BeginReading() { MOZ_ASSERT(mMsgType != kMsgTypeStream, "Stream should have been converted to string by now"); - return (uint8_t *)(mMsg.pString ? mMsg.pString->BeginReading() : nullptr); + return (uint8_t *)(mMsg.pString.mValue ? mMsg.pString.mValue->BeginReading() : nullptr); + } + + uint8_t* BeginOrigReading() { + MOZ_ASSERT(mMsgType != kMsgTypeStream, + "Stream should have been converted to string by now"); + if (!mDeflated) + return BeginReading(); + return (uint8_t *)(mMsg.pString.mOrigValue ? mMsg.pString.mOrigValue->BeginReading() : nullptr); } nsresult ConvertStreamToString() @@ -1031,7 +1044,8 @@ public: mMsg.pStream->Close(); mMsg.pStream->Release(); - mMsg.pString = temp.forget(); + mMsg.pString.mValue = temp.forget(); + mMsg.pString.mOrigValue = nullptr; mMsgType = kMsgTypeBinaryString; return NS_OK; @@ -1074,14 +1088,17 @@ public: mOrigLength = mLength; mDeflated = true; mLength = temp->Length(); - delete mMsg.pString; - mMsg.pString = temp.forget(); + mMsg.pString.mOrigValue = mMsg.pString.mValue; + mMsg.pString.mValue = temp.forget(); return true; } private: union { - nsCString *pString; + struct { + nsCString *mValue; + nsCString *mOrigValue; + } pString; nsIInputStream *pStream; } mMsg; WsMsgType mMsgType; @@ -1121,8 +1138,6 @@ NS_IMPL_ISUPPORTS(OutboundEnqueuer, nsIRunnable) // WebSocketChannel //----------------------------------------------------------------------------- -uint32_t WebSocketChannel::sSerialSeed = 0; - WebSocketChannel::WebSocketChannel() : mPort(0), mCloseTimeout(20000), @@ -1149,7 +1164,7 @@ WebSocketChannel::WebSocketChannel() : mStopOnClose(NS_OK), mServerCloseCode(CLOSE_ABNORMAL), mScriptCloseCode(0), - mFragmentOpcode(kContinuation), + mFragmentOpcode(nsIWebSocketFrame::OPCODE_CONTINUATION), mFragmentAccumulator(0), mBuffered(0), mBufferSize(kIncomingBufferInitialSize), @@ -1178,7 +1193,7 @@ WebSocketChannel::WebSocketChannel() : if (NS_FAILED(rv)) LOG(("Failed to initiate dashboard service.")); - mSerial = sSerialSeed++; + mService = WebSocketEventService::GetOrCreate(); } WebSocketChannel::~WebSocketChannel() @@ -1210,6 +1225,7 @@ WebSocketChannel::~WebSocketChannel() NS_ReleaseOnMainThread(mLoadGroup); NS_ReleaseOnMainThread(mLoadInfo); + NS_ReleaseOnMainThread(static_cast(mService.forget().take())); } NS_IMETHODIMP @@ -1502,11 +1518,15 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) uint32_t totalAvail = avail; while (avail >= 2) { - int64_t payloadLength64 = mFramePtr[1] & 0x7F; + int64_t payloadLength64 = mFramePtr[1] & kPayloadLengthBitsMask; uint8_t finBit = mFramePtr[0] & kFinalFragBit; uint8_t rsvBits = mFramePtr[0] & kRsvBitsMask; + uint8_t rsvBit1 = mFramePtr[0] & kRsv1Bit; + uint8_t rsvBit2 = mFramePtr[0] & kRsv2Bit; + uint8_t rsvBit3 = mFramePtr[0] & kRsv3Bit; + uint8_t opcode = mFramePtr[0] & kOpcodeBitsMask; uint8_t maskBit = mFramePtr[1] & kMaskBit; - uint8_t opcode = mFramePtr[0] & 0x0F; + uint32_t mask = 0; uint32_t framingLength = 2; if (maskBit) @@ -1565,7 +1585,7 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) // frames to the client, but it is allowed LOG(("WebSocketChannel:: Client RECEIVING masked frame.")); - uint32_t mask = NetworkEndian::readUint32(payload - 4); + mask = NetworkEndian::readUint32(payload - 4); ApplyMask(mask, payload, payloadLength); } @@ -1589,22 +1609,23 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) } } - if (!finBit || opcode == kContinuation) { + if (!finBit || opcode == nsIWebSocketFrame::OPCODE_CONTINUATION) { // This is part of a fragment response // Only the first frame has a non zero op code: Make sure we don't see a // first frame while some old fragments are open - if ((mFragmentAccumulator != 0) && (opcode != kContinuation)) { + if ((mFragmentAccumulator != 0) && + (opcode != nsIWebSocketFrame::OPCODE_CONTINUATION)) { LOG(("WebSocketChannel:: nested fragments\n")); return NS_ERROR_ILLEGAL_VALUE; } LOG(("WebSocketChannel:: Accumulating Fragment %ld\n", payloadLength)); - if (opcode == kContinuation) { + if (opcode == nsIWebSocketFrame::OPCODE_CONTINUATION) { // Make sure this continuation fragment isn't the first fragment - if (mFragmentOpcode == kContinuation) { + if (mFragmentOpcode == nsIWebSocketFrame::OPCODE_CONTINUATION) { LOG(("WebSocketHeandler:: continuation code in first fragment\n")); return NS_ERROR_ILLEGAL_VALUE; } @@ -1629,9 +1650,9 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) mFragmentAccumulator = 0; opcode = mFragmentOpcode; // reset to detect if next message illegally starts with continuation - mFragmentOpcode = kContinuation; + mFragmentOpcode = nsIWebSocketFrame::OPCODE_CONTINUATION; } else { - opcode = kContinuation; + opcode = nsIWebSocketFrame::OPCODE_CONTINUATION; mFragmentAccumulator += payloadLength; } } else if (mFragmentAccumulator != 0 && !(opcode & kControlFrameMask)) { @@ -1649,7 +1670,7 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) } else if (mStopped) { LOG(("WebSocketChannel:: ignoring read frame code %d after completion\n", opcode)); - } else if (opcode == kText) { + } else if (opcode == nsIWebSocketFrame::OPCODE_TEXT) { bool isDeflated = mPMCECompressor && mPMCECompressor->IsMessageDeflated(); LOG(("WebSocketChannel:: %stext frame received\n", isDeflated ? "deflated " : "")); @@ -1678,6 +1699,14 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) return NS_ERROR_CANNOT_CONVERT_DATA; } + RefPtr frame = + mService->CreateFrameIfNeeded(finBit, rsvBit1, rsvBit2, rsvBit3, + opcode, maskBit, mask, utf8Data); + + if (frame) { + mService->FrameReceived(mSerial, mInnerWindowID, frame); + } + mTargetThread->Dispatch(new CallOnMessageAvailable(this, utf8Data, -1), NS_DISPATCH_NORMAL); if (mConnectionLogService && !mPrivateBrowsing) { @@ -1693,7 +1722,12 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) return NS_ERROR_ILLEGAL_VALUE; } - if (opcode == kClose) { + RefPtr frame = + mService->CreateFrameIfNeeded(finBit, rsvBit1, rsvBit2, rsvBit3, + opcode, maskBit, mask, payload, + payloadLength); + + if (opcode == nsIWebSocketFrame::OPCODE_CLOSE) { LOG(("WebSocketChannel:: close received\n")); mServerClosed = 1; @@ -1725,6 +1759,14 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) mCloseTimer->Cancel(); mCloseTimer = nullptr; } + + if (frame) { + // We send the frame immediately becuase we want to have it dispatched + // before the CallOnServerClose. + mService->FrameReceived(mSerial, mInnerWindowID, frame); + frame = nullptr; + } + if (mListenerMT) { mTargetThread->Dispatch(new CallOnServerClose(this, mServerCloseCode, mServerCloseReason), @@ -1733,12 +1775,12 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) if (mClientClosed) ReleaseSession(); - } else if (opcode == kPing) { + } else if (opcode == nsIWebSocketFrame::OPCODE_PING) { LOG(("WebSocketChannel:: ping received\n")); GeneratePong(payload, payloadLength); - } else if (opcode == kPong) { - // opcode kPong: the mere act of receiving the packet is all we need - // to do for the pong to trigger the activity timers + } else if (opcode == nsIWebSocketFrame::OPCODE_PONG) { + // opcode OPCODE_PONG: the mere act of receiving the packet is all we + // need to do for the pong to trigger the activity timers LOG(("WebSocketChannel:: pong received\n")); } else { /* unknown control frame opcode */ @@ -1759,7 +1801,11 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) mBuffered -= framingLength + payloadLength; payloadLength = 0; } - } else if (opcode == kBinary) { + + if (frame) { + mService->FrameReceived(mSerial, mInnerWindowID, frame); + } + } else if (opcode == nsIWebSocketFrame::OPCODE_BINARY) { bool isDeflated = mPMCECompressor && mPMCECompressor->IsMessageDeflated(); LOG(("WebSocketChannel:: %sbinary frame received\n", isDeflated ? "deflated " : "")); @@ -1782,6 +1828,13 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) } } + RefPtr frame = + mService->CreateFrameIfNeeded(finBit, rsvBit1, rsvBit2, rsvBit3, + opcode, maskBit, mask, binaryData); + if (frame) { + mService->FrameReceived(mSerial, mInnerWindowID, frame); + } + mTargetThread->Dispatch( new CallOnMessageAvailable(this, binaryData, binaryData.Length()), NS_DISPATCH_NORMAL); @@ -1791,7 +1844,7 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) LOG(("Added new received msg for %s", mHost.get())); } } - } else if (opcode != kContinuation) { + } else if (opcode != nsIWebSocketFrame::OPCODE_CONTINUATION) { /* unknown opcode */ LOG(("WebSocketChannel:: unknown op code %d\n", opcode)); return NS_ERROR_ILLEGAL_VALUE; @@ -1842,7 +1895,7 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count) return NS_OK; } -void +/* static */ void WebSocketChannel::ApplyMask(uint32_t mask, uint8_t *data, uint64_t len) { if (!data || len == 0) @@ -1985,7 +2038,7 @@ WebSocketChannel::PrimeNewOutgoingMessage() } mClientClosed = 1; - mOutHeader[0] = kFinalFragBit | kClose; + mOutHeader[0] = kFinalFragBit | nsIWebSocketFrame::OPCODE_CLOSE; mOutHeader[1] = kMaskBit; // payload is offset 6 including 4 for the mask @@ -2039,13 +2092,13 @@ WebSocketChannel::PrimeNewOutgoingMessage() } else { switch (msgType) { case kMsgTypePong: - mOutHeader[0] = kFinalFragBit | kPong; + mOutHeader[0] = kFinalFragBit | nsIWebSocketFrame::OPCODE_PONG; break; case kMsgTypePing: - mOutHeader[0] = kFinalFragBit | kPing; + mOutHeader[0] = kFinalFragBit | nsIWebSocketFrame::OPCODE_PING; break; case kMsgTypeString: - mOutHeader[0] = kFinalFragBit | kText; + mOutHeader[0] = kFinalFragBit | nsIWebSocketFrame::OPCODE_TEXT; break; case kMsgTypeStream: // HACK ALERT: read in entire stream into string. @@ -2062,7 +2115,7 @@ WebSocketChannel::PrimeNewOutgoingMessage() // no break: fall down into binary string case case kMsgTypeBinaryString: - mOutHeader[0] = kFinalFragBit | kBinary; + mOutHeader[0] = kFinalFragBit | nsIWebSocketFrame::OPCODE_BINARY; break; case kMsgTypeFin: MOZ_ASSERT(false, "unreachable"); // avoid compiler warning @@ -2124,6 +2177,23 @@ WebSocketChannel::PrimeNewOutgoingMessage() // handful of bytes and might rotate the mask, so we can just do it locally. // For real data frames we ship the bulk of the payload off to ApplyMask() + RefPtr frame = + mService->CreateFrameIfNeeded( + mOutHeader[0] & WebSocketChannel::kFinalFragBit, + mOutHeader[0] & WebSocketChannel::kRsv1Bit, + mOutHeader[0] & WebSocketChannel::kRsv2Bit, + mOutHeader[0] & WebSocketChannel::kRsv3Bit, + mOutHeader[0] & WebSocketChannel::kOpcodeBitsMask, + mOutHeader[1] & WebSocketChannel::kMaskBit, + mask, + payload, mHdrOutToSend - (payload - mOutHeader), + mCurrentOut->BeginOrigReading(), + mCurrentOut->OrigLength()); + + if (frame) { + mService->FrameSent(mSerial, mInnerWindowID, frame); + } + while (payload < (mOutHeader + mHdrOutToSend)) { *payload ^= mask >> 24; mask = RotateLeft(mask, 8); @@ -2146,7 +2216,7 @@ WebSocketChannel::PrimeNewOutgoingMessage() // Transmitting begins - mHdrOutToSend bytes from mOutHeader and // mCurrentOut->Length() bytes from mCurrentOut. The latter may be // coaleseced into the former for small messages or as the result of the - // compression process, + // compression process. } void @@ -3137,6 +3207,7 @@ WebSocketChannel::GetSecurityInfo(nsISupports **aSecurityInfo) NS_IMETHODIMP WebSocketChannel::AsyncOpen(nsIURI *aURI, const nsACString &aOrigin, + uint64_t aInnerWindowID, nsIWebSocketListener *aListener, nsISupports *aContext) { @@ -3243,6 +3314,7 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI, mURI = mOriginalURI; mURI->GetHostPort(mHost); mOrigin = aOrigin; + mInnerWindowID = aInnerWindowID; nsCOMPtr localURI; nsCOMPtr localChannel; diff --git a/netwerk/protocol/websocket/WebSocketChannel.h b/netwerk/protocol/websocket/WebSocketChannel.h index 6d69380a85..c054b96fe9 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.h +++ b/netwerk/protocol/websocket/WebSocketChannel.h @@ -39,7 +39,8 @@ class nsIRandomGenerator; class nsISocketTransport; class nsIURI; -namespace mozilla { namespace net { +namespace mozilla { +namespace net { class OutboundMessage; class OutboundEnqueuer; @@ -49,6 +50,7 @@ class CallOnMessageAvailable; class CallOnStop; class CallOnServerClose; class CallAcknowledge; +class WebSocketEventService; // Used to enforce "1 connecting websocket per host" rule, and reconnect delays enum wsConnectingState { @@ -70,6 +72,8 @@ class WebSocketChannel : public BaseWebSocketChannel, public nsIInterfaceRequestor, public nsIChannelEventSink { + friend class WebSocketFrame; + public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIHTTPUPGRADELISTENER @@ -88,6 +92,7 @@ public: // NS_IMETHOD AsyncOpen(nsIURI *aURI, const nsACString &aOrigin, + uint64_t aWindowID, nsIWebSocketListener *aListener, nsISupports *aContext) override; NS_IMETHOD Close(uint16_t aCode, const nsACString & aReason) override; @@ -104,23 +109,19 @@ public: void GetEffectiveURL(nsAString& aEffectiveURL) const override; bool IsEncrypted() const override; - enum { - // Non Control Frames - kContinuation = 0x0, - kText = 0x1, - kBinary = 0x2, - - // Control Frames - kClose = 0x8, - kPing = 0x9, - kPong = 0xA - }; - const static uint32_t kControlFrameMask = 0x8; - const static uint8_t kMaskBit = 0x80; + + // First byte of the header const static uint8_t kFinalFragBit = 0x80; const static uint8_t kRsvBitsMask = 0x70; const static uint8_t kRsv1Bit = 0x40; + const static uint8_t kRsv2Bit = 0x20; + const static uint8_t kRsv3Bit = 0x10; + const static uint8_t kOpcodeBitsMask = 0x0F; + + // Second byte of the header + const static uint8_t kMaskBit = 0x80; + const static uint8_t kPayloadLengthBitsMask = 0x7F; protected: virtual ~WebSocketChannel(); @@ -166,7 +167,8 @@ private: void DecrementSessionCount(); void EnsureHdrOut(uint32_t size); - void ApplyMask(uint32_t mask, uint8_t *data, uint64_t len); + + static void ApplyMask(uint32_t mask, uint8_t *data, uint64_t len); bool IsPersistentFramePtr(); nsresult ProcessInput(uint8_t *buffer, uint32_t count); @@ -218,8 +220,12 @@ private: const static int32_t kLingeringCloseTimeout = 1000; const static int32_t kLingeringCloseThreshold = 50; + RefPtr mService; + int32_t mMaxConcurrentConnections; + uint64_t mInnerWindowID; + // following members are accessed only on the main thread uint32_t mGotUpgradeOK : 1; uint32_t mRecvdHttpUpgradeTransport : 1; @@ -281,8 +287,6 @@ private: bool mPrivateBrowsing; nsCOMPtr mConnectionLogService; - uint32_t mSerial; - static uint32_t sSerialSeed; mozilla::Mutex mMutex; diff --git a/netwerk/protocol/websocket/WebSocketChannelChild.cpp b/netwerk/protocol/websocket/WebSocketChannelChild.cpp index 681935a38b..34f87a9868 100644 --- a/netwerk/protocol/websocket/WebSocketChannelChild.cpp +++ b/netwerk/protocol/websocket/WebSocketChannelChild.cpp @@ -456,6 +456,7 @@ WebSocketChannelChild::OnServerClose(const uint16_t& aCode, NS_IMETHODIMP WebSocketChannelChild::AsyncOpen(nsIURI *aURI, const nsACString &aOrigin, + uint64_t aInnerWindowID, nsIWebSocketListener *aListener, nsISupports *aContext) { @@ -488,9 +489,10 @@ WebSocketChannelChild::AsyncOpen(nsIURI *aURI, NS_ENSURE_SUCCESS(rv, rv); gNeckoChild->SendPWebSocketConstructor(this, tabChild, - IPC::SerializedLoadContext(this)); - if (!SendAsyncOpen(uri, nsCString(aOrigin), mProtocol, mEncrypted, - mPingInterval, mClientSetPingInterval, + IPC::SerializedLoadContext(this), + mSerial); + if (!SendAsyncOpen(uri, nsCString(aOrigin), aInnerWindowID, mProtocol, + mEncrypted, mPingInterval, mClientSetPingInterval, mPingResponseTimeout, mClientSetPingTimeout, loadInfoArgs)) { return NS_ERROR_UNEXPECTED; } diff --git a/netwerk/protocol/websocket/WebSocketChannelChild.h b/netwerk/protocol/websocket/WebSocketChannelChild.h index c5761f3db9..9fbb707ed0 100644 --- a/netwerk/protocol/websocket/WebSocketChannelChild.h +++ b/netwerk/protocol/websocket/WebSocketChannelChild.h @@ -29,7 +29,9 @@ class WebSocketChannelChild final : public BaseWebSocketChannel, // nsIWebSocketChannel methods BaseWebSocketChannel didn't implement for us // NS_IMETHOD AsyncOpen(nsIURI *aURI, const nsACString &aOrigin, - nsIWebSocketListener *aListener, nsISupports *aContext) override; + uint64_t aInnerWindowID, + nsIWebSocketListener *aListener, + nsISupports *aContext) override; NS_IMETHOD Close(uint16_t code, const nsACString & reason) override; NS_IMETHOD SendMsg(const nsACString &aMsg) override; NS_IMETHOD SendBinaryMsg(const nsACString &aMsg) override; diff --git a/netwerk/protocol/websocket/WebSocketChannelParent.cpp b/netwerk/protocol/websocket/WebSocketChannelParent.cpp index 7daabab062..2354703b0f 100644 --- a/netwerk/protocol/websocket/WebSocketChannelParent.cpp +++ b/netwerk/protocol/websocket/WebSocketChannelParent.cpp @@ -26,10 +26,12 @@ NS_IMPL_ISUPPORTS(WebSocketChannelParent, WebSocketChannelParent::WebSocketChannelParent(nsIAuthPromptProvider* aAuthProvider, nsILoadContext* aLoadContext, - PBOverrideStatus aOverrideStatus) + PBOverrideStatus aOverrideStatus, + uint32_t aSerial) : mAuthProvider(aAuthProvider) , mLoadContext(aLoadContext) , mIPCOpen(true) + , mSerial(aSerial) { // Websocket channels can't have a private browsing override MOZ_ASSERT_IF(!aLoadContext, aOverrideStatus == kPBOverride_Unset); @@ -60,6 +62,7 @@ WebSocketChannelParent::RecvDeleteSelf() bool WebSocketChannelParent::RecvAsyncOpen(const URIParams& aURI, const nsCString& aOrigin, + const uint64_t& aInnerWindowID, const nsCString& aProtocol, const bool& aSecure, const uint32_t& aPingInterval, @@ -94,6 +97,11 @@ WebSocketChannelParent::RecvAsyncOpen(const URIParams& aURI, if (NS_FAILED(rv)) goto fail; + rv = mChannel->SetSerial(mSerial); + if (NS_WARN_IF(NS_FAILED(rv))) { + goto fail; + } + rv = LoadInfoArgsToLoadInfo(aLoadInfoArgs, getter_AddRefs(loadInfo)); if (NS_FAILED(rv)) goto fail; @@ -128,7 +136,7 @@ WebSocketChannelParent::RecvAsyncOpen(const URIParams& aURI, mChannel->SetPingTimeout(aPingTimeout / 1000); } - rv = mChannel->AsyncOpen(uri, aOrigin, this, nullptr); + rv = mChannel->AsyncOpen(uri, aOrigin, aInnerWindowID, this, nullptr); if (NS_FAILED(rv)) goto fail; diff --git a/netwerk/protocol/websocket/WebSocketChannelParent.h b/netwerk/protocol/websocket/WebSocketChannelParent.h index 065b97e44f..293223f8a6 100644 --- a/netwerk/protocol/websocket/WebSocketChannelParent.h +++ b/netwerk/protocol/websocket/WebSocketChannelParent.h @@ -35,11 +35,13 @@ class WebSocketChannelParent : public PWebSocketParent, WebSocketChannelParent(nsIAuthPromptProvider* aAuthProvider, nsILoadContext* aLoadContext, - PBOverrideStatus aOverrideStatus); + PBOverrideStatus aOverrideStatus, + uint32_t aSerial); private: bool RecvAsyncOpen(const URIParams& aURI, const nsCString& aOrigin, + const uint64_t& aInnerWindowID, const nsCString& aProtocol, const bool& aSecure, const uint32_t& aPingInterval, @@ -65,6 +67,7 @@ class WebSocketChannelParent : public PWebSocketParent, nsCOMPtr mLoadContext; bool mIPCOpen; + uint32_t mSerial; }; } // namespace net diff --git a/netwerk/protocol/websocket/WebSocketEventListenerChild.cpp b/netwerk/protocol/websocket/WebSocketEventListenerChild.cpp new file mode 100644 index 0000000000..08a57b817b --- /dev/null +++ b/netwerk/protocol/websocket/WebSocketEventListenerChild.cpp @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "WebSocketEventListenerChild.h" + +#include "WebSocketEventService.h" +#include "WebSocketFrame.h" + +namespace mozilla { +namespace net { + +WebSocketEventListenerChild::WebSocketEventListenerChild(uint64_t aInnerWindowID) + : mService(WebSocketEventService::GetOrCreate()) + , mInnerWindowID(aInnerWindowID) +{} + +WebSocketEventListenerChild::~WebSocketEventListenerChild() +{ + MOZ_ASSERT(!mService); +} + +bool +WebSocketEventListenerChild::RecvWebSocketCreated(const uint32_t& aWebSocketSerialID, + const nsString& aURI, + const nsCString& aProtocols) +{ + if (mService) { + mService->WebSocketCreated(aWebSocketSerialID, mInnerWindowID, aURI, + aProtocols); + } + + return true; +} + +bool +WebSocketEventListenerChild::RecvWebSocketOpened(const uint32_t& aWebSocketSerialID, + const nsString& aEffectiveURI, + const nsCString& aProtocols, + const nsCString& aExtensions) +{ + if (mService) { + mService->WebSocketOpened(aWebSocketSerialID, mInnerWindowID, + aEffectiveURI, aProtocols, aExtensions); + } + + return true; +} + +bool +WebSocketEventListenerChild::RecvWebSocketMessageAvailable(const uint32_t& aWebSocketSerialID, + const nsCString& aData, + const uint16_t& aMessageType) +{ + if (mService) { + mService->WebSocketMessageAvailable(aWebSocketSerialID, mInnerWindowID, + aData, aMessageType); + } + + return true; +} + +bool +WebSocketEventListenerChild::RecvWebSocketClosed(const uint32_t& aWebSocketSerialID, + const bool& aWasClean, + const uint16_t& aCode, + const nsString& aReason) +{ + if (mService) { + mService->WebSocketClosed(aWebSocketSerialID, mInnerWindowID, + aWasClean, aCode, aReason); + } + + return true; +} + +bool +WebSocketEventListenerChild::RecvFrameReceived(const uint32_t& aWebSocketSerialID, + const WebSocketFrameData& aFrameData) +{ + if (mService) { + RefPtr frame = new WebSocketFrame(aFrameData); + mService->FrameReceived(aWebSocketSerialID, mInnerWindowID, frame); + } + + return true; +} + +bool +WebSocketEventListenerChild::RecvFrameSent(const uint32_t& aWebSocketSerialID, + const WebSocketFrameData& aFrameData) +{ + if (mService) { + RefPtr frame = new WebSocketFrame(aFrameData); + mService->FrameSent(aWebSocketSerialID, mInnerWindowID, frame); + } + + return true; +} + +void +WebSocketEventListenerChild::Close() +{ + mService = nullptr; + SendClose(); +} + +void +WebSocketEventListenerChild::ActorDestroy(ActorDestroyReason aWhy) +{ + mService = nullptr; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/websocket/WebSocketEventListenerChild.h b/netwerk/protocol/websocket/WebSocketEventListenerChild.h new file mode 100644 index 0000000000..8a42abac2b --- /dev/null +++ b/netwerk/protocol/websocket/WebSocketEventListenerChild.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_net_WebSocketEventListenerChild_h +#define mozilla_net_WebSocketEventListenerChild_h + +#include "mozilla/net/PWebSocketEventListenerChild.h" + +namespace mozilla { +namespace net { + +class WebSocketEventService; + +class WebSocketEventListenerChild final : public PWebSocketEventListenerChild +{ +public: + NS_INLINE_DECL_REFCOUNTING(WebSocketEventListenerChild) + + explicit WebSocketEventListenerChild(uint64_t aInnerWindowID); + + bool RecvWebSocketCreated(const uint32_t& aWebSocketSerialID, + const nsString& aURI, + const nsCString& aProtocols) override; + + bool RecvWebSocketOpened(const uint32_t& aWebSocketSerialID, + const nsString& aEffectiveURI, + const nsCString& aProtocols, + const nsCString& aExtensions) override; + + bool RecvWebSocketMessageAvailable(const uint32_t& aWebSocketSerialID, + const nsCString& aData, + const uint16_t& aMessageType) override; + + bool RecvWebSocketClosed(const uint32_t& aWebSocketSerialID, + const bool& aWasClean, + const uint16_t& aCode, + const nsString& aReason) override; + + bool RecvFrameReceived(const uint32_t& aWebSocketSerialID, + const WebSocketFrameData& aFrameData) override; + + bool RecvFrameSent(const uint32_t& aWebSocketSerialID, + const WebSocketFrameData& aFrameData) override; + + void Close(); + +private: + ~WebSocketEventListenerChild(); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + RefPtr mService; + uint64_t mInnerWindowID; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_WebSocketEventListenerChild_h diff --git a/netwerk/protocol/websocket/WebSocketEventListenerParent.cpp b/netwerk/protocol/websocket/WebSocketEventListenerParent.cpp new file mode 100644 index 0000000000..ef1d57188e --- /dev/null +++ b/netwerk/protocol/websocket/WebSocketEventListenerParent.cpp @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "WebSocketEventListenerParent.h" +#include "mozilla/unused.h" + +namespace mozilla { +namespace net { + +NS_INTERFACE_MAP_BEGIN(WebSocketEventListenerParent) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebSocketEventListener) + NS_INTERFACE_MAP_ENTRY(nsIWebSocketEventListener) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(WebSocketEventListenerParent) +NS_IMPL_RELEASE(WebSocketEventListenerParent) + +WebSocketEventListenerParent::WebSocketEventListenerParent(uint64_t aInnerWindowID) + : mService(WebSocketEventService::GetOrCreate()) + , mInnerWindowID(aInnerWindowID) +{ + mService->AddListener(mInnerWindowID, this); +} + +WebSocketEventListenerParent::~WebSocketEventListenerParent() +{ + MOZ_ASSERT(!mService); +} + +bool +WebSocketEventListenerParent::RecvClose() +{ + if (mService) { + UnregisterListener(); + unused << Send__delete__(this); + } + + return true; +} + +void +WebSocketEventListenerParent::ActorDestroy(ActorDestroyReason aWhy) +{ + UnregisterListener(); +} + +void +WebSocketEventListenerParent::UnregisterListener() +{ + if (mService) { + mService->RemoveListener(mInnerWindowID, this); + mService = nullptr; + } +} + +NS_IMETHODIMP +WebSocketEventListenerParent::WebSocketCreated(uint32_t aWebSocketSerialID, + const nsAString& aURI, + const nsACString& aProtocols) +{ + unused << SendWebSocketCreated(aWebSocketSerialID, nsString(aURI), + nsCString(aProtocols)); + return NS_OK; +} + +NS_IMETHODIMP +WebSocketEventListenerParent::WebSocketOpened(uint32_t aWebSocketSerialID, + const nsAString& aEffectiveURI, + const nsACString& aProtocols, + const nsACString& aExtensions) +{ + unused << SendWebSocketOpened(aWebSocketSerialID, nsString(aEffectiveURI), + nsCString(aProtocols), nsCString(aExtensions)); + return NS_OK; +} + +NS_IMETHODIMP +WebSocketEventListenerParent::WebSocketClosed(uint32_t aWebSocketSerialID, + bool aWasClean, + uint16_t aCode, + const nsAString& aReason) +{ + unused << SendWebSocketClosed(aWebSocketSerialID, aWasClean, aCode, + nsString(aReason)); + return NS_OK; +} + +NS_IMETHODIMP +WebSocketEventListenerParent::WebSocketMessageAvailable(uint32_t aWebSocketSerialID, + const nsACString& aData, + uint16_t aMessageType) +{ + unused << SendWebSocketMessageAvailable(aWebSocketSerialID, nsCString(aData), + aMessageType); + return NS_OK; +} + +NS_IMETHODIMP +WebSocketEventListenerParent::FrameReceived(uint32_t aWebSocketSerialID, + nsIWebSocketFrame* aFrame) +{ + if (!aFrame) { + return NS_ERROR_FAILURE; + } + + WebSocketFrame* frame = static_cast(aFrame); + unused << SendFrameReceived(aWebSocketSerialID, frame->Data()); + return NS_OK; +} + +NS_IMETHODIMP +WebSocketEventListenerParent::FrameSent(uint32_t aWebSocketSerialID, + nsIWebSocketFrame* aFrame) +{ + if (!aFrame) { + return NS_ERROR_FAILURE; + } + + WebSocketFrame* frame = static_cast(aFrame); + unused << SendFrameSent(aWebSocketSerialID, frame->Data()); + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/websocket/WebSocketEventListenerParent.h b/netwerk/protocol/websocket/WebSocketEventListenerParent.h new file mode 100644 index 0000000000..5b5b10d734 --- /dev/null +++ b/netwerk/protocol/websocket/WebSocketEventListenerParent.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_net_WebSocketEventListenerParent_h +#define mozilla_net_WebSocketEventListenerParent_h + +#include "mozilla/net/PWebSocketEventListenerParent.h" +#include "nsIWebSocketEventService.h" + +namespace mozilla { +namespace net { + +class WebSocketEventService; + +class WebSocketEventListenerParent final : public PWebSocketEventListenerParent + , public nsIWebSocketEventListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIWEBSOCKETEVENTLISTENER + + explicit WebSocketEventListenerParent(uint64_t aInnerWindowID); + +private: + ~WebSocketEventListenerParent(); + + virtual bool RecvClose() override; + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + void UnregisterListener(); + + RefPtr mService; + uint64_t mInnerWindowID; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_WebSocketEventListenerParent_h diff --git a/netwerk/protocol/websocket/WebSocketEventService.cpp b/netwerk/protocol/websocket/WebSocketEventService.cpp new file mode 100644 index 0000000000..06984e75ef --- /dev/null +++ b/netwerk/protocol/websocket/WebSocketEventService.cpp @@ -0,0 +1,579 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "WebSocketEventListenerChild.h" +#include "WebSocketEventService.h" +#include "WebSocketFrame.h" + +#include "mozilla/net/NeckoChild.h" +#include "mozilla/StaticPtr.h" +#include "nsISupportsPrimitives.h" +#include "nsXULAppAPI.h" + +extern PRThread *gSocketThread; + +namespace mozilla { +namespace net { + +namespace { + +StaticRefPtr gWebSocketEventService; + +bool +IsChildProcess() +{ + return XRE_GetProcessType() != GeckoProcessType_Default; +} + +} // anonymous namespace + +class WebSocketBaseRunnable : public nsRunnable +{ +public: + WebSocketBaseRunnable(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID) + : mWebSocketSerialID(aWebSocketSerialID) + , mInnerWindowID(aInnerWindowID) + {} + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + RefPtr service = WebSocketEventService::GetOrCreate(); + MOZ_ASSERT(service); + + WebSocketEventService::WindowListeners listeners; + service->GetListeners(mInnerWindowID, listeners); + + for (uint32_t i = 0; i < listeners.Length(); ++i) { + DoWork(listeners[i]); + } + + return NS_OK; + } + +protected: + ~WebSocketBaseRunnable() + {} + + virtual void DoWork(nsIWebSocketEventListener* aListener) = 0; + + uint32_t mWebSocketSerialID; + uint64_t mInnerWindowID; +}; + +class WebSocketFrameRunnable final : public WebSocketBaseRunnable +{ +public: + WebSocketFrameRunnable(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + WebSocketFrame* aFrame, + bool aFrameSent) + : WebSocketBaseRunnable(aWebSocketSerialID, aInnerWindowID) + , mFrame(aFrame) + , mFrameSent(aFrameSent) + {} + +private: + virtual void DoWork(nsIWebSocketEventListener* aListener) override + { + nsresult rv; + + if (mFrameSent) { + rv = aListener->FrameSent(mWebSocketSerialID, mFrame); + } else { + rv = aListener->FrameReceived(mWebSocketSerialID, mFrame); + } + + NS_WARN_IF(NS_FAILED(rv)); + } + + RefPtr mFrame; + bool mFrameSent; +}; + +class WebSocketCreatedRunnable final : public WebSocketBaseRunnable +{ +public: + WebSocketCreatedRunnable(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + const nsAString& aURI, + const nsACString& aProtocols) + : WebSocketBaseRunnable(aWebSocketSerialID, aInnerWindowID) + , mURI(aURI) + , mProtocols(aProtocols) + {} + +private: + virtual void DoWork(nsIWebSocketEventListener* aListener) override + { + nsresult rv = aListener->WebSocketCreated(mWebSocketSerialID, + mURI, mProtocols); + NS_WARN_IF(NS_FAILED(rv)); + } + + const nsString mURI; + const nsCString mProtocols; +}; + +class WebSocketOpenedRunnable final : public WebSocketBaseRunnable +{ +public: + WebSocketOpenedRunnable(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + const nsAString& aEffectiveURI, + const nsACString& aProtocols, + const nsACString& aExtensions) + : WebSocketBaseRunnable(aWebSocketSerialID, aInnerWindowID) + , mEffectiveURI(aEffectiveURI) + , mProtocols(aProtocols) + , mExtensions(aExtensions) + {} + +private: + virtual void DoWork(nsIWebSocketEventListener* aListener) override + { + nsresult rv = aListener->WebSocketOpened(mWebSocketSerialID, + mEffectiveURI, + mProtocols, + mExtensions); + NS_WARN_IF(NS_FAILED(rv)); + } + + const nsString mEffectiveURI; + const nsCString mProtocols; + const nsCString mExtensions; +}; + +class WebSocketMessageAvailableRunnable final : public WebSocketBaseRunnable +{ +public: + WebSocketMessageAvailableRunnable(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + const nsACString& aData, + uint16_t aMessageType) + : WebSocketBaseRunnable(aWebSocketSerialID, aInnerWindowID) + , mData(aData) + , mMessageType(aMessageType) + {} + +private: + virtual void DoWork(nsIWebSocketEventListener* aListener) override + { + nsresult rv = aListener->WebSocketMessageAvailable(mWebSocketSerialID, + mData, mMessageType); + NS_WARN_IF(NS_FAILED(rv)); + } + + const nsCString mData; + uint16_t mMessageType; +}; + +class WebSocketClosedRunnable final : public WebSocketBaseRunnable +{ +public: + WebSocketClosedRunnable(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + bool aWasClean, + uint16_t aCode, + const nsAString& aReason) + : WebSocketBaseRunnable(aWebSocketSerialID, aInnerWindowID) + , mWasClean(aWasClean) + , mCode(aCode) + , mReason(aReason) + {} + +private: + virtual void DoWork(nsIWebSocketEventListener* aListener) override + { + nsresult rv = aListener->WebSocketClosed(mWebSocketSerialID, + mWasClean, mCode, mReason); + NS_WARN_IF(NS_FAILED(rv)); + } + + bool mWasClean; + uint16_t mCode; + const nsString mReason; +}; + +/* static */ already_AddRefed +WebSocketEventService::GetOrCreate() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!gWebSocketEventService) { + gWebSocketEventService = new WebSocketEventService(); + } + + RefPtr service = gWebSocketEventService.get(); + return service.forget(); +} + +NS_INTERFACE_MAP_BEGIN(WebSocketEventService) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebSocketEventService) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsIWebSocketEventService) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(WebSocketEventService) +NS_IMPL_RELEASE(WebSocketEventService) + +WebSocketEventService::WebSocketEventService() + : mCountListeners(0) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + obs->AddObserver(this, "xpcom-shutdown", false); + obs->AddObserver(this, "inner-window-destroyed", false); + } +} + +WebSocketEventService::~WebSocketEventService() +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +void +WebSocketEventService::WebSocketCreated(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + const nsAString& aURI, + const nsACString& aProtocols) +{ + // Let's continue only if we have some listeners. + if (!HasListeners()) { + return; + } + + RefPtr runnable = + new WebSocketCreatedRunnable(aWebSocketSerialID, aInnerWindowID, + aURI, aProtocols); + nsresult rv = NS_DispatchToMainThread(runnable); + NS_WARN_IF(NS_FAILED(rv)); +} + +void +WebSocketEventService::WebSocketOpened(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + const nsAString& aEffectiveURI, + const nsACString& aProtocols, + const nsACString& aExtensions) +{ + // Let's continue only if we have some listeners. + if (!HasListeners()) { + return; + } + + RefPtr runnable = + new WebSocketOpenedRunnable(aWebSocketSerialID, aInnerWindowID, + aEffectiveURI, aProtocols, aExtensions); + nsresult rv = NS_DispatchToMainThread(runnable); + NS_WARN_IF(NS_FAILED(rv)); +} + +void +WebSocketEventService::WebSocketMessageAvailable(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + const nsACString& aData, + uint16_t aMessageType) +{ + // Let's continue only if we have some listeners. + if (!HasListeners()) { + return; + } + + RefPtr runnable = + new WebSocketMessageAvailableRunnable(aWebSocketSerialID, aInnerWindowID, + aData, aMessageType); + nsresult rv = NS_DispatchToMainThread(runnable); + NS_WARN_IF(NS_FAILED(rv)); +} + +void +WebSocketEventService::WebSocketClosed(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + bool aWasClean, + uint16_t aCode, + const nsAString& aReason) +{ + // Let's continue only if we have some listeners. + if (!HasListeners()) { + return; + } + + RefPtr runnable = + new WebSocketClosedRunnable(aWebSocketSerialID, aInnerWindowID, + aWasClean, aCode, aReason); + nsresult rv = NS_DispatchToMainThread(runnable); + NS_WARN_IF(NS_FAILED(rv)); +} + +void +WebSocketEventService::FrameReceived(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + WebSocketFrame* aFrame) +{ + MOZ_ASSERT(aFrame); + + // Let's continue only if we have some listeners. + if (!HasListeners()) { + return; + } + + RefPtr runnable = + new WebSocketFrameRunnable(aWebSocketSerialID, aInnerWindowID, + aFrame, false /* frameSent */); + nsresult rv = NS_DispatchToMainThread(runnable); + NS_WARN_IF(NS_FAILED(rv)); +} + +void +WebSocketEventService::FrameSent(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + WebSocketFrame* aFrame) +{ + MOZ_ASSERT(aFrame); + + // Let's continue only if we have some listeners. + if (!HasListeners()) { + return; + } + + RefPtr runnable = + new WebSocketFrameRunnable(aWebSocketSerialID, aInnerWindowID, + aFrame, true /* frameSent */); + + nsresult rv = NS_DispatchToMainThread(runnable); + NS_WARN_IF(NS_FAILED(rv)); +} + +NS_IMETHODIMP +WebSocketEventService::AddListener(uint64_t aInnerWindowID, + nsIWebSocketEventListener* aListener) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!aListener) { + return NS_ERROR_FAILURE; + } + + ++mCountListeners; + + WindowListener* listener = mWindows.Get(aInnerWindowID); + if (!listener) { + listener = new WindowListener(); + + if (IsChildProcess()) { + PWebSocketEventListenerChild* actor = + gNeckoChild->SendPWebSocketEventListenerConstructor(aInnerWindowID); + + listener->mActor = static_cast(actor); + MOZ_ASSERT(listener->mActor); + } + + mWindows.Put(aInnerWindowID, listener); + } + + listener->mListeners.AppendElement(aListener); + + return NS_OK; +} + +NS_IMETHODIMP +WebSocketEventService::RemoveListener(uint64_t aInnerWindowID, + nsIWebSocketEventListener* aListener) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!aListener) { + return NS_ERROR_FAILURE; + } + + WindowListener* listener = mWindows.Get(aInnerWindowID); + if (!listener) { + return NS_ERROR_FAILURE; + } + + if (!listener->mListeners.RemoveElement(aListener)) { + return NS_ERROR_FAILURE; + } + + // The last listener for this window. + if (listener->mListeners.IsEmpty()) { + if (IsChildProcess()) { + ShutdownActorListener(listener); + } + + mWindows.Remove(aInnerWindowID); + } + + MOZ_ASSERT(mCountListeners); + --mCountListeners; + + return NS_OK; +} + +NS_IMETHODIMP +WebSocketEventService::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!strcmp(aTopic, "xpcom-shutdown")) { + Shutdown(); + return NS_OK; + } + + if (!strcmp(aTopic, "inner-window-destroyed") && HasListeners()) { + nsCOMPtr wrapper = do_QueryInterface(aSubject); + NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE); + + uint64_t innerID; + nsresult rv = wrapper->GetData(&innerID); + NS_ENSURE_SUCCESS(rv, rv); + + WindowListener* listener = mWindows.Get(innerID); + if (!listener) { + return NS_OK; + } + + MOZ_ASSERT(mCountListeners >= listener->mListeners.Length()); + mCountListeners -= listener->mListeners.Length(); + + if (IsChildProcess()) { + ShutdownActorListener(listener); + } + + mWindows.Remove(innerID); + } + + // This should not happen. + return NS_ERROR_FAILURE; +} + +void +WebSocketEventService::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (gWebSocketEventService) { + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + obs->RemoveObserver(gWebSocketEventService, "xpcom-shutdown"); + obs->RemoveObserver(gWebSocketEventService, "inner-window-destroyed"); + } + + mWindows.Clear(); + gWebSocketEventService = nullptr; + } +} + +bool +WebSocketEventService::HasListeners() const +{ + return !!mCountListeners; +} + +void +WebSocketEventService::GetListeners(uint64_t aInnerWindowID, + WebSocketEventService::WindowListeners& aListeners) const +{ + aListeners.Clear(); + + WindowListener* listener = mWindows.Get(aInnerWindowID); + if (!listener) { + return; + } + + aListeners.AppendElements(listener->mListeners); +} + +void +WebSocketEventService::ShutdownActorListener(WindowListener* aListener) +{ + MOZ_ASSERT(aListener); + MOZ_ASSERT(aListener->mActor); + aListener->mActor->Close(); + aListener->mActor = nullptr; +} + +WebSocketFrame* +WebSocketEventService::CreateFrameIfNeeded(bool aFinBit, bool aRsvBit1, + bool aRsvBit2, bool aRsvBit3, + uint8_t aOpCode, bool aMaskBit, + uint32_t aMask, + const nsCString& aPayload) +{ + if (!HasListeners()) { + return nullptr; + } + + return new WebSocketFrame(aFinBit, aRsvBit1, aRsvBit2, aRsvBit3, aOpCode, + aMaskBit, aMask, aPayload); +} + +WebSocketFrame* +WebSocketEventService::CreateFrameIfNeeded(bool aFinBit, bool aRsvBit1, + bool aRsvBit2, bool aRsvBit3, + uint8_t aOpCode, bool aMaskBit, + uint32_t aMask, uint8_t* aPayload, + uint32_t aPayloadLength) +{ + if (!HasListeners()) { + return nullptr; + } + + nsAutoCString payloadStr; + if (NS_WARN_IF(!(payloadStr.Assign((const char*) aPayload, aPayloadLength, + mozilla::fallible)))) { + return nullptr; + } + + return new WebSocketFrame(aFinBit, aRsvBit1, aRsvBit2, aRsvBit3, aOpCode, + aMaskBit, aMask, payloadStr); +} + +WebSocketFrame* +WebSocketEventService::CreateFrameIfNeeded(bool aFinBit, bool aRsvBit1, + bool aRsvBit2, bool aRsvBit3, + uint8_t aOpCode, bool aMaskBit, + uint32_t aMask, + uint8_t* aPayloadInHdr, + uint32_t aPayloadInHdrLength, + uint8_t* aPayload, + uint32_t aPayloadLength) +{ + if (!HasListeners()) { + return nullptr; + } + + uint32_t payloadLength = aPayloadLength + aPayloadInHdrLength; + + nsAutoArrayPtr payload(new uint8_t[payloadLength]); + if (NS_WARN_IF(!payload)) { + return nullptr; + } + + if (aPayloadInHdrLength) { + memcpy(payload, aPayloadInHdr, aPayloadInHdrLength); + } + + memcpy(payload + aPayloadInHdrLength, aPayload, aPayloadLength); + + nsAutoCString payloadStr; + if (NS_WARN_IF(!(payloadStr.Assign((const char*) payload.get(), payloadLength, + mozilla::fallible)))) { + return nullptr; + } + + return new WebSocketFrame(aFinBit, aRsvBit1, aRsvBit2, aRsvBit3, aOpCode, + aMaskBit, aMask, payloadStr); +} + +} // net namespace +} // mozilla namespace diff --git a/netwerk/protocol/websocket/WebSocketEventService.h b/netwerk/protocol/websocket/WebSocketEventService.h new file mode 100644 index 0000000000..2515c05ebf --- /dev/null +++ b/netwerk/protocol/websocket/WebSocketEventService.h @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_net_WebSocketEventService_h +#define mozilla_net_WebSocketEventService_h + +#include "mozilla/Atomics.h" +#include "nsIWebSocketEventService.h" +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" +#include "nsIObserver.h" +#include "nsISupportsImpl.h" +#include "nsTArray.h" + +namespace mozilla { +namespace net { + +class WebSocketFrame; +class WebSocketEventListenerChild; + +class WebSocketEventService final : public nsIWebSocketEventService + , public nsIObserver +{ + friend class WebSocketBaseRunnable; + +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + NS_DECL_NSIWEBSOCKETEVENTSERVICE + + static already_AddRefed GetOrCreate(); + + void WebSocketCreated(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + const nsAString& aURI, + const nsACString& aProtocols); + + void WebSocketOpened(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + const nsAString& aEffectiveURI, + const nsACString& aProtocols, + const nsACString& aExtensions); + + void WebSocketMessageAvailable(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + const nsACString& aData, + uint16_t aMessageType); + + void WebSocketClosed(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + bool aWasClean, + uint16_t aCode, + const nsAString& aReason); + + void FrameReceived(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + WebSocketFrame* aFrame); + + void FrameSent(uint32_t aWebSocketSerialID, + uint64_t aInnerWindowID, + WebSocketFrame* aFrame); + + WebSocketFrame* + CreateFrameIfNeeded(bool aFinBit, bool aRsvBit1, bool aRsvBit2, bool aRsvBit3, + uint8_t aOpCode, bool aMaskBit, uint32_t aMask, + const nsCString& aPayload); + + WebSocketFrame* + CreateFrameIfNeeded(bool aFinBit, bool aRsvBit1, bool aRsvBit2, bool aRsvBit3, + uint8_t aOpCode, bool aMaskBit, uint32_t aMask, + uint8_t* aPayload, uint32_t aPayloadLength); + + WebSocketFrame* + CreateFrameIfNeeded(bool aFinBit, bool aRsvBit1, bool aRsvBit2, bool aRsvBit3, + uint8_t aOpCode, bool aMaskBit, uint32_t aMask, + uint8_t* aPayloadInHdr, uint32_t aPayloadInHdrLength, + uint8_t* aPayload, uint32_t aPayloadLength); + +private: + WebSocketEventService(); + ~WebSocketEventService(); + + bool HasListeners() const; + void Shutdown(); + + typedef nsTArray> WindowListeners; + + struct WindowListener + { + WindowListeners mListeners; + RefPtr mActor; + }; + + void GetListeners(uint64_t aInnerWindowID, + WindowListeners& aListeners) const; + + void ShutdownActorListener(WindowListener* aListener); + + // Used only on the main-thread. + nsClassHashtable mWindows; + + Atomic mCountListeners; +}; + +} // net namespace +} // mozilla namespace + +#endif // mozilla_net_WebSocketEventService_h diff --git a/netwerk/protocol/websocket/WebSocketFrame.cpp b/netwerk/protocol/websocket/WebSocketFrame.cpp new file mode 100644 index 0000000000..209942dc56 --- /dev/null +++ b/netwerk/protocol/websocket/WebSocketFrame.cpp @@ -0,0 +1,168 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "WebSocketFrame.h" + +#include "WebSocketChannel.h" + +extern PRThread *gSocketThread; + +namespace mozilla { +namespace net { + +NS_INTERFACE_MAP_BEGIN(WebSocketFrame) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebSocketFrame) + NS_INTERFACE_MAP_ENTRY(nsIWebSocketFrame) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(WebSocketFrame) +NS_IMPL_RELEASE(WebSocketFrame) + +WebSocketFrame::WebSocketFrame(const WebSocketFrameData& aData) + : mData(aData) +{} + +WebSocketFrame::WebSocketFrame(bool aFinBit, bool aRsvBit1, bool aRsvBit2, + bool aRsvBit3, uint8_t aOpCode, bool aMaskBit, + uint32_t aMask, const nsCString& aPayload) + : mData(PR_Now(), aFinBit, aRsvBit1, aRsvBit2, aRsvBit3, aOpCode, aMaskBit, + aMask, aPayload) +{ + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + mData.mTimeStamp = PR_Now(); +} + +WebSocketFrame::~WebSocketFrame() +{} + +#define WSF_GETTER( method, value , type ) \ +NS_IMETHODIMP \ +WebSocketFrame::method(type* aValue) \ +{ \ + MOZ_ASSERT(NS_IsMainThread()); \ + if (!aValue) { \ + return NS_ERROR_FAILURE; \ + } \ + *aValue = value; \ + return NS_OK; \ +} + +WSF_GETTER(GetTimeStamp, mData.mTimeStamp, DOMHighResTimeStamp); +WSF_GETTER(GetFinBit, mData.mFinBit, bool); +WSF_GETTER(GetRsvBit1, mData.mRsvBit1, bool); +WSF_GETTER(GetRsvBit2, mData.mRsvBit2, bool); +WSF_GETTER(GetRsvBit3, mData.mRsvBit3, bool); +WSF_GETTER(GetOpCode, mData.mOpCode, uint16_t); +WSF_GETTER(GetMaskBit, mData.mMaskBit, bool); +WSF_GETTER(GetMask, mData.mMask, uint32_t); + +#undef WSF_GETTER + +NS_IMETHODIMP +WebSocketFrame::GetPayload(nsACString& aValue) +{ + MOZ_ASSERT(NS_IsMainThread()); + aValue = mData.mPayload; + return NS_OK; +} + +WebSocketFrameData::WebSocketFrameData() + : mTimeStamp(0) + , mFinBit(false) + , mRsvBit1(false) + , mRsvBit2(false) + , mRsvBit3(false) + , mMaskBit(false) + , mOpCode(0) + , mMask(0) +{ + MOZ_COUNT_CTOR(WebSocketFrameData); +} + +WebSocketFrameData::WebSocketFrameData(DOMHighResTimeStamp aTimeStamp, + bool aFinBit, bool aRsvBit1, + bool aRsvBit2, bool aRsvBit3, + uint8_t aOpCode, bool aMaskBit, + uint32_t aMask, + const nsCString& aPayload) + : mTimeStamp(aTimeStamp) + , mFinBit(aFinBit) + , mRsvBit1(aRsvBit1) + , mRsvBit2(aRsvBit2) + , mRsvBit3(aRsvBit3) + , mMaskBit(aMaskBit) + , mOpCode(aOpCode) + , mMask(aMask) + , mPayload(aPayload) +{ + MOZ_COUNT_CTOR(WebSocketFrameData); +} + +WebSocketFrameData::WebSocketFrameData(const WebSocketFrameData& aData) + : mTimeStamp(aData.mTimeStamp) + , mFinBit(aData.mFinBit) + , mRsvBit1(aData.mRsvBit1) + , mRsvBit2(aData.mRsvBit2) + , mRsvBit3(aData.mRsvBit3) + , mMaskBit(aData.mMaskBit) + , mOpCode(aData.mOpCode) + , mMask(aData.mMask) + , mPayload(aData.mPayload) +{ + MOZ_COUNT_CTOR(WebSocketFrameData); +} + +WebSocketFrameData::~WebSocketFrameData() +{ + MOZ_COUNT_DTOR(WebSocketFrameData); +} + +void +WebSocketFrameData::WriteIPCParams(IPC::Message* aMessage) const +{ + WriteParam(aMessage, mTimeStamp); + WriteParam(aMessage, mFinBit); + WriteParam(aMessage, mRsvBit1); + WriteParam(aMessage, mRsvBit2); + WriteParam(aMessage, mRsvBit3); + WriteParam(aMessage, mOpCode); + WriteParam(aMessage, mMaskBit); + WriteParam(aMessage, mMask); + WriteParam(aMessage, mPayload); +} + +bool +WebSocketFrameData::ReadIPCParams(const IPC::Message* aMessage, + void** aIter) +{ + if (!ReadParam(aMessage, aIter, &mTimeStamp)) { + return false; + } + +#define ReadParamHelper(x) \ + { \ + bool bit; \ + if (!ReadParam(aMessage, aIter, &bit)) { \ + return false; \ + } \ + x = bit; \ + } + + ReadParamHelper(mFinBit); + ReadParamHelper(mRsvBit1); + ReadParamHelper(mRsvBit2); + ReadParamHelper(mRsvBit3); + ReadParamHelper(mMaskBit); + +#undef ReadParamHelper + + return ReadParam(aMessage, aIter, &mOpCode) && + ReadParam(aMessage, aIter, &mMask) && + ReadParam(aMessage, aIter, &mPayload); +} + +} // net namespace +} // mozilla namespace diff --git a/netwerk/protocol/websocket/WebSocketFrame.h b/netwerk/protocol/websocket/WebSocketFrame.h new file mode 100644 index 0000000000..56f1a27e9a --- /dev/null +++ b/netwerk/protocol/websocket/WebSocketFrame.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_net_WebSocketFrame_h +#define mozilla_net_WebSocketFrame_h + +#include "nsAutoPtr.h" +#include "nsIWebSocketEventService.h" +#include "nsString.h" + +namespace IPC { +class Message; +} + +namespace mozilla { +namespace net { + +class WebSocketFrameData final +{ +public: + WebSocketFrameData(); + + explicit WebSocketFrameData(const WebSocketFrameData& aData); + + WebSocketFrameData(DOMHighResTimeStamp aTimeStamp, bool aFinBit, + bool aRsvBit1, bool aRsvBit2, bool aRsvBit3, + uint8_t aOpCode, bool aMaskBit, uint32_t aMask, + const nsCString& aPayload); + + ~WebSocketFrameData(); + + // For IPC serialization + void WriteIPCParams(IPC::Message* aMessage) const; + bool ReadIPCParams(const IPC::Message* aMessage, void** aIter); + + DOMHighResTimeStamp mTimeStamp; + + bool mFinBit : 1; + bool mRsvBit1 : 1; + bool mRsvBit2 : 1; + bool mRsvBit3 : 1; + bool mMaskBit : 1; + uint8_t mOpCode; + + uint32_t mMask; + + nsCString mPayload; +}; + +class WebSocketFrame final : public nsIWebSocketFrame +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIWEBSOCKETFRAME + + explicit WebSocketFrame(const WebSocketFrameData& aData); + + WebSocketFrame(bool aFinBit, bool aRsvBit1, bool aRsvBit2, bool aRsvBit3, + uint8_t aOpCode, bool aMaskBit, uint32_t aMask, + const nsCString& aPayload); + + const WebSocketFrameData& Data() const + { + return mData; + } + +private: + ~WebSocketFrame(); + + WebSocketFrameData mData; +}; + +} // net namespace +} // mozilla namespace + +#endif // mozilla_net_WebSocketFrame_h diff --git a/netwerk/protocol/websocket/moz.build b/netwerk/protocol/websocket/moz.build index 58efa7c6fb..86848c368e 100644 --- a/netwerk/protocol/websocket/moz.build +++ b/netwerk/protocol/websocket/moz.build @@ -6,6 +6,7 @@ XPIDL_SOURCES += [ 'nsIWebSocketChannel.idl', + 'nsIWebSocketEventService.idl', 'nsIWebSocketListener.idl', ] @@ -16,6 +17,10 @@ EXPORTS.mozilla.net += [ 'WebSocketChannel.h', 'WebSocketChannelChild.h', 'WebSocketChannelParent.h', + 'WebSocketEventListenerChild.h', + 'WebSocketEventListenerParent.h', + 'WebSocketEventService.h', + 'WebSocketFrame.h', ] UNIFIED_SOURCES += [ @@ -23,10 +28,15 @@ UNIFIED_SOURCES += [ 'WebSocketChannel.cpp', 'WebSocketChannelChild.cpp', 'WebSocketChannelParent.cpp', + 'WebSocketEventListenerChild.cpp', + 'WebSocketEventListenerParent.cpp', + 'WebSocketEventService.cpp', + 'WebSocketFrame.cpp', ] IPDL_SOURCES += [ 'PWebSocket.ipdl', + 'PWebSocketEventListener.ipdl', ] include('/ipc/chromium/chromium-config.mozbuild') diff --git a/netwerk/protocol/websocket/nsIWebSocketChannel.idl b/netwerk/protocol/websocket/nsIWebSocketChannel.idl index 19ebdd62d8..dd6fd46de7 100644 --- a/netwerk/protocol/websocket/nsIWebSocketChannel.idl +++ b/netwerk/protocol/websocket/nsIWebSocketChannel.idl @@ -22,7 +22,7 @@ interface nsIPrincipal; * We are also making it scriptable for now, but this may change once we have * WebSockets for Workers. */ -[scriptable, uuid(21217c03-2ff7-4f9c-9d10-6d3d94a7d843)] +[scriptable, uuid(ce71d028-322a-4105-a947-a894689b52bf)] interface nsIWebSocketChannel : nsISupports { /** @@ -138,11 +138,13 @@ interface nsIWebSocketChannel : nsISupports * * @param aURI the uri of the websocket protocol - may be redirected * @param aOrigin the uri of the originating resource + * @param aInnerWindowID the inner window ID * @param aListener the nsIWebSocketListener implementation * @param aContext an opaque parameter forwarded to aListener's methods */ void asyncOpen(in nsIURI aURI, in ACString aOrigin, + in unsigned long long aInnerWindowID, in nsIWebSocketListener aListener, in nsISupports aContext); @@ -221,4 +223,21 @@ interface nsIWebSocketChannel : nsISupports */ attribute unsigned long pingTimeout; + /** + * Unique ID for this channel. It's not readonly because when the channel is + * created via IPC, the serial number is received from the child process. + */ + attribute unsigned long serial; + +%{C++ + inline uint32_t Serial() + { + uint32_t serial; + nsresult rv = GetSerial(&serial); + if (NS_WARN_IF(NS_FAILED(rv))) { + return 0; + } + return serial; + } +%} }; diff --git a/netwerk/protocol/websocket/nsIWebSocketEventService.idl b/netwerk/protocol/websocket/nsIWebSocketEventService.idl new file mode 100644 index 0000000000..c2986dc2f0 --- /dev/null +++ b/netwerk/protocol/websocket/nsIWebSocketEventService.idl @@ -0,0 +1,79 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ + +#include "domstubs.idl" +#include "nsISupports.idl" + +[scriptable, builtinclass, uuid(6714a6be-2265-4f73-a988-d78a12416037)] +interface nsIWebSocketFrame : nsISupports +{ + readonly attribute DOMHighResTimeStamp timeStamp; + + readonly attribute boolean finBit; + + readonly attribute boolean rsvBit1; + readonly attribute boolean rsvBit2; + readonly attribute boolean rsvBit3; + + readonly attribute unsigned short opCode; + + readonly attribute boolean maskBit; + + readonly attribute unsigned long mask; + + readonly attribute ACString payload; + + // Non-Control opCode values: + const unsigned short OPCODE_CONTINUATION = 0x0; + const unsigned short OPCODE_TEXT = 0x1; + const unsigned short OPCODE_BINARY = 0x2; + + // Control opCode values: + const unsigned short OPCODE_CLOSE = 0x8; + const unsigned short OPCODE_PING = 0x9; + const unsigned short OPCODE_PONG = 0xA; +}; + +[scriptable, uuid(e7c005ab-e694-489b-b741-96db43ffb16f)] +interface nsIWebSocketEventListener : nsISupports +{ + void webSocketCreated(in unsigned long aWebSocketSerialID, + in AString aURI, + in ACString aProtocols); + + void webSocketOpened(in unsigned long aWebSocketSerialID, + in AString aEffectiveURI, + in ACString aProtocols, + in ACString aExtensions); + + const unsigned short TYPE_STRING = 0x0; + const unsigned short TYPE_BLOB = 0x1; + const unsigned short TYPE_ARRAYBUFFER = 0x2; + + void webSocketMessageAvailable(in unsigned long aWebSocketSerialID, + in ACString aMessage, + in unsigned short aType); + + void webSocketClosed(in unsigned long aWebSocketSerialID, + in boolean aWasClean, + in unsigned short aCode, + in AString aReason); + + void frameReceived(in unsigned long aWebSocketSerialID, + in nsIWebSocketFrame aFrame); + + void frameSent(in unsigned long aWebSocketSerialID, + in nsIWebSocketFrame aFrame); +}; + +[scriptable, builtinclass, uuid(b89d1b90-2cf3-4d8f-ac21-5aedfb25c760)] +interface nsIWebSocketEventService : nsISupports +{ + void addListener(in unsigned long long aInnerWindowID, + in nsIWebSocketEventListener aListener); + + void removeListener(in unsigned long long aInnerWindowID, + in nsIWebSocketEventListener aListener); +}; diff --git a/netwerk/test/unit/test_dns_proxy_bypass.js b/netwerk/test/unit/test_dns_proxy_bypass.js index aca6ac6af6..29e076af70 100644 --- a/netwerk/test/unit/test_dns_proxy_bypass.js +++ b/netwerk/test/unit/test_dns_proxy_bypass.js @@ -71,7 +71,7 @@ function run_test() { Ci.nsIContentPolicy.TYPE_WEBSOCKET); var uri = ioService.newURI(url, null, null); - chan.asyncOpen(uri, url, listener, null); + chan.asyncOpen(uri, url, 0, listener, null); do_test_pending(); } diff --git a/netwerk/test/unit/test_websocket_offline.js b/netwerk/test/unit/test_websocket_offline.js index c4cae464b3..79662974a5 100644 --- a/netwerk/test/unit/test_websocket_offline.js +++ b/netwerk/test/unit/test_websocket_offline.js @@ -42,7 +42,7 @@ function run_test() { Ci.nsIContentPolicy.TYPE_WEBSOCKET); var uri = Services.io.newURI(url, null, null); - chan.asyncOpen(uri, url, listener, null); + chan.asyncOpen(uri, url, 0, listener, null); do_test_pending(); } catch (x) { dump("throwing " + x); diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 96e5be54de..65abbeab7a 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -5114,6 +5114,14 @@ "keyed" : true, "description" : "Exceptions thrown by add-ons" }, + "IPC_SAME_PROCESS_MESSAGE_COPY_OOM_KB": { + "expires_in_version": "50", + "kind": "exponential", + "low": 100, + "high": 10000000, + "n_buckets": 10, + "description": "Whenever the same-process MessageManager cannot be sent through sendAsyncMessage as it would cause an OOM, the size of the message content, in kb." + }, "MISBEHAVING_ADDONS_CPOW_TIME_MS": { "expires_in_version": "never", "kind": "exponential", diff --git a/toolkit/xre/nsNativeAppSupportWin.cpp b/toolkit/xre/nsNativeAppSupportWin.cpp index 10644525d7..2a05d5f598 100644 --- a/toolkit/xre/nsNativeAppSupportWin.cpp +++ b/toolkit/xre/nsNativeAppSupportWin.cpp @@ -19,6 +19,7 @@ #include "nsISupportsPrimitives.h" #include "nsIWindowWatcher.h" #include "nsPIDOMWindow.h" +#include "nsGlobalWindow.h" #include "nsIDocShell.h" #include "nsIDocShellTreeItem.h" #include "nsIBaseWindow.h" @@ -92,8 +93,8 @@ activateWindow( nsIDOMWindow *win ) { #endif // Simple Win32 mutex wrapper. -struct Mutex { - Mutex( const char16_t *name ) +struct Win32Mutex { + Win32Mutex( const char16_t *name ) : mName( name ), mHandle( 0 ), mState( -1 ) { @@ -102,7 +103,7 @@ struct Mutex { printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() ); #endif } - ~Mutex() { + ~Win32Mutex() { if ( mHandle ) { // Make sure we release it if we own it. Unlock(); @@ -669,7 +670,7 @@ nsNativeAppSupportWin::Start( bool *aResult ) { MOZ_MUTEX_NAMESPACE, NS_ConvertUTF8toUTF16(gAppData->name).get(), MOZ_STARTUP_MUTEX_NAME ); - Mutex startupLock = Mutex( mMutexName ); + Win32Mutex startupLock = Win32Mutex( mMutexName ); NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE ); @@ -759,7 +760,7 @@ nsNativeAppSupportWin::Stop( bool *aResult ) { nsresult rv = NS_OK; *aResult = true; - Mutex ddeLock( mMutexName ); + Win32Mutex ddeLock( mMutexName ); if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) { if ( mConversations == 0 ) { @@ -799,7 +800,7 @@ nsNativeAppSupportWin::Quit() { // to wait to hold the lock, in which case they will not find the // window as we will destroy ours under our lock. // When the mutex goes off the stack, it is unlocked via destructor. - Mutex mutexLock(mMutexName); + Win32Mutex mutexLock(mMutexName); NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE); // If we've got a message window to receive IPC or new window requests, @@ -1007,18 +1008,15 @@ nsNativeAppSupportWin::HandleDDENotification( UINT uType, // transaction t nsCOMPtr navWin; GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) ); - if ( !navWin ) { + nsCOMPtr piNavWin = do_QueryInterface(navWin); + if ( !piNavWin ) { // There is not a window open break; } + // Get content window. - nsCOMPtr content; - navWin->GetContent( getter_AddRefs( content ) ); - if ( !content ) { - break; - } - // Convert that to internal interface. - nsCOMPtr internalContent( do_QueryInterface( content ) ); + nsCOMPtr internalContent_ = nsGlobalWindow::Cast(piNavWin)->GetContent(); + nsCOMPtr internalContent = do_QueryInterface(internalContent_); if ( !internalContent ) { break; } diff --git a/widget/gonk/GonkPermission.cpp b/widget/gonk/GonkPermission.cpp index f54061b308..876354a783 100644 --- a/widget/gonk/GonkPermission.cpp +++ b/widget/gonk/GonkPermission.cpp @@ -89,9 +89,11 @@ GonkPermissionChecker::Run() } // Now iterate its apps... - for (uint32_t i = 0; i < contentParent->ManagedPBrowserParent().Length(); i++) { + const ManagedContainer& browsers = + contentParent->ManagedPBrowserParent(); + for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) { dom::TabParent *tabParent = - static_cast(contentParent->ManagedPBrowserParent()[i]); + static_cast(iter.Get()->GetKey()); nsCOMPtr mozApp = tabParent->GetOwnOrContainingApp(); if (!mozApp) { continue; diff --git a/widget/nsBaseDragService.cpp b/widget/nsBaseDragService.cpp index 4e22f492ee..7c179b7239 100644 --- a/widget/nsBaseDragService.cpp +++ b/widget/nsBaseDragService.cpp @@ -562,7 +562,8 @@ nsBaseDragService::DrawDrag(nsIDOMNode* aDOMNode, // draw the image for selections if (mSelection) { nsIntPoint pnt(aScreenDragRect->x, aScreenDragRect->y); - *aSurface = presShell->RenderSelection(mSelection, pnt, aScreenDragRect); + *aSurface = presShell->RenderSelection(mSelection, pnt, aScreenDragRect, + mImage ? 0 : nsIPresShell::RENDER_AUTO_SCALE); return NS_OK; } @@ -604,7 +605,8 @@ nsBaseDragService::DrawDrag(nsIDOMNode* aDOMNode, nsIntPoint pnt(aScreenDragRect->x, aScreenDragRect->y); *aSurface = presShell->RenderNode(dragNode, aRegion ? &clipRegion : nullptr, - pnt, aScreenDragRect); + pnt, aScreenDragRect, + mImage ? 0 : nsIPresShell::RENDER_AUTO_SCALE); } // if an image was specified, reposition the drag rectangle to diff --git a/xpcom/glue/nsTHashtable.h b/xpcom/glue/nsTHashtable.h index 21e142b529..098118e8fe 100644 --- a/xpcom/glue/nsTHashtable.h +++ b/xpcom/glue/nsTHashtable.h @@ -116,6 +116,11 @@ public: */ uint32_t Count() const { return mTable.EntryCount(); } + /** + * Return true if the hashtable is empty. + */ + bool IsEmpty() const { return Count() == 0; } + /** * Get the entry associated with a key. * @param aKey the key to retrieve