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.
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.
9 var DocumentNatives
= requireNative('document_natives');
10 var GuestView
= require('guestView').GuestView
;
11 var GuestViewContainer
= require('guestViewContainer').GuestViewContainer
;
12 var GuestViewInternalNatives
= requireNative('guest_view_internal');
13 var WebViewConstants
= require('webViewConstants').WebViewConstants
;
14 var WebViewEvents
= require('webViewEvents').WebViewEvents
;
15 var WebViewInternal
= require('webViewInternal').WebViewInternal
;
17 // Represents the internal state of <webview>.
18 function WebViewImpl(webviewElement
) {
19 GuestViewContainer
.call(this, webviewElement
, 'webview');
21 this.setupElementProperties();
22 new WebViewEvents(this, this.viewInstanceId
);
25 WebViewImpl
.prototype.__proto__
= GuestViewContainer
.prototype;
27 WebViewImpl
.VIEW_TYPE
= 'WebView';
29 // Add extra functionality to |this.element|.
30 WebViewImpl
.setupElement = function(proto
) {
31 // Public-facing API methods.
32 var apiMethods
= WebViewImpl
.getApiMethods();
34 // Create default implementations for undefined API methods.
35 var createDefaultApiMethod = function(m
) {
36 return function(var_args
) {
37 if (!this.guest
.getId()) {
40 var args
= $Array
.concat([this.guest
.getId()], $Array
.slice(arguments
));
41 $Function
.apply(WebViewInternal
[m
], null, args
);
45 for (var i
= 0; i
!= apiMethods
.length
; ++i
) {
46 if (WebViewImpl
.prototype[apiMethods
[i
]] == undefined) {
47 WebViewImpl
.prototype[apiMethods
[i
]] =
48 createDefaultApiMethod(apiMethods
[i
]);
52 // Forward proto.foo* method calls to WebViewImpl.foo*.
53 GuestViewContainer
.forwardApiMethods(proto
, apiMethods
);
56 // Initiates navigation once the <webview> element is attached to the DOM.
57 WebViewImpl
.prototype.onElementAttached = function() {
58 // Mark all attributes as dirty on attachment.
59 for (var i
in this.attributes
) {
60 this.attributes
[i
].dirty
= true;
62 for (var i
in this.attributes
) {
63 this.attributes
[i
].attach();
67 // Resets some state upon detaching <webview> element from the DOM.
68 WebViewImpl
.prototype.onElementDetached = function() {
70 for (var i
in this.attributes
) {
71 this.attributes
[i
].dirty
= false;
73 for (var i
in this.attributes
) {
74 this.attributes
[i
].detach();
78 // Sets the <webview>.request property.
79 WebViewImpl
.prototype.setRequestPropertyOnWebViewElement = function(request
) {
80 Object
.defineProperty(
90 WebViewImpl
.prototype.setupElementProperties = function() {
91 // We cannot use {writable: true} property descriptor because we want a
92 // dynamic getter value.
93 Object
.defineProperty(this.element
, 'contentWindow', {
95 return this.guest
.getContentWindow();
102 WebViewImpl
.prototype.onSizeChanged = function(webViewEvent
) {
103 var newWidth
= webViewEvent
.newWidth
;
104 var newHeight
= webViewEvent
.newHeight
;
106 var element
= this.element
;
108 var width
= element
.offsetWidth
;
109 var height
= element
.offsetHeight
;
111 // Check the current bounds to make sure we do not resize <webview>
112 // outside of current constraints.
113 var maxWidth
= this.attributes
[
114 WebViewConstants
.ATTRIBUTE_MAXWIDTH
].getValue() || width
;
115 var minWidth
= this.attributes
[
116 WebViewConstants
.ATTRIBUTE_MINWIDTH
].getValue() || width
;
117 var maxHeight
= this.attributes
[
118 WebViewConstants
.ATTRIBUTE_MAXHEIGHT
].getValue() || height
;
119 var minHeight
= this.attributes
[
120 WebViewConstants
.ATTRIBUTE_MINHEIGHT
].getValue() || height
;
122 minWidth
= Math
.min(minWidth
, maxWidth
);
123 minHeight
= Math
.min(minHeight
, maxHeight
);
125 if (!this.attributes
[WebViewConstants
.ATTRIBUTE_AUTOSIZE
].getValue() ||
126 (newWidth
>= minWidth
&&
127 newWidth
<= maxWidth
&&
128 newHeight
>= minHeight
&&
129 newHeight
<= maxHeight
)) {
130 element
.style
.width
= newWidth
+ 'px';
131 element
.style
.height
= newHeight
+ 'px';
132 // Only fire the DOM event if the size of the <webview> has actually
134 this.dispatchEvent(webViewEvent
);
138 WebViewImpl
.prototype.createGuest = function() {
139 this.guest
.create(this.buildParams(), function() {
144 WebViewImpl
.prototype.onFrameNameChanged = function(name
) {
145 this.attributes
[WebViewConstants
.ATTRIBUTE_NAME
].setValueIgnoreMutation(name
);
148 // Updates state upon loadcommit.
149 WebViewImpl
.prototype.onLoadCommit = function(
150 baseUrlForDataUrl
, currentEntryIndex
, entryCount
,
151 processId
, url
, isTopLevel
) {
152 this.baseUrlForDataUrl
= baseUrlForDataUrl
;
153 this.currentEntryIndex
= currentEntryIndex
;
154 this.entryCount
= entryCount
;
155 this.processId
= processId
;
157 // Touching the src attribute triggers a navigation. To avoid
158 // triggering a page reload on every guest-initiated navigation,
159 // we do not handle this mutation.
161 WebViewConstants
.ATTRIBUTE_SRC
].setValueIgnoreMutation(url
);
165 WebViewImpl
.prototype.onAttach = function(storagePartitionId
) {
166 this.attributes
[WebViewConstants
.ATTRIBUTE_PARTITION
].setValueIgnoreMutation(
170 WebViewImpl
.prototype.buildContainerParams = function() {
171 var params
= { 'userAgentOverride': this.userAgentOverride
};
172 for (var i
in this.attributes
) {
173 var value
= this.attributes
[i
].getValueIfDirty();
180 WebViewImpl
.prototype.attachWindow = function(opt_guestInstanceId
) {
181 // If |opt_guestInstanceId| was provided, then a different existing guest is
182 // being attached to this webview, and the current one will get destroyed.
183 if (opt_guestInstanceId
) {
184 if (this.guest
.getId() == opt_guestInstanceId
) {
187 this.guest
.destroy();
188 this.guest
= new GuestView('webview', opt_guestInstanceId
);
191 return GuestViewContainer
.prototype.attachWindow
.call(this);
194 // Shared implementation of executeScript() and insertCSS().
195 WebViewImpl
.prototype.executeCode = function(func
, args
) {
196 if (!this.guest
.getId()) {
197 window
.console
.error(WebViewConstants
.ERROR_MSG_CANNOT_INJECT_SCRIPT
);
201 var webviewSrc
= this.attributes
[WebViewConstants
.ATTRIBUTE_SRC
].getValue();
202 if (this.baseUrlForDataUrl
) {
203 webviewSrc
= this.baseUrlForDataUrl
;
206 args
= $Array
.concat([this.guest
.getId(), webviewSrc
],
208 $Function
.apply(func
, null, args
);
212 // Requests the <webview> element wihtin the embedder to enter fullscreen.
213 WebViewImpl
.prototype.makeElementFullscreen = function() {
214 GuestViewInternalNatives
.RunWithGesture(function() {
215 this.element
.webkitRequestFullScreen();
219 // Implemented when the ChromeWebView API is available.
220 WebViewImpl
.prototype.maybeSetupContextMenus = function() {};
222 GuestViewContainer
.registerElement(WebViewImpl
);
225 exports
.WebViewImpl
= WebViewImpl
;