Refactor common functionality out of webview, appview, and extensionoptions.
[chromium-blink-merge.git] / extensions / renderer / resources / guest_view / web_view_attributes.js
blobaa1b7598480f93243affeb71320b026fe7f83d8e
1 // Copyright (c) 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 // -----------------------------------------------------------------------------
14 // Attribute objects.
16 // Default implementation of a WebView attribute.
17 function WebViewAttribute(name, webViewImpl) {
18   this.name = name;
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;
38   this.webViewImpl.element.setAttribute(this.name, value || '');
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, {
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   });
55 // Called when the attribute's value changes.
56 WebViewAttribute.prototype.handleMutation = function(oldValue, newValue) {};
58 // An attribute that is treated as a Boolean.
59 function BooleanAttribute(name, webViewImpl) {
60   WebViewAttribute.call(this, name, webViewImpl);
63 BooleanAttribute.prototype.__proto__ = WebViewAttribute.prototype;
65 BooleanAttribute.prototype.getValue = function() {
66   return this.webViewImpl.element.hasAttribute(this.name);
69 BooleanAttribute.prototype.setValue = function(value) {
70   if (!value) {
71     this.webViewImpl.element.removeAttribute(this.name);
72   } else {
73     this.webViewImpl.element.setAttribute(this.name, '');
74   }
77 // Attribute that specifies whether transparency is allowed in the webview.
78 function AllowTransparencyAttribute(webViewImpl) {
79   BooleanAttribute.call(
80       this, WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, webViewImpl);
83 AllowTransparencyAttribute.prototype.__proto__ = BooleanAttribute.prototype;
85 AllowTransparencyAttribute.prototype.handleMutation = function(oldValue,
86                                                                newValue) {
87   if (!this.webViewImpl.guest.getId()) {
88     return;
89   }
91   WebViewInternal.setAllowTransparency(
92       this.webViewImpl.guest.getId(),
93       this.webViewImpl.attributes[
94           WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].getValue());
97 // Attribute used to define the demension limits of autosizing.
98 function AutosizeDimensionAttribute(name, webViewImpl) {
99   WebViewAttribute.call(this, name, webViewImpl);
102 AutosizeDimensionAttribute.prototype.__proto__ = WebViewAttribute.prototype;
104 AutosizeDimensionAttribute.prototype.getValue = function() {
105   return parseInt(this.webViewImpl.element.getAttribute(this.name)) || 0;
108 AutosizeDimensionAttribute.prototype.handleMutation = function(
109     oldValue, newValue) {
110   if (!this.webViewImpl.guest.getId()) {
111     return;
112   }
113   GuestViewInternal.setAutoSize(this.webViewImpl.guest.getId(), {
114     'enableAutoSize': this.webViewImpl.attributes[
115       WebViewConstants.ATTRIBUTE_AUTOSIZE].getValue(),
116     'min': {
117       'width': this.webViewImpl.attributes[
118           WebViewConstants.ATTRIBUTE_MINWIDTH].getValue(),
119       'height': this.webViewImpl.attributes[
120           WebViewConstants.ATTRIBUTE_MINHEIGHT].getValue()
121     },
122     'max': {
123       'width': this.webViewImpl.attributes[
124           WebViewConstants.ATTRIBUTE_MAXWIDTH].getValue(),
125       'height': this.webViewImpl.attributes[
126           WebViewConstants.ATTRIBUTE_MAXHEIGHT].getValue()
127     }
128   });
129   return;
132 // Attribute that specifies whether the webview should be autosized.
133 function AutosizeAttribute(webViewImpl) {
134   BooleanAttribute.call(this, WebViewConstants.ATTRIBUTE_AUTOSIZE, webViewImpl);
137 AutosizeAttribute.prototype.__proto__ = BooleanAttribute.prototype;
139 AutosizeAttribute.prototype.handleMutation =
140     AutosizeDimensionAttribute.prototype.handleMutation;
142 // Attribute that sets the guest content's window.name object.
143 function NameAttribute(webViewImpl) {
144   WebViewAttribute.call(this, WebViewConstants.ATTRIBUTE_NAME, webViewImpl);
147 NameAttribute.prototype.__proto__ = WebViewAttribute.prototype
149 NameAttribute.prototype.handleMutation = function(oldValue, newValue) {
150   oldValue = oldValue || '';
151   newValue = newValue || '';
152   if (oldValue === newValue || !this.webViewImpl.guest.getId()) {
153     return;
154   }
156   WebViewInternal.setName(this.webViewImpl.guest.getId(), newValue);
159 // Attribute representing the state of the storage partition.
160 function PartitionAttribute(webViewImpl) {
161   WebViewAttribute.call(
162       this, WebViewConstants.ATTRIBUTE_PARTITION, webViewImpl);
163   this.validPartitionId = true;
166 PartitionAttribute.prototype.__proto__ = WebViewAttribute.prototype;
168 PartitionAttribute.prototype.handleMutation = function(oldValue, newValue) {
169   newValue = newValue || '';
171   // The partition cannot change if the webview has already navigated.
172   if (!this.webViewImpl.beforeFirstNavigation) {
173     window.console.error(WebViewConstants.ERROR_MSG_ALREADY_NAVIGATED);
174     this.setValueIgnoreMutation(oldValue);
175     return;
176   }
177   if (newValue == 'persist:') {
178     this.validPartitionId = false;
179     window.console.error(
180         WebViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE);
181   }
184 // Attribute that handles the location and navigation of the webview.
185 function SrcAttribute(webViewImpl) {
186   WebViewAttribute.call(this, WebViewConstants.ATTRIBUTE_SRC, webViewImpl);
187   this.setupMutationObserver();
190 SrcAttribute.prototype.__proto__ = WebViewAttribute.prototype;
192 SrcAttribute.prototype.setValueIgnoreMutation = function(value) {
193   // takeRecords() is needed to clear queued up src mutations. Without it, it is
194   // possible for this change to get picked up asyncronously by src's mutation
195   // observer |observer|, and then get handled even though we do not want to
196   // handle this mutation.
197   this.observer.takeRecords();
198   this.ignoreMutation = true;
199   this.webViewImpl.element.setAttribute(this.name, value || '');
200   this.ignoreMutation = false;
203 SrcAttribute.prototype.handleMutation = function(oldValue, newValue) {
204   // Once we have navigated, we don't allow clearing the src attribute.
205   // Once <webview> enters a navigated state, it cannot return to a
206   // placeholder state.
207   if (!newValue && oldValue) {
208     // src attribute changes normally initiate a navigation. We suppress
209     // the next src attribute handler call to avoid reloading the page
210     // on every guest-initiated navigation.
211     this.setValueIgnoreMutation(oldValue);
212     return;
213   }
214   this.webViewImpl.parseSrcAttribute();
217 // The purpose of this mutation observer is to catch assignment to the src
218 // attribute without any changes to its value. This is useful in the case
219 // where the webview guest has crashed and navigating to the same address
220 // spawns off a new process.
221 SrcAttribute.prototype.setupMutationObserver =
222     function() {
223   this.observer = new MutationObserver(function(mutations) {
224     $Array.forEach(mutations, function(mutation) {
225       var oldValue = mutation.oldValue;
226       var newValue = this.getValue();
227       if (oldValue != newValue) {
228         return;
229       }
230       this.handleMutation(oldValue, newValue);
231     }.bind(this));
232   }.bind(this));
233   var params = {
234     attributes: true,
235     attributeOldValue: true,
236     attributeFilter: [this.name]
237   };
238   this.observer.observe(this.webViewImpl.element, params);
241 // -----------------------------------------------------------------------------
243 // Sets up all of the webview attributes.
244 WebViewImpl.prototype.setupWebViewAttributes = function() {
245   this.attributes = {};
247   this.attributes[WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY] =
248       new AllowTransparencyAttribute(this);
249   this.attributes[WebViewConstants.ATTRIBUTE_AUTOSIZE] =
250       new AutosizeAttribute(this);
251   this.attributes[WebViewConstants.ATTRIBUTE_NAME] =
252       new NameAttribute(this);
253   this.attributes[WebViewConstants.ATTRIBUTE_PARTITION] =
254       new PartitionAttribute(this);
255   this.attributes[WebViewConstants.ATTRIBUTE_SRC] =
256       new SrcAttribute(this);
258   var autosizeAttributes = [WebViewConstants.ATTRIBUTE_MAXHEIGHT,
259                             WebViewConstants.ATTRIBUTE_MAXWIDTH,
260                             WebViewConstants.ATTRIBUTE_MINHEIGHT,
261                             WebViewConstants.ATTRIBUTE_MINWIDTH];
262   for (var i = 0; autosizeAttributes[i]; ++i) {
263     this.attributes[autosizeAttributes[i]] =
264         new AutosizeDimensionAttribute(autosizeAttributes[i], this);
265   }