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 |