Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | 'use strict'; |
michael@0 | 6 | |
michael@0 | 7 | const { classes: Cc, interfaces: Ci, utils: Cu } = Components; |
michael@0 | 8 | |
michael@0 | 9 | Cu.import('resource://gre/modules/XPCOMUtils.jsm'); |
michael@0 | 10 | Cu.import('resource://gre/modules/Services.jsm'); |
michael@0 | 11 | |
michael@0 | 12 | this.EXPORTED_SYMBOLS = ['SystemAppProxy']; |
michael@0 | 13 | |
michael@0 | 14 | let SystemAppProxy = { |
michael@0 | 15 | _frame: null, |
michael@0 | 16 | _isReady: false, |
michael@0 | 17 | _pendingEvents: [], |
michael@0 | 18 | _pendingListeners: [], |
michael@0 | 19 | |
michael@0 | 20 | // To call when a new system app iframe is created |
michael@0 | 21 | registerFrame: function (frame) { |
michael@0 | 22 | this._isReady = false; |
michael@0 | 23 | this._frame = frame; |
michael@0 | 24 | |
michael@0 | 25 | // Register all DOM event listeners added before we got a ref to the app iframe |
michael@0 | 26 | this._pendingListeners |
michael@0 | 27 | .forEach((args) => |
michael@0 | 28 | this.addEventListener.apply(this, args)); |
michael@0 | 29 | this._pendingListeners = []; |
michael@0 | 30 | }, |
michael@0 | 31 | |
michael@0 | 32 | // To call when it is ready to receive events |
michael@0 | 33 | setIsReady: function () { |
michael@0 | 34 | if (this._isReady) { |
michael@0 | 35 | Cu.reportError('SystemApp has already been declared as being ready.'); |
michael@0 | 36 | } |
michael@0 | 37 | this._isReady = true; |
michael@0 | 38 | |
michael@0 | 39 | // Dispatch all events being queued while the system app was still loading |
michael@0 | 40 | this._pendingEvents |
michael@0 | 41 | .forEach(([type, details]) => |
michael@0 | 42 | this._sendCustomEvent(type, details)); |
michael@0 | 43 | this._pendingEvents = []; |
michael@0 | 44 | }, |
michael@0 | 45 | |
michael@0 | 46 | /* |
michael@0 | 47 | * Common way to send an event to the system app. |
michael@0 | 48 | * |
michael@0 | 49 | * // In gecko code: |
michael@0 | 50 | * SystemAppProxy.sendCustomEvent('foo', { data: 'bar' }); |
michael@0 | 51 | * // In system app: |
michael@0 | 52 | * window.addEventListener('foo', function (event) { |
michael@0 | 53 | * event.details == 'bar' |
michael@0 | 54 | * }); |
michael@0 | 55 | */ |
michael@0 | 56 | _sendCustomEvent: function systemApp_sendCustomEvent(type, details) { |
michael@0 | 57 | let content = this._frame ? this._frame.contentWindow : null; |
michael@0 | 58 | |
michael@0 | 59 | // If the system app isn't ready yet, |
michael@0 | 60 | // queue events until someone calls setIsLoaded |
michael@0 | 61 | if (!this._isReady || !content) { |
michael@0 | 62 | this._pendingEvents.push([type, details]); |
michael@0 | 63 | return null; |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | let event = content.document.createEvent('CustomEvent'); |
michael@0 | 67 | |
michael@0 | 68 | let payload; |
michael@0 | 69 | // If the root object already has __exposedProps__, |
michael@0 | 70 | // we consider the caller already wrapped (correctly) the object. |
michael@0 | 71 | if ('__exposedProps__' in details) { |
michael@0 | 72 | payload = details; |
michael@0 | 73 | } else { |
michael@0 | 74 | payload = details ? Cu.cloneInto(details, content) : {}; |
michael@0 | 75 | } |
michael@0 | 76 | |
michael@0 | 77 | event.initCustomEvent(type, true, false, payload); |
michael@0 | 78 | content.dispatchEvent(event); |
michael@0 | 79 | |
michael@0 | 80 | return event; |
michael@0 | 81 | }, |
michael@0 | 82 | |
michael@0 | 83 | // Now deprecated, use sendCustomEvent with a custom event name |
michael@0 | 84 | dispatchEvent: function systemApp_sendChromeEvent(details) { |
michael@0 | 85 | return this._sendCustomEvent('mozChromeEvent', details); |
michael@0 | 86 | }, |
michael@0 | 87 | |
michael@0 | 88 | // Listen for dom events on the system app |
michael@0 | 89 | addEventListener: function systemApp_addEventListener() { |
michael@0 | 90 | let content = this._frame ? this._frame.contentWindow : null; |
michael@0 | 91 | if (!content) { |
michael@0 | 92 | this._pendingListeners.push(arguments); |
michael@0 | 93 | return false; |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | content.addEventListener.apply(content, arguments); |
michael@0 | 97 | return true; |
michael@0 | 98 | }, |
michael@0 | 99 | |
michael@0 | 100 | removeEventListener: function systemApp_removeEventListener(name, listener) { |
michael@0 | 101 | let content = this._frame ? this._frame.contentWindow : null; |
michael@0 | 102 | if (content) { |
michael@0 | 103 | content.removeEventListener.apply(content, arguments); |
michael@0 | 104 | } else { |
michael@0 | 105 | let idx = this._pendingListeners.indexOf(listener); |
michael@0 | 106 | if (idx != -1) { |
michael@0 | 107 | this._pendingListeners.splice(idx, 1); |
michael@0 | 108 | } |
michael@0 | 109 | } |
michael@0 | 110 | } |
michael@0 | 111 | |
michael@0 | 112 | }; |
michael@0 | 113 | this.SystemAppProxy = SystemAppProxy; |
michael@0 | 114 |