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 // This module implements the attributes of the <webview> tag. | |
6 | |
7 var GuestViewInternal = | |
8 require('binding').Binding.create('guestViewInternal').generate(); | |
9 var WebViewImpl = require('webView').WebViewImpl; | |
10 var WebViewConstants = require('webViewConstants').WebViewConstants; | |
11 var WebViewInternal = require('webViewInternal').WebViewInternal; | |
12 | |
13 // ----------------------------------------------------------------------------- | |
14 // Attribute objects. | |
15 | |
16 // Default implementation of a WebView attribute. | |
17 function WebViewAttribute(name, webViewImpl) { | |
18 this.name = name; | |
19 this.webViewImpl = webViewImpl; | |
20 this.ignoreMutation = false; | |
21 | |
22 this.defineProperty(); | |
23 } | |
24 | |
25 // Retrieves and returns the attribute's value. | |
26 WebViewAttribute.prototype.getValue = function() { | |
27 return this.webViewImpl.element.getAttribute(this.name) || ''; | |
28 }; | |
29 | |
30 // Sets the attribute's value. | |
31 WebViewAttribute.prototype.setValue = function(value) { | |
32 this.webViewImpl.element.setAttribute(this.name, value || ''); | |
33 }; | |
34 | |
35 // Changes the attribute's value without triggering its mutation handler. | |
36 WebViewAttribute.prototype.setValueIgnoreMutation = function(value) { | |
37 this.ignoreMutation = true; | |
38 this.setValue(value); | |
39 this.ignoreMutation = false; | |
40 } | |
41 | |
42 // Defines this attribute as a property on the webview node. | |
43 WebViewAttribute.prototype.defineProperty = function() { | |
44 Object.defineProperty(this.webViewImpl.element, this.name, { | |
45 get: function() { | |
46 return this.getValue(); | |
47 }.bind(this), | |
48 set: function(value) { | |
49 this.setValue(value); | |
50 }.bind(this), | |
51 enumerable: true | |
52 }); | |
53 }; | |
54 | |
55 // Called when the attribute's value changes. | |
56 WebViewAttribute.prototype.maybeHandleMutation = function(oldValue, newValue) { | |
57 if (this.ignoreMutation) { | |
58 return; | |
59 } | |
60 | |
61 this.handleMutation(oldValue, newValue); | |
62 }; | |
63 | |
64 // Called when a change that isn't ignored occurs to the attribute's value. | |
65 WebViewAttribute.prototype.handleMutation = function(oldValue, newValue) {}; | |
66 | |
67 // Called when the <webview> element is attached to the DOM tree. | |
68 WebViewAttribute.prototype.attach = function() {}; | |
69 | |
70 // Called when the <webview> element is detached from the DOM tree. | |
71 WebViewAttribute.prototype.detach = function() {}; | |
72 | |
73 // An attribute that is treated as a Boolean. | |
74 function BooleanAttribute(name, webViewImpl) { | |
75 WebViewAttribute.call(this, name, webViewImpl); | |
76 } | |
77 | |
78 BooleanAttribute.prototype.__proto__ = WebViewAttribute.prototype; | |
79 | |
80 BooleanAttribute.prototype.getValue = function() { | |
81 return this.webViewImpl.element.hasAttribute(this.name); | |
82 }; | |
83 | |
84 BooleanAttribute.prototype.setValue = function(value) { | |
85 if (!value) { | |
86 this.webViewImpl.element.removeAttribute(this.name); | |
87 } else { | |
88 this.webViewImpl.element.setAttribute(this.name, ''); | |
89 } | |
90 }; | |
91 | |
92 // Attribute that specifies whether transparency is allowed in the webview. | |
93 function AllowTransparencyAttribute(webViewImpl) { | |
94 BooleanAttribute.call( | |
95 this, WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, webViewImpl); | |
96 } | |
97 | |
98 AllowTransparencyAttribute.prototype.__proto__ = BooleanAttribute.prototype; | |
99 | |
100 AllowTransparencyAttribute.prototype.handleMutation = function(oldValue, | |
101 newValue) { | |
102 if (!this.webViewImpl.guest.getId()) { | |
103 return; | |
104 } | |
105 | |
106 WebViewInternal.setAllowTransparency(this.webViewImpl.guest.getId(), | |
107 this.getValue()); | |
108 }; | |
109 | |
110 // Attribute that specifies whether transparency is allowed in the webview. | |
111 function AllowScalingAttribute(webViewImpl) { | |
112 BooleanAttribute.call( | |
113 this, WebViewConstants.ATTRIBUTE_ALLOWSCALING, webViewImpl); | |
114 } | |
115 | |
116 AllowScalingAttribute.prototype.__proto__ = BooleanAttribute.prototype; | |
117 | |
118 AllowScalingAttribute.prototype.handleMutation = function(oldValue, | |
119 newValue) { | |
120 if (!this.webViewImpl.guest.getId()) { | |
121 return; | |
122 } | |
123 | |
124 WebViewInternal.setAllowScaling(this.webViewImpl.guest.getId(), | |
125 this.getValue()); | |
126 }; | |
127 | |
128 // Attribute used to define the demension limits of autosizing. | |
129 function AutosizeDimensionAttribute(name, webViewImpl) { | |
130 WebViewAttribute.call(this, name, webViewImpl); | |
131 } | |
132 | |
133 AutosizeDimensionAttribute.prototype.__proto__ = WebViewAttribute.prototype; | |
134 | |
135 AutosizeDimensionAttribute.prototype.getValue = function() { | |
136 return parseInt(this.webViewImpl.element.getAttribute(this.name)) || 0; | |
137 }; | |
138 | |
139 AutosizeDimensionAttribute.prototype.handleMutation = function( | |
140 oldValue, newValue) { | |
141 if (!this.webViewImpl.guest.getId()) { | |
142 return; | |
143 } | |
144 this.webViewImpl.guest.setSize({ | |
145 'enableAutoSize': this.webViewImpl.attributes[ | |
146 WebViewConstants.ATTRIBUTE_AUTOSIZE].getValue(), | |
147 'min': { | |
148 'width': this.webViewImpl.attributes[ | |
149 WebViewConstants.ATTRIBUTE_MINWIDTH].getValue(), | |
150 'height': this.webViewImpl.attributes[ | |
151 WebViewConstants.ATTRIBUTE_MINHEIGHT].getValue() | |
152 }, | |
153 'max': { | |
154 'width': this.webViewImpl.attributes[ | |
155 WebViewConstants.ATTRIBUTE_MAXWIDTH].getValue(), | |
156 'height': this.webViewImpl.attributes[ | |
157 WebViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() | |
158 } | |
159 }); | |
160 return; | |
161 }; | |
162 | |
163 // Attribute that specifies whether the webview should be autosized. | |
164 function AutosizeAttribute(webViewImpl) { | |
165 BooleanAttribute.call(this, WebViewConstants.ATTRIBUTE_AUTOSIZE, webViewImpl); | |
166 } | |
167 | |
168 AutosizeAttribute.prototype.__proto__ = BooleanAttribute.prototype; | |
169 | |
170 AutosizeAttribute.prototype.handleMutation = | |
171 AutosizeDimensionAttribute.prototype.handleMutation; | |
172 | |
173 // Attribute that sets the guest content's window.name object. | |
174 function NameAttribute(webViewImpl) { | |
175 WebViewAttribute.call(this, WebViewConstants.ATTRIBUTE_NAME, webViewImpl); | |
176 } | |
177 | |
178 NameAttribute.prototype.__proto__ = WebViewAttribute.prototype | |
179 | |
180 NameAttribute.prototype.handleMutation = function(oldValue, newValue) { | |
181 oldValue = oldValue || ''; | |
182 newValue = newValue || ''; | |
183 if (oldValue === newValue || !this.webViewImpl.guest.getId()) { | |
184 return; | |
185 } | |
186 | |
187 WebViewInternal.setName(this.webViewImpl.guest.getId(), newValue); | |
188 }; | |
189 | |
190 NameAttribute.prototype.setValue = function(value) { | |
191 value = value || ''; | |
192 if (value === '') { | |
193 this.webViewImpl.element.removeAttribute(this.name); | |
194 } else { | |
195 this.webViewImpl.element.setAttribute(this.name, value); | |
196 } | |
197 }; | |
198 | |
199 // Attribute representing the state of the storage partition. | |
200 function PartitionAttribute(webViewImpl) { | |
201 WebViewAttribute.call( | |
202 this, WebViewConstants.ATTRIBUTE_PARTITION, webViewImpl); | |
203 this.validPartitionId = true; | |
204 } | |
205 | |
206 PartitionAttribute.prototype.__proto__ = WebViewAttribute.prototype; | |
207 | |
208 PartitionAttribute.prototype.handleMutation = function(oldValue, newValue) { | |
209 newValue = newValue || ''; | |
210 | |
211 // The partition cannot change if the webview has already navigated. | |
212 if (!this.webViewImpl.attributes[ | |
213 WebViewConstants.ATTRIBUTE_SRC].beforeFirstNavigation) { | |
214 window.console.error(WebViewConstants.ERROR_MSG_ALREADY_NAVIGATED); | |
215 this.setValueIgnoreMutation(oldValue); | |
216 return; | |
217 } | |
218 if (newValue == 'persist:') { | |
219 this.validPartitionId = false; | |
220 window.console.error( | |
221 WebViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE); | |
222 } | |
223 }; | |
224 | |
225 PartitionAttribute.prototype.detach = function() { | |
226 this.validPartitionId = true; | |
227 }; | |
228 | |
229 // Attribute that handles the location and navigation of the webview. | |
230 function SrcAttribute(webViewImpl) { | |
231 WebViewAttribute.call(this, WebViewConstants.ATTRIBUTE_SRC, webViewImpl); | |
232 this.setupMutationObserver(); | |
233 this.beforeFirstNavigation = true; | |
234 this.elementAttached = false; | |
235 } | |
236 | |
237 SrcAttribute.prototype.__proto__ = WebViewAttribute.prototype; | |
238 | |
239 SrcAttribute.prototype.setValueIgnoreMutation = function(value) { | |
240 WebViewAttribute.prototype.setValueIgnoreMutation.call(this, value); | |
241 // takeRecords() is needed to clear queued up src mutations. Without it, it is | |
242 // possible for this change to get picked up asyncronously by src's mutation | |
243 // observer |observer|, and then get handled even though we do not want to | |
244 // handle this mutation. | |
245 this.observer.takeRecords(); | |
246 } | |
247 | |
248 SrcAttribute.prototype.handleMutation = function(oldValue, newValue) { | |
249 // Once we have navigated, we don't allow clearing the src attribute. | |
250 // Once <webview> enters a navigated state, it cannot return to a | |
251 // placeholder state. | |
252 if (!newValue && oldValue) { | |
253 // src attribute changes normally initiate a navigation. We suppress | |
254 // the next src attribute handler call to avoid reloading the page | |
255 // on every guest-initiated navigation. | |
256 this.setValueIgnoreMutation(oldValue); | |
257 return; | |
258 } | |
259 this.parse(); | |
260 }; | |
261 | |
262 SrcAttribute.prototype.attach = function() { | |
263 this.elementAttached = true; | |
264 this.parse(); | |
265 }; | |
266 | |
267 SrcAttribute.prototype.detach = function() { | |
268 this.beforeFirstNavigation = true; | |
269 this.elementAttached = false; | |
270 }; | |
271 | |
272 // The purpose of this mutation observer is to catch assignment to the src | |
273 // attribute without any changes to its value. This is useful in the case | |
274 // where the webview guest has crashed and navigating to the same address | |
275 // spawns off a new process. | |
276 SrcAttribute.prototype.setupMutationObserver = | |
277 function() { | |
278 this.observer = new MutationObserver(function(mutations) { | |
279 $Array.forEach(mutations, function(mutation) { | |
280 var oldValue = mutation.oldValue; | |
281 var newValue = this.getValue(); | |
282 if (oldValue != newValue) { | |
283 return; | |
284 } | |
285 this.handleMutation(oldValue, newValue); | |
286 }.bind(this)); | |
287 }.bind(this)); | |
288 var params = { | |
289 attributes: true, | |
290 attributeOldValue: true, | |
291 attributeFilter: [this.name] | |
292 }; | |
293 this.observer.observe(this.webViewImpl.element, params); | |
294 }; | |
295 | |
296 SrcAttribute.prototype.parse = function() { | |
297 if (!this.elementAttached || | |
298 !this.webViewImpl.attributes[ | |
299 WebViewConstants.ATTRIBUTE_PARTITION].validPartitionId || | |
300 !this.getValue()) { | |
301 return; | |
302 } | |
303 | |
304 if (!this.webViewImpl.guest.getId()) { | |
305 if (this.beforeFirstNavigation) { | |
306 this.beforeFirstNavigation = false; | |
307 this.webViewImpl.createGuest(); | |
308 } | |
309 return; | |
310 } | |
311 | |
312 // Navigate to |src|. | |
313 WebViewInternal.navigate(this.webViewImpl.guest.getId(), this.getValue()); | |
314 }; | |
315 | |
316 // ----------------------------------------------------------------------------- | |
317 | |
318 // Sets up all of the webview attributes. | |
319 WebViewImpl.prototype.setupWebViewAttributes = function() { | |
320 this.attributes = {}; | |
321 | |
322 this.attributes[WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY] = | |
323 new AllowTransparencyAttribute(this); | |
324 this.attributes[WebViewConstants.ATTRIBUTE_ALLOWSCALING] = | |
325 new AllowScalingAttribute(this); | |
326 this.attributes[WebViewConstants.ATTRIBUTE_AUTOSIZE] = | |
327 new AutosizeAttribute(this); | |
328 this.attributes[WebViewConstants.ATTRIBUTE_NAME] = | |
329 new NameAttribute(this); | |
330 this.attributes[WebViewConstants.ATTRIBUTE_PARTITION] = | |
331 new PartitionAttribute(this); | |
332 this.attributes[WebViewConstants.ATTRIBUTE_SRC] = | |
333 new SrcAttribute(this); | |
334 | |
335 var autosizeAttributes = [WebViewConstants.ATTRIBUTE_MAXHEIGHT, | |
336 WebViewConstants.ATTRIBUTE_MAXWIDTH, | |
337 WebViewConstants.ATTRIBUTE_MINHEIGHT, | |
338 WebViewConstants.ATTRIBUTE_MINWIDTH]; | |
339 for (var i = 0; autosizeAttributes[i]; ++i) { | |
340 this.attributes[autosizeAttributes[i]] = | |
341 new AutosizeDimensionAttribute(autosizeAttributes[i], this); | |
342 } | |
343 }; | |
OLD | NEW |