diff options
author | kelvinp@chromium.org <kelvinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-12 22:47:14 +0000 |
---|---|---|
committer | kelvinp@chromium.org <kelvinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-12 22:49:05 +0000 |
commit | f34acc690bbeb3d0ebd3cb8be1b254dd9f8d86ee (patch) | |
tree | e28fb0e4b04cb7865189c9e6c342a3d7e54f5a72 /remoting/webapp | |
parent | 2e84223764658781fc704ebf6f4ba8dcaca989f2 (diff) | |
download | chromium_src-f34acc690bbeb3d0ebd3cb8be1b254dd9f8d86ee.zip chromium_src-f34acc690bbeb3d0ebd3cb8be1b254dd9f8d86ee.tar.gz chromium_src-f34acc690bbeb3d0ebd3cb8be1b254dd9f8d86ee.tar.bz2 |
Hangout remote desktop part II - background.html and AppLauncher
This CL:
- Moves background.js to background.html
- Introduces remoting.appLauncher that allows the caller to launch and close the webapp without knowing the implementation difference between a v1 app and a v2 app.
NOTRY=true
R=jamiewalch@chromium.org
Review URL: https://codereview.chromium.org/450383003
Cr-Commit-Position: refs/heads/master@{#289096}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289096 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/webapp')
-rw-r--r-- | remoting/webapp/background.js | 33 | ||||
-rw-r--r-- | remoting/webapp/background/app_launcher.js | 132 | ||||
-rw-r--r-- | remoting/webapp/background/background.js | 57 | ||||
-rw-r--r-- | remoting/webapp/base.js | 20 | ||||
-rw-r--r-- | remoting/webapp/hangout_session.js | 2 | ||||
-rw-r--r-- | remoting/webapp/host_daemon_facade.js | 2 | ||||
-rw-r--r-- | remoting/webapp/html/template_background.html | 15 | ||||
-rw-r--r-- | remoting/webapp/it2me_host_facade.js | 2 | ||||
-rw-r--r-- | remoting/webapp/js_proto/chrome_proto.js | 111 | ||||
-rw-r--r-- | remoting/webapp/manifest.json.jinja2 | 4 | ||||
-rw-r--r-- | remoting/webapp/unittests/base_unittest.js | 19 |
11 files changed, 325 insertions, 72 deletions
diff --git a/remoting/webapp/background.js b/remoting/webapp/background.js deleted file mode 100644 index 1976b91..0000000 --- a/remoting/webapp/background.js +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** @type {string} */ -var kNewWindowId = 'new-window'; - -function createWindow() { - chrome.app.window.create('main.html', { - 'width': 800, - 'height': 600, - 'frame': 'none' - }); -}; - -/** @param {OnClickData} info */ -function onContextMenu(info) { - if (info.menuItemId == kNewWindowId) { - createWindow(); - } -}; - -function initializeContextMenu() { - chrome.contextMenus.create({ - id: kNewWindowId, - contexts: ['launcher'], - title: chrome.i18n.getMessage(/*i18n-content*/'NEW_WINDOW') - }); -} - -chrome.app.runtime.onLaunched.addListener(createWindow); -chrome.contextMenus.onClicked.addListener(onContextMenu); -initializeContextMenu(); diff --git a/remoting/webapp/background/app_launcher.js b/remoting/webapp/background/app_launcher.js new file mode 100644 index 0000000..d1fc57c --- /dev/null +++ b/remoting/webapp/background/app_launcher.js @@ -0,0 +1,132 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview + * AppLauncher is an interface that allows the client code to launch and close + * the app without knowing the implementation difference between a v1 app and + * a v2 app. + * + * To launch an app: + * var appLauncher = new remoting.V1AppLauncher(); + * var appId = ""; + * appLauncher.launch({arg1:'someValue'}).then(function(id){ + * appId = id; + * }); + * + * To close an app: + * appLauncher.close(appId); + */ + +'use strict'; + +/** @suppress {duplicate} */ +var remoting = remoting || {}; + +/** @interface */ +remoting.AppLauncher = function() {}; + +/** + * @param {Object=} opt_launchArgs + * @return {Promise} The promise will resolve when the app is launched. It will + * provide the caller with the appId (which is either the id of the hosting tab + * or window). The caller can use the appId to close the app. + */ +remoting.AppLauncher.prototype.launch = function(opt_launchArgs) { }; + +/** + * @param {string} id The id of the app to close. + * @return {Promise} The promise will resolve when the app is closed. + */ +remoting.AppLauncher.prototype.close = function(id) {}; + +/** + * @constructor + * @implements {remoting.AppLauncher} + */ +remoting.V1AppLauncher = function() {}; + +remoting.V1AppLauncher.prototype.launch = function(opt_launchArgs) { + var url = base.urlJoin('main.html', opt_launchArgs); + + /** + * @param {function(*=):void} resolve + * @param {function(*=):void} reject + */ + return new Promise(function(resolve, reject) { + chrome.tabs.create({ url: url, selected: true }, + /** @param {chrome.Tab} tab The created tab. */ + function(tab) { + if (!tab) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(tab.id); + } + }); + }); +}; + +remoting.V1AppLauncher.prototype.close = function(id) { + /** + * @param {function(*=):void} resolve + * @param {function(*=):void} reject + */ + return new Promise(function(resolve, reject) { + /** @param {chrome.Tab} tab The retrieved tab. */ + chrome.tabs.get(id, function(tab) { + if (!tab) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + chrome.tabs.remove(tab.id, resolve); + } + }); + }); +}; + + +/** + * @constructor + * @implements {remoting.AppLauncher} + */ +remoting.V2AppLauncher = function() {}; + +/** + * @type {number} + * @private + */ +remoting.V2AppLauncher.nextWindowId_ = 0; + +remoting.V2AppLauncher.prototype.launch = function(opt_launchArgs) { + var url = base.urlJoin('main.html', opt_launchArgs); + + /** + * @param {function(*=):void} resolve + * @param {function(*=):void} reject + */ + return new Promise(function(resolve, reject) { + chrome.app.window.create(url, { + 'width': 800, + 'height': 600, + 'frame': 'none', + 'id': String(remoting.V2AppLauncher.nextWindowId_++) + }, + /** @param {AppWindow} appWindow */ + function(appWindow) { + if (!appWindow) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(appWindow.id); + } + }); + }); +}; + +remoting.V2AppLauncher.prototype.close = function(id) { + var appWindow = chrome.app.window.get(id); + if (!appWindow) { + return Promise.reject(new Error(chrome.runtime.lastError.message)); + } + appWindow.close(); + return Promise.resolve(); +}; diff --git a/remoting/webapp/background/background.js b/remoting/webapp/background/background.js new file mode 100644 index 0000000..5f26788 --- /dev/null +++ b/remoting/webapp/background/background.js @@ -0,0 +1,57 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** @suppress {duplicate} */ +var remoting = remoting || {}; + +(function(){ + +/** @return {boolean} */ +function isAppsV2() { + var manifest = chrome.runtime.getManifest(); + if (manifest && manifest.app && manifest.app.background) { + return true; + } + return false; +} + +/** @param {remoting.AppLauncher} appLauncher */ +function initializeAppV2(appLauncher) { + /** @type {string} */ + var kNewWindowId = 'new-window'; + + /** @param {OnClickData} info */ + function onContextMenu(info) { + if (info.menuItemId == kNewWindowId) { + appLauncher.launch(); + } + } + + function initializeContextMenu() { + chrome.contextMenus.create({ + id: kNewWindowId, + contexts: ['launcher'], + title: chrome.i18n.getMessage(/*i18n-content*/'NEW_WINDOW') + }); + chrome.contextMenus.onClicked.addListener(onContextMenu); + } + + initializeContextMenu(); + chrome.app.runtime.onLaunched.addListener( + appLauncher.launch.bind(appLauncher) + ); +} + +function main() { + /** @type {remoting.AppLauncher} */ + var appLauncher = new remoting.V1AppLauncher(); + if (isAppsV2()) { + appLauncher = new remoting.V2AppLauncher(); + initializeAppV2(appLauncher); + } +} + +window.addEventListener('load', main, false); + +}()); diff --git a/remoting/webapp/base.js b/remoting/webapp/base.js index 4771e53..d12db235 100644 --- a/remoting/webapp/base.js +++ b/remoting/webapp/base.js @@ -112,6 +112,26 @@ base.values = function(dict) { }); }; + +/** + * Joins the |url| with optional query parameters defined in |opt_params| + * See unit test for usage. + * @param {string} url + * @param {Object.<string>=} opt_params + * @return {string} + */ +base.urlJoin = function(url, opt_params) { + if (!opt_params) { + return url; + } + var queryParameters = []; + for (var key in opt_params) { + queryParameters.push(encodeURIComponent(key) + "=" + + encodeURIComponent(opt_params[key])); + } + return url + '?' + queryParameters.join('&'); +}; + base.Promise = function() {}; /** diff --git a/remoting/webapp/hangout_session.js b/remoting/webapp/hangout_session.js index 7f11ee3..58c1804 100644 --- a/remoting/webapp/hangout_session.js +++ b/remoting/webapp/hangout_session.js @@ -21,7 +21,7 @@ var remoting = remoting || {}; remoting.HangoutSession = function() { /** * @private - * @type {chrome.extension.Port} + * @type {chrome.runtime.Port} */ this.port_ = null; }; diff --git a/remoting/webapp/host_daemon_facade.js b/remoting/webapp/host_daemon_facade.js index 5b1567a..abd9719 100644 --- a/remoting/webapp/host_daemon_facade.js +++ b/remoting/webapp/host_daemon_facade.js @@ -28,7 +28,7 @@ remoting.HostDaemonFacade = function() { */ this.pendingReplies_ = {}; - /** @type {?chrome.extension.Port} @private */ + /** @type {?chrome.runtime.Port} @private */ this.port_ = null; /** @type {string} @private */ diff --git a/remoting/webapp/html/template_background.html b/remoting/webapp/html/template_background.html new file mode 100644 index 0000000..c9d416b --- /dev/null +++ b/remoting/webapp/html/template_background.html @@ -0,0 +1,15 @@ +<!doctype html> +<!-- +Copyright (c) 2013 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<html> + <head> + <meta charset="utf-8"> + <meta-include type="javascript"/> + </head> + <body> + </body> +</html> diff --git a/remoting/webapp/it2me_host_facade.js b/remoting/webapp/it2me_host_facade.js index e83e225..b8ce5d6 100644 --- a/remoting/webapp/it2me_host_facade.js +++ b/remoting/webapp/it2me_host_facade.js @@ -23,7 +23,7 @@ remoting.It2MeHostFacade = function() { this.nextId_ = 0; /** - * @type {?chrome.extension.Port} + * @type {?chrome.runtime.Port} * @private */ this.port_ = null; diff --git a/remoting/webapp/js_proto/chrome_proto.js b/remoting/webapp/js_proto/chrome_proto.js index f7b9a6f..455af5e 100644 --- a/remoting/webapp/js_proto/chrome_proto.js +++ b/remoting/webapp/js_proto/chrome_proto.js @@ -9,6 +9,14 @@ /** @type {Object} */ var chrome = {}; +/** @constructor */ +chrome.Event = function() {}; + +/** @param {Function} callback */ +chrome.Event.prototype.addListener = function(callback) {}; + +/** @param {Function} callback */ +chrome.Event.prototype.removeListener = function(callback) {}; /** @type {Object} */ chrome.app = {}; @@ -31,7 +39,12 @@ chrome.app.window = { /** * @return {AppWindow} */ - current: function() {} + current: function() {}, + /** + * @param {string} id + * @param {function()=} opt_callback + */ + get: function(id, opt_callback) {} }; @@ -43,19 +56,29 @@ chrome.runtime = { message: '' }, /** @return {{version: string, app: {background: Object}}} */ - getManifest: function() {} + getManifest: function() {}, + /** @type {chrome.Event} */ + onSuspend: null, + /** @type {chrome.Event} */ + onConnect: null, + /** @type {chrome.Event} */ + onConnectExternal: null, + /** @type {chrome.Event} */ + onMessage: null, + /** @type {chrome.Event} */ + onMessageExternal: null }; /** - * @type {?function(string):chrome.extension.Port} + * @type {?function(string):chrome.runtime.Port} */ chrome.runtime.connectNative = function(name) {}; /** - * @param {{name:string}} connectInfo - * @return {chrome.extension.Port} + * @param {{ name: string}} config + * @return {chrome.runtime.Port} */ -chrome.runtime.connect = function(connectInfo) {}; +chrome.runtime.connect = function(config) {}; /** * @param {string} extensionId @@ -66,22 +89,40 @@ chrome.runtime.connect = function(connectInfo) {}; chrome.runtime.sendMessage = function( extensionId, message, opt_options, opt_callback) {}; -/** @type {Object} */ -chrome.extension = {}; +/** @constructor */ +chrome.runtime.MessageSender = function(){ + /** @type {chrome.Tab} */ + this.tab = null; +}; /** @constructor */ -chrome.extension.Port = function() {}; +chrome.runtime.Port = function() { + this.onMessage = new chrome.Event(); + this.onDisconnect = new chrome.Event(); + + /** @type {string} */ + this.name = ''; + + /** @type {chrome.runtime.MessageSender} */ + this.sender = null; +}; /** @type {chrome.Event} */ -chrome.extension.Port.prototype.onMessage; +chrome.runtime.Port.prototype.onMessage = null; /** @type {chrome.Event} */ -chrome.extension.Port.prototype.onDisconnect; +chrome.runtime.Port.prototype.onDisconnect = null; + +chrome.runtime.Port.prototype.disconnect = function() {}; /** * @param {Object} message */ -chrome.extension.Port.prototype.postMessage = function(message) {}; +chrome.runtime.Port.prototype.postMessage = function(message) {}; + + +/** @type {Object} */ +chrome.extension = {}; /** * @param {*} message @@ -148,7 +189,7 @@ chrome.Storage.prototype.clear = function(opt_callback) {}; * src/chrome/common/extensions/api/context_menus.json */ chrome.contextMenus = {}; -/** @type {ChromeEvent} */ +/** @type {chrome.Event} */ chrome.contextMenus.onClicked; /** * @param {!Object} createProperties @@ -216,27 +257,6 @@ chrome.identity = { launchWebAuthFlow: function(parameters, callback) {} }; -// TODO(garykac): Combine chrome.Event and ChromeEvent -/** @constructor */ -function ChromeEvent() {} -/** @param {Function} callback */ -ChromeEvent.prototype.addListener = function(callback) {}; -/** @param {Function} callback */ -ChromeEvent.prototype.removeListener = function(callback) {}; -/** @param {Function} callback */ -ChromeEvent.prototype.hasListener = function(callback) {}; -/** @param {Function} callback */ -ChromeEvent.prototype.hasListeners = function(callback) {}; - -/** @constructor */ -chrome.Event = function() {}; - -/** @param {function():void} callback */ -chrome.Event.prototype.addListener = function(callback) {}; - -/** @param {function():void} callback */ -chrome.Event.prototype.removeListener = function(callback) {}; - /** @type {Object} */ chrome.permissions = { @@ -259,12 +279,33 @@ chrome.tabs = {}; /** @param {function(chrome.Tab):void} callback */ chrome.tabs.getCurrent = function(callback) {}; +/** + * @param {Object?} options + * @param {function(chrome.Tab)=} opt_callback + */ +chrome.tabs.create = function(options, opt_callback) {}; + +/** + * @param {string} id + * @param {function(chrome.Tab)} callback + */ +chrome.tabs.get = function(id, callback) {}; + +/** + * @param {string} id + * @param {function()=} opt_callback + */ +chrome.tabs.remove = function(id, opt_callback) {}; + + /** @constructor */ chrome.Tab = function() { /** @type {boolean} */ this.pinned = false; /** @type {number} */ this.windowId = 0; + /** @type {string} */ + this.id = ''; }; @@ -294,6 +335,8 @@ var AppWindow = function() { this.onMaximized = null; /** @type {chrome.Event} */ this.onFullscreened = null; + /** @type {string} */ + this.id = ''; }; AppWindow.prototype.close = function() {}; diff --git a/remoting/webapp/manifest.json.jinja2 b/remoting/webapp/manifest.json.jinja2 index 1ce73d2..a5efc1f 100644 --- a/remoting/webapp/manifest.json.jinja2 +++ b/remoting/webapp/manifest.json.jinja2 @@ -13,8 +13,8 @@ } {% else %} "background": { - "scripts": ["background.js"] - } + "page": "background.html" + } {% endif %} }, "icons": { diff --git a/remoting/webapp/unittests/base_unittest.js b/remoting/webapp/unittests/base_unittest.js index d021a80..17a1903 100644 --- a/remoting/webapp/unittests/base_unittest.js +++ b/remoting/webapp/unittests/base_unittest.js @@ -56,6 +56,25 @@ test('dispose(obj) should not crash if |obj| is null', base.dispose(null); }); +test('urljoin(url, opt_param) should return url if |opt_param| is missing', + function() { + QUnit.equal( + base.urlJoin('http://www.chromium.org'), 'http://www.chromium.org'); +}); + +test('urljoin(url, opt_param) should urlencode |opt_param|', + function() { + var result = base.urlJoin('http://www.chromium.org', { + a: 'a', + foo: 'foo', + escapist: ':/?#[]@$&+,;=' + }); + QUnit.equal( + result, + 'http://www.chromium.org?a=a&foo=foo' + + '&escapist=%3A%2F%3F%23%5B%5D%40%24%26%2B%2C%3B%3D'); +}); + QUnit.asyncTest('Promise.sleep(delay) should fulfill the promise after |delay|', function() { var isCalled = false; |