summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkelvinp@chromium.org <kelvinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-12 22:47:14 +0000
committerkelvinp@chromium.org <kelvinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-12 22:47:14 +0000
commit5264fda07b73e7b5f290d062ec02aafe271e9d3e (patch)
treee28fb0e4b04cb7865189c9e6c342a3d7e54f5a72
parent8ba027dbfbc95361f8af04fe76b1ee69e7bc8686 (diff)
downloadchromium_src-5264fda07b73e7b5f290d062ec02aafe271e9d3e.zip
chromium_src-5264fda07b73e7b5f290d062ec02aafe271e9d3e.tar.gz
chromium_src-5264fda07b73e7b5f290d062ec02aafe271e9d3e.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 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289096 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--remoting/remoting.gyp2
-rw-r--r--remoting/remoting_client.gypi17
-rw-r--r--remoting/remoting_test.gypi1
-rw-r--r--remoting/remoting_webapp.gypi1
-rw-r--r--remoting/remoting_webapp_files.gypi18
-rw-r--r--remoting/webapp/background.js33
-rw-r--r--remoting/webapp/background/app_launcher.js132
-rw-r--r--remoting/webapp/background/background.js57
-rw-r--r--remoting/webapp/base.js20
-rw-r--r--remoting/webapp/hangout_session.js2
-rw-r--r--remoting/webapp/host_daemon_facade.js2
-rw-r--r--remoting/webapp/html/template_background.html15
-rw-r--r--remoting/webapp/it2me_host_facade.js2
-rw-r--r--remoting/webapp/js_proto/chrome_proto.js111
-rw-r--r--remoting/webapp/manifest.json.jinja24
-rw-r--r--remoting/webapp/unittests/base_unittest.js19
16 files changed, 361 insertions, 75 deletions
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index f197916..2a902d6 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -165,7 +165,7 @@
'host/win/host_messages.mc.jinja2',
'host/win/version.rc.jinja2',
'resources/play_store_resources.cc',
- 'webapp/background.js',
+ 'webapp/background/background.js',
'webapp/butter_bar.js',
'webapp/client_screen.js',
'webapp/error.js',
diff --git a/remoting/remoting_client.gypi b/remoting/remoting_client.gypi
index d7d66f1..3da83ae 100644
--- a/remoting/remoting_client.gypi
+++ b/remoting/remoting_client.gypi
@@ -85,6 +85,22 @@
'--js', '<@(remoting_webapp_wcs_sandbox_html_js_files)',
],
},
+ {
+ 'action_name': 'Build Remoting Webapp background.html',
+ 'inputs': [
+ 'webapp/build-html.py',
+ '<(remoting_webapp_template_background)',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/background.html',
+ ],
+ 'action': [
+ 'python', 'webapp/build-html.py',
+ '<(SHARED_INTERMEDIATE_DIR)/background.html',
+ '<(remoting_webapp_template_background)',
+ '--js', '<@(remoting_webapp_background_js_files)',
+ ],
+ },
],
}, # end of target 'remoting_webapp_html'
@@ -114,7 +130,6 @@
'variables': {
'output_dir': '<(PRODUCT_DIR)/remoting/remoting.webapp.v2',
'zip_path': '<(PRODUCT_DIR)/remoting-webapp.v2.zip',
- 'extra_files': [ 'webapp/background.js' ],
},
'conditions': [
['disable_nacl==0 and disable_nacl_untrusted==0', {
diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi
index 70d5f9f..03292a4 100644
--- a/remoting/remoting_test.gypi
+++ b/remoting/remoting_test.gypi
@@ -297,6 +297,7 @@
'webapp_js_files': [
'<@(remoting_webapp_main_html_js_files)',
'<@(remoting_webapp_js_wcs_sandbox_files)',
+ '<@(remoting_webapp_background_js_files)',
]
},
'copies': [
diff --git a/remoting/remoting_webapp.gypi b/remoting/remoting_webapp.gypi
index ea498b6..19692d1 100644
--- a/remoting/remoting_webapp.gypi
+++ b/remoting/remoting_webapp.gypi
@@ -11,6 +11,7 @@
'generated_html_files': [
'<(SHARED_INTERMEDIATE_DIR)/main.html',
'<(SHARED_INTERMEDIATE_DIR)/wcs_sandbox.html',
+ '<(SHARED_INTERMEDIATE_DIR)/background.html',
],
},
'dependencies': [
diff --git a/remoting/remoting_webapp_files.gypi b/remoting/remoting_webapp_files.gypi
index 93b74bb..7bfa958 100644
--- a/remoting/remoting_webapp_files.gypi
+++ b/remoting/remoting_webapp_files.gypi
@@ -131,6 +131,9 @@
],
# These product files are excluded from our JavaScript unittest
'remoting_webapp_unittest_exclude_files': [
+ # background.js is where the onLoad handler is defined, which
+ # makes it the entry point of the background page.
+ 'webapp/background/background.js',
# event_handlers.js is where the onLoad handler is defined, which
# makes it the entry point of the webapp.
'webapp/event_handlers.js',
@@ -165,7 +168,16 @@
'<@(remoting_webapp_js_wcs_container_files)',
# Uncomment this line to include browser test files in the web app
# to expedite debugging or local development.
- '<@(remoting_webapp_js_browser_test_files)'
+ # '<@(remoting_webapp_js_browser_test_files)'
+ ],
+
+ # The JavaScript files that are used as background pages.
+ 'remoting_webapp_background_js_files': [
+ 'webapp/base.js',
+ 'webapp/client_session.js',
+ 'webapp/typecheck.js',
+ 'webapp/background/app_launcher.js',
+ 'webapp/background/background.js'
],
# The JavaScript files required by wcs_sandbox.html.
@@ -179,6 +191,7 @@
'remoting_webapp_all_js_files': [
# JS files for main.html.
'<@(remoting_webapp_main_html_js_files)',
+ '<@(remoting_webapp_background_js_files)',
# JS files for wcs_sandbox.html.
# Use r_w_js_wcs_sandbox_files instead of r_w_wcs_sandbox_html_js_files
# so that we don't double include error.js and plugin_settings.js.
@@ -231,6 +244,9 @@
'remoting_webapp_template_wcs_sandbox':
'webapp/html/template_wcs_sandbox.html',
+ 'remoting_webapp_template_background':
+ 'webapp/html/template_background.html',
+
'remoting_webapp_template_files': [
'webapp/html/butterbar.html',
'webapp/html/client_plugin.html',
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;