app_view.js revision 116680a4aac90f2aa7413d9095a592090648e557
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.
4
5var DocumentNatives = requireNative('document_natives');
6var GuestViewInternal =
7    require('binding').Binding.create('guestViewInternal').generate();
8var IdGenerator = requireNative('id_generator');
9
10function AppViewInternal(appviewNode) {
11  privates(appviewNode).internal = this;
12  this.appviewNode = appviewNode;
13
14  this.browserPluginNode = this.createBrowserPluginNode();
15  var shadowRoot = this.appviewNode.createShadowRoot();
16  shadowRoot.appendChild(this.browserPluginNode);
17  this.viewInstanceId = IdGenerator.GetNextId();
18}
19
20AppViewInternal.prototype.getErrorNode = function() {
21  if (!this.errorNode) {
22    this.errorNode = document.createElement('div');
23    this.errorNode.innerText = 'Unable to connect to app.';
24    this.errorNode.style.position = 'absolute';
25    this.errorNode.style.left = '0px';
26    this.errorNode.style.top = '0px';
27    this.errorNode.style.width = '100%';
28    this.errorNode.style.height = '100%';
29    this.appviewNode.shadowRoot.appendChild(this.errorNode);
30  }
31  return this.errorNode;
32};
33
34AppViewInternal.prototype.createBrowserPluginNode = function() {
35  // We create BrowserPlugin as a custom element in order to observe changes
36  // to attributes synchronously.
37  var browserPluginNode = new AppViewInternal.BrowserPlugin();
38  privates(browserPluginNode).internal = this;
39  return browserPluginNode;
40};
41
42AppViewInternal.prototype.connect = function(app, callback) {
43  var params = {
44    'appId': app
45  };
46  var self = this;
47  GuestViewInternal.createGuest(
48    'appview',
49    params,
50    function(instanceId) {
51      if (!instanceId) {
52        self.browserPluginNode.style.visibility = 'hidden';
53        var errorMsg = 'Unable to connect to app "' + app + '".';
54        window.console.warn(errorMsg);
55        self.getErrorNode().innerText = errorMsg;
56        if (callback) {
57          callback(false);
58        }
59        return;
60      }
61      self.attachWindow(instanceId);
62      if (callback) {
63        callback(true);
64      }
65    }
66  );
67};
68
69AppViewInternal.prototype.attachWindow = function(instanceId) {
70  this.instanceId = instanceId;
71  var params = {
72    'instanceId': this.viewInstanceId,
73  };
74  this.browserPluginNode.style.visibility = 'visible';
75  return this.browserPluginNode['-internal-attach'](instanceId, params);
76};
77
78function registerBrowserPluginElement() {
79  var proto = Object.create(HTMLObjectElement.prototype);
80
81  proto.createdCallback = function() {
82    this.setAttribute('type', 'application/browser-plugin');
83    this.style.width = '100%';
84    this.style.height = '100%';
85  };
86
87  proto.attachedCallback = function() {
88    // Load the plugin immediately.
89    var unused = this.nonExistentAttribute;
90  };
91
92  AppViewInternal.BrowserPlugin =
93      DocumentNatives.RegisterElement('appplugin', {extends: 'object',
94                                                    prototype: proto});
95
96  delete proto.createdCallback;
97  delete proto.attachedCallback;
98  delete proto.detachedCallback;
99  delete proto.attributeChangedCallback;
100}
101
102function registerAppViewElement() {
103  var proto = Object.create(HTMLElement.prototype);
104
105  proto.createdCallback = function() {
106    new AppViewInternal(this);
107  };
108
109  proto.connect = function() {
110    var internal = privates(this).internal;
111    $Function.apply(internal.connect, internal, arguments);
112  }
113  window.AppView =
114      DocumentNatives.RegisterElement('appview', {prototype: proto});
115
116  // Delete the callbacks so developers cannot call them and produce unexpected
117  // behavior.
118  delete proto.createdCallback;
119  delete proto.attachedCallback;
120  delete proto.detachedCallback;
121  delete proto.attributeChangedCallback;
122}
123
124var useCapture = true;
125window.addEventListener('readystatechange', function listener(event) {
126  if (document.readyState == 'loading')
127    return;
128
129  registerBrowserPluginElement();
130  registerAppViewElement();
131  window.removeEventListener(event.type, listener, useCapture);
132}, useCapture);
133