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.
5 // This module implements the attributes of the <webview> tag.
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;
13 // -----------------------------------------------------------------------------
16 // Default implementation of a WebView attribute.
17 function WebViewAttribute(name, webViewImpl) {
19 this.webViewImpl = webViewImpl;
20 this.ignoreMutation = false;
22 this.defineProperty();
25 // Retrieves and returns the attribute's value.
26 WebViewAttribute.prototype.getValue = function() {
27 return this.webViewImpl.element.getAttribute(this.name) || '';
30 // Sets the attribute's value.
31 WebViewAttribute.prototype.setValue = function(value) {
32 this.webViewImpl.element.setAttribute(this.name, value || '');
35 // Changes the attribute's value without triggering its mutation handler.
36 WebViewAttribute.prototype.setValueIgnoreMutation = function(value) {
37 this.ignoreMutation = true;
39 this.ignoreMutation = false;
42 // Defines this attribute as a property on the webview node.
43 WebViewAttribute.prototype.defineProperty = function() {
44 Object.defineProperty(this.webViewImpl.element, this.name, {
46 return this.getValue();
48 set: function(value) {
55 // Called when the attribute's value changes.
56 WebViewAttribute.prototype.maybeHandleMutation = function(oldValue, newValue) {
57 if (this.ignoreMutation) {
61 this.handleMutation(oldValue, newValue);
64 // Called when a change that isn't ignored occurs to the attribute's value.
65 WebViewAttribute.prototype.handleMutation = function(oldValue, newValue) {};
67 // Called when the <webview> element is attached to the DOM tree.
68 WebViewAttribute.prototype.attach = function() {};
70 // Called when the <webview> element is detached from the DOM tree.
71 WebViewAttribute.prototype.detach = function() {};
73 // An attribute that is treated as a Boolean.
74 function BooleanAttribute(name, webViewImpl) {
75 WebViewAttribute.call(this, name, webViewImpl);
78 BooleanAttribute.prototype.__proto__ = WebViewAttribute.prototype;
80 BooleanAttribute.prototype.getValue = function() {
81 return this.webViewImpl.element.hasAttribute(this.name);
84 BooleanAttribute.prototype.setValue = function(value) {
86 this.webViewImpl.element.removeAttribute(this.name);
88 this.webViewImpl.element.setAttribute(this.name, '');
92 // Attribute that specifies whether transparency is allowed in the webview.
93 function AllowTransparencyAttribute(webViewImpl) {
94 BooleanAttribute.call(
95 this, WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, webViewImpl);
98 AllowTransparencyAttribute.prototype.__proto__ = BooleanAttribute.prototype;
100 AllowTransparencyAttribute.prototype.handleMutation = function(oldValue,
102 if (!this.webViewImpl.guest.getId()) {
106 WebViewInternal.setAllowTransparency(this.webViewImpl.guest.getId(),
110 // Attribute that specifies whether transparency is allowed in the webview.
111 function AllowScalingAttribute(webViewImpl) {
112 BooleanAttribute.call(
113 this, WebViewConstants.ATTRIBUTE_ALLOWSCALING, webViewImpl);
116 AllowScalingAttribute.prototype.__proto__ = BooleanAttribute.prototype;
118 AllowScalingAttribute.prototype.handleMutation = function(oldValue,
120 if (!this.webViewImpl.guest.getId()) {
124 WebViewInternal.setAllowScaling(this.webViewImpl.guest.getId(),
128 // Attribute used to define the demension limits of autosizing.
129 function AutosizeDimensionAttribute(name, webViewImpl) {
130 WebViewAttribute.call(this, name, webViewImpl);
133 AutosizeDimensionAttribute.prototype.__proto__ = WebViewAttribute.prototype;
135 AutosizeDimensionAttribute.prototype.getValue = function() {
136 return parseInt(this.webViewImpl.element.getAttribute(this.name)) || 0;
139 AutosizeDimensionAttribute.prototype.handleMutation = function(
140 oldValue, newValue) {
141 if (!this.webViewImpl.guest.getId()) {
144 this.webViewImpl.guest.setSize({
145 'enableAutoSize': this.webViewImpl.attributes[
146 WebViewConstants.ATTRIBUTE_AUTOSIZE].getValue(),
148 'width': this.webViewImpl.attributes[
149 WebViewConstants.ATTRIBUTE_MINWIDTH].getValue(),
150 'height': this.webViewImpl.attributes[
151 WebViewConstants.ATTRIBUTE_MINHEIGHT].getValue()
154 'width': this.webViewImpl.attributes[
155 WebViewConstants.ATTRIBUTE_MAXWIDTH].getValue(),
156 'height': this.webViewImpl.attributes[
157 WebViewConstants.ATTRIBUTE_MAXHEIGHT].getValue()
163 // Attribute that specifies whether the webview should be autosized.
164 function AutosizeAttribute(webViewImpl) {
165 BooleanAttribute.call(this, WebViewConstants.ATTRIBUTE_AUTOSIZE, webViewImpl);
168 AutosizeAttribute.prototype.__proto__ = BooleanAttribute.prototype;
170 AutosizeAttribute.prototype.handleMutation =
171 AutosizeDimensionAttribute.prototype.handleMutation;
173 // Attribute that sets the guest content's window.name object.
174 function NameAttribute(webViewImpl) {
175 WebViewAttribute.call(this, WebViewConstants.ATTRIBUTE_NAME, webViewImpl);
178 NameAttribute.prototype.__proto__ = WebViewAttribute.prototype
180 NameAttribute.prototype.handleMutation = function(oldValue, newValue) {
181 oldValue = oldValue || '';
182 newValue = newValue || '';
183 if (oldValue === newValue || !this.webViewImpl.guest.getId()) {
187 WebViewInternal.setName(this.webViewImpl.guest.getId(), newValue);
190 NameAttribute.prototype.setValue = function(value) {
193 this.webViewImpl.element.removeAttribute(this.name);
195 this.webViewImpl.element.setAttribute(this.name, value);
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;
206 PartitionAttribute.prototype.__proto__ = WebViewAttribute.prototype;
208 PartitionAttribute.prototype.handleMutation = function(oldValue, newValue) {
209 newValue = newValue || '';
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);
218 if (newValue == 'persist:') {
219 this.validPartitionId = false;
220 window.console.error(
221 WebViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE);
225 PartitionAttribute.prototype.detach = function() {
226 this.validPartitionId = true;
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;
237 SrcAttribute.prototype.__proto__ = WebViewAttribute.prototype;
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();
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);
262 SrcAttribute.prototype.attach = function() {
263 this.elementAttached = true;
267 SrcAttribute.prototype.detach = function() {
268 this.beforeFirstNavigation = true;
269 this.elementAttached = false;
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 =
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) {
285 this.handleMutation(oldValue, newValue);
290 attributeOldValue: true,
291 attributeFilter: [this.name]
293 this.observer.observe(this.webViewImpl.element, params);
296 SrcAttribute.prototype.parse = function() {
297 if (!this.elementAttached ||
298 !this.webViewImpl.attributes[
299 WebViewConstants.ATTRIBUTE_PARTITION].validPartitionId ||
304 if (!this.webViewImpl.guest.getId()) {
305 if (this.beforeFirstNavigation) {
306 this.beforeFirstNavigation = false;
307 this.webViewImpl.createGuest();
312 // Navigate to |src|.
313 WebViewInternal.navigate(this.webViewImpl.guest.getId(), this.getValue());
316 // -----------------------------------------------------------------------------
318 // Sets up all of the webview attributes.
319 WebViewImpl.prototype.setupWebViewAttributes = function() {
320 this.attributes = {};
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);
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);