1// Copyright 2013 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'use strict'; 6 7/** 8 * @param {WebView} webView Web View tag. 9 * @param {?string} ext File extension. 10 * @param {?string} mime File mime type. 11 * @param {?string} searchQuery Search query. 12 * @param {number} width Width of the CWS widget. 13 * @param {number} height Height of the CWS widget. 14 * @param {string} url Share Url for an entry. 15 * @param {string} target Target (scheme + host + port) of the widget. 16 * @constructor 17 */ 18function CWSContainerClient( 19 webView, ext, mime, searchQuery, width, height, url, target) { 20 this.webView_ = webView; 21 this.ext_ = (ext && ext[0] == '.') ? ext.substr(1) : ext; 22 this.mime_ = mime; 23 this.searchQuery_ = searchQuery; 24 this.width_ = width; 25 this.height_ = height; 26 this.url_ = url; 27 this.target_ = target; 28 29 this.loaded_ = false; 30 this.loading_ = false; 31 32 this.onMessageBound_ = this.onMessage_.bind(this); 33 this.onLoadStopBound_ = this.onLoadStop_.bind(this); 34 this.onLoadAbortBound_ = this.onLoadAbort_.bind(this); 35} 36 37CWSContainerClient.prototype = { 38 __proto__: cr.EventTarget.prototype 39}; 40 41/** 42 * Events CWSContainerClient fires 43 * 44 * @enum {string} 45 * @const 46 */ 47CWSContainerClient.Events = { 48 LOADED: 'CWSContainerClient.Events.LOADED', 49 LOAD_FAILED: 'CWSContainerClient.Events.LOAD_FAILED', 50 REQUEST_INSTALL: 'CWSContainerClient.Events.REQUEST_INSTALL' 51}; 52Object.freeze(CWSContainerClient.Events); 53 54/** 55 * Handles messages from the widget 56 * @param {Event} event Message event. 57 * @private 58 */ 59CWSContainerClient.prototype.onMessage_ = function(event) { 60 if (event.origin != this.target_) 61 return; 62 63 var data = event.data; 64 switch (data['message']) { 65 case 'widget_loaded': 66 this.onWidgetLoaded_(); 67 break; 68 case 'widget_load_failed': 69 this.onWidgetLoadFailed_(); 70 break; 71 case 'before_install': 72 this.sendInstallRequest_(data['item_id']); 73 break; 74 default: 75 console.error('Unexpected message: ' + data['message'], data); 76 } 77}; 78 79/** 80 * Called when receiving 'loadstop' event from the <webview>. 81 * @param {Event} event Message event. 82 * @private 83 */ 84CWSContainerClient.prototype.onLoadStop_ = function(event) { 85 if (this.url_ == this.webView_.src && !this.loaded_) { 86 this.loaded_ = true; 87 this.postInitializeMessage_(); 88 } 89}; 90 91/** 92 * Called when the widget is loaded successfully. 93 * @private 94 */ 95CWSContainerClient.prototype.onWidgetLoaded_ = function() { 96 cr.dispatchSimpleEvent(this, CWSContainerClient.Events.LOADED); 97}; 98 99/** 100 * Called when the widget is failed to load. 101 * @private 102 */ 103CWSContainerClient.prototype.onWidgetLoadFailed_ = function() { 104 this.sendWidgetLoadFailed_(); 105}; 106 107/** 108 * Called when receiving the 'loadabort' event from <webview>. 109 * @param {Event} event Message event. 110 * @private 111 */ 112CWSContainerClient.prototype.onLoadAbort_ = function(event) { 113 this.sendWidgetLoadFailed_(); 114}; 115 116/** 117 * Called when the installation is completed from the suggest-app dialog. 118 * 119 * @param {boolean} result True if the installation is success, false if failed. 120 * @param {string} itemId Item id to be installed. 121 */ 122CWSContainerClient.prototype.onInstallCompleted = function(result, itemId) { 123 if (result) 124 this.postInstallSuccessMessage_(itemId); 125 else 126 this.postInstallFailureMessage_(itemId); 127}; 128 129/** 130 * Send the fail message to the suggest-app dialog. 131 * @private 132 */ 133CWSContainerClient.prototype.sendWidgetLoadFailed_ = function() { 134 cr.dispatchSimpleEvent(this, CWSContainerClient.Events.LOAD_FAILED); 135}; 136 137/** 138 * Send the install request to the suggest-app dialog. 139 * 140 * @param {string} itemId Item id to be installed. 141 * @private 142 */ 143CWSContainerClient.prototype.sendInstallRequest_ = function(itemId) { 144 var event = new Event(CWSContainerClient.Events.REQUEST_INSTALL); 145 event.itemId = itemId; 146 this.dispatchEvent(event); 147}; 148 149/** 150 * Send the 'install_failure' message to the widget. 151 * 152 * @param {string} itemId Item id to be installed. 153 * @private 154 */ 155CWSContainerClient.prototype.postInstallFailureMessage_ = function(itemId) { 156 var message = { 157 message: 'install_failure', 158 item_id: itemId, 159 v: 1 160 }; 161 162 this.postMessage_(message); 163}; 164 165/** 166 * Send the 'install_success' message to the widget. 167 * 168 * @param {string} itemId Item id to be installed. 169 * @private 170 */ 171CWSContainerClient.prototype.postInstallSuccessMessage_ = function(itemId) { 172 var message = { 173 message: 'install_success', 174 item_id: itemId, 175 v: 1 176 }; 177 178 this.postMessage_(message); 179}; 180 181/** 182 * Send the 'initialize' message to the widget. 183 * @private 184 */ 185CWSContainerClient.prototype.postInitializeMessage_ = function() { 186 var message = { 187 message: 'initialize', 188 hl: util.getCurrentLocaleOrDefault(), 189 width: this.width_, 190 height: this.height_, 191 v: 1 192 }; 193 194 if (this.searchQuery_) { 195 message['search_query'] = this.searchQuery_; 196 } else { 197 message['file_extension'] = this.ext_; 198 message['mime_type'] = this.mime_; 199 } 200 201 this.postMessage_(message); 202}; 203 204/** 205 * Send a message to the widget. This method shouldn't be called directly, 206 * should from more specified posting function (eg. postXyzMessage_()). 207 * 208 * @param {object} message Message object to be posted. 209 * @private 210 */ 211CWSContainerClient.prototype.postMessage_ = function(message) { 212 if (!this.webView_.contentWindow) 213 return; 214 215 this.webView_.contentWindow.postMessage(message, this.target_); 216}; 217 218/** 219 * Loads the page to <webview>. Can be called only once. 220 */ 221CWSContainerClient.prototype.load = function() { 222 if (this.loading_ || this.loaded_) 223 throw new Error('Already loaded.'); 224 this.loading_ = true; 225 this.loaded_ = false; 226 227 window.addEventListener('message', this.onMessageBound_); 228 this.webView_.addEventListener('loadstop', this.onLoadStopBound_); 229 this.webView_.addEventListener('loadabort', this.onLoadAbortBound_); 230 this.webView_.setAttribute('src', this.url_); 231}; 232 233/** 234 * Aborts loading of the embedded dialog and performs cleanup. 235 */ 236CWSContainerClient.prototype.abort = function() { 237 window.removeEventListener('message', this.onMessageBound_); 238 this.webView_.removeEventListener('loadstop', this.onLoadStopBound_); 239 this.webView_.removeEventListener( 240 'loadabort', this.onLoadAbortBound_); 241 this.webView_.stop(); 242}; 243 244/** 245 * Cleans the dialog by removing all handlers. 246 */ 247CWSContainerClient.prototype.dispose = function() { 248 this.abort(); 249}; 250