michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: 'use strict'; michael@0: michael@0: const { classes: Cc, interfaces: Ci, utils: Cu } = Components; michael@0: michael@0: Cu.import('resource://gre/modules/XPCOMUtils.jsm'); michael@0: Cu.import('resource://gre/modules/Services.jsm'); michael@0: michael@0: this.EXPORTED_SYMBOLS = ['SystemAppProxy']; michael@0: michael@0: let SystemAppProxy = { michael@0: _frame: null, michael@0: _isReady: false, michael@0: _pendingEvents: [], michael@0: _pendingListeners: [], michael@0: michael@0: // To call when a new system app iframe is created michael@0: registerFrame: function (frame) { michael@0: this._isReady = false; michael@0: this._frame = frame; michael@0: michael@0: // Register all DOM event listeners added before we got a ref to the app iframe michael@0: this._pendingListeners michael@0: .forEach((args) => michael@0: this.addEventListener.apply(this, args)); michael@0: this._pendingListeners = []; michael@0: }, michael@0: michael@0: // To call when it is ready to receive events michael@0: setIsReady: function () { michael@0: if (this._isReady) { michael@0: Cu.reportError('SystemApp has already been declared as being ready.'); michael@0: } michael@0: this._isReady = true; michael@0: michael@0: // Dispatch all events being queued while the system app was still loading michael@0: this._pendingEvents michael@0: .forEach(([type, details]) => michael@0: this._sendCustomEvent(type, details)); michael@0: this._pendingEvents = []; michael@0: }, michael@0: michael@0: /* michael@0: * Common way to send an event to the system app. michael@0: * michael@0: * // In gecko code: michael@0: * SystemAppProxy.sendCustomEvent('foo', { data: 'bar' }); michael@0: * // In system app: michael@0: * window.addEventListener('foo', function (event) { michael@0: * event.details == 'bar' michael@0: * }); michael@0: */ michael@0: _sendCustomEvent: function systemApp_sendCustomEvent(type, details) { michael@0: let content = this._frame ? this._frame.contentWindow : null; michael@0: michael@0: // If the system app isn't ready yet, michael@0: // queue events until someone calls setIsLoaded michael@0: if (!this._isReady || !content) { michael@0: this._pendingEvents.push([type, details]); michael@0: return null; michael@0: } michael@0: michael@0: let event = content.document.createEvent('CustomEvent'); michael@0: michael@0: let payload; michael@0: // If the root object already has __exposedProps__, michael@0: // we consider the caller already wrapped (correctly) the object. michael@0: if ('__exposedProps__' in details) { michael@0: payload = details; michael@0: } else { michael@0: payload = details ? Cu.cloneInto(details, content) : {}; michael@0: } michael@0: michael@0: event.initCustomEvent(type, true, false, payload); michael@0: content.dispatchEvent(event); michael@0: michael@0: return event; michael@0: }, michael@0: michael@0: // Now deprecated, use sendCustomEvent with a custom event name michael@0: dispatchEvent: function systemApp_sendChromeEvent(details) { michael@0: return this._sendCustomEvent('mozChromeEvent', details); michael@0: }, michael@0: michael@0: // Listen for dom events on the system app michael@0: addEventListener: function systemApp_addEventListener() { michael@0: let content = this._frame ? this._frame.contentWindow : null; michael@0: if (!content) { michael@0: this._pendingListeners.push(arguments); michael@0: return false; michael@0: } michael@0: michael@0: content.addEventListener.apply(content, arguments); michael@0: return true; michael@0: }, michael@0: michael@0: removeEventListener: function systemApp_removeEventListener(name, listener) { michael@0: let content = this._frame ? this._frame.contentWindow : null; michael@0: if (content) { michael@0: content.removeEventListener.apply(content, arguments); michael@0: } else { michael@0: let idx = this._pendingListeners.indexOf(listener); michael@0: if (idx != -1) { michael@0: this._pendingListeners.splice(idx, 1); michael@0: } michael@0: } michael@0: } michael@0: michael@0: }; michael@0: this.SystemAppProxy = SystemAppProxy; michael@0: