Reland the ULONG -> SIZE_T change from 317177
[chromium-blink-merge.git] / extensions / renderer / resources / guest_view / guest_view_container.js
blobb880cd4d8b8a5f53e46dd149c956f9415968879e
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 shared functionality for different guestview
6 // containers, such as web_view, app_view, etc.
8 var DocumentNatives = requireNative('document_natives');
9 var GuestView = require('guestView').GuestView;
10 var GuestViewInternalNatives = requireNative('guest_view_internal');
11 var IdGenerator = requireNative('id_generator');
13 function GuestViewContainer(element, viewType) {
14   privates(element).internal = this;
15   this.element = element;
16   this.elementAttached = false;
17   this.guest = new GuestView(viewType);
18   this.viewInstanceId = IdGenerator.GetNextId();
19   this.viewType = viewType;
21   privates(this).browserPluginElement = this.createBrowserPluginElement();
22   this.setupFocusPropagation();
24   var shadowRoot = this.element.createShadowRoot();
25   shadowRoot.appendChild(privates(this).browserPluginElement);
28 // Forward public API methods from |proto| to their internal implementations.
29 GuestViewContainer.forwardApiMethods = function(proto, apiMethods) {
30   var createProtoHandler = function(m) {
31     return function(var_args) {
32       var internal = privates(this).internal;
33       return $Function.apply(internal[m], internal, arguments);
34     };
35   };
36   for (var i = 0; apiMethods[i]; ++i) {
37     proto[apiMethods[i]] = createProtoHandler(apiMethods[i]);
38   }
41 // Registers the browserplugin and guestview as custom elements once the
42 // document has loaded.
43 GuestViewContainer.registerElement =
44     function(guestViewContainerType) {
45   var useCapture = true;
46   window.addEventListener('readystatechange', function listener(event) {
47     if (document.readyState == 'loading') {
48       return;
49     }
51     registerBrowserPluginElement(
52         guestViewContainerType.VIEW_TYPE.toLowerCase());
53     registerGuestViewElement(guestViewContainerType);
54     window.removeEventListener(event.type, listener, useCapture);
55   }, useCapture);
58 GuestViewContainer.prototype.createBrowserPluginElement = function() {
59   // We create BrowserPlugin as a custom element in order to observe changes
60   // to attributes synchronously.
61   var browserPluginElement =
62       new GuestViewContainer[this.viewType + 'BrowserPlugin']();
63   privates(browserPluginElement).internal = this;
64   return browserPluginElement;
67 GuestViewContainer.prototype.setupFocusPropagation = function() {
68   if (!this.element.hasAttribute('tabIndex')) {
69     // GuestViewContainer needs a tabIndex in order to be focusable.
70     // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute
71     // to allow GuestViewContainer to be focusable.
72     // See http://crbug.com/231664.
73     this.element.setAttribute('tabIndex', -1);
74   }
75   this.element.addEventListener('focus', function(e) {
76     // Focus the BrowserPlugin when the GuestViewContainer takes focus.
77     privates(this).browserPluginElement.focus();
78   }.bind(this));
79   this.element.addEventListener('blur', function(e) {
80     // Blur the BrowserPlugin when the GuestViewContainer loses focus.
81     privates(this).browserPluginElement.blur();
82   }.bind(this));
85 GuestViewContainer.prototype.attachWindow = function() {
86   if (!this.internalInstanceId) {
87     return true;
88   }
90   this.guest.attach(this.internalInstanceId,
91                     this.viewInstanceId,
92                     this.buildParams());
93   return true;
96 GuestViewContainer.prototype.handleBrowserPluginAttributeMutation =
97     function(name, oldValue, newValue) {
98   if (name == 'internalinstanceid' && !oldValue && !!newValue) {
99     privates(this).browserPluginElement.removeAttribute('internalinstanceid');
100     this.internalInstanceId = parseInt(newValue);
102     // Track when the element resizes using the element resize callback.
103     GuestViewInternalNatives.RegisterElementResizeCallback(
104         this.internalInstanceId, this.onElementResize.bind(this));
106     if (!this.guest.getId()) {
107       return;
108     }
109     this.guest.attach(this.internalInstanceId,
110                       this.viewInstanceId,
111                       this.buildParams());
112   }
115 GuestViewContainer.prototype.onElementResize = function(oldWidth, oldHeight,
116                                                         newWidth, newHeight) {
117   if (!this.guest.getId())
118     return;
119   this.guest.setSize(
120       {normal: {width: newWidth, height: newHeight}});
123 GuestViewContainer.prototype.buildParams = function() {
124   var params = this.buildContainerParams();
125   params['instanceId'] = this.viewInstanceId;
126   var elementRect = this.element.getBoundingClientRect();
127   params['elementWidth'] = parseInt(elementRect.width);
128   params['elementHeight'] = parseInt(elementRect.height);
129   return params;
132 // Implemented by the specific view type, if needed.
133 GuestViewContainer.prototype.buildContainerParams = function() { return {}; };
134 GuestViewContainer.prototype.handleAttributeMutation = function() {};
135 GuestViewContainer.prototype.onElementAttached = function() {};
136 GuestViewContainer.prototype.onElementDetached = function() {
137   this.guest.destroy();
140 // Registers the browser plugin <object> custom element. |viewType| is the
141 // name of the specific guestview container (e.g. 'webview').
142 function registerBrowserPluginElement(viewType) {
143   var proto = Object.create(HTMLObjectElement.prototype);
145   proto.createdCallback = function() {
146     this.setAttribute('type', 'application/browser-plugin');
147     this.setAttribute('id', 'browser-plugin-' + IdGenerator.GetNextId());
148     this.style.width = '100%';
149     this.style.height = '100%';
150   };
152   proto.attachedCallback = function() {
153     // Load the plugin immediately.
154     var unused = this.nonExistentAttribute;
155   };
157   proto.attributeChangedCallback = function(name, oldValue, newValue) {
158     var internal = privates(this).internal;
159     if (!internal) {
160       return;
161     }
162     internal.handleBrowserPluginAttributeMutation(name, oldValue, newValue);
163   };
165   GuestViewContainer[viewType + 'BrowserPlugin'] =
166       DocumentNatives.RegisterElement(viewType + 'browserplugin',
167                                       {extends: 'object', prototype: proto});
169   delete proto.createdCallback;
170   delete proto.attachedCallback;
171   delete proto.detachedCallback;
172   delete proto.attributeChangedCallback;
175 // Registers the guestview container as a custom element.
176 // |guestViewContainerType| is the type of guestview container
177 // (e.g.WebViewImpl).
178 function registerGuestViewElement(guestViewContainerType) {
179   var proto = Object.create(HTMLElement.prototype);
181   proto.createdCallback = function() {
182     new guestViewContainerType(this);
183   };
185   proto.attachedCallback = function() {
186     var internal = privates(this).internal;
187     if (!internal) {
188       return;
189     }
190     internal.elementAttached = true;
191     internal.onElementAttached();
192   };
194   proto.attributeChangedCallback = function(name, oldValue, newValue) {
195     var internal = privates(this).internal;
196     if (!internal) {
197       return;
198     }
199     internal.handleAttributeMutation(name, oldValue, newValue);
200   };
202   proto.detachedCallback = function() {
203     var internal = privates(this).internal;
204     if (!internal) {
205       return;
206     }
207     internal.elementAttached = false;
208     internal.internalInstanceId = 0;
209     internal.onElementDetached();
210   };
212   // Let the specific view type add extra functionality to its custom element
213   // through |proto|.
214   if (guestViewContainerType.setupElement) {
215     guestViewContainerType.setupElement(proto);
216   }
218   window[guestViewContainerType.VIEW_TYPE] =
219       DocumentNatives.RegisterElement(
220           guestViewContainerType.VIEW_TYPE.toLowerCase(),
221           {prototype: proto});
223   // Delete the callbacks so developers cannot call them and produce unexpected
224   // behavior.
225   delete proto.createdCallback;
226   delete proto.attachedCallback;
227   delete proto.detachedCallback;
228   delete proto.attributeChangedCallback;
231 // Exports.
232 exports.GuestViewContainer = GuestViewContainer;