summaryrefslogtreecommitdiffstats
path: root/chrome/renderer
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/renderer')
-rw-r--r--chrome/renderer/extensions/dispatcher.cc3
-rw-r--r--chrome/renderer/resources/extensions/image_util.js69
-rw-r--r--chrome/renderer/resources/extensions/notification_custom_bindings.js128
-rw-r--r--chrome/renderer/resources/renderer_resources.grd2
4 files changed, 202 insertions, 0 deletions
diff --git a/chrome/renderer/extensions/dispatcher.cc b/chrome/renderer/extensions/dispatcher.cc
index dcd161f..a76441f 100644
--- a/chrome/renderer/extensions/dispatcher.cc
+++ b/chrome/renderer/extensions/dispatcher.cc
@@ -632,6 +632,7 @@ void Dispatcher::PopulateSourceMap() {
// Libraries.
source_map_.RegisterSource("contentWatcher", IDR_CONTENT_WATCHER_JS);
+ source_map_.RegisterSource("imageUtil", IDR_IMAGE_UTIL_JS);
source_map_.RegisterSource("lastError", IDR_LAST_ERROR_JS);
source_map_.RegisterSource("schemaUtils", IDR_SCHEMA_UTILS_JS);
source_map_.RegisterSource("sendRequest", IDR_SEND_REQUEST_JS);
@@ -659,6 +660,8 @@ void Dispatcher::PopulateSourceMap() {
IDR_EXPERIMENTAL_MEDIA_GALLERIES_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("experimental.offscreen",
IDR_EXPERIMENTAL_OFFSCREENTABS_CUSTOM_BINDINGS_JS);
+ source_map_.RegisterSource("experimental.notification",
+ IDR_NOTIFICATION_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("extension", IDR_EXTENSION_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("fileBrowserHandler",
IDR_FILE_BROWSER_HANDLER_CUSTOM_BINDINGS_JS);
diff --git a/chrome/renderer/resources/extensions/image_util.js b/chrome/renderer/resources/extensions/image_util.js
new file mode 100644
index 0000000..4f418e2
--- /dev/null
+++ b/chrome/renderer/resources/extensions/image_util.js
@@ -0,0 +1,69 @@
+// Copyright 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.
+
+// This function takes an object |imageSpec| with the key |path| -
+// corresponding to the internet URL to be translated - and optionally
+// |width| and |height| which are the maximum dimensions to be used when
+// converting the image.
+function loadDataUrl(imageSpec, callbacks) {
+ var path = imageSpec.path;
+ var img = new Image();
+ if (typeof callbacks.onerror === 'function') {
+ img.onerror = function() {
+ callbacks.onerror({ problem: 'could_not_load', path: path });
+ };
+ }
+ img.onload = function() {
+ var canvas = document.createElement('canvas');
+
+ canvas.width = img.width;
+ if (imageSpec.width && imageSpec.width < img.width)
+ canvas.width = imageSpec.width;
+ canvas.height = img.height;
+ if (imageSpec.height && imageSpec.height < img.height)
+ canvas.height = imageSpec.height;
+
+ var canvas_context = canvas.getContext('2d');
+ canvas_context.clearRect(0, 0, canvas.width, canvas.height);
+ canvas_context.drawImage(img, 0, 0, canvas.width, canvas.height);
+ try {
+ var dataUrl = canvas.toDataURL();
+ if (typeof callbacks.oncomplete === 'function') {
+ callbacks.oncomplete(dataUrl);
+ }
+ } catch (e) {
+ if (typeof callbacks.onerror === 'function') {
+ callbacks.onerror({ problem: 'data_url_unavailable', path: path });
+ }
+ }
+ }
+ img.src = path;
+}
+
+function on_complete_index(index, err, loading, finished, callbacks) {
+ return function(imageData) {
+ delete loading[index];
+ finished[index] = imageData;
+ if (err)
+ callbacks.onerror(index);
+ if (Object.keys(loading).length == 0)
+ callbacks.oncomplete(finished);
+ }
+}
+
+function loadAllImages(imageSpecs, callbacks) {
+ var loading = {}, finished = [],
+ index, pathname;
+
+ for (var index = 0; index < imageSpecs.length; index++) {
+ loading[index] = imageSpecs[index];
+ loadDataUrl(imageSpecs[index], {
+ oncomplete: on_complete_index(index, false, loading, finished, callbacks),
+ onerror: on_complete_index(index, true, loading, finished, callbacks)
+ });
+ }
+}
+
+exports.loadDataUrl = loadDataUrl;
+exports.loadAllImages = loadAllImages;
diff --git a/chrome/renderer/resources/extensions/notification_custom_bindings.js b/chrome/renderer/resources/extensions/notification_custom_bindings.js
new file mode 100644
index 0000000..b88be09
--- /dev/null
+++ b/chrome/renderer/resources/extensions/notification_custom_bindings.js
@@ -0,0 +1,128 @@
+// Copyright 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.
+
+// Custom bindings for the notification API.
+
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
+var sendRequest = require('sendRequest').sendRequest;
+var imageUtil = require('imageUtil');
+var lastError = require('lastError');
+
+function url_getter(context, key) {
+ var f = function() {
+ return this[key];
+ };
+ return f.bind(context);
+}
+
+function url_setter(context, key) {
+ var f = function(val) {
+ this[key] = val;
+ };
+ return f.bind(context);
+}
+
+function replaceNotificationOptionURLs(notification_details, callback) {
+ // A URL Spec is an object with the following keys:
+ // path: The resource to be downloaded.
+ // callback: A function to be called when the URL is complete. It
+ // should accept an ImageData object and set the appropriate
+ // field in the output of create.
+
+ // TODO(dewittj): Try to remove hard-coding.
+ // |iconUrl| is required.
+ var url_specs = [{
+ path: notification_details.iconUrl,
+ width: 80,
+ height: 80,
+ callback: url_setter(notification_details, 'iconUrl')
+ }];
+
+ // |secondIconUrl| is optional.
+ if (notification_details.secondIconUrl) {
+ url_specs.push({
+ path: notification_details.secondIconUrl,
+ width: 80,
+ height: 80,
+ callback: url_setter(notification_details, 'secondIconUrl')
+ });
+ }
+
+ // |imageUrl| is optional.
+ if (notification_details.imageUrl) {
+ url_specs.push({
+ path: notification_details.imageUrl,
+ width: 300,
+ height: 300,
+ callback: url_setter(notification_details, 'imageUrl')
+ });
+ }
+
+ // Each button has an optional icon.
+ var button_list = notification_details.buttons;
+ if (button_list && typeof button_list.length === 'number') {
+ var num_buttons = button_list.length;
+ for (var i = 0; i < num_buttons; i++) {
+ if (button_list[i].iconUrl) {
+ url_specs.push({
+ path: button_list[i].iconUrl,
+ width: 16,
+ height: 16,
+ callback: url_setter(button_list[i], 'iconUrl')
+ });
+ }
+ }
+ }
+
+ var errors = 0;
+
+ imageUtil.loadAllImages(url_specs, {
+ onerror: function(index) {
+ errors++;
+ },
+ oncomplete: function(imageData) {
+ if (errors > 0) {
+ callback(false);
+ return;
+ }
+ for (var index = 0; index < url_specs.length; index++) {
+ var url_spec = url_specs[index];
+ url_spec.callback(imageData[index]);
+ }
+ callback(true);
+ }
+ });
+}
+
+function genHandle(failure_function) {
+ return function(id, input_notification_details, callback) {
+ // TODO(dewittj): Remove this hack. This is used as a way to deep
+ // copy a complex JSON object.
+ var notification_details = JSON.parse(
+ JSON.stringify(input_notification_details));
+ var that = this;
+ replaceNotificationOptionURLs(notification_details, function(success) {
+ if (success) {
+ sendRequest(that.name,
+ [id, notification_details, callback],
+ that.definition.parameters);
+ return;
+ }
+ lastError.set('Unable to download all specified images.');
+ failure_function(callback, id);
+ });
+ };
+}
+
+var handleCreate = genHandle(function(callback, id) { callback(id); });
+var handleUpdate = genHandle(function(callback, id) { callback(false); });
+
+var experimentalNotificationCustomHook = function(bindingsAPI, extensionId) {
+ var apiFunctions = bindingsAPI.apiFunctions;
+ apiFunctions.setHandleRequest('create', handleCreate);
+ apiFunctions.setHandleRequest('update', handleCreate);
+};
+
+chromeHidden.registerCustomHook('experimental.notification',
+ experimentalNotificationCustomHook);
diff --git a/chrome/renderer/resources/renderer_resources.grd b/chrome/renderer/resources/renderer_resources.grd
index 1643ff4..225a75d 100644
--- a/chrome/renderer/resources/renderer_resources.grd
+++ b/chrome/renderer/resources/renderer_resources.grd
@@ -37,6 +37,7 @@ without changes to the corresponding grd file. fb9 -->
<!-- Libraries. -->
<include name="IDR_TAG_WATCHER_JS" file="extensions\tag_watcher.js" type="BINDATA" />
+ <include name="IDR_IMAGE_UTIL_JS" file="extensions\image_util.js" type="BINDATA" />
<include name="IDR_LAST_ERROR_JS" file="extensions\last_error.js" type="BINDATA" />
<include name="IDR_SCHEMA_UTILS_JS" file="extensions\schema_utils.js" type="BINDATA" />
<include name="IDR_SEND_REQUEST_JS" file="extensions\send_request.js" type="BINDATA" />
@@ -65,6 +66,7 @@ without changes to the corresponding grd file. fb9 -->
<include name="IDR_I18N_CUSTOM_BINDINGS_JS" file="extensions\i18n_custom_bindings.js" type="BINDATA" />
<include name="IDR_INPUT_IME_CUSTOM_BINDINGS_JS" file="extensions\input.ime_custom_bindings.js" type="BINDATA" />
<include name="IDR_MEDIA_GALLERIES_CUSTOM_BINDINGS_JS" file="extensions\media_galleries_custom_bindings.js" type="BINDATA" />
+ <include name="IDR_NOTIFICATION_CUSTOM_BINDINGS_JS" file="extensions\notification_custom_bindings.js" type="BINDATA" />
<include name="IDR_OMNIBOX_CUSTOM_BINDINGS_JS" file="extensions\omnibox_custom_bindings.js" type="BINDATA" />
<include name="IDR_PAGE_ACTIONS_CUSTOM_BINDINGS_JS" file="extensions\page_actions_custom_bindings.js" type="BINDATA" />
<include name="IDR_PAGE_ACTION_CUSTOM_BINDINGS_JS" file="extensions\page_action_custom_bindings.js" type="BINDATA" />