| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 var DocumentNatives = requireNative('document_natives'); | |
| 6 var GuestViewInternal = | |
| 7 require('binding').Binding.create('guestViewInternal').generate(); | |
| 8 var IdGenerator = requireNative('id_generator'); | |
| 9 var guestViewInternalNatives = requireNative('guest_view_internal'); | |
| 10 | |
| 11 function AppViewInternal(appviewNode) { | |
| 12 privates(appviewNode).internal = this; | |
| 13 this.appviewNode = appviewNode; | |
| 14 this.elementAttached = false; | |
| 15 this.pendingGuestCreation = false; | |
| 16 | |
| 17 this.browserPluginNode = this.createBrowserPluginNode(); | |
| 18 var shadowRoot = this.appviewNode.createShadowRoot(); | |
| 19 shadowRoot.appendChild(this.browserPluginNode); | |
| 20 this.viewInstanceId = IdGenerator.GetNextId(); | |
| 21 } | |
| 22 | |
| 23 AppViewInternal.prototype.getErrorNode = function() { | |
| 24 if (!this.errorNode) { | |
| 25 this.errorNode = document.createElement('div'); | |
| 26 this.errorNode.innerText = 'Unable to connect to app.'; | |
| 27 this.errorNode.style.position = 'absolute'; | |
| 28 this.errorNode.style.left = '0px'; | |
| 29 this.errorNode.style.top = '0px'; | |
| 30 this.errorNode.style.width = '100%'; | |
| 31 this.errorNode.style.height = '100%'; | |
| 32 this.appviewNode.shadowRoot.appendChild(this.errorNode); | |
| 33 } | |
| 34 return this.errorNode; | |
| 35 }; | |
| 36 | |
| 37 AppViewInternal.prototype.createBrowserPluginNode = function() { | |
| 38 // We create BrowserPlugin as a custom element in order to observe changes | |
| 39 // to attributes synchronously. | |
| 40 var browserPluginNode = new AppViewInternal.BrowserPlugin(); | |
| 41 privates(browserPluginNode).internal = this; | |
| 42 return browserPluginNode; | |
| 43 }; | |
| 44 | |
| 45 AppViewInternal.prototype.connect = function(app, data, callback) { | |
| 46 if (!this.elementAttached || this.pendingGuestCreation) { | |
| 47 if (callback) { | |
| 48 callback(false); | |
| 49 } | |
| 50 return; | |
| 51 } | |
| 52 var createParams = { | |
| 53 'appId': app, | |
| 54 'data': data || {} | |
| 55 }; | |
| 56 GuestViewInternal.createGuest( | |
| 57 'appview', | |
| 58 createParams, | |
| 59 function(guestInstanceId) { | |
| 60 this.pendingGuestCreation = false; | |
| 61 if (guestInstanceId && !this.elementAttached) { | |
| 62 GuestViewInternal.destroyGuest(guestInstanceId); | |
| 63 guestInstanceId = 0; | |
| 64 } | |
| 65 if (!guestInstanceId) { | |
| 66 this.browserPluginNode.style.visibility = 'hidden'; | |
| 67 var errorMsg = 'Unable to connect to app "' + app + '".'; | |
| 68 window.console.warn(errorMsg); | |
| 69 this.getErrorNode().innerText = errorMsg; | |
| 70 if (callback) { | |
| 71 callback(false); | |
| 72 } | |
| 73 return; | |
| 74 } | |
| 75 this.attachWindow(guestInstanceId); | |
| 76 if (callback) { | |
| 77 callback(true); | |
| 78 } | |
| 79 }.bind(this) | |
| 80 ); | |
| 81 this.pendingGuestCreation = true; | |
| 82 }; | |
| 83 | |
| 84 AppViewInternal.prototype.attachWindow = function(guestInstanceId) { | |
| 85 this.guestInstanceId = guestInstanceId; | |
| 86 if (!this.internalInstanceId) { | |
| 87 return; | |
| 88 } | |
| 89 var params = { | |
| 90 'instanceId': this.viewInstanceId | |
| 91 }; | |
| 92 this.browserPluginNode.style.visibility = 'visible'; | |
| 93 return guestViewInternalNatives.AttachGuest( | |
| 94 this.internalInstanceId, | |
| 95 guestInstanceId, | |
| 96 params); | |
| 97 }; | |
| 98 | |
| 99 AppViewInternal.prototype.handleBrowserPluginAttributeMutation = | |
| 100 function(name, oldValue, newValue) { | |
| 101 if (name == 'internalinstanceid' && !oldValue && !!newValue) { | |
| 102 this.browserPluginNode.removeAttribute('internalinstanceid'); | |
| 103 this.internalInstanceId = parseInt(newValue); | |
| 104 | |
| 105 if (!!this.guestInstanceId && this.guestInstanceId != 0) { | |
| 106 var params = { | |
| 107 'instanceId': this.viewInstanceId | |
| 108 }; | |
| 109 guestViewInternalNatives.AttachGuest( | |
| 110 this.internalInstanceId, | |
| 111 this.guestInstanceId, | |
| 112 params); | |
| 113 } | |
| 114 return; | |
| 115 } | |
| 116 }; | |
| 117 | |
| 118 AppViewInternal.prototype.reset = function() { | |
| 119 if (this.guestInstanceId) { | |
| 120 GuestViewInternal.destroyGuest(this.guestInstanceId); | |
| 121 this.guestInstanceId = undefined; | |
| 122 } | |
| 123 }; | |
| 124 | |
| 125 function registerBrowserPluginElement() { | |
| 126 var proto = Object.create(HTMLObjectElement.prototype); | |
| 127 | |
| 128 proto.createdCallback = function() { | |
| 129 this.setAttribute('type', 'application/browser-plugin'); | |
| 130 this.style.width = '100%'; | |
| 131 this.style.height = '100%'; | |
| 132 }; | |
| 133 | |
| 134 proto.attachedCallback = function() { | |
| 135 // Load the plugin immediately. | |
| 136 var unused = this.nonExistentAttribute; | |
| 137 }; | |
| 138 | |
| 139 proto.attributeChangedCallback = function(name, oldValue, newValue) { | |
| 140 var internal = privates(this).internal; | |
| 141 if (!internal) { | |
| 142 return; | |
| 143 } | |
| 144 internal.handleBrowserPluginAttributeMutation(name, oldValue, newValue); | |
| 145 }; | |
| 146 | |
| 147 AppViewInternal.BrowserPlugin = | |
| 148 DocumentNatives.RegisterElement('appplugin', {extends: 'object', | |
| 149 prototype: proto}); | |
| 150 | |
| 151 delete proto.createdCallback; | |
| 152 delete proto.attachedCallback; | |
| 153 delete proto.detachedCallback; | |
| 154 delete proto.attributeChangedCallback; | |
| 155 } | |
| 156 | |
| 157 function registerAppViewElement() { | |
| 158 var proto = Object.create(HTMLElement.prototype); | |
| 159 | |
| 160 proto.createdCallback = function() { | |
| 161 new AppViewInternal(this); | |
| 162 }; | |
| 163 | |
| 164 proto.attachedCallback = function() { | |
| 165 var internal = privates(this).internal; | |
| 166 if (!internal) { | |
| 167 return; | |
| 168 } | |
| 169 internal.elementAttached = true; | |
| 170 }; | |
| 171 | |
| 172 proto.detachedCallback = function() { | |
| 173 var internal = privates(this).internal; | |
| 174 if (!internal) { | |
| 175 return; | |
| 176 } | |
| 177 internal.elementAttached = false; | |
| 178 internal.reset(); | |
| 179 }; | |
| 180 | |
| 181 proto.connect = function() { | |
| 182 var internal = privates(this).internal; | |
| 183 $Function.apply(internal.connect, internal, arguments); | |
| 184 } | |
| 185 | |
| 186 window.AppView = | |
| 187 DocumentNatives.RegisterElement('appview', {prototype: proto}); | |
| 188 | |
| 189 // Delete the callbacks so developers cannot call them and produce unexpected | |
| 190 // behavior. | |
| 191 delete proto.createdCallback; | |
| 192 delete proto.attachedCallback; | |
| 193 delete proto.detachedCallback; | |
| 194 delete proto.attributeChangedCallback; | |
| 195 } | |
| 196 | |
| 197 var useCapture = true; | |
| 198 window.addEventListener('readystatechange', function listener(event) { | |
| 199 if (document.readyState == 'loading') | |
| 200 return; | |
| 201 | |
| 202 registerBrowserPluginElement(); | |
| 203 registerAppViewElement(); | |
| 204 window.removeEventListener(event.type, listener, useCapture); | |
| 205 }, useCapture); | |
| OLD | NEW |