Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / extensions / renderer / resources / guest_view / guest_view_container.js
blob4108e0eeaae6aac90fc3b6bd9bf223ee1fda912f
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.attributes = {};
16 this.element = element;
17 this.elementAttached = false;
18 this.viewInstanceId = IdGenerator.GetNextId();
19 this.viewType = viewType;
21 this.setupGuestProperty();
22 this.guest = new GuestView(viewType);
23 this.setupAttributes();
25 privates(this).browserPluginElement = this.createBrowserPluginElement();
26 this.setupFocusPropagation();
27 var shadowRoot = this.element.createShadowRoot();
28 shadowRoot.appendChild(privates(this).browserPluginElement);
31 // Forward public API methods from |proto| to their internal implementations.
32 GuestViewContainer.forwardApiMethods = function(proto, apiMethods) {
33 var createProtoHandler = function(m) {
34 return function(var_args) {
35 var internal = privates(this).internal;
36 return $Function.apply(internal[m], internal, arguments);
39 for (var i = 0; apiMethods[i]; ++i) {
40 proto[apiMethods[i]] = createProtoHandler(apiMethods[i]);
44 // Registers the browserplugin and guestview as custom elements once the
45 // document has loaded.
46 GuestViewContainer.registerElement = function(guestViewContainerType) {
47 var useCapture = true;
48 window.addEventListener('readystatechange', function listener(event) {
49 if (document.readyState == 'loading')
50 return;
52 registerBrowserPluginElement(
53 guestViewContainerType.VIEW_TYPE.toLowerCase());
54 registerGuestViewElement(guestViewContainerType);
55 window.removeEventListener(event.type, listener, useCapture);
56 }, useCapture);
59 // Create the 'guest' property to track new GuestViews and always listen for
60 // their resizes.
61 GuestViewContainer.prototype.setupGuestProperty = function() {
62 $Object.defineProperty(this, 'guest', {
63 get: function() {
64 return privates(this).guest;
65 }.bind(this),
66 set: function(value) {
67 privates(this).guest = value;
68 if (!value) {
69 return;
71 privates(this).guest.onresize = function(e) {
72 // Dispatch the 'contentresize' event.
73 var contentResizeEvent = new Event('contentresize', { bubbles: true });
74 contentResizeEvent.oldWidth = e.oldWidth;
75 contentResizeEvent.oldHeight = e.oldHeight;
76 contentResizeEvent.newWidth = e.newWidth;
77 contentResizeEvent.newHeight = e.newHeight;
78 this.dispatchEvent(contentResizeEvent);
79 }.bind(this);
80 }.bind(this),
81 enumerable: true
82 });
85 GuestViewContainer.prototype.createBrowserPluginElement = function() {
86 // We create BrowserPlugin as a custom element in order to observe changes
87 // to attributes synchronously.
88 var browserPluginElement =
89 new GuestViewContainer[this.viewType + 'BrowserPlugin']();
90 privates(browserPluginElement).internal = this;
91 return browserPluginElement;
94 GuestViewContainer.prototype.setupFocusPropagation = function() {
95 if (!this.element.hasAttribute('tabIndex')) {
96 // GuestViewContainer needs a tabIndex in order to be focusable.
97 // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute
98 // to allow GuestViewContainer to be focusable.
99 // See http://crbug.com/231664.
100 this.element.setAttribute('tabIndex', -1);
102 this.element.addEventListener('focus', function(e) {
103 // Focus the BrowserPlugin when the GuestViewContainer takes focus.
104 privates(this).browserPluginElement.focus();
105 }.bind(this));
106 this.element.addEventListener('blur', function(e) {
107 // Blur the BrowserPlugin when the GuestViewContainer loses focus.
108 privates(this).browserPluginElement.blur();
109 }.bind(this));
112 GuestViewContainer.prototype.attachWindow = function() {
113 if (!this.internalInstanceId) {
114 return true;
117 this.guest.attach(this.internalInstanceId,
118 this.viewInstanceId,
119 this.buildParams());
120 return true;
123 GuestViewContainer.prototype.handleBrowserPluginAttributeMutation =
124 function(name, oldValue, newValue) {
125 if (name == 'internalinstanceid' && !oldValue && !!newValue) {
126 privates(this).browserPluginElement.removeAttribute('internalinstanceid');
127 this.internalInstanceId = parseInt(newValue);
129 // Track when the element resizes using the element resize callback.
130 GuestViewInternalNatives.RegisterElementResizeCallback(
131 this.internalInstanceId, this.onElementResize.bind(this));
133 if (!this.guest.getId()) {
134 return;
136 this.guest.attach(this.internalInstanceId,
137 this.viewInstanceId,
138 this.buildParams());
142 GuestViewContainer.prototype.onElementResize = function(newWidth, newHeight) {
143 if (!this.guest.getId())
144 return;
145 this.guest.setSize({normal: {width: newWidth, height: newHeight}});
148 GuestViewContainer.prototype.buildParams = function() {
149 var params = this.buildContainerParams();
150 params['instanceId'] = this.viewInstanceId;
151 // When the GuestViewContainer is not participating in layout (display:none)
152 // then getBoundingClientRect() would report a width and height of 0.
153 // However, in the case where the GuestViewContainer has a fixed size we can
154 // use that value to initially size the guest so as to avoid a relayout of the
155 // on display:block.
156 var css = window.getComputedStyle(this.element, null);
157 var elementRect = this.element.getBoundingClientRect();
158 params['elementWidth'] = parseInt(elementRect.width) ||
159 parseInt(css.getPropertyValue('width'));
160 params['elementHeight'] = parseInt(elementRect.height) ||
161 parseInt(css.getPropertyValue('height'));
162 return params;
165 GuestViewContainer.prototype.dispatchEvent = function(event) {
166 return this.element.dispatchEvent(event);
169 // Implemented by the specific view type, if needed.
170 GuestViewContainer.prototype.buildContainerParams = function() { return {}; };
171 GuestViewContainer.prototype.onElementAttached = function() {};
172 GuestViewContainer.prototype.onElementDetached = function() {};
173 GuestViewContainer.prototype.setupAttributes = function() {};
175 // Registers the browser plugin <object> custom element. |viewType| is the
176 // name of the specific guestview container (e.g. 'webview').
177 function registerBrowserPluginElement(viewType) {
178 var proto = $Object.create(HTMLElement.prototype);
180 proto.createdCallback = function() {
181 this.setAttribute('type', 'application/browser-plugin');
182 this.setAttribute('id', 'browser-plugin-' + IdGenerator.GetNextId());
183 this.style.width = '100%';
184 this.style.height = '100%';
187 proto.attachedCallback = function() {
188 // Load the plugin immediately.
189 var unused = this.nonExistentAttribute;
192 proto.attributeChangedCallback = function(name, oldValue, newValue) {
193 var internal = privates(this).internal;
194 if (!internal) {
195 return;
197 internal.handleBrowserPluginAttributeMutation(name, oldValue, newValue);
200 GuestViewContainer[viewType + 'BrowserPlugin'] =
201 DocumentNatives.RegisterElement(viewType + 'browserplugin',
202 {extends: 'object', prototype: proto});
204 delete proto.createdCallback;
205 delete proto.attachedCallback;
206 delete proto.detachedCallback;
207 delete proto.attributeChangedCallback;
210 // Registers the guestview container as a custom element.
211 // |guestViewContainerType| is the type of guestview container
212 // (e.g.WebViewImpl).
213 function registerGuestViewElement(guestViewContainerType) {
214 var proto = $Object.create(HTMLElement.prototype);
216 proto.createdCallback = function() {
217 new guestViewContainerType(this);
220 proto.attachedCallback = function() {
221 var internal = privates(this).internal;
222 if (!internal) {
223 return;
225 internal.elementAttached = true;
226 internal.onElementAttached();
229 proto.attributeChangedCallback = function(name, oldValue, newValue) {
230 var internal = privates(this).internal;
231 if (!internal || !internal.attributes[name]) {
232 return;
235 // Let the changed attribute handle its own mutation.
236 internal.attributes[name].maybeHandleMutation(oldValue, newValue);
239 proto.detachedCallback = function() {
240 var internal = privates(this).internal;
241 if (!internal) {
242 return;
244 internal.elementAttached = false;
245 internal.internalInstanceId = 0;
246 internal.guest.destroy();
247 internal.onElementDetached();
250 // Let the specific view type add extra functionality to its custom element
251 // through |proto|.
252 if (guestViewContainerType.setupElement) {
253 guestViewContainerType.setupElement(proto);
256 window[guestViewContainerType.VIEW_TYPE] =
257 DocumentNatives.RegisterElement(
258 guestViewContainerType.VIEW_TYPE.toLowerCase(),
259 {prototype: proto});
261 // Delete the callbacks so developers cannot call them and produce unexpected
262 // behavior.
263 delete proto.createdCallback;
264 delete proto.attachedCallback;
265 delete proto.detachedCallback;
266 delete proto.attributeChangedCallback;
269 // Exports.
270 exports.GuestViewContainer = GuestViewContainer;