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