OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // This module implements the shared functionality for different guestview | 5 // This module implements the shared functionality for different guestview |
6 // containers, such as web_view, app_view, etc. | 6 // containers, such as web_view, app_view, etc. |
7 | 7 |
8 var DocumentNatives = requireNative('document_natives'); | 8 var DocumentNatives = requireNative('document_natives'); |
9 var GuestView = require('guestView').GuestView; | 9 var GuestView = require('guestView').GuestView; |
10 var GuestViewInternalNatives = requireNative('guest_view_internal'); | 10 var GuestViewInternalNatives = requireNative('guest_view_internal'); |
11 var IdGenerator = requireNative('id_generator'); | 11 var IdGenerator = requireNative('id_generator'); |
12 var MessagingNatives = requireNative('messaging_natives'); | 12 var MessagingNatives = requireNative('messaging_natives'); |
13 | 13 |
14 function GuestViewContainer(element, viewType) { | 14 function GuestViewContainer(element, viewType) { |
15 privates(element).internal = this; | 15 privates(element).internal = this; |
16 this.attributes = {}; | 16 this.attributes = {}; |
17 this.element = element; | 17 this.element = element; |
18 this.elementAttached = false; | 18 this.elementAttached = false; |
19 this.viewInstanceId = IdGenerator.GetNextId(); | 19 this.viewInstanceId = IdGenerator.GetNextId(); |
20 this.viewType = viewType; | 20 this.viewType = viewType; |
21 | 21 |
22 this.setupGuestProperty(); | 22 this.setupGuestProperty(); |
23 this.guest = new GuestView(viewType); | 23 this.guest = new GuestView(viewType); |
24 this.setupAttributes(); | 24 this.setupAttributes(); |
25 | 25 |
26 privates(this).browserPluginElement = this.createBrowserPluginElement(); | 26 privates(this).internalElement = this.createInternalElement$(); |
27 this.setupFocusPropagation(); | 27 this.setupFocusPropagation(); |
28 var shadowRoot = this.element.createShadowRoot(); | 28 var shadowRoot = this.element.createShadowRoot(); |
29 shadowRoot.appendChild(privates(this).browserPluginElement); | 29 shadowRoot.appendChild(privates(this).internalElement); |
30 | 30 |
31 GuestViewInternalNatives.RegisterView(this.viewInstanceId, this); | 31 GuestViewInternalNatives.RegisterView(this.viewInstanceId, this); |
32 } | 32 } |
33 | 33 |
34 // Forward public API methods from |proto| to their internal implementations. | 34 // Forward public API methods from |proto| to their internal implementations. |
35 GuestViewContainer.forwardApiMethods = function(proto, apiMethods) { | 35 GuestViewContainer.forwardApiMethods = function(proto, apiMethods) { |
36 var createProtoHandler = function(m) { | 36 var createProtoHandler = function(m) { |
37 return function(var_args) { | 37 return function(var_args) { |
38 var internal = privates(this).internal; | 38 var internal = privates(this).internal; |
39 return $Function.apply(internal[m], internal, arguments); | 39 return $Function.apply(internal[m], internal, arguments); |
40 }; | 40 }; |
41 }; | 41 }; |
42 for (var i = 0; apiMethods[i]; ++i) { | 42 for (var i = 0; apiMethods[i]; ++i) { |
43 proto[apiMethods[i]] = createProtoHandler(apiMethods[i]); | 43 proto[apiMethods[i]] = createProtoHandler(apiMethods[i]); |
44 } | 44 } |
45 }; | 45 }; |
46 | 46 |
47 // Registers the browserplugin and guestview as custom elements once the | 47 // Registers the browserplugin and guestview as custom elements once the |
48 // document has loaded. | 48 // document has loaded. |
49 GuestViewContainer.registerElement = function(guestViewContainerType) { | 49 GuestViewContainer.registerElement = function(guestViewContainerType) { |
50 var useCapture = true; | 50 var useCapture = true; |
51 window.addEventListener('readystatechange', function listener(event) { | 51 window.addEventListener('readystatechange', function listener(event) { |
52 if (document.readyState == 'loading') | 52 if (document.readyState == 'loading') |
53 return; | 53 return; |
54 | 54 |
55 registerBrowserPluginElement( | 55 registerInternalElement(guestViewContainerType.VIEW_TYPE.toLowerCase()); |
56 guestViewContainerType.VIEW_TYPE.toLowerCase()); | |
57 registerGuestViewElement(guestViewContainerType); | 56 registerGuestViewElement(guestViewContainerType); |
58 window.removeEventListener(event.type, listener, useCapture); | 57 window.removeEventListener(event.type, listener, useCapture); |
59 }, useCapture); | 58 }, useCapture); |
60 }; | 59 }; |
61 | 60 |
62 // Create the 'guest' property to track new GuestViews and always listen for | 61 // Create the 'guest' property to track new GuestViews and always listen for |
63 // their resizes. | 62 // their resizes. |
64 GuestViewContainer.prototype.setupGuestProperty = function() { | 63 GuestViewContainer.prototype.setupGuestProperty = function() { |
65 $Object.defineProperty(this, 'guest', { | 64 $Object.defineProperty(this, 'guest', { |
66 get: function() { | 65 get: function() { |
(...skipping 11 matching lines...) Expand all Loading... |
78 contentResizeEvent.oldHeight = e.oldHeight; | 77 contentResizeEvent.oldHeight = e.oldHeight; |
79 contentResizeEvent.newWidth = e.newWidth; | 78 contentResizeEvent.newWidth = e.newWidth; |
80 contentResizeEvent.newHeight = e.newHeight; | 79 contentResizeEvent.newHeight = e.newHeight; |
81 this.dispatchEvent(contentResizeEvent); | 80 this.dispatchEvent(contentResizeEvent); |
82 }.bind(this); | 81 }.bind(this); |
83 }.bind(this), | 82 }.bind(this), |
84 enumerable: true | 83 enumerable: true |
85 }); | 84 }); |
86 }; | 85 }; |
87 | 86 |
88 GuestViewContainer.prototype.createBrowserPluginElement = function() { | 87 GuestViewContainer.prototype.createInternalElement$ = function() { |
89 // We create BrowserPlugin as a custom element in order to observe changes | 88 // We create BrowserPlugin as a custom element in order to observe changes |
90 // to attributes synchronously. | 89 // to attributes synchronously. |
91 var browserPluginElement = | 90 var browserPluginElement = |
92 new GuestViewContainer[this.viewType + 'BrowserPlugin'](); | 91 new GuestViewContainer[this.viewType + 'BrowserPlugin'](); |
93 privates(browserPluginElement).internal = this; | 92 privates(browserPluginElement).internal = this; |
94 return browserPluginElement; | 93 return browserPluginElement; |
95 }; | 94 }; |
96 | 95 |
97 GuestViewContainer.prototype.setupFocusPropagation = function() { | 96 GuestViewContainer.prototype.setupFocusPropagation = function() { |
98 if (!this.element.hasAttribute('tabIndex')) { | 97 if (!this.element.hasAttribute('tabIndex')) { |
99 // GuestViewContainer needs a tabIndex in order to be focusable. | 98 // GuestViewContainer needs a tabIndex in order to be focusable. |
100 // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute | 99 // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute |
101 // to allow GuestViewContainer to be focusable. | 100 // to allow GuestViewContainer to be focusable. |
102 // See http://crbug.com/231664. | 101 // See http://crbug.com/231664. |
103 this.element.setAttribute('tabIndex', -1); | 102 this.element.setAttribute('tabIndex', -1); |
104 } | 103 } |
105 this.element.addEventListener('focus', this.weakWrapper(function(e) { | 104 this.element.addEventListener('focus', this.weakWrapper(function(e) { |
106 // Focus the BrowserPlugin when the GuestViewContainer takes focus. | 105 // Focus the BrowserPlugin when the GuestViewContainer takes focus. |
107 privates(this).browserPluginElement.focus(); | 106 privates(this).internalElement.focus(); |
108 })); | 107 })); |
109 this.element.addEventListener('blur', this.weakWrapper(function(e) { | 108 this.element.addEventListener('blur', this.weakWrapper(function(e) { |
110 // Blur the BrowserPlugin when the GuestViewContainer loses focus. | 109 // Blur the BrowserPlugin when the GuestViewContainer loses focus. |
111 privates(this).browserPluginElement.blur(); | 110 privates(this).internalElement.blur(); |
112 })); | 111 })); |
113 }; | 112 }; |
114 | 113 |
115 GuestViewContainer.prototype.attachWindow = function() { | 114 GuestViewContainer.prototype.attachWindow$ = function() { |
116 if (!this.internalInstanceId) { | 115 if (!this.internalInstanceId) { |
117 return true; | 116 return true; |
118 } | 117 } |
119 | 118 |
120 this.guest.attach(this.internalInstanceId, | 119 this.guest.attach(this.internalInstanceId, |
121 this.viewInstanceId, | 120 this.viewInstanceId, |
122 this.buildParams()); | 121 this.buildParams()); |
123 return true; | 122 return true; |
124 }; | 123 }; |
125 | 124 |
126 GuestViewContainer.prototype.makeGCOwnContainer = function(internalInstanceId) { | 125 GuestViewContainer.prototype.makeGCOwnContainer = function(internalInstanceId) { |
127 MessagingNatives.BindToGC(this, function() { | 126 MessagingNatives.BindToGC(this, function() { |
128 GuestViewInternalNatives.DestroyContainer(internalInstanceId); | 127 GuestViewInternalNatives.DestroyContainer(internalInstanceId); |
129 }, -1); | 128 }, -1); |
130 }; | 129 }; |
131 | 130 |
132 GuestViewContainer.prototype.handleBrowserPluginAttributeMutation = | 131 GuestViewContainer.prototype.onInternalInstanceId = function( |
| 132 internalInstanceId) { |
| 133 this.internalInstanceId = internalInstanceId; |
| 134 this.makeGCOwnContainer(this.internalInstanceId); |
| 135 |
| 136 // Track when the element resizes using the element resize callback. |
| 137 GuestViewInternalNatives.RegisterElementResizeCallback( |
| 138 this.internalInstanceId, this.weakWrapper(this.onElementResize)); |
| 139 |
| 140 if (!this.guest.getId()) { |
| 141 return; |
| 142 } |
| 143 this.guest.attach(this.internalInstanceId, |
| 144 this.viewInstanceId, |
| 145 this.buildParams()); |
| 146 }; |
| 147 |
| 148 GuestViewContainer.prototype.handleInternalElementAttributeMutation = |
133 function(name, oldValue, newValue) { | 149 function(name, oldValue, newValue) { |
134 if (name == 'internalinstanceid' && !oldValue && !!newValue) { | 150 if (name == 'internalinstanceid' && !oldValue && !!newValue) { |
135 privates(this).browserPluginElement.removeAttribute('internalinstanceid'); | 151 privates(this).internalElement.removeAttribute('internalinstanceid'); |
136 this.internalInstanceId = parseInt(newValue); | 152 this.onInternalInstanceId(parseInt(newValue)); |
137 | |
138 this.makeGCOwnContainer(this.internalInstanceId); | |
139 | |
140 // Track when the element resizes using the element resize callback. | |
141 GuestViewInternalNatives.RegisterElementResizeCallback( | |
142 this.internalInstanceId, this.weakWrapper(this.onElementResize)); | |
143 | |
144 if (!this.guest.getId()) { | |
145 return; | |
146 } | |
147 this.guest.attach(this.internalInstanceId, | |
148 this.viewInstanceId, | |
149 this.buildParams()); | |
150 } | 153 } |
151 }; | 154 }; |
152 | 155 |
153 GuestViewContainer.prototype.onElementResize = function(newWidth, newHeight) { | 156 GuestViewContainer.prototype.onElementResize = function(newWidth, newHeight) { |
154 if (!this.guest.getId()) | 157 if (!this.guest.getId()) |
155 return; | 158 return; |
156 this.guest.setSize({normal: {width: newWidth, height: newHeight}}); | 159 this.guest.setSize({normal: {width: newWidth, height: newHeight}}); |
157 }; | 160 }; |
158 | 161 |
159 GuestViewContainer.prototype.buildParams = function() { | 162 GuestViewContainer.prototype.buildParams = function() { |
(...skipping 29 matching lines...) Expand all Loading... |
189 }; | 192 }; |
190 | 193 |
191 // Implemented by the specific view type, if needed. | 194 // Implemented by the specific view type, if needed. |
192 GuestViewContainer.prototype.buildContainerParams = function() { return {}; }; | 195 GuestViewContainer.prototype.buildContainerParams = function() { return {}; }; |
193 GuestViewContainer.prototype.onElementAttached = function() {}; | 196 GuestViewContainer.prototype.onElementAttached = function() {}; |
194 GuestViewContainer.prototype.onElementDetached = function() {}; | 197 GuestViewContainer.prototype.onElementDetached = function() {}; |
195 GuestViewContainer.prototype.setupAttributes = function() {}; | 198 GuestViewContainer.prototype.setupAttributes = function() {}; |
196 | 199 |
197 // Registers the browser plugin <object> custom element. |viewType| is the | 200 // Registers the browser plugin <object> custom element. |viewType| is the |
198 // name of the specific guestview container (e.g. 'webview'). | 201 // name of the specific guestview container (e.g. 'webview'). |
199 function registerBrowserPluginElement(viewType) { | 202 function registerInternalElement(viewType) { |
200 var proto = $Object.create(HTMLElement.prototype); | 203 var proto = $Object.create(HTMLElement.prototype); |
201 | 204 |
202 proto.createdCallback = function() { | 205 proto.createdCallback = function() { |
203 this.setAttribute('type', 'application/browser-plugin'); | 206 this.setAttribute('type', 'application/browser-plugin'); |
204 this.setAttribute('id', 'browser-plugin-' + IdGenerator.GetNextId()); | 207 this.setAttribute('id', 'browser-plugin-' + IdGenerator.GetNextId()); |
205 this.style.width = '100%'; | 208 this.style.width = '100%'; |
206 this.style.height = '100%'; | 209 this.style.height = '100%'; |
207 }; | 210 }; |
208 | 211 |
209 proto.attachedCallback = function() { | 212 proto.attachedCallback = function() { |
210 // Load the plugin immediately. | 213 // Load the plugin immediately. |
211 var unused = this.nonExistentAttribute; | 214 var unused = this.nonExistentAttribute; |
212 }; | 215 }; |
213 | 216 |
214 proto.attributeChangedCallback = function(name, oldValue, newValue) { | 217 proto.attributeChangedCallback = function(name, oldValue, newValue) { |
215 var internal = privates(this).internal; | 218 var internal = privates(this).internal; |
216 if (!internal) { | 219 if (!internal) { |
217 return; | 220 return; |
218 } | 221 } |
219 internal.handleBrowserPluginAttributeMutation(name, oldValue, newValue); | 222 internal.handleInternalElementAttributeMutation(name, oldValue, newValue); |
220 }; | 223 }; |
221 | 224 |
222 GuestViewContainer[viewType + 'BrowserPlugin'] = | 225 GuestViewContainer[viewType + 'BrowserPlugin'] = |
223 DocumentNatives.RegisterElement(viewType + 'browserplugin', | 226 DocumentNatives.RegisterElement(viewType + 'browserplugin', |
224 {extends: 'object', prototype: proto}); | 227 {extends: 'object', prototype: proto}); |
225 | 228 |
226 delete proto.createdCallback; | 229 delete proto.createdCallback; |
227 delete proto.attachedCallback; | 230 delete proto.attachedCallback; |
228 delete proto.detachedCallback; | 231 delete proto.detachedCallback; |
229 delete proto.attributeChangedCallback; | 232 delete proto.attributeChangedCallback; |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
283 // Delete the callbacks so developers cannot call them and produce unexpected | 286 // Delete the callbacks so developers cannot call them and produce unexpected |
284 // behavior. | 287 // behavior. |
285 delete proto.createdCallback; | 288 delete proto.createdCallback; |
286 delete proto.attachedCallback; | 289 delete proto.attachedCallback; |
287 delete proto.detachedCallback; | 290 delete proto.detachedCallback; |
288 delete proto.attributeChangedCallback; | 291 delete proto.attributeChangedCallback; |
289 } | 292 } |
290 | 293 |
291 // Exports. | 294 // Exports. |
292 exports.GuestViewContainer = GuestViewContainer; | 295 exports.GuestViewContainer = GuestViewContainer; |
OLD | NEW |