OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 WebView (<webview>) as a custom element that wraps a | |
6 // BrowserPlugin object element. The object element is hidden within | |
7 // the shadow DOM of the WebView element. | |
8 | |
9 var DocumentNatives = requireNative('document_natives'); | |
10 var GuestView = require('guestView').GuestView; | |
11 var GuestViewContainer = require('guestViewContainer').GuestViewContainer; | |
12 var WebViewConstants = require('webViewConstants').WebViewConstants; | |
13 var WebViewEvents = require('webViewEvents').WebViewEvents; | |
14 var WebViewInternal = require('webViewInternal').WebViewInternal; | |
15 | |
16 // Represents the internal state of <webview>. | |
17 function WebViewImpl(webviewElement) { | |
18 GuestViewContainer.call(this, webviewElement, 'webview'); | |
19 | |
20 this.setupWebViewAttributes(); | |
21 this.setupElementProperties(); | |
22 | |
23 new WebViewEvents(this, this.viewInstanceId); | |
24 } | |
25 | |
26 WebViewImpl.prototype.__proto__ = GuestViewContainer.prototype; | |
27 | |
28 WebViewImpl.VIEW_TYPE = 'WebView'; | |
29 | |
30 // Add extra functionality to |this.element|. | |
31 WebViewImpl.setupElement = function(proto) { | |
32 // Public-facing API methods. | |
33 var apiMethods = WebViewImpl.getApiMethods(); | |
34 | |
35 // Add the experimental API methods, if available. | |
36 var experimentalApiMethods = | |
37 WebViewImpl.maybeGetExperimentalApiMethods(); | |
38 apiMethods = $Array.concat(apiMethods, experimentalApiMethods); | |
39 | |
40 // Create default implementations for undefined API methods. | |
41 var createDefaultApiMethod = function(m) { | |
42 return function(var_args) { | |
43 if (!this.guest.getId()) { | |
44 return false; | |
45 } | |
46 var args = $Array.concat([this.guest.getId()], $Array.slice(arguments)); | |
47 $Function.apply(WebViewInternal[m], null, args); | |
48 return true; | |
49 }; | |
50 }; | |
51 for (var i = 0; i != apiMethods.length; ++i) { | |
52 if (WebViewImpl.prototype[apiMethods[i]] == undefined) { | |
53 WebViewImpl.prototype[apiMethods[i]] = | |
54 createDefaultApiMethod(apiMethods[i]); | |
55 } | |
56 } | |
57 | |
58 // Forward proto.foo* method calls to WebViewImpl.foo*. | |
59 GuestViewContainer.forwardApiMethods(proto, apiMethods); | |
60 }; | |
61 | |
62 // Initiates navigation once the <webview> element is attached to the DOM. | |
63 WebViewImpl.prototype.onElementAttached = function() { | |
64 for (var i in this.attributes) { | |
65 this.attributes[i].attach(); | |
66 } | |
67 }; | |
68 | |
69 // Resets some state upon detaching <webview> element from the DOM. | |
70 WebViewImpl.prototype.onElementDetached = function() { | |
71 this.guest.destroy(); | |
72 for (var i in this.attributes) { | |
73 this.attributes[i].detach(); | |
74 } | |
75 }; | |
76 | |
77 // Sets the <webview>.request property. | |
78 WebViewImpl.prototype.setRequestPropertyOnWebViewElement = function(request) { | |
79 Object.defineProperty( | |
80 this.element, | |
81 'request', | |
82 { | |
83 value: request, | |
84 enumerable: true | |
85 } | |
86 ); | |
87 }; | |
88 | |
89 WebViewImpl.prototype.setupElementProperties = function() { | |
90 // We cannot use {writable: true} property descriptor because we want a | |
91 // dynamic getter value. | |
92 Object.defineProperty(this.element, 'contentWindow', { | |
93 get: function() { | |
94 if (this.guest.getContentWindow()) { | |
95 return this.guest.getContentWindow(); | |
96 } | |
97 window.console.error( | |
98 WebViewConstants.ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE); | |
99 }.bind(this), | |
100 // No setter. | |
101 enumerable: true | |
102 }); | |
103 }; | |
104 | |
105 // This observer monitors mutations to attributes of the <webview>. | |
106 WebViewImpl.prototype.handleAttributeMutation = function( | |
107 attributeName, oldValue, newValue) { | |
108 if (!this.attributes[attributeName]) { | |
109 return; | |
110 } | |
111 | |
112 // Let the changed attribute handle its own mutation; | |
113 this.attributes[attributeName].maybeHandleMutation(oldValue, newValue); | |
114 }; | |
115 | |
116 WebViewImpl.prototype.onSizeChanged = function(webViewEvent) { | |
117 var newWidth = webViewEvent.newWidth; | |
118 var newHeight = webViewEvent.newHeight; | |
119 | |
120 var element = this.element; | |
121 | |
122 var width = element.offsetWidth; | |
123 var height = element.offsetHeight; | |
124 | |
125 // Check the current bounds to make sure we do not resize <webview> | |
126 // outside of current constraints. | |
127 var maxWidth = this.attributes[ | |
128 WebViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || width; | |
129 var minWidth = this.attributes[ | |
130 WebViewConstants.ATTRIBUTE_MINWIDTH].getValue() || width; | |
131 var maxHeight = this.attributes[ | |
132 WebViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || height; | |
133 var minHeight = this.attributes[ | |
134 WebViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || height; | |
135 | |
136 minWidth = Math.min(minWidth, maxWidth); | |
137 minHeight = Math.min(minHeight, maxHeight); | |
138 | |
139 if (!this.attributes[WebViewConstants.ATTRIBUTE_AUTOSIZE].getValue() || | |
140 (newWidth >= minWidth && | |
141 newWidth <= maxWidth && | |
142 newHeight >= minHeight && | |
143 newHeight <= maxHeight)) { | |
144 element.style.width = newWidth + 'px'; | |
145 element.style.height = newHeight + 'px'; | |
146 // Only fire the DOM event if the size of the <webview> has actually | |
147 // changed. | |
148 this.dispatchEvent(webViewEvent); | |
149 } | |
150 }; | |
151 | |
152 WebViewImpl.prototype.createGuest = function() { | |
153 this.guest.create(this.buildParams(), function() { | |
154 this.attachWindow(); | |
155 }.bind(this)); | |
156 }; | |
157 | |
158 WebViewImpl.prototype.onFrameNameChanged = function(name) { | |
159 this.attributes[WebViewConstants.ATTRIBUTE_NAME].setValueIgnoreMutation(name); | |
160 }; | |
161 | |
162 // Updates state upon loadcommit. | |
163 WebViewImpl.prototype.onLoadCommit = function( | |
164 baseUrlForDataUrl, currentEntryIndex, entryCount, | |
165 processId, url, isTopLevel) { | |
166 this.baseUrlForDataUrl = baseUrlForDataUrl; | |
167 this.currentEntryIndex = currentEntryIndex; | |
168 this.entryCount = entryCount; | |
169 this.processId = processId; | |
170 if (isTopLevel) { | |
171 // Touching the src attribute triggers a navigation. To avoid | |
172 // triggering a page reload on every guest-initiated navigation, | |
173 // we do not handle this mutation. | |
174 this.attributes[ | |
175 WebViewConstants.ATTRIBUTE_SRC].setValueIgnoreMutation(url); | |
176 } | |
177 }; | |
178 | |
179 WebViewImpl.prototype.onAttach = function(storagePartitionId) { | |
180 this.attributes[WebViewConstants.ATTRIBUTE_PARTITION].setValueIgnoreMutation( | |
181 storagePartitionId); | |
182 }; | |
183 | |
184 WebViewImpl.prototype.buildContainerParams = function() { | |
185 var params = { 'userAgentOverride': this.userAgentOverride }; | |
186 for (var i in this.attributes) { | |
187 params[i] = this.attributes[i].getValue(); | |
188 } | |
189 return params; | |
190 }; | |
191 | |
192 WebViewImpl.prototype.attachWindow = function(opt_guestInstanceId) { | |
193 // If |opt_guestInstanceId| was provided, then a different existing guest is | |
194 // being attached to this webview, and the current one will get destroyed. | |
195 if (opt_guestInstanceId) { | |
196 if (this.guest.getId() == opt_guestInstanceId) { | |
197 return true; | |
198 } | |
199 this.guest.destroy(); | |
200 this.guest = new GuestView('webview', opt_guestInstanceId); | |
201 } | |
202 | |
203 return GuestViewContainer.prototype.attachWindow.call(this); | |
204 }; | |
205 | |
206 // Shared implementation of executeScript() and insertCSS(). | |
207 WebViewImpl.prototype.executeCode = function(func, args) { | |
208 if (!this.guest.getId()) { | |
209 window.console.error(WebViewConstants.ERROR_MSG_CANNOT_INJECT_SCRIPT); | |
210 return false; | |
211 } | |
212 | |
213 var webviewSrc = this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue(); | |
214 if (this.baseUrlForDataUrl) { | |
215 webviewSrc = this.baseUrlForDataUrl; | |
216 } | |
217 | |
218 args = $Array.concat([this.guest.getId(), webviewSrc], | |
219 $Array.slice(args)); | |
220 $Function.apply(func, null, args); | |
221 return true; | |
222 } | |
223 | |
224 // Implemented when the ChromeWebView API is available. | |
225 WebViewImpl.prototype.maybeGetChromeWebViewEvents = function() {}; | |
226 | |
227 // Implemented when the experimental WebView API is available. | |
228 WebViewImpl.maybeGetExperimentalApiMethods = function() { return []; }; | |
229 WebViewImpl.prototype.setupExperimentalContextMenus = function() {}; | |
230 | |
231 GuestViewContainer.registerElement(WebViewImpl); | |
232 | |
233 // Exports. | |
234 exports.WebViewImpl = WebViewImpl; | |
OLD | NEW |