Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(999)

Side by Side Diff: chrome/renderer/resources/extension_process_bindings.js

Issue 7980055: Move bindings javascript into its own directory. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 // This script contains privileged chrome extension related javascript APIs.
6 // It is loaded by pages whose URL has the chrome-extension protocol.
7
8 var chrome = chrome || {};
9 (function() {
10 native function GetExtensionAPIDefinition();
11 native function StartRequest();
12 native function GetChromeHidden();
13 native function GetNextRequestId();
14 native function Print();
15
16 native function GetCurrentPageActions(extensionId);
17 native function GetExtensionViews();
18 native function GetNextContextMenuId();
19 native function GetNextTtsEventId();
20 native function OpenChannelToTab();
21 native function GetRenderViewId();
22 native function SetIconCommon();
23 native function GetUniqueSubEventName(eventName);
24 native function GetLocalFileSystem(name, path);
25 native function DecodeJPEG(jpeg_image);
26
27 var chromeHidden = GetChromeHidden();
28
29 if (!chrome)
30 chrome = {};
31
32 function forEach(dict, f) {
33 for (key in dict) {
34 if (dict.hasOwnProperty(key))
35 f(key, dict[key]);
36 }
37 }
38
39 // Validate arguments.
40 chromeHidden.validationTypes = [];
41 chromeHidden.validate = function(args, schemas) {
42 if (args.length > schemas.length)
43 throw new Error("Too many arguments.");
44
45 for (var i = 0; i < schemas.length; i++) {
46 if (i in args && args[i] !== null && args[i] !== undefined) {
47 var validator = new chromeHidden.JSONSchemaValidator();
48 validator.addTypes(chromeHidden.validationTypes);
49 validator.validate(args[i], schemas[i]);
50 if (validator.errors.length == 0)
51 continue;
52
53 var message = "Invalid value for argument " + (i + 1) + ". ";
54 for (var i = 0, err; err = validator.errors[i]; i++) {
55 if (err.path) {
56 message += "Property '" + err.path + "': ";
57 }
58 message += err.message;
59 message = message.substring(0, message.length - 1);
60 message += ", ";
61 }
62 message = message.substring(0, message.length - 2);
63 message += ".";
64
65 throw new Error(message);
66 } else if (!schemas[i].optional) {
67 throw new Error("Parameter " + (i + 1) + " is required.");
68 }
69 }
70 };
71
72 // Callback handling.
73 var requests = [];
74 chromeHidden.handleResponse = function(requestId, name,
75 success, response, error) {
76 try {
77 var request = requests[requestId];
78 if (success) {
79 delete chrome.extension.lastError;
80 } else {
81 if (!error) {
82 error = "Unknown error.";
83 }
84 console.error("Error during " + name + ": " + error);
85 chrome.extension.lastError = {
86 "message": error
87 };
88 }
89
90 if (request.customCallback) {
91 request.customCallback(name, request, response);
92 }
93
94 if (request.callback) {
95 // Callbacks currently only support one callback argument.
96 var callbackArgs = response ? [chromeHidden.JSON.parse(response)] : [];
97
98 // Validate callback in debug only -- and only when the
99 // caller has provided a callback. Implementations of api
100 // calls my not return data if they observe the caller
101 // has not provided a callback.
102 if (chromeHidden.validateCallbacks && !error) {
103 try {
104 if (!request.callbackSchema.parameters) {
105 throw "No callback schemas defined";
106 }
107
108 if (request.callbackSchema.parameters.length > 1) {
109 throw "Callbacks may only define one parameter";
110 }
111
112 chromeHidden.validate(callbackArgs,
113 request.callbackSchema.parameters);
114 } catch (exception) {
115 return "Callback validation error during " + name + " -- " +
116 exception.stack;
117 }
118 }
119
120 if (response) {
121 request.callback(callbackArgs[0]);
122 } else {
123 request.callback();
124 }
125 }
126 } finally {
127 delete requests[requestId];
128 delete chrome.extension.lastError;
129 }
130
131 return undefined;
132 };
133
134 function prepareRequest(args, argSchemas) {
135 var request = {};
136 var argCount = args.length;
137
138 // Look for callback param.
139 if (argSchemas.length > 0 &&
140 args.length == argSchemas.length &&
141 argSchemas[argSchemas.length - 1].type == "function") {
142 request.callback = args[argSchemas.length - 1];
143 request.callbackSchema = argSchemas[argSchemas.length - 1];
144 --argCount;
145 }
146
147 request.args = [];
148 for (var k = 0; k < argCount; k++) {
149 request.args[k] = args[k];
150 }
151
152 return request;
153 }
154
155 // Send an API request and optionally register a callback.
156 // |opt_args| is an object with optional parameters as follows:
157 // - noStringify: true if we should not stringify the request arguments.
158 // - customCallback: a callback that should be called instead of the standard
159 // callback.
160 // - nativeFunction: the v8 native function to handle the request, or
161 // StartRequest if missing.
162 // - forIOThread: true if this function should be handled on the browser IO
163 // thread.
164 function sendRequest(functionName, args, argSchemas, opt_args) {
165 if (!opt_args)
166 opt_args = {};
167 var request = prepareRequest(args, argSchemas);
168 if (opt_args.customCallback) {
169 request.customCallback = opt_args.customCallback;
170 }
171 // JSON.stringify doesn't support a root object which is undefined.
172 if (request.args === undefined)
173 request.args = null;
174
175 var sargs = opt_args.noStringify ?
176 request.args : chromeHidden.JSON.stringify(request.args);
177 var nativeFunction = opt_args.nativeFunction || StartRequest;
178
179 var requestId = GetNextRequestId();
180 requests[requestId] = request;
181 var hasCallback =
182 (request.callback || opt_args.customCallback) ? true : false;
183 return nativeFunction(functionName, sargs, requestId, hasCallback,
184 opt_args.forIOThread);
185 }
186
187 // --- Setup additional api's not currently handled in common/extensions/api
188
189 // WebRequestEvent object. This is used for special webRequest events with
190 // extra parameters. Each invocation of addListener creates a new named
191 // sub-event. That sub-event is associated with the extra parameters in the
192 // browser process, so that only it is dispatched when the main event occurs
193 // matching the extra parameters.
194 //
195 // Example:
196 // chrome.webRequest.onBeforeRequest.addListener(
197 // callback, {urls: "http://*.google.com/*"});
198 // ^ callback will only be called for onBeforeRequests matching the filter.
199 chrome.WebRequestEvent =
200 function(eventName, opt_argSchemas, opt_extraArgSchemas) {
201 if (typeof eventName != "string")
202 throw new Error("chrome.WebRequestEvent requires an event name.");
203
204 this.eventName_ = eventName;
205 this.argSchemas_ = opt_argSchemas;
206 this.extraArgSchemas_ = opt_extraArgSchemas;
207 this.subEvents_ = [];
208 };
209
210 // Test if the given callback is registered for this event.
211 chrome.WebRequestEvent.prototype.hasListener = function(cb) {
212 return this.findListener_(cb) > -1;
213 };
214
215 // Test if any callbacks are registered fur thus event.
216 chrome.WebRequestEvent.prototype.hasListeners = function(cb) {
217 return this.subEvents_.length > 0;
218 };
219
220 // Registers a callback to be called when this event is dispatched. If
221 // opt_filter is specified, then the callback is only called for events that
222 // match the given filters. If opt_extraInfo is specified, the given optional
223 // info is sent to the callback.
224 chrome.WebRequestEvent.prototype.addListener =
225 function(cb, opt_filter, opt_extraInfo) {
226 var subEventName = GetUniqueSubEventName(this.eventName_);
227 // Note: this could fail to validate, in which case we would not add the
228 // subEvent listener.
229 chromeHidden.validate(Array.prototype.slice.call(arguments, 1),
230 this.extraArgSchemas_);
231 chrome.experimental.webRequest.addEventListener(
232 cb, opt_filter, opt_extraInfo, this.eventName_, subEventName);
233
234 var subEvent = new chrome.Event(subEventName, this.argSchemas_);
235 var subEventCallback = cb;
236 if (opt_extraInfo && opt_extraInfo.indexOf("blocking") >= 0) {
237 var eventName = this.eventName_;
238 subEventCallback = function() {
239 var requestId = arguments[0].requestId;
240 try {
241 var result = cb.apply(null, arguments);
242 chrome.experimental.webRequest.eventHandled(
243 eventName, subEventName, requestId, result);
244 } catch (e) {
245 chrome.experimental.webRequest.eventHandled(
246 eventName, subEventName, requestId);
247 throw e;
248 }
249 };
250 }
251 this.subEvents_.push(
252 {subEvent: subEvent, callback: cb, subEventCallback: subEventCallback});
253 subEvent.addListener(subEventCallback);
254 };
255
256 // Unregisters a callback.
257 chrome.WebRequestEvent.prototype.removeListener = function(cb) {
258 var idx = this.findListener_(cb);
259 if (idx < 0) {
260 return;
261 }
262
263 var e = this.subEvents_[idx];
264 e.subEvent.removeListener(e.subEventCallback);
265 if (e.subEvent.hasListeners()) {
266 console.error(
267 "Internal error: webRequest subEvent has orphaned listeners.");
268 }
269 this.subEvents_.splice(idx, 1);
270 };
271
272 chrome.WebRequestEvent.prototype.findListener_ = function(cb) {
273 for (var i in this.subEvents_) {
274 var e = this.subEvents_[i];
275 if (e.callback === cb) {
276 if (e.subEvent.findListener_(e.subEventCallback) > -1)
277 return i;
278 console.error("Internal error: webRequest subEvent has no callback.");
279 }
280 }
281
282 return -1;
283 };
284
285 function CustomBindingsObject() {
286 }
287 CustomBindingsObject.prototype.setSchema = function(schema) {
288 // The functions in the schema are in list form, so we move them into a
289 // dictionary for easier access.
290 var self = this;
291 self.parameters = {};
292 schema.functions.forEach(function(f) {
293 self.parameters[f.name] = f.parameters;
294 });
295 };
296
297 function extendSchema(schema) {
298 var extendedSchema = schema.slice();
299 extendedSchema.unshift({'type': 'string'});
300 return extendedSchema;
301 }
302
303 var customBindings = {};
304
305 function setupChromeSetting() {
306 function ChromeSetting(prefKey, valueSchema) {
307 this.get = function(details, callback) {
308 var getSchema = this.parameters.get;
309 chromeHidden.validate([details, callback], getSchema);
310 return sendRequest('types.ChromeSetting.get',
311 [prefKey, details, callback],
312 extendSchema(getSchema));
313 };
314 this.set = function(details, callback) {
315 var setSchema = this.parameters.set.slice();
316 setSchema[0].properties.value = valueSchema;
317 chromeHidden.validate([details, callback], setSchema);
318 return sendRequest('types.ChromeSetting.set',
319 [prefKey, details, callback],
320 extendSchema(setSchema));
321 };
322 this.clear = function(details, callback) {
323 var clearSchema = this.parameters.clear;
324 chromeHidden.validate([details, callback], clearSchema);
325 return sendRequest('types.ChromeSetting.clear',
326 [prefKey, details, callback],
327 extendSchema(clearSchema));
328 };
329 this.onChange = new chrome.Event('types.ChromeSetting.' + prefKey +
330 '.onChange');
331 };
332 ChromeSetting.prototype = new CustomBindingsObject();
333 customBindings['ChromeSetting'] = ChromeSetting;
334 }
335
336 function setupContentSetting() {
337 function ContentSetting(contentType, settingSchema) {
338 this.get = function(details, callback) {
339 var getSchema = this.parameters.get;
340 chromeHidden.validate([details, callback], getSchema);
341 return sendRequest('experimental.contentSettings.get',
342 [contentType, details, callback],
343 extendSchema(getSchema));
344 };
345 this.set = function(details, callback) {
346 var setSchema = this.parameters.set.slice();
347 setSchema[0].properties.setting = settingSchema;
348 chromeHidden.validate([details, callback], setSchema);
349 return sendRequest('experimental.contentSettings.set',
350 [contentType, details, callback],
351 extendSchema(setSchema));
352 };
353 this.clear = function(details, callback) {
354 var clearSchema = this.parameters.clear;
355 chromeHidden.validate([details, callback], clearSchema);
356 return sendRequest('experimental.contentSettings.clear',
357 [contentType, details, callback],
358 extendSchema(clearSchema));
359 };
360 this.getResourceIdentifiers = function(callback) {
361 var schema = this.parameters.getResourceIdentifiers;
362 chromeHidden.validate([callback], schema);
363 return sendRequest(
364 'experimental.contentSettings.getResourceIdentifiers',
365 [contentType, callback],
366 extendSchema(schema));
367 };
368 }
369 ContentSetting.prototype = new CustomBindingsObject();
370 customBindings['ContentSetting'] = ContentSetting;
371 }
372
373 function setupInputEvents() {
374 chrome.experimental.input.onKeyEvent.dispatch =
375 function(engineID, keyData) {
376 var args = Array.prototype.slice.call(arguments);
377 if (this.validate_) {
378 var validationErrors = this.validate_(args);
379 if (validationErrors) {
380 chrome.experimental.input.eventHandled(requestId, false);
381 return validationErrors;
382 }
383 }
384 if (this.listeners_.length > 1) {
385 console.error("Too many listeners for 'onKeyEvent': " + e.stack);
386 chrome.experimental.input.eventHandled(requestId, false);
387 return;
388 }
389 for (var i = 0; i < this.listeners_.length; i++) {
390 try {
391 var requestId = keyData.requestId;
392 var result = this.listeners_[i].apply(null, args);
393 chrome.experimental.input.eventHandled(requestId, result);
394 } catch (e) {
395 console.error("Error in event handler for 'onKeyEvent': " + e.stack);
396 chrome.experimental.input.eventHandled(requestId, false);
397 }
398 }
399 };
400 }
401
402 // Page action events send (pageActionId, {tabId, tabUrl}).
403 function setupPageActionEvents(extensionId) {
404 var pageActions = GetCurrentPageActions(extensionId);
405
406 var oldStyleEventName = "pageActions";
407 // TODO(EXTENSIONS_DEPRECATED): only one page action
408 for (var i = 0; i < pageActions.length; ++i) {
409 // Setup events for each extension_id/page_action_id string we find.
410 chrome.pageActions[pageActions[i]] = new chrome.Event(oldStyleEventName);
411 }
412 }
413
414 function setupToolstripEvents(renderViewId) {
415 chrome.toolstrip = chrome.toolstrip || {};
416 chrome.toolstrip.onExpanded =
417 new chrome.Event("toolstrip.onExpanded." + renderViewId);
418 chrome.toolstrip.onCollapsed =
419 new chrome.Event("toolstrip.onCollapsed." + renderViewId);
420 }
421
422 function setupHiddenContextMenuEvent(extensionId) {
423 chromeHidden.contextMenus = {};
424 chromeHidden.contextMenus.handlers = {};
425 var eventName = "contextMenus";
426 chromeHidden.contextMenus.event = new chrome.Event(eventName);
427 chromeHidden.contextMenus.ensureListenerSetup = function() {
428 if (chromeHidden.contextMenus.listening) {
429 return;
430 }
431 chromeHidden.contextMenus.listening = true;
432 chromeHidden.contextMenus.event.addListener(function() {
433 // An extension context menu item has been clicked on - fire the onclick
434 // if there is one.
435 var id = arguments[0].menuItemId;
436 var onclick = chromeHidden.contextMenus.handlers[id];
437 if (onclick) {
438 onclick.apply(null, arguments);
439 }
440 });
441 };
442 }
443
444 // Parses the xml syntax supported by omnibox suggestion results. Returns an
445 // object with two properties: 'description', which is just the text content,
446 // and 'descriptionStyles', which is an array of style objects in a format
447 // understood by the C++ backend.
448 function parseOmniboxDescription(input) {
449 var domParser = new DOMParser();
450
451 // The XML parser requires a single top-level element, but we want to
452 // support things like 'hello, <match>world</match>!'. So we wrap the
453 // provided text in generated root level element.
454 var root = domParser.parseFromString(
455 '<fragment>' + input + '</fragment>', 'text/xml');
456
457 // DOMParser has a terrible error reporting facility. Errors come out nested
458 // inside the returned document.
459 var error = root.querySelector('parsererror div');
460 if (error) {
461 throw new Error(error.textContent);
462 }
463
464 // Otherwise, it's valid, so build up the result.
465 var result = {
466 description: '',
467 descriptionStyles: []
468 };
469
470 // Recursively walk the tree.
471 (function(node) {
472 for (var i = 0, child; child = node.childNodes[i]; i++) {
473 // Append text nodes to our description.
474 if (child.nodeType == Node.TEXT_NODE) {
475 result.description += child.nodeValue;
476 continue;
477 }
478
479 // Process and descend into a subset of recognized tags.
480 if (child.nodeType == Node.ELEMENT_NODE &&
481 (child.nodeName == 'dim' || child.nodeName == 'match' ||
482 child.nodeName == 'url')) {
483 var style = {
484 'type': child.nodeName,
485 'offset': result.description.length
486 };
487 result.descriptionStyles.push(style);
488 arguments.callee(child);
489 style.length = result.description.length - style.offset;
490 continue;
491 }
492
493 // Descend into all other nodes, even if they are unrecognized, for
494 // forward compat.
495 arguments.callee(child);
496 }
497 })(root);
498
499 return result;
500 }
501
502 function setupOmniboxEvents() {
503 chrome.omnibox.onInputChanged.dispatch =
504 function(text, requestId) {
505 var suggestCallback = function(suggestions) {
506 chrome.omnibox.sendSuggestions(requestId, suggestions);
507 };
508 chrome.Event.prototype.dispatch.apply(this, [text, suggestCallback]);
509 };
510 }
511
512 function setupTtsEvents() {
513 chromeHidden.tts = {};
514 chromeHidden.tts.handlers = {};
515 chrome.ttsEngine.onSpeak.dispatch =
516 function(text, options, requestId) {
517 var sendTtsEvent = function(event) {
518 chrome.ttsEngine.sendTtsEvent(requestId, event);
519 };
520 chrome.Event.prototype.dispatch.apply(
521 this, [text, options, sendTtsEvent]);
522 };
523 try {
524 chrome.tts.onEvent.addListener(
525 function(event) {
526 var eventHandler = chromeHidden.tts.handlers[event.srcId];
527 if (eventHandler) {
528 eventHandler({
529 type: event.type,
530 charIndex: event.charIndex,
531 errorMessage: event.errorMessage
532 });
533 if (event.isFinalEvent) {
534 delete chromeHidden.tts.handlers[event.srcId];
535 }
536 }
537 });
538 } catch (e) {
539 // This extension doesn't have permission to access TTS, so we
540 // can safely ignore this.
541 }
542 }
543
544 // Get the platform from navigator.appVersion.
545 function getPlatform() {
546 var platforms = [
547 [/CrOS Touch/, "chromeos touch"],
548 [/CrOS/, "chromeos"],
549 [/Linux/, "linux"],
550 [/Mac/, "mac"],
551 [/Win/, "win"],
552 ];
553
554 for (var i = 0; i < platforms.length; i++) {
555 if (platforms[i][0].test(navigator.appVersion)) {
556 return platforms[i][1];
557 }
558 }
559 return "unknown";
560 }
561
562 chromeHidden.onLoad.addListener(function(extensionId, isExtensionProcess,
563 isIncognitoProcess) {
564 if (!isExtensionProcess)
565 return;
566
567 // Setup the ChromeSetting class so we can use it to construct
568 // ChromeSetting objects from the API definition.
569 setupChromeSetting();
570
571 // Setup the ContentSetting class so we can use it to construct
572 // ContentSetting objects from the API definition.
573 setupContentSetting();
574
575 // |apiFunctions| is a hash of name -> object that stores the
576 // name & definition of the apiFunction. Custom handling of api functions
577 // is implemented by adding a "handleRequest" function to the object.
578 var apiFunctions = {};
579
580 // Read api definitions and setup api functions in the chrome namespace.
581 // TODO(rafaelw): Consider defining a json schema for an api definition
582 // and validating either here, in a unit_test or both.
583 // TODO(rafaelw): Handle synchronous functions.
584 // TOOD(rafaelw): Consider providing some convenient override points
585 // for api functions that wish to insert themselves into the call.
586 var apiDefinitions = chromeHidden.JSON.parse(GetExtensionAPIDefinition());
587 var platform = getPlatform();
588
589 apiDefinitions.forEach(function(apiDef) {
590 // Check platform, if apiDef has platforms key.
591 if (apiDef.platforms && apiDef.platforms.indexOf(platform) == -1) {
592 return;
593 }
594
595 var module = chrome;
596 var namespaces = apiDef.namespace.split('.');
597 for (var index = 0, name; name = namespaces[index]; index++) {
598 module[name] = module[name] || {};
599 module = module[name];
600 }
601
602 // Add types to global validationTypes
603 if (apiDef.types) {
604 apiDef.types.forEach(function(t) {
605 chromeHidden.validationTypes.push(t);
606 if (t.type == 'object' && customBindings[t.id]) {
607 customBindings[t.id].prototype.setSchema(t);
608 }
609 });
610 }
611
612 // Setup Functions.
613 if (apiDef.functions) {
614 apiDef.functions.forEach(function(functionDef) {
615 // Module functions may have been defined earlier by hand. Don't
616 // clobber them.
617 if (module[functionDef.name])
618 return;
619
620 var apiFunction = {};
621 apiFunction.definition = functionDef;
622 apiFunction.name = apiDef.namespace + "." + functionDef.name;
623 apiFunctions[apiFunction.name] = apiFunction;
624
625 module[functionDef.name] = (function() {
626 var args = arguments;
627 if (this.updateArgumentsPreValidate)
628 args = this.updateArgumentsPreValidate.apply(this, args);
629 chromeHidden.validate(args, this.definition.parameters);
630 if (this.updateArgumentsPostValidate)
631 args = this.updateArgumentsPostValidate.apply(this, args);
632
633 var retval;
634 if (this.handleRequest) {
635 retval = this.handleRequest.apply(this, args);
636 } else {
637 retval = sendRequest(this.name, args,
638 this.definition.parameters,
639 {customCallback: this.customCallback});
640 }
641
642 // Validate return value if defined - only in debug.
643 if (chromeHidden.validateCallbacks &&
644 chromeHidden.validate &&
645 this.definition.returns) {
646 chromeHidden.validate([retval], [this.definition.returns]);
647 }
648 return retval;
649 }).bind(apiFunction);
650 });
651 }
652
653 // Setup Events
654 if (apiDef.events) {
655 apiDef.events.forEach(function(eventDef) {
656 // Module events may have been defined earlier by hand. Don't clobber
657 // them.
658 if (module[eventDef.name])
659 return;
660
661 var eventName = apiDef.namespace + "." + eventDef.name;
662 if (apiDef.namespace == "experimental.webRequest") {
663 module[eventDef.name] = new chrome.WebRequestEvent(eventName,
664 eventDef.parameters, eventDef.extraParameters);
665 } else {
666 module[eventDef.name] = new chrome.Event(eventName,
667 eventDef.parameters);
668 }
669 });
670 }
671
672 function addProperties(m, def) {
673 // Parse any values defined for properties.
674 if (def.properties) {
675 forEach(def.properties, function(prop, property) {
676 var value = property.value;
677 if (value) {
678 if (property.type === 'integer') {
679 value = parseInt(value);
680 } else if (property.type === 'boolean') {
681 value = value === "true";
682 } else if (property["$ref"]) {
683 var constructor = customBindings[property["$ref"]];
684 var args = value;
685 // For an object property, |value| is an array of constructor
686 // arguments, but we want to pass the arguments directly
687 // (i.e. not as an array), so we have to fake calling |new| on
688 // the constructor.
689 value = { __proto__: constructor.prototype };
690 constructor.apply(value, args);
691 } else if (property.type === 'object') {
692 // Recursively add properties.
693 addProperties(value, property);
694 } else if (property.type !== 'string') {
695 throw "NOT IMPLEMENTED (extension_api.json error): Cannot " +
696 "parse values for type \"" + property.type + "\"";
697 }
698 }
699 if (value) {
700 m[prop] = value;
701 }
702 });
703 }
704 }
705
706 addProperties(module, apiDef);
707
708 // getTabContentses is retained for backwards compatibility
709 // See http://crbug.com/21433
710 chrome.extension.getTabContentses = chrome.extension.getExtensionTabs;
711 });
712
713 apiFunctions["tabs.connect"].handleRequest = function(tabId, connectInfo) {
714 var name = "";
715 if (connectInfo) {
716 name = connectInfo.name || name;
717 }
718 var portId = OpenChannelToTab(tabId, chromeHidden.extensionId, name);
719 return chromeHidden.Port.createPort(portId, name);
720 };
721
722 apiFunctions["tabs.sendRequest"].handleRequest =
723 function(tabId, request, responseCallback) {
724 var port = chrome.tabs.connect(tabId,
725 {name: chromeHidden.kRequestChannel});
726 port.postMessage(request);
727 port.onDisconnect.addListener(function() {
728 // For onDisconnects, we only notify the callback if there was an error.
729 if (chrome.extension.lastError && responseCallback)
730 responseCallback();
731 });
732 port.onMessage.addListener(function(response) {
733 try {
734 if (responseCallback)
735 responseCallback(response);
736 } finally {
737 port.disconnect();
738 port = null;
739 }
740 });
741 };
742
743 apiFunctions["fileBrowserPrivate.requestLocalFileSystem"].customCallback =
744 function(name, request, response) {
745 var resp = response ? [chromeHidden.JSON.parse(response)] : [];
746 var fs = null;
747 if (!resp[0].error)
748 fs = GetLocalFileSystem(resp[0].name, resp[0].path);
749 if (request.callback)
750 request.callback(fs);
751 request.callback = null;
752 };
753
754 apiFunctions["chromePrivate.decodeJPEG"].handleRequest =
755 function(jpeg_image) {
756 return DecodeJPEG(jpeg_image);
757 };
758
759 apiFunctions["extension.getViews"].handleRequest = function(properties) {
760 var windowId = -1;
761 var type = "ALL";
762 if (typeof(properties) != "undefined") {
763 if (typeof(properties.type) != "undefined") {
764 type = properties.type;
765 }
766 if (typeof(properties.windowId) != "undefined") {
767 windowId = properties.windowId;
768 }
769 }
770 return GetExtensionViews(windowId, type) || null;
771 };
772
773 apiFunctions["extension.getBackgroundPage"].handleRequest = function() {
774 return GetExtensionViews(-1, "BACKGROUND")[0] || null;
775 };
776
777 apiFunctions["extension.getToolstrips"].handleRequest =
778 function(windowId) {
779 if (typeof(windowId) == "undefined")
780 windowId = -1;
781 return GetExtensionViews(windowId, "TOOLSTRIP");
782 };
783
784 apiFunctions["extension.getExtensionTabs"].handleRequest =
785 function(windowId) {
786 if (typeof(windowId) == "undefined")
787 windowId = -1;
788 return GetExtensionViews(windowId, "TAB");
789 };
790
791 apiFunctions["devtools.getTabEvents"].handleRequest = function(tabId) {
792 var tabIdProxy = {};
793 var functions = ["onPageEvent", "onTabClose"];
794 functions.forEach(function(name) {
795 // Event disambiguation is handled by name munging. See
796 // chrome/browser/extensions/extension_devtools_events.h for the C++
797 // equivalent of this logic.
798 tabIdProxy[name] = new chrome.Event("devtools." + tabId + "." + name);
799 });
800 return tabIdProxy;
801 };
802
803 var canvas;
804 function setIconCommon(details, name, parameters, actionType, iconSize,
805 nativeFunction) {
806 if ("iconIndex" in details) {
807 sendRequest(name, [details], parameters);
808 } else if ("imageData" in details) {
809 // Verify that this at least looks like an ImageData element.
810 // Unfortunately, we cannot use instanceof because the ImageData
811 // constructor is not public.
812 //
813 // We do this manually instead of using JSONSchema to avoid having these
814 // properties show up in the doc.
815 if (!("width" in details.imageData) ||
816 !("height" in details.imageData) ||
817 !("data" in details.imageData)) {
818 throw new Error(
819 "The imageData property must contain an ImageData object.");
820 }
821
822 if (details.imageData.width > iconSize ||
823 details.imageData.height > iconSize) {
824 throw new Error(
825 "The imageData property must contain an ImageData object that " +
826 "is no larger than " + iconSize + " pixels square.");
827 }
828
829 sendRequest(name, [details], parameters,
830 {noStringify: true, nativeFunction: nativeFunction});
831 } else if ("path" in details) {
832 var img = new Image();
833 img.onerror = function() {
834 console.error("Could not load " + actionType + " icon '" +
835 details.path + "'.");
836 };
837 img.onload = function() {
838 var canvas = document.createElement("canvas");
839 canvas.width = img.width > iconSize ? iconSize : img.width;
840 canvas.height = img.height > iconSize ? iconSize : img.height;
841
842 var canvas_context = canvas.getContext('2d');
843 canvas_context.clearRect(0, 0, canvas.width, canvas.height);
844 canvas_context.drawImage(img, 0, 0, canvas.width, canvas.height);
845 delete details.path;
846 details.imageData = canvas_context.getImageData(0, 0, canvas.width,
847 canvas.height);
848 sendRequest(name, [details], parameters,
849 {noStringify: true, nativeFunction: nativeFunction});
850 };
851 img.src = details.path;
852 } else {
853 throw new Error(
854 "Either the path or imageData property must be specified.");
855 }
856 }
857
858 function setExtensionActionIconCommon(details, name, parameters,
859 actionType) {
860 var EXTENSION_ACTION_ICON_SIZE = 19;
861 setIconCommon(details, name, parameters, actionType,
862 EXTENSION_ACTION_ICON_SIZE, SetIconCommon);
863 }
864
865 apiFunctions["browserAction.setIcon"].handleRequest = function(details) {
866 setExtensionActionIconCommon(
867 details, this.name, this.definition.parameters, "browser action");
868 };
869
870 apiFunctions["pageAction.setIcon"].handleRequest = function(details) {
871 setExtensionActionIconCommon(
872 details, this.name, this.definition.parameters, "page action");
873 };
874
875 apiFunctions["experimental.sidebar.setIcon"].handleRequest =
876 function(details) {
877 var SIDEBAR_ICON_SIZE = 16;
878 setIconCommon(
879 details, this.name, this.definition.parameters, "sidebar",
880 SIDEBAR_ICON_SIZE, SetIconCommon);
881 };
882
883 apiFunctions["contextMenus.create"].handleRequest =
884 function() {
885 var args = arguments;
886 var id = GetNextContextMenuId();
887 args[0].generatedId = id;
888 sendRequest(this.name, args, this.definition.parameters,
889 {customCallback: this.customCallback});
890 return id;
891 };
892
893 apiFunctions["omnibox.setDefaultSuggestion"].handleRequest =
894 function(details) {
895 var parseResult = parseOmniboxDescription(details.description);
896 sendRequest(this.name, [parseResult], this.definition.parameters);
897 };
898
899 apiFunctions["experimental.webRequest.addEventListener"].handleRequest =
900 function() {
901 var args = Array.prototype.slice.call(arguments);
902 sendRequest(this.name, args, this.definition.parameters,
903 {forIOThread: true});
904 };
905
906 apiFunctions["experimental.webRequest.eventHandled"].handleRequest =
907 function() {
908 var args = Array.prototype.slice.call(arguments);
909 sendRequest(this.name, args, this.definition.parameters,
910 {forIOThread: true});
911 };
912
913 apiFunctions["contextMenus.create"].customCallback =
914 function(name, request, response) {
915 if (chrome.extension.lastError) {
916 return;
917 }
918
919 var id = request.args[0].generatedId;
920
921 // Set up the onclick handler if we were passed one in the request.
922 var onclick = request.args.length ? request.args[0].onclick : null;
923 if (onclick) {
924 chromeHidden.contextMenus.ensureListenerSetup();
925 chromeHidden.contextMenus.handlers[id] = onclick;
926 }
927 };
928
929 apiFunctions["contextMenus.remove"].customCallback =
930 function(name, request, response) {
931 if (chrome.extension.lastError) {
932 return;
933 }
934 var id = request.args[0];
935 delete chromeHidden.contextMenus.handlers[id];
936 };
937
938 apiFunctions["contextMenus.update"].customCallback =
939 function(name, request, response) {
940 if (chrome.extension.lastError) {
941 return;
942 }
943 var id = request.args[0];
944 if (request.args[1].onclick) {
945 chromeHidden.contextMenus.handlers[id] = request.args[1].onclick;
946 }
947 };
948
949 apiFunctions["contextMenus.removeAll"].customCallback =
950 function(name, request, response) {
951 if (chrome.extension.lastError) {
952 return;
953 }
954 chromeHidden.contextMenus.handlers = {};
955 };
956
957 apiFunctions["tabs.captureVisibleTab"].updateArgumentsPreValidate =
958 function() {
959 // Old signature:
960 // captureVisibleTab(int windowId, function callback);
961 // New signature:
962 // captureVisibleTab(int windowId, object details, function callback);
963 //
964 // TODO(skerner): The next step to omitting optional arguments is the
965 // replacement of this code with code that matches arguments by type.
966 // Once this is working for captureVisibleTab() it can be enabled for
967 // the rest of the API. See crbug/29215 .
968 if (arguments.length == 2 && typeof(arguments[1]) == "function") {
969 // If the old signature is used, add a null details object.
970 newArgs = [arguments[0], null, arguments[1]];
971 } else {
972 newArgs = arguments;
973 }
974 return newArgs;
975 };
976
977 apiFunctions["omnibox.sendSuggestions"].updateArgumentsPostValidate =
978 function(requestId, userSuggestions) {
979 var suggestions = [];
980 for (var i = 0; i < userSuggestions.length; i++) {
981 var parseResult = parseOmniboxDescription(
982 userSuggestions[i].description);
983 parseResult.content = userSuggestions[i].content;
984 suggestions.push(parseResult);
985 }
986 return [requestId, suggestions];
987 };
988
989 apiFunctions["tts.speak"].handleRequest = function() {
990 var args = arguments;
991 if (args.length > 1 && args[1] && args[1].onEvent) {
992 var id = GetNextTtsEventId();
993 args[1].srcId = id;
994 chromeHidden.tts.handlers[id] = args[1].onEvent;
995 }
996 sendRequest(this.name, args, this.definition.parameters);
997 return id;
998 };
999
1000 if (chrome.test) {
1001 chrome.test.getApiDefinitions = GetExtensionAPIDefinition;
1002 }
1003
1004 setupPageActionEvents(extensionId);
1005 setupToolstripEvents(GetRenderViewId());
1006 setupHiddenContextMenuEvent(extensionId);
1007 setupInputEvents();
1008 setupOmniboxEvents();
1009 setupTtsEvents();
1010 });
1011
1012 if (!chrome.experimental)
1013 chrome.experimental = {};
1014
1015 if (!chrome.experimental.accessibility)
1016 chrome.experimental.accessibility = {};
1017
1018 if (!chrome.tts)
1019 chrome.tts = {};
1020
1021 if (!chrome.ttsEngine)
1022 chrome.ttsEngine = {};
1023
1024 if (!chrome.experimental.downloads)
1025 chrome.experimental.downloads = {};
1026 })();
OLDNEW
« no previous file with comments | « chrome/renderer/resources/extension_apitest.js ('k') | chrome/renderer/resources/extensions/apitest.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698