;
?9
W
J
f
A
0
h3
}
ݷ
(
%
v
4
q
T#
(
*
/
B
-E
UI
P
-n
ڂ
}
-
i
_
7
(
w
-'
Y6
8
F
VI
N
U
]
*e
g
m
y t u _ Ē l [ ɱ X [ 1 ~ 1 = . ' " $ + z3 U8 6E H K N R R T V ` c d 1j o p p q q r 1s s t t #u u v w x uy y ^z { { | } [~ 0 Ã w L ! ܹ j 1 ݿ E ` E T y Y 7 j x 2 f W 9 y " s
O
/ t C W & k : N ! " # $b % & '1 (v ) * +E , - . /Y 0 1 25 3z 4 5 6L 7 8 9' :v ; < =L > ? @/ A{ B C D\ E F G< H I J Ki L M NI O P Q* Ru S T UY V W X7 Y Z [ \O ]E ^< _ ` a b ! ! O" " " # #$ $ % , 4 : H; ; ; A< < 0= = > ? ?
@ @ A A B B -C D D E RF F G mH [I "J J K L 8M M N ~O aP yQ R R S >U ^V SW X X kY
Z P[ "\ w\ \ "] ] ] \^ ^ _ d_ _ _ 0` {` ` a Xa a a [b b Sc c c Fd d -e e e =f f f Vg g
h `h h i pi i Pj j \k k l l l m Km m m In
n n o
o Up p Eq q q r s s s Pt t "u u v jv v &w pw w !x "^x #x $/z %{ &Ɂ ' (2 )͆ *p +Ƈ ,{ - .4 / 0 1 2ڌ 3D 4 5 6p 7ߎ 84 9ɐ : ;9 <Ē =R > ? @1 A B> C> D E F< Gx Hb I= J K8 Lê Mp N Oj P֬ QD R S% T U Vj Wϯ X Y Z [C \ ]! ^n _ڲ `F a b cR d e f5 g h i j k l9 m n o px qP r s tC u v w x y' zn { |@ } ~~
U
:
p R { K & 2 # g z M !p " # $K % & ' (c ) * +b , -D . /N 0- 1 2w 3 4% 5 6 7; 8 9D : ;" <e = > ?1 @ A Bo C D Eh F GB H I J K L Ma
N
O P Q RC S Ty U- V W X d o ' " # % ) w+ , - ^. N/ / 0 0 1 1 #2 2 2 J3 3 3 <4 4 5 5 5 ;6 6 6 7 28 8 : X; YZ? Z? [@ \1A ]B ^D _E `F acG bH c3I dK ePL fN g@Q hV iMW jmX kyY lY mMZ nZ o![ py[ q[ r7\ s\ t\ uJ] v] w] x_^ y^ z_ {[_ |_ }
` ~d` ` $a b b {c c rd ,e e e -f xf f Cg g 5h h i k m ^p q u ;w y | ~ φ d ' Ȑ - @ > K h ۛ 8
\ J ؟ 3 } U ڦ " r § ب _ u G B ޯ ) ذ & 2 ִ % % C B ( v $ C E x @ _ c
d K X ! " # $y %O ' ( )\ * + ,X - 9 :3 ; <7 AND BF CF D3G EG FG GIH HH IH JaI KI LJ MvJ NJ O/K PK QK R?L SL TM UXM VM WN XtN YN Z0O [O \O ]FP ^JR _lU aUW dY eOZ f9[ g[ h7\ i^ j` kb lGc nyf oTh ph qNi vi {l |m }~m ~m ?n n o Uo o Ap q q r r Ks t v v "x
d#
w$
z%
&
&
%'
'
(
H(
(
(
h)
*
*
$+
h+
+
7,
,
,
D-
-
.
.
/
0
3
3
Z5
5
6
7
7
Z8
8
8
D9
9
9
L:
:
;
u<
<
B=
=
=
O>
>
?
?
{@
B
C
*D
D
xJ
O
U
aZ
^
b
Fg
i
j
k
vl
l
0m
m
m
Ln
n
9p
r
}u
z
z
˸
V
m
7
C
p
f (
2 d< D M 3X `
i n 5w
}x z| | 6} } ~ O~ ~ ~ 1 ? ׁ # s !Ă " #| $ %2 &~ 'ʄ ( )f * +( , - . / 04 1 2 3x 4 5b 6 7 8Nj 9 :c ;͌ <R =ݍ >8 ?ǎ @W A B C8 D E1 F G H` I J K3 Lx M N OM P Qޔ R' Sp T U VB W XԖ Y Zf [ \ ]> ^ _ʘ ` a\ K ά & ; κ 8 M Y 8 6 w e k # . S q W R T G
W 3 m o l A f < ( l " A# # #
$ Z$ $ n' r) ) X* * %+ x+ + t, , - A/ Y0 0 1 %2 2 X3 3 }4 Y7 G9
8F KH K N O qP 2Q Q R RgmR Sgc Tgp Ug7q vq v6t v)w vw v+z v| v} v~ v: v; v v v6 v v
// Copyright 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.
var chrome;
if (!chrome)
chrome = {};
if (!chrome.embeddedSearch) {
chrome.embeddedSearch = new function() {
this.searchBox = new function() {
// =======================================================================
// Private functions
// =======================================================================
native function Focus();
native function GetDisplayInstantResults();
native function GetMostVisitedItemData();
native function GetQuery();
native function GetSearchRequestParams();
native function GetRightToLeft();
native function GetStartMargin();
native function GetSuggestionToPrefetch();
native function IsFocused();
native function IsKeyCaptureEnabled();
native function Paste();
native function StartCapturingKeyStrokes();
native function StopCapturingKeyStrokes();
// =======================================================================
// Exported functions
// =======================================================================
this.__defineGetter__('displayInstantResults', GetDisplayInstantResults);
this.__defineGetter__('isFocused', IsFocused);
this.__defineGetter__('isKeyCaptureEnabled', IsKeyCaptureEnabled);
this.__defineGetter__('rtl', GetRightToLeft);
this.__defineGetter__('startMargin', GetStartMargin);
this.__defineGetter__('suggestion', GetSuggestionToPrefetch);
this.__defineGetter__('value', GetQuery);
Object.defineProperty(this, 'requestParams',
{ get: GetSearchRequestParams });
this.focus = function() {
Focus();
};
// This method is restricted to chrome-search://most-visited pages by
// checking the invoking context's origin in searchbox_extension.cc.
this.getMostVisitedItemData = function(restrictedId) {
var item = GetMostVisitedItemData(restrictedId);
if (item) {
var sizeInPx = Math.floor(48 * window.devicePixelRatio + 0.5);
// Populate large icon and fallback icon data, if they exist. We'll
// render everything here, once these become available by default.
if (item.largeIconUrl) {
item.largeIconUrl +=
sizeInPx + "/" + item.renderViewId + "/" + item.rid;
}
if (item.fallbackIconUrl) {
item.fallbackIconUrl +=
sizeInPx + ",,,,/" + item.renderViewId + "/" + item.rid;
}
}
return item;
};
this.paste = function(value) {
Paste(value);
};
this.startCapturingKeyStrokes = function() {
StartCapturingKeyStrokes();
};
this.stopCapturingKeyStrokes = function() {
StopCapturingKeyStrokes();
};
this.onfocuschange = null;
this.onkeycapturechange = null;
this.onmarginchange = null;
this.onsubmit = null;
this.onsuggestionchange = null;
//TODO(jered): Remove this empty method when google no longer requires it.
this.setRestrictedValue = function() {};
};
this.newTabPage = new function() {
// =======================================================================
// Private functions
// =======================================================================
native function CheckIsUserSignedInToChromeAs();
native function CheckIsUserSyncingHistory();
native function DeleteMostVisitedItem();
native function GetAppLauncherEnabled();
native function GetDispositionFromClick();
native function GetMostVisitedItems();
native function GetThemeBackgroundInfo();
native function IsInputInProgress();
native function LogEvent();
native function LogMostVisitedImpression();
native function LogMostVisitedNavigation();
native function NavigateContentWindow();
native function UndoAllMostVisitedDeletions();
native function UndoMostVisitedDeletion();
function GetMostVisitedItemsWrapper() {
var mostVisitedItems = GetMostVisitedItems();
for (var i = 0, item; item = mostVisitedItems[i]; ++i) {
item.faviconUrl = GenerateFaviconURL(item.renderViewId, item.rid);
// These properties are private data and should not be returned to
// the page. They are only accessible via getMostVisitedItemData().
delete item.url;
delete item.title;
delete item.domain;
delete item.direction;
delete item.renderViewId;
delete item.largeIconUrl;
delete item.fallbackIconUrl;
}
return mostVisitedItems;
}
function GenerateFaviconURL(renderViewId, rid) {
return "chrome-search://favicon/size/16@" +
window.devicePixelRatio + "x/" +
renderViewId + "/" + rid;
}
// =======================================================================
// Exported functions
// =======================================================================
this.__defineGetter__('appLauncherEnabled', GetAppLauncherEnabled);
this.__defineGetter__('isInputInProgress', IsInputInProgress);
this.__defineGetter__('mostVisited', GetMostVisitedItemsWrapper);
this.__defineGetter__('themeBackgroundInfo', GetThemeBackgroundInfo);
this.deleteMostVisitedItem = function(restrictedId) {
DeleteMostVisitedItem(restrictedId);
};
this.getDispositionFromClick = function(middle_button,
alt_key,
ctrl_key,
meta_key,
shift_key) {
return GetDispositionFromClick(middle_button,
alt_key,
ctrl_key,
meta_key,
shift_key);
};
this.checkIsUserSignedIntoChromeAs = function(identity) {
CheckIsUserSignedInToChromeAs(identity);
};
this.checkIsUserSyncingHistory = function() {
CheckIsUserSyncingHistory();
};
// This method is restricted to chrome-search://most-visited pages by
// checking the invoking context's origin in searchbox_extension.cc.
this.logEvent = function(histogram_name) {
LogEvent(histogram_name);
};
// This method is restricted to chrome-search://most-visited pages by
// checking the invoking context's origin in searchbox_extension.cc.
this.logMostVisitedImpression = function(position, provider) {
LogMostVisitedImpression(position, provider);
};
// This method is restricted to chrome-search://most-visited pages by
// checking the invoking context's origin in searchbox_extension.cc.
this.logMostVisitedNavigation = function(position, provider) {
LogMostVisitedNavigation(position, provider);
};
this.navigateContentWindow = function(destination, disposition) {
NavigateContentWindow(destination, disposition);
};
this.undoAllMostVisitedDeletions = function() {
UndoAllMostVisitedDeletions();
};
this.undoMostVisitedDeletion = function(restrictedId) {
UndoMostVisitedDeletion(restrictedId);
};
this.onsignedincheckdone = null;
this.onhistorysynccheckdone = null;
this.oninputcancel = null;
this.oninputstart = null;
this.onmostvisitedchange = null;
this.onthemechange = null;
};
// TODO(jered): Remove when google no longer expects this object.
chrome.searchBox = this.searchBox;
};
}
// 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.
// Custom binding for the app API.
var GetAvailability = requireNative('v8_context').GetAvailability;
if (!GetAvailability('app').is_available) {
exports.binding = {};
exports.onInstallStateResponse = function(){};
return;
}
var appNatives = requireNative('app');
var process = requireNative('process');
var extensionId = process.GetExtensionId();
var logActivity = requireNative('activityLogger');
function wrapForLogging(fun) {
if (!extensionId)
return fun; // nothing interesting to log without an extension
return function() {
// TODO(ataly): We need to make sure we use the right prototype for
// fun.apply. Array slice can either be rewritten or similarly defined.
logActivity.LogAPICall(extensionId, "app." + fun.name,
$Array.slice(arguments));
return $Function.apply(fun, this, arguments);
};
}
// This becomes chrome.app
var app = {
getIsInstalled: wrapForLogging(appNatives.GetIsInstalled),
getDetails: wrapForLogging(appNatives.GetDetails),
runningState: wrapForLogging(appNatives.GetRunningState)
};
// Tricky; "getIsInstalled" is actually exposed as the getter "isInstalled",
// but we don't have a way to express this in the schema JSON (nor is it
// worth it for this one special case).
//
// So, define it manually, and let the getIsInstalled function act as its
// documentation.
app.__defineGetter__('isInstalled', wrapForLogging(appNatives.GetIsInstalled));
// Called by app_bindings.cc.
function onInstallStateResponse(state, callbackId) {
var callback = callbacks[callbackId];
delete callbacks[callbackId];
if (typeof(callback) == 'function') {
try {
callback(state);
} catch (e) {
console.error('Exception in chrome.app.installState response handler: ' +
e.stack);
}
}
}
// TODO(kalman): move this stuff to its own custom bindings.
var callbacks = {};
var nextCallbackId = 1;
app.installState = function getInstallState(callback) {
var callbackId = nextCallbackId++;
callbacks[callbackId] = callback;
appNatives.GetInstallState(callbackId);
};
if (extensionId)
app.installState = wrapForLogging(app.installState);
exports.binding = app;
exports.onInstallStateResponse = onInstallStateResponse;
// 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.
// Custom bindings for the automation API.
var AutomationNode = require('automationNode').AutomationNode;
var AutomationRootNode = require('automationNode').AutomationRootNode;
var automation = require('binding').Binding.create('automation');
var automationInternal =
require('binding').Binding.create('automationInternal').generate();
var eventBindings = require('event_bindings');
var Event = eventBindings.Event;
var exceptionHandler = require('uncaught_exception_handler');
var forEach = require('utils').forEach;
var lastError = require('lastError');
var logging = requireNative('logging');
var nativeAutomationInternal = requireNative('automationInternal');
var GetRoutingID = nativeAutomationInternal.GetRoutingID;
var GetSchemaAdditions = nativeAutomationInternal.GetSchemaAdditions;
var DestroyAccessibilityTree =
nativeAutomationInternal.DestroyAccessibilityTree;
var GetIntAttribute = nativeAutomationInternal.GetIntAttribute;
var StartCachingAccessibilityTrees =
nativeAutomationInternal.StartCachingAccessibilityTrees;
var schema = GetSchemaAdditions();
/**
* A namespace to export utility functions to other files in automation.
*/
window.automationUtil = function() {};
// TODO(aboxhall): Look into using WeakMap
var idToCallback = {};
var DESKTOP_TREE_ID = 0;
automationUtil.storeTreeCallback = function(id, callback) {
if (!callback)
return;
var targetTree = AutomationRootNode.get(id);
if (!targetTree) {
// If we haven't cached the tree, hold the callback until the tree is
// populated by the initial onAccessibilityEvent call.
if (id in idToCallback)
idToCallback[id].push(callback);
else
idToCallback[id] = [callback];
} else {
callback(targetTree);
}
};
/**
* Global list of tree change observers.
* @type {Array}
*/
automationUtil.treeChangeObservers = [];
automation.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
// TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj.
apiFunctions.setHandleRequest('getTree', function getTree(tabID, callback) {
var routingID = GetRoutingID();
StartCachingAccessibilityTrees();
// enableTab() ensures the renderer for the active or specified tab has
// accessibility enabled, and fetches its ax tree id to use as
// a key in the idToAutomationRootNode map. The callback to
// enableTab is bound to the callback passed in to getTree(), so that once
// the tree is available (either due to having been cached earlier, or after
// an accessibility event occurs which causes the tree to be populated), the
// callback can be called.
var params = { routingID: routingID, tabID: tabID };
automationInternal.enableTab(params,
function onEnable(id) {
if (lastError.hasError(chrome)) {
callback();
return;
}
automationUtil.storeTreeCallback(id, callback);
});
});
var desktopTree = null;
apiFunctions.setHandleRequest('getDesktop', function(callback) {
StartCachingAccessibilityTrees();
desktopTree = AutomationRootNode.get(DESKTOP_TREE_ID);
if (!desktopTree) {
if (DESKTOP_TREE_ID in idToCallback)
idToCallback[DESKTOP_TREE_ID].push(callback);
else
idToCallback[DESKTOP_TREE_ID] = [callback];
var routingID = GetRoutingID();
// TODO(dtseng): Disable desktop tree once desktop object goes out of
// scope.
automationInternal.enableDesktop(routingID, function() {
if (lastError.hasError(chrome)) {
AutomationRootNode.destroy(DESKTOP_TREE_ID);
callback();
return;
}
});
} else {
callback(desktopTree);
}
});
function removeTreeChangeObserver(observer) {
var observers = automationUtil.treeChangeObservers;
for (var i = 0; i < observers.length; i++) {
if (observer == observers[i])
observers.splice(i, 1);
}
}
apiFunctions.setHandleRequest('removeTreeChangeObserver', function(observer) {
removeTreeChangeObserver(observer);
});
function addTreeChangeObserver(observer) {
removeTreeChangeObserver(observer);
automationUtil.treeChangeObservers.push(observer);
}
apiFunctions.setHandleRequest('addTreeChangeObserver', function(observer) {
addTreeChangeObserver(observer);
});
apiFunctions.setHandleRequest('setDocumentSelection', function(params) {
var anchorNodeImpl = privates(params.anchorObject).impl;
var focusNodeImpl = privates(params.focusObject).impl;
if (anchorNodeImpl.treeID !== focusNodeImpl.treeID)
throw new Error('Selection anchor and focus must be in the same tree.');
if (anchorNodeImpl.treeID === DESKTOP_TREE_ID) {
throw new Error('Use AutomationNode.setSelection to set the selection ' +
'in the desktop tree.');
}
automationInternal.performAction({ treeID: anchorNodeImpl.treeID,
automationNodeID: anchorNodeImpl.id,
actionType: 'setSelection'},
{ focusNodeID: focusNodeImpl.id,
anchorOffset: params.anchorOffset,
focusOffset: params.focusOffset });
});
});
automationInternal.onTreeChange.addListener(function(treeID,
nodeID,
changeType) {
var tree = AutomationRootNode.getOrCreate(treeID);
if (!tree)
return;
var node = privates(tree).impl.get(nodeID);
if (!node)
return;
if (node.role == 'webView' || node.role == 'embeddedObject') {
// A WebView in the desktop tree has a different AX tree as its child.
// When we encounter a WebView with a child AX tree id that we don't
// currently have cached, explicitly request that AX tree from the
// browser process and set up a callback when it loads to attach that
// tree as a child of this node and fire appropriate events.
var childTreeID = GetIntAttribute(treeID, nodeID, 'childTreeId');
if (!childTreeID)
return;
var subroot = AutomationRootNode.get(childTreeID);
if (!subroot) {
automationUtil.storeTreeCallback(childTreeID, function(root) {
privates(root).impl.setHostNode(node);
if (root.docLoaded)
privates(root).impl.dispatchEvent(schema.EventType.loadComplete);
privates(node).impl.dispatchEvent(schema.EventType.childrenChanged);
});
automationInternal.enableFrame(childTreeID);
} else {
privates(subroot).impl.setHostNode(node);
}
}
var treeChange = {target: node, type: changeType};
// Make a copy of the observers in case one of these callbacks tries
// to change the list of observers.
var observers = automationUtil.treeChangeObservers.slice();
for (var i = 0; i < observers.length; i++) {
try {
observers[i](treeChange);
} catch (e) {
exceptionHandler.handle('Error in tree change observer for ' +
treeChange.type, e);
}
}
if (changeType == schema.TreeChangeType.nodeRemoved) {
privates(tree).impl.remove(nodeID);
}
});
// Listen to the automationInternal.onAccessibilityEvent event, which is
// essentially a proxy for the AccessibilityHostMsg_Events IPC from the
// renderer.
automationInternal.onAccessibilityEvent.addListener(function(data) {
var id = data.treeID;
var targetTree = AutomationRootNode.getOrCreate(id);
if (!privates(targetTree).impl.onAccessibilityEvent(data))
return;
// If we're not waiting on a callback to getTree(), we can early out here.
if (!(id in idToCallback))
return;
// We usually get a 'placeholder' tree first, which doesn't have any url
// attribute or child nodes. If we've got that, wait for the full tree before
// calling the callback.
// TODO(dmazzoni): Don't send down placeholder (crbug.com/397553)
if (id != DESKTOP_TREE_ID && !targetTree.url &&
targetTree.children.length == 0)
return;
// If the tree wasn't available when getTree() was called, the callback will
// have been cached in idToCallback, so call and delete it now that we
// have the complete tree.
for (var i = 0; i < idToCallback[id].length; i++) {
var callback = idToCallback[id][i];
callback(targetTree);
}
delete idToCallback[id];
});
automationInternal.onAccessibilityTreeDestroyed.addListener(function(id) {
// Destroy the AutomationRootNode.
var targetTree = AutomationRootNode.get(id);
if (targetTree) {
privates(targetTree).impl.destroy();
AutomationRootNode.destroy(id);
} else {
logging.WARNING('no targetTree to destroy');
}
// Destroy the native cache of the accessibility tree.
DestroyAccessibilityTree(id);
});
exports.binding = automation.generate();
// Add additional accessibility bindings not specified in the automation IDL.
// Accessibility and automation share some APIs (see
// ui/accessibility/ax_enums.idl).
forEach(schema, function(k, v) {
exports.binding[k] = v;
});
// 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.
var utils = require('utils');
var AutomationEventImpl = function(type, target) {
this.propagationStopped = false;
// TODO(aboxhall): make these read-only properties
this.type = type;
this.target = target;
this.eventPhase = Event.NONE;
};
AutomationEventImpl.prototype = {
stopPropagation: function() {
this.propagationStopped = true;
}
};
exports.AutomationEvent = utils.expose(
'AutomationEvent',
AutomationEventImpl,
{ functions: ['stopPropagation'],
readonly: ['type', 'target', 'eventPhase'] });
// 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.
var AutomationEvent = require('automationEvent').AutomationEvent;
var automationInternal =
require('binding').Binding.create('automationInternal').generate();
var exceptionHandler = require('uncaught_exception_handler');
var IsInteractPermitted =
requireNative('automationInternal').IsInteractPermitted;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @return {?number} The id of the root node.
*/
var GetRootID = requireNative('automationInternal').GetRootID;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @return {?string} The title of the document.
*/
var GetDocTitle = requireNative('automationInternal').GetDocTitle;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @return {?string} The url of the document.
*/
var GetDocURL = requireNative('automationInternal').GetDocURL;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @return {?boolean} True if the document has finished loading.
*/
var GetDocLoaded = requireNative('automationInternal').GetDocLoaded;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @return {?number} The loading progress, from 0.0 to 1.0 (fully loaded).
*/
var GetDocLoadingProgress =
requireNative('automationInternal').GetDocLoadingProgress;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @return {?number} The ID of the selection anchor object.
*/
var GetAnchorObjectID = requireNative('automationInternal').GetAnchorObjectID;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @return {?number} The selection anchor offset.
*/
var GetAnchorOffset = requireNative('automationInternal').GetAnchorOffset;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @return {?number} The ID of the selection focus object.
*/
var GetFocusObjectID = requireNative('automationInternal').GetFocusObjectID;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @return {?number} The selection focus offset.
*/
var GetFocusOffset = requireNative('automationInternal').GetFocusOffset;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?number} The id of the node's parent, or undefined if it's the
* root of its tree or if the tree or node wasn't found.
*/
var GetParentID = requireNative('automationInternal').GetParentID;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?number} The number of children of the node, or undefined if
* the tree or node wasn't found.
*/
var GetChildCount = requireNative('automationInternal').GetChildCount;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {number} childIndex An index of a child of this node.
* @return {?number} The id of the child at the given index, or undefined
* if the tree or node or child at that index wasn't found.
*/
var GetChildIDAtIndex = requireNative('automationInternal').GetChildIDAtIndex;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?number} The index of this node in its parent, or undefined if
* the tree or node or node parent wasn't found.
*/
var GetIndexInParent = requireNative('automationInternal').GetIndexInParent;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?Object} An object with a string key for every state flag set,
* or undefined if the tree or node or node parent wasn't found.
*/
var GetState = requireNative('automationInternal').GetState;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {string} The role of the node, or undefined if the tree or
* node wasn't found.
*/
var GetRole = requireNative('automationInternal').GetRole;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?automation.Rect} The location of the node, or undefined if
* the tree or node wasn't found.
*/
var GetLocation = requireNative('automationInternal').GetLocation;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of a string attribute.
* @return {?string} The value of this attribute, or undefined if the tree,
* node, or attribute wasn't found.
*/
var GetStringAttribute = requireNative('automationInternal').GetStringAttribute;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute.
* @return {?boolean} The value of this attribute, or undefined if the tree,
* node, or attribute wasn't found.
*/
var GetBoolAttribute = requireNative('automationInternal').GetBoolAttribute;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute.
* @return {?number} The value of this attribute, or undefined if the tree,
* node, or attribute wasn't found.
*/
var GetIntAttribute = requireNative('automationInternal').GetIntAttribute;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute.
* @return {?number} The value of this attribute, or undefined if the tree,
* node, or attribute wasn't found.
*/
var GetFloatAttribute = requireNative('automationInternal').GetFloatAttribute;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute.
* @return {?Array.} The value of this attribute, or undefined
* if the tree, node, or attribute wasn't found.
*/
var GetIntListAttribute =
requireNative('automationInternal').GetIntListAttribute;
/**
* @param {number} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an HTML attribute.
* @return {?string} The value of this attribute, or undefined if the tree,
* node, or attribute wasn't found.
*/
var GetHtmlAttribute = requireNative('automationInternal').GetHtmlAttribute;
var lastError = require('lastError');
var logging = requireNative('logging');
var schema = requireNative('automationInternal').GetSchemaAdditions();
var utils = require('utils');
/**
* A single node in the Automation tree.
* @param {AutomationRootNodeImpl} root The root of the tree.
* @constructor
*/
function AutomationNodeImpl(root) {
this.rootImpl = root;
// Public attributes. No actual data gets set on this object.
this.listeners = {};
}
AutomationNodeImpl.prototype = {
treeID: -1,
id: -1,
role: '',
state: { busy: true },
isRootNode: false,
get root() {
return this.rootImpl.wrapper;
},
get parent() {
if (this.hostNode_)
return this.hostNode_;
var parentID = GetParentID(this.treeID, this.id);
return this.rootImpl.get(parentID);
},
get state() {
return GetState(this.treeID, this.id);
},
get role() {
return GetRole(this.treeID, this.id);
},
get location() {
return GetLocation(this.treeID, this.id);
},
get indexInParent() {
return GetIndexInParent(this.treeID, this.id);
},
get childTree() {
var childTreeID = GetIntAttribute(this.treeID, this.id, 'childTreeId');
if (childTreeID)
return AutomationRootNodeImpl.get(childTreeID);
},
get firstChild() {
if (this.childTree)
return this.childTree;
if (!GetChildCount(this.treeID, this.id))
return undefined;
var firstChildID = GetChildIDAtIndex(this.treeID, this.id, 0);
return this.rootImpl.get(firstChildID);
},
get lastChild() {
if (this.childTree)
return this.childTree;
var count = GetChildCount(this.treeID, this.id);
if (!count)
return undefined;
var lastChildID = GetChildIDAtIndex(this.treeID, this.id, count - 1);
return this.rootImpl.get(lastChildID);
},
get children() {
if (this.childTree)
return [this.childTree];
var children = [];
var count = GetChildCount(this.treeID, this.id);
for (var i = 0; i < count; ++i) {
var childID = GetChildIDAtIndex(this.treeID, this.id, i);
var child = this.rootImpl.get(childID);
children.push(child);
}
return children;
},
get previousSibling() {
var parent = this.parent;
var indexInParent = GetIndexInParent(this.treeID, this.id);
if (parent && indexInParent > 0)
return parent.children[indexInParent - 1];
return undefined;
},
get nextSibling() {
var parent = this.parent;
var indexInParent = GetIndexInParent(this.treeID, this.id);
if (parent && indexInParent < parent.children.length)
return parent.children[indexInParent + 1];
return undefined;
},
doDefault: function() {
this.performAction_('doDefault');
},
focus: function() {
this.performAction_('focus');
},
makeVisible: function() {
this.performAction_('makeVisible');
},
setSelection: function(startIndex, endIndex) {
if (this.role == 'textField' || this.role == 'textBox') {
this.performAction_('setSelection',
{ focusNodeID: this.id,
anchorOffset: startIndex,
focusOffset: endIndex });
}
},
showContextMenu: function() {
this.performAction_('showContextMenu');
},
domQuerySelector: function(selector, callback) {
automationInternal.querySelector(
{ treeID: this.rootImpl.treeID,
automationNodeID: this.id,
selector: selector },
this.domQuerySelectorCallback_.bind(this, callback));
},
find: function(params) {
return this.findInternal_(params);
},
findAll: function(params) {
return this.findInternal_(params, []);
},
matches: function(params) {
return this.matchInternal_(params);
},
addEventListener: function(eventType, callback, capture) {
this.removeEventListener(eventType, callback);
if (!this.listeners[eventType])
this.listeners[eventType] = [];
this.listeners[eventType].push({callback: callback, capture: !!capture});
},
// TODO(dtseng/aboxhall): Check this impl against spec.
removeEventListener: function(eventType, callback) {
if (this.listeners[eventType]) {
var listeners = this.listeners[eventType];
for (var i = 0; i < listeners.length; i++) {
if (callback === listeners[i].callback)
listeners.splice(i, 1);
}
}
},
toJSON: function() {
return { treeID: this.treeID,
id: this.id,
role: this.role,
attributes: this.attributes };
},
dispatchEvent: function(eventType) {
var path = [];
var parent = this.parent;
while (parent) {
path.push(parent);
parent = parent.parent;
}
var event = new AutomationEvent(eventType, this.wrapper);
// Dispatch the event through the propagation path in three phases:
// - capturing: starting from the root and going down to the target's parent
// - targeting: dispatching the event on the target itself
// - bubbling: starting from the target's parent, going back up to the root.
// At any stage, a listener may call stopPropagation() on the event, which
// will immediately stop event propagation through this path.
if (this.dispatchEventAtCapturing_(event, path)) {
if (this.dispatchEventAtTargeting_(event, path))
this.dispatchEventAtBubbling_(event, path);
}
},
toString: function() {
var parentID = GetParentID(this.treeID, this.id);
var childTreeID = GetIntAttribute(this.treeID, this.id, 'childTreeId');
var count = GetChildCount(this.treeID, this.id);
var childIDs = [];
for (var i = 0; i < count; ++i) {
var childID = GetChildIDAtIndex(this.treeID, this.id, i);
childIDs.push(childID);
}
var result = 'node id=' + this.id +
' role=' + this.role +
' state=' + $JSON.stringify(this.state) +
' parentID=' + parentID +
' childIds=' + $JSON.stringify(childIDs);
if (this.hostNode_) {
var hostNodeImpl = privates(this.hostNode_).impl;
result += ' host treeID=' + hostNodeImpl.treeID +
' host nodeID=' + hostNodeImpl.id;
}
if (childTreeID)
result += ' childTreeID=' + childTreeID;
return result;
},
dispatchEventAtCapturing_: function(event, path) {
privates(event).impl.eventPhase = Event.CAPTURING_PHASE;
for (var i = path.length - 1; i >= 0; i--) {
this.fireEventListeners_(path[i], event);
if (privates(event).impl.propagationStopped)
return false;
}
return true;
},
dispatchEventAtTargeting_: function(event) {
privates(event).impl.eventPhase = Event.AT_TARGET;
this.fireEventListeners_(this.wrapper, event);
return !privates(event).impl.propagationStopped;
},
dispatchEventAtBubbling_: function(event, path) {
privates(event).impl.eventPhase = Event.BUBBLING_PHASE;
for (var i = 0; i < path.length; i++) {
this.fireEventListeners_(path[i], event);
if (privates(event).impl.propagationStopped)
return false;
}
return true;
},
fireEventListeners_: function(node, event) {
var nodeImpl = privates(node).impl;
var listeners = nodeImpl.listeners[event.type];
if (!listeners)
return;
var eventPhase = event.eventPhase;
for (var i = 0; i < listeners.length; i++) {
if (eventPhase == Event.CAPTURING_PHASE && !listeners[i].capture)
continue;
if (eventPhase == Event.BUBBLING_PHASE && listeners[i].capture)
continue;
try {
listeners[i].callback(event);
} catch (e) {
exceptionHandler.handle('Error in event handler for ' + event.type +
' during phase ' + eventPhase, e);
}
}
},
performAction_: function(actionType, opt_args) {
// Not yet initialized.
if (this.rootImpl.treeID === undefined ||
this.id === undefined) {
return;
}
// Check permissions.
if (!IsInteractPermitted()) {
throw new Error(actionType + ' requires {"desktop": true} or' +
' {"interact": true} in the "automation" manifest key.');
}
automationInternal.performAction({ treeID: this.rootImpl.treeID,
automationNodeID: this.id,
actionType: actionType },
opt_args || {});
},
domQuerySelectorCallback_: function(userCallback, resultAutomationNodeID) {
// resultAutomationNodeID could be zero or undefined or (unlikely) null;
// they all amount to the same thing here, which is that no node was
// returned.
if (!resultAutomationNodeID) {
userCallback(null);
return;
}
var resultNode = this.rootImpl.get(resultAutomationNodeID);
if (!resultNode) {
logging.WARNING('Query selector result not in tree: ' +
resultAutomationNodeID);
userCallback(null);
}
userCallback(resultNode);
},
findInternal_: function(params, opt_results) {
var result = null;
this.forAllDescendants_(function(node) {
if (privates(node).impl.matchInternal_(params)) {
if (opt_results)
opt_results.push(node);
else
result = node;
return !opt_results;
}
});
if (opt_results)
return opt_results;
return result;
},
/**
* Executes a closure for all of this node's descendants, in pre-order.
* Early-outs if the closure returns true.
* @param {Function(AutomationNode):boolean} closure Closure to be executed
* for each node. Return true to early-out the traversal.
*/
forAllDescendants_: function(closure) {
var stack = this.wrapper.children.reverse();
while (stack.length > 0) {
var node = stack.pop();
if (closure(node))
return;
var children = node.children;
for (var i = children.length - 1; i >= 0; i--)
stack.push(children[i]);
}
},
matchInternal_: function(params) {
if (Object.keys(params).length == 0)
return false;
if ('role' in params && this.role != params.role)
return false;
if ('state' in params) {
for (var state in params.state) {
if (params.state[state] != (state in this.state))
return false;
}
}
if ('attributes' in params) {
for (var attribute in params.attributes) {
var attrValue = params.attributes[attribute];
if (typeof attrValue != 'object') {
if (this[attribute] !== attrValue)
return false;
} else if (attrValue instanceof RegExp) {
if (typeof this[attribute] != 'string')
return false;
if (!attrValue.test(this[attribute]))
return false;
} else {
// TODO(aboxhall): handle intlist case.
return false;
}
}
}
return true;
}
};
var stringAttributes = [
'accessKey',
'action',
'ariaInvalidValue',
'autoComplete',
'containerLiveRelevant',
'containerLiveStatus',
'description',
'display',
'dropeffect',
'help',
'htmlTag',
'liveRelevant',
'liveStatus',
'name',
'placeholder',
'shortcut',
'textInputType',
'url',
'value'];
var boolAttributes = [
'ariaReadonly',
'buttonMixed',
'canSetValue',
'canvasHasFallback',
'containerLiveAtomic',
'containerLiveBusy',
'grabbed',
'isAxTreeHost',
'liveAtomic',
'liveBusy',
'updateLocationOnly'];
var intAttributes = [
'backgroundColor',
'color',
'colorValue',
'hierarchicalLevel',
'invalidState',
'posInSet',
'scrollX',
'scrollXMax',
'scrollXMin',
'scrollY',
'scrollYMax',
'scrollYMin',
'setSize',
'sortDirection',
'tableCellColumnIndex',
'tableCellColumnSpan',
'tableCellRowIndex',
'tableCellRowSpan',
'tableColumnCount',
'tableColumnIndex',
'tableRowCount',
'tableRowIndex',
'textDirection',
'textSelEnd',
'textSelStart',
'textStyle'];
var nodeRefAttributes = [
['activedescendantId', 'activedescendant'],
['tableColumnHeaderId', 'tableColumnHeader'],
['tableHeaderId', 'tableHeader'],
['tableRowHeaderId', 'tableRowHeader'],
['titleUiElement', 'titleUIElement']];
var intListAttributes = [
'characterOffsets',
'lineBreaks',
'wordEnds',
'wordStarts'];
var nodeRefListAttributes = [
['cellIds', 'cells'],
['controlsIds', 'controls'],
['describedbyIds', 'describedBy'],
['flowtoIds', 'flowTo'],
['labelledbyIds', 'labelledBy'],
['uniqueCellIds', 'uniqueCells']];
var floatAttributes = [
'valueForRange',
'minValueForRange',
'maxValueForRange',
'fontSize'];
var htmlAttributes = [
['type', 'inputType']];
var publicAttributes = [];
stringAttributes.forEach(function (attributeName) {
publicAttributes.push(attributeName);
Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
get: function() {
return GetStringAttribute(this.treeID, this.id, attributeName);
}
});
});
boolAttributes.forEach(function (attributeName) {
publicAttributes.push(attributeName);
Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
get: function() {
return GetBoolAttribute(this.treeID, this.id, attributeName);
}
});
});
intAttributes.forEach(function (attributeName) {
publicAttributes.push(attributeName);
Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
get: function() {
return GetIntAttribute(this.treeID, this.id, attributeName);
}
});
});
nodeRefAttributes.forEach(function (params) {
var srcAttributeName = params[0];
var dstAttributeName = params[1];
publicAttributes.push(dstAttributeName);
Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
get: function() {
var id = GetIntAttribute(this.treeID, this.id, srcAttributeName);
if (id)
return this.rootImpl.get(id);
else
return undefined;
}
});
});
intListAttributes.forEach(function (attributeName) {
publicAttributes.push(attributeName);
Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
get: function() {
return GetIntListAttribute(this.treeID, this.id, attributeName);
}
});
});
nodeRefListAttributes.forEach(function (params) {
var srcAttributeName = params[0];
var dstAttributeName = params[1];
publicAttributes.push(dstAttributeName);
Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
get: function() {
var ids = GetIntListAttribute(this.treeID, this.id, srcAttributeName);
if (!ids)
return undefined;
var result = [];
for (var i = 0; i < ids.length; ++i) {
var node = this.rootImpl.get(ids[i]);
if (node)
result.push(node);
}
return result;
}
});
});
floatAttributes.forEach(function (attributeName) {
publicAttributes.push(attributeName);
Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
get: function() {
return GetFloatAttribute(this.treeID, this.id, attributeName);
}
});
});
htmlAttributes.forEach(function (params) {
var srcAttributeName = params[0];
var dstAttributeName = params[1];
publicAttributes.push(dstAttributeName);
Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
get: function() {
return GetHtmlAttribute(this.treeID, this.id, srcAttributeName);
}
});
});
/**
* AutomationRootNode.
*
* An AutomationRootNode is the javascript end of an AXTree living in the
* browser. AutomationRootNode handles unserializing incremental updates from
* the source AXTree. Each update contains node data that form a complete tree
* after applying the update.
*
* A brief note about ids used through this class. The source AXTree assigns
* unique ids per node and we use these ids to build a hash to the actual
* AutomationNode object.
* Thus, tree traversals amount to a lookup in our hash.
*
* The tree itself is identified by the accessibility tree id of the
* renderer widget host.
* @constructor
*/
function AutomationRootNodeImpl(treeID) {
AutomationNodeImpl.call(this, this);
this.treeID = treeID;
this.axNodeDataCache_ = {};
}
AutomationRootNodeImpl.idToAutomationRootNode_ = {};
AutomationRootNodeImpl.get = function(treeID) {
var result = AutomationRootNodeImpl.idToAutomationRootNode_[treeID];
return result || undefined;
}
AutomationRootNodeImpl.getOrCreate = function(treeID) {
if (AutomationRootNodeImpl.idToAutomationRootNode_[treeID])
return AutomationRootNodeImpl.idToAutomationRootNode_[treeID];
var result = new AutomationRootNode(treeID);
AutomationRootNodeImpl.idToAutomationRootNode_[treeID] = result;
return result;
}
AutomationRootNodeImpl.destroy = function(treeID) {
delete AutomationRootNodeImpl.idToAutomationRootNode_[treeID];
}
AutomationRootNodeImpl.prototype = {
__proto__: AutomationNodeImpl.prototype,
/**
* @type {boolean}
*/
isRootNode: true,
/**
* @type {number}
*/
treeID: -1,
/**
* The parent of this node from a different tree.
* @type {?AutomationNode}
* @private
*/
hostNode_: null,
/**
* A map from id to AutomationNode.
* @type {Object.}
* @private
*/
axNodeDataCache_: null,
get id() {
var result = GetRootID(this.treeID);
// Don't return undefined, because the id is often passed directly
// as an argument to a native binding that expects only a valid number.
if (result === undefined)
return -1;
return result;
},
get docUrl() {
return GetDocURL(this.treeID);
},
get docTitle() {
return GetDocTitle(this.treeID);
},
get docLoaded() {
return GetDocLoaded(this.treeID);
},
get docLoadingProgress() {
return GetDocLoadingProgress(this.treeID);
},
get anchorObject() {
var id = GetAnchorObjectID(this.treeID);
if (id && id != -1)
return this.get(id);
else
return undefined;
},
get anchorOffset() {
var id = GetAnchorObjectID(this.treeID);
if (id && id != -1)
return GetAnchorOffset(this.treeID);
},
get focusObject() {
var id = GetFocusObjectID(this.treeID);
if (id && id != -1)
return this.get(id);
else
return undefined;
},
get focusOffset() {
var id = GetFocusObjectID(this.treeID);
if (id && id != -1)
return GetFocusOffset(this.treeID);
},
get: function(id) {
if (id == undefined)
return undefined;
if (id == this.id)
return this.wrapper;
var obj = this.axNodeDataCache_[id];
if (obj)
return obj;
obj = new AutomationNode(this);
privates(obj).impl.treeID = this.treeID;
privates(obj).impl.id = id;
this.axNodeDataCache_[id] = obj;
return obj;
},
remove: function(id) {
delete this.axNodeDataCache_[id];
},
destroy: function() {
this.dispatchEvent(schema.EventType.destroyed);
},
setHostNode(hostNode) {
this.hostNode_ = hostNode;
},
onAccessibilityEvent: function(eventParams) {
var targetNode = this.get(eventParams.targetID);
if (targetNode) {
var targetNodeImpl = privates(targetNode).impl;
targetNodeImpl.dispatchEvent(eventParams.eventType);
} else {
logging.WARNING('Got ' + eventParams.eventType +
' event on unknown node: ' + eventParams.targetID +
'; this: ' + this.id);
}
return true;
},
toString: function() {
function toStringInternal(nodeImpl, indent) {
if (!nodeImpl)
return '';
var output = '';
if (nodeImpl.isRootNode)
output += indent + 'tree id=' + nodeImpl.treeID + '\n';
output += indent +
AutomationNodeImpl.prototype.toString.call(nodeImpl) +
'\n';
indent += ' ';
var children = nodeImpl.children;
for (var i = 0; i < children.length; ++i)
output += toStringInternal(privates(children[i]).impl, indent);
return output;
}
return toStringInternal(this, '');
},
};
var AutomationNode = utils.expose('AutomationNode',
AutomationNodeImpl,
{ functions: ['doDefault',
'find',
'findAll',
'focus',
'makeVisible',
'matches',
'setSelection',
'showContextMenu',
'addEventListener',
'removeEventListener',
'domQuerySelector',
'toString' ],
readonly: publicAttributes.concat(
['parent',
'firstChild',
'lastChild',
'children',
'previousSibling',
'nextSibling',
'isRootNode',
'role',
'state',
'location',
'indexInParent',
'root']) });
var AutomationRootNode = utils.expose('AutomationRootNode',
AutomationRootNodeImpl,
{ superclass: AutomationNode,
readonly: ['docTitle',
'docUrl',
'docLoaded',
'docLoadingProgress',
'anchorObject',
'anchorOffset',
'focusObject',
'focusOffset'] });
AutomationRootNode.get = function(treeID) {
return AutomationRootNodeImpl.get(treeID);
}
AutomationRootNode.getOrCreate = function(treeID) {
return AutomationRootNodeImpl.getOrCreate(treeID);
}
AutomationRootNode.destroy = function(treeID) {
AutomationRootNodeImpl.destroy(treeID);
}
exports.AutomationNode = AutomationNode;
exports.AutomationRootNode = AutomationRootNode;
// 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.
// Custom binding for the browserAction API.
var binding = require('binding').Binding.create('browserAction');
var setIcon = require('setIcon').setIcon;
var getExtensionViews = requireNative('runtime').GetExtensionViews;
var sendRequest = require('sendRequest').sendRequest;
var lastError = require('lastError');
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setHandleRequest('setIcon', function(details, callback) {
setIcon(details, function(args) {
sendRequest(this.name, [args, callback], this.definition.parameters);
}.bind(this));
});
apiFunctions.setCustomCallback('openPopup',
function(name, request, callback, response) {
if (!callback)
return;
if (lastError.hasError(chrome)) {
callback();
} else {
var views = getExtensionViews(-1, 'POPUP');
callback(views.length > 0 ? views[0] : null);
}
});
});
exports.binding = binding.generate();
// 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 binding for the Cast Streaming RtpStream API.
var binding = require('binding').Binding.create('cast.streaming.rtpStream');
var natives = requireNative('cast_streaming_natives');
binding.registerCustomHook(function(bindingsAPI, extensionId) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setHandleRequest('destroy',
function(transportId) {
natives.DestroyCastRtpStream(transportId);
});
apiFunctions.setHandleRequest('getSupportedParams',
function(transportId) {
return natives.GetSupportedParamsCastRtpStream(transportId);
});
apiFunctions.setHandleRequest('start',
function(transportId, params) {
natives.StartCastRtpStream(transportId, params);
});
apiFunctions.setHandleRequest('stop',
function(transportId) {
natives.StopCastRtpStream(transportId);
});
apiFunctions.setHandleRequest('toggleLogging',
function(transportId, enable) {
natives.ToggleLogging(transportId, enable);
});
apiFunctions.setHandleRequest('getRawEvents',
function(transportId, extraData, callback) {
natives.GetRawEvents(transportId, extraData, callback);
});
apiFunctions.setHandleRequest('getStats',
function(transportId, callback) {
natives.GetStats(transportId, callback);
});
});
exports.binding = binding.generate();
// 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 binding for the Cast Streaming Session API.
var binding = require('binding').Binding.create('cast.streaming.session');
var natives = requireNative('cast_streaming_natives');
binding.registerCustomHook(function(bindingsAPI, extensionId) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setHandleRequest('create',
function(audioTrack, videoTrack, callback) {
natives.CreateSession(audioTrack, videoTrack, callback);
});
});
exports.binding = binding.generate();
// 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 binding for the Cast Streaming UdpTransport API.
var binding = require('binding').Binding.create('cast.streaming.udpTransport');
var natives = requireNative('cast_streaming_natives');
binding.registerCustomHook(function(bindingsAPI, extensionId) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setHandleRequest('destroy', function(transportId) {
natives.DestroyCastUdpTransport(transportId);
});
apiFunctions.setHandleRequest('setDestination',
function(transportId, destination) {
natives.SetDestinationCastUdpTransport(transportId, destination);
});
apiFunctions.setHandleRequest('setOptions',
function(transportId, options) {
natives.SetOptionsCastUdpTransport(transportId, options);
});
});
exports.binding = binding.generate();
// 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 binding for the Cast Streaming Session API.
var binding = require('binding').Binding.create(
'cast.streaming.receiverSession');
var natives = requireNative('cast_streaming_natives');
binding.registerCustomHook(function(bindingsAPI, extensionId) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setHandleRequest('createAndBind',
function(ap, vp, local, weidgth, height, fr, url, cb, op) {
natives.StartCastRtpReceiver(
ap, vp, local, weidgth, height, fr, url, cb, op);
});
});
exports.binding = binding.generate();
// Copyright 2015 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.
var binding = require('binding').Binding.create('certificateProvider');
var certificateProviderInternal = require('binding').Binding.create(
'certificateProviderInternal').generate();
var eventBindings = require('event_bindings');
var certificateProviderSchema =
requireNative('schema_registry').GetSchema('certificateProvider')
var utils = require('utils');
var validate = require('schemaUtils').validate;
// Custom bindings for chrome.certificateProvider API.
// The bindings are used to implement callbacks for the API events. Internally
// each event is passed a requestId argument used to identify the callback
// associated with the event. This argument is massaged out from the event
// arguments before dispatching the event to consumers. A callback is appended
// to the event arguments. The callback wraps an appropriate
// chrome.certificateProviderInternal API function that is used to report the
// event result from the extension. The function is passed the requestId and
// values provided by the extension. It validates that the values provided by
// the extension match chrome.certificateProvider event callback schemas. It
// also ensures that a callback is run at most once. In case there is an
// exception during event dispatching, the chrome.certificateProviderInternal
// function is called with a default error value.
// Handles a chrome.certificateProvider event as described in the file comment.
// |eventName|: The event name. The first argument of the event must be a
// request id.
// |internalReportFunc|: The function that should be called to report results in
// reply to an event. The first argument of the function must be the request
// id that was received with the event.
function handleEvent(eventName, internalReportFunc) {
var eventSchema =
utils.lookup(certificateProviderSchema.events, 'name', eventName);
var callbackSchema = utils.lookup(eventSchema.parameters, 'type', 'function');
eventBindings.registerArgumentMassager(
'certificateProvider.' + eventName,
function(args, dispatch) {
var responded = false;
// Function provided to the extension as the event callback argument.
// The extension calls this to report results in reply to the event.
// It throws an exception if called more than once and if the provided
// results don't match the callback schema.
var reportFunc = function(reportArg1, reportArg2) {
if (responded) {
throw new Error(
'Event callback must not be called more than once.');
}
var reportArgs = [reportArg1];
if (reportArg2 !== undefined)
reportArgs.push(reportArg2);
var finalArgs = [];
try {
// Validates that the results reported by the extension matche the
// callback schema of the event. Throws an exception in case of an
// error.
validate(reportArgs, callbackSchema.parameters);
finalArgs = reportArgs;
} finally {
responded = true;
internalReportFunc.apply(
null, [args[0] /* requestId */].concat(finalArgs));
}
};
dispatch(args.slice(1).concat(reportFunc));
});
}
handleEvent('onCertificatesRequested',
certificateProviderInternal.reportCertificates);
handleEvent('onSignDigestRequested',
certificateProviderInternal.reportSignature);
exports.binding = binding.generate();
// 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.
var Event = require('event_bindings').Event;
var sendRequest = require('sendRequest').sendRequest;
var validate = require('schemaUtils').validate;
function extendSchema(schema) {
var extendedSchema = $Array.slice(schema);
extendedSchema.unshift({'type': 'string'});
return extendedSchema;
}
function ChromeDirectSetting(prefKey, valueSchema) {
this.get = function(details, callback) {
var getSchema = this.functionSchemas.get.definition.parameters;
validate([details, callback], getSchema);
return sendRequest('types.private.ChromeDirectSetting.get',
[prefKey, details, callback],
extendSchema(getSchema));
};
this.set = function(details, callback) {
var setSchema = $Array.slice(
this.functionSchemas.set.definition.parameters);
setSchema[0].properties.value = valueSchema;
validate([details, callback], setSchema);
return sendRequest('types.private.ChromeDirectSetting.set',
[prefKey, details, callback],
extendSchema(setSchema));
};
this.clear = function(details, callback) {
var clearSchema = this.functionSchemas.clear.definition.parameters;
validate([details, callback], clearSchema);
return sendRequest('types.private.ChromeDirectSetting.clear',
[prefKey, details, callback],
extendSchema(clearSchema));
};
this.onChange = new Event('types.private.ChromeDirectSetting.' +
prefKey +
'.onChange');
};
exports.ChromeDirectSetting = ChromeDirectSetting;
// 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.
var Event = require('event_bindings').Event;
var sendRequest = require('sendRequest').sendRequest;
var validate = require('schemaUtils').validate;
function extendSchema(schema) {
var extendedSchema = $Array.slice(schema);
extendedSchema.unshift({'type': 'string'});
return extendedSchema;
}
function ChromeSetting(prefKey, valueSchema) {
this.get = function(details, callback) {
var getSchema = this.functionSchemas.get.definition.parameters;
validate([details, callback], getSchema);
return sendRequest('types.ChromeSetting.get',
[prefKey, details, callback],
extendSchema(getSchema));
};
this.set = function(details, callback) {
var setSchema = $Array.slice(
this.functionSchemas.set.definition.parameters);
setSchema[0].properties.value = valueSchema;
validate([details, callback], setSchema);
return sendRequest('types.ChromeSetting.set',
[prefKey, details, callback],
extendSchema(setSchema));
};
this.clear = function(details, callback) {
var clearSchema = this.functionSchemas.clear.definition.parameters;
validate([details, callback], clearSchema);
return sendRequest('types.ChromeSetting.clear',
[prefKey, details, callback],
extendSchema(clearSchema));
};
this.onChange = new Event('types.ChromeSetting.' + prefKey + '.onChange');
};
exports.ChromeSetting = ChromeSetting;
// 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.
var binding = require('binding').Binding.create('chromeWebViewInternal');
var contextMenusHandlers = require('contextMenusHandlers');
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
var handlers = contextMenusHandlers.create(true /* isWebview */);
apiFunctions.setHandleRequest('contextMenusCreate',
handlers.requestHandlers.create);
apiFunctions.setCustomCallback('contextMenusCreate',
handlers.callbacks.create);
apiFunctions.setCustomCallback('contextMenusUpdate',
handlers.callbacks.update);
apiFunctions.setCustomCallback('contextMenusRemove',
handlers.callbacks.remove);
apiFunctions.setCustomCallback('contextMenusRemoveAll',
handlers.callbacks.removeAll);
});
exports.ChromeWebView = binding.generate();
// 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.
// This module implements chrome-specific API.
// See web_view_api_methods.js for details.
var ChromeWebView = require('chromeWebViewInternal').ChromeWebView;
var ChromeWebViewSchema =
requireNative('schema_registry').GetSchema('chromeWebViewInternal');
var CreateEvent = require('guestViewEvents').CreateEvent;
var EventBindings = require('event_bindings');
var GuestViewInternalNatives = requireNative('guest_view_internal');
var idGeneratorNatives = requireNative('id_generator');
var Utils = require('utils');
var WebViewImpl = require('webView').WebViewImpl;
// This is the only "webViewInternal.onClicked" named event for this renderer.
//
// Since we need an event per , we define events with suffix
// (subEventName) in each of the . Behind the scenes, this event is
// registered as a ContextMenusEvent, with filter set to the webview's
// |viewInstanceId|. Any time a ContextMenusEvent is dispatched, we re-dispatch
// it to the subEvent's listeners. This way
// .contextMenus.onClicked behave as a regular chrome Event type.
var ContextMenusEvent = CreateEvent('chromeWebViewInternal.onClicked');
// See comment above.
var ContextMenusHandlerEvent =
CreateEvent('chromeWebViewInternal.onContextMenuShow');
// -----------------------------------------------------------------------------
// ContextMenusOnClickedEvent object.
// This event is exposed as .contextMenus.onClicked.
function ContextMenusOnClickedEvent(webViewInstanceId,
opt_eventName,
opt_argSchemas,
opt_eventOptions) {
var subEventName = GetUniqueSubEventName(opt_eventName);
EventBindings.Event.call(this,
subEventName,
opt_argSchemas,
opt_eventOptions,
webViewInstanceId);
var view = GuestViewInternalNatives.GetViewFromID(webViewInstanceId);
if (!view) {
return;
}
view.events.addScopedListener(ContextMenusEvent, function() {
// Re-dispatch to subEvent's listeners.
$Function.apply(this.dispatch, this, $Array.slice(arguments));
}.bind(this), {instanceId: webViewInstanceId});
}
ContextMenusOnClickedEvent.prototype.__proto__ = EventBindings.Event.prototype;
function ContextMenusOnContextMenuEvent(webViewInstanceId,
opt_eventName,
opt_argSchemas,
opt_eventOptions) {
var subEventName = GetUniqueSubEventName(opt_eventName);
EventBindings.Event.call(this,
subEventName,
opt_argSchemas,
opt_eventOptions,
webViewInstanceId);
var view = GuestViewInternalNatives.GetViewFromID(webViewInstanceId);
if (!view) {
return;
}
view.events.addScopedListener(ContextMenusHandlerEvent, function(e) {
var defaultPrevented = false;
var event = {
'preventDefault': function() { defaultPrevented = true; }
};
// Re-dispatch to subEvent's listeners.
$Function.apply(this.dispatch, this, [event]);
if (!defaultPrevented) {
// TODO(lazyboy): Remove |items| parameter completely from
// ChromeWebView.showContextMenu as we don't do anything useful with it
// currently.
var items = [];
var guestInstanceId = GuestViewInternalNatives.
GetViewFromID(webViewInstanceId).guest.getId();
ChromeWebView.showContextMenu(guestInstanceId, e.requestId, items);
}
}.bind(this), {instanceId: webViewInstanceId});
}
ContextMenusOnContextMenuEvent.prototype.__proto__ =
EventBindings.Event.prototype;
// -----------------------------------------------------------------------------
// WebViewContextMenusImpl object.
// An instance of this class is exposed as .contextMenus.
function WebViewContextMenusImpl(viewInstanceId) {
this.viewInstanceId_ = viewInstanceId;
}
WebViewContextMenusImpl.prototype.create = function() {
var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
return $Function.apply(ChromeWebView.contextMenusCreate, null, args);
};
WebViewContextMenusImpl.prototype.remove = function() {
var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
return $Function.apply(ChromeWebView.contextMenusRemove, null, args);
};
WebViewContextMenusImpl.prototype.removeAll = function() {
var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
return $Function.apply(ChromeWebView.contextMenusRemoveAll, null, args);
};
WebViewContextMenusImpl.prototype.update = function() {
var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
return $Function.apply(ChromeWebView.contextMenusUpdate, null, args);
};
var WebViewContextMenus = Utils.expose(
'WebViewContextMenus', WebViewContextMenusImpl,
{ functions: ['create', 'remove', 'removeAll', 'update'] });
// -----------------------------------------------------------------------------
WebViewImpl.prototype.maybeSetupContextMenus = function() {
if (!this.contextMenusOnContextMenuEvent_) {
var eventName = 'chromeWebViewInternal.onContextMenuShow';
var eventSchema =
Utils.lookup(ChromeWebViewSchema.events, 'name', 'onShow');
var eventOptions = {supportsListeners: true};
this.contextMenusOnContextMenuEvent_ = new ContextMenusOnContextMenuEvent(
this.viewInstanceId, eventName, eventSchema, eventOptions);
}
var createContextMenus = function() {
return this.weakWrapper(function() {
if (this.contextMenus_) {
return this.contextMenus_;
}
this.contextMenus_ = new WebViewContextMenus(this.viewInstanceId);
// Define 'onClicked' event property on |this.contextMenus_|.
var getOnClickedEvent = function() {
return this.weakWrapper(function() {
if (!this.contextMenusOnClickedEvent_) {
var eventName = 'chromeWebViewInternal.onClicked';
var eventSchema =
Utils.lookup(ChromeWebViewSchema.events, 'name', 'onClicked');
var eventOptions = {supportsListeners: true};
var onClickedEvent = new ContextMenusOnClickedEvent(
this.viewInstanceId, eventName, eventSchema, eventOptions);
this.contextMenusOnClickedEvent_ = onClickedEvent;
return onClickedEvent;
}
return this.contextMenusOnClickedEvent_;
});
}.bind(this);
$Object.defineProperty(
this.contextMenus_,
'onClicked',
{get: getOnClickedEvent(), enumerable: true});
$Object.defineProperty(
this.contextMenus_,
'onShow',
{
get: this.weakWrapper(function() {
return this.contextMenusOnContextMenuEvent_;
}),
enumerable: true
});
return this.contextMenus_;
});
}.bind(this);
// Expose .contextMenus object.
// TODO(lazyboy): Add documentation for contextMenus:
// http://crbug.com/470979.
$Object.defineProperty(
this.element,
'contextMenus',
{
get: createContextMenus(),
enumerable: true
});
};
function GetUniqueSubEventName(eventName) {
return eventName + '/' + idGeneratorNatives.GetNextId();
}
// 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.
// Custom binding for the contentSettings API.
var sendRequest = require('sendRequest').sendRequest;
var validate = require('schemaUtils').validate;
function extendSchema(schema) {
var extendedSchema = $Array.slice(schema);
extendedSchema.unshift({'type': 'string'});
return extendedSchema;
}
function ContentSetting(contentType, settingSchema) {
this.get = function(details, callback) {
var getSchema = this.functionSchemas.get.definition.parameters;
validate([details, callback], getSchema);
return sendRequest('contentSettings.get',
[contentType, details, callback],
extendSchema(getSchema));
};
this.set = function(details, callback) {
var setSchema = $Array.slice(
this.functionSchemas.set.definition.parameters);
setSchema[0].properties.setting = settingSchema;
validate([details, callback], setSchema);
return sendRequest('contentSettings.set',
[contentType, details, callback],
extendSchema(setSchema));
};
this.clear = function(details, callback) {
var clearSchema = this.functionSchemas.clear.definition.parameters;
validate([details, callback], clearSchema);
return sendRequest('contentSettings.clear',
[contentType, details, callback],
extendSchema(clearSchema));
};
this.getResourceIdentifiers = function(callback) {
var schema =
this.functionSchemas.getResourceIdentifiers.definition.parameters;
validate([callback], schema);
return sendRequest(
'contentSettings.getResourceIdentifiers',
[contentType, callback],
extendSchema(schema));
};
}
exports.ContentSetting = ContentSetting;
// 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.
// Custom binding for the declarativeContent API.
var binding = require('binding').Binding.create('declarativeContent');
var utils = require('utils');
var validate = require('schemaUtils').validate;
var canonicalizeCompoundSelector =
requireNative('css_natives').CanonicalizeCompoundSelector;
var setIcon = require('setIcon').setIcon;
binding.registerCustomHook( function(api) {
var declarativeContent = api.compiledApi;
// Returns the schema definition of type |typeId| defined in |namespace|.
function getSchema(typeId) {
return utils.lookup(api.schema.types,
'id',
'declarativeContent.' + typeId);
}
// Helper function for the constructor of concrete datatypes of the
// declarative content API.
// Makes sure that |this| contains the union of parameters and
// {'instanceType': 'declarativeContent.' + typeId} and validates the
// generated union dictionary against the schema for |typeId|.
function setupInstance(instance, parameters, typeId) {
for (var key in parameters) {
if ($Object.hasOwnProperty(parameters, key)) {
instance[key] = parameters[key];
}
}
instance.instanceType = 'declarativeContent.' + typeId;
var schema = getSchema(typeId);
validate([instance], [schema]);
}
function canonicalizeCssSelectors(selectors) {
for (var i = 0; i < selectors.length; i++) {
var canonicalizedSelector = canonicalizeCompoundSelector(selectors[i]);
if (canonicalizedSelector == '') {
throw new Error(
'Element of \'css\' array must be a ' +
'list of valid compound selectors: ' +
selectors[i]);
}
selectors[i] = canonicalizedSelector;
}
}
// Setup all data types for the declarative content API.
declarativeContent.PageStateMatcher = function(parameters) {
setupInstance(this, parameters, 'PageStateMatcher');
if ($Object.hasOwnProperty(this, 'css')) {
canonicalizeCssSelectors(this.css);
}
};
declarativeContent.ShowPageAction = function(parameters) {
setupInstance(this, parameters, 'ShowPageAction');
};
declarativeContent.RequestContentScript = function(parameters) {
setupInstance(this, parameters, 'RequestContentScript');
};
// TODO(rockot): Do not expose this in M39 stable. Making this restriction
// possible will take some extra work. See http://crbug.com/415315
declarativeContent.SetIcon = function(parameters) {
setIcon(parameters, function (data) {
setupInstance(this, data, 'SetIcon');
}.bind(this));
};
});
exports.binding = binding.generate();
// 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 binding for the desktopCapture API.
var binding = require('binding').Binding.create('desktopCapture');
var sendRequest = require('sendRequest').sendRequest;
var idGenerator = requireNative('id_generator');
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
var pendingRequests = {};
function onRequestResult(id, result) {
if (id in pendingRequests) {
var callback = pendingRequests[id];
delete pendingRequests[id];
callback(result);
}
}
apiFunctions.setHandleRequest('chooseDesktopMedia',
function(sources, target_tab, callback) {
// |target_tab| is an optional parameter.
if (callback === undefined) {
callback = target_tab;
target_tab = undefined;
}
var id = idGenerator.GetNextId();
pendingRequests[id] = callback;
sendRequest(this.name,
[id, sources, target_tab, onRequestResult.bind(null, id)],
this.definition.parameters, {});
return id;
});
apiFunctions.setHandleRequest('cancelChooseDesktopMedia', function(id) {
if (id in pendingRequests) {
delete pendingRequests[id];
sendRequest(this.name, [id], this.definition.parameters, {});
}
});
});
exports.binding = binding.generate();
// 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.
// Custom binding for the developerPrivate API.
var binding = require('binding').Binding.create('developerPrivate');
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
// Converts the argument of |functionName| from DirectoryEntry to URL.
function bindFileSystemFunction(functionName) {
apiFunctions.setUpdateArgumentsPostValidate(
functionName, function(directoryEntry, callback) {
var fileSystemName = directoryEntry.filesystem.name;
var relativePath = $String.slice(directoryEntry.fullPath, 1);
var url = directoryEntry.toURL();
return [fileSystemName, relativePath, url, callback];
});
}
bindFileSystemFunction('loadDirectory');
// developerPrivate.enable is the same as chrome.management.setEnabled.
// TODO(devlin): Migrate callers off developerPrivate.enable.
bindingsAPI.compiledApi.enable = chrome.management.setEnabled;
apiFunctions.setHandleRequest('allowFileAccess',
function(id, allow, callback) {
chrome.developerPrivate.updateExtensionConfiguration(
{extensionId: id, fileAccess: allow}, callback);
});
apiFunctions.setHandleRequest('allowIncognito',
function(id, allow, callback) {
chrome.developerPrivate.updateExtensionConfiguration(
{extensionId: id, incognitoAccess: allow}, callback);
});
apiFunctions.setHandleRequest('inspect', function(options, callback) {
var renderViewId = options.render_view_id;
if (typeof renderViewId == 'string') {
renderViewId = parseInt(renderViewId);
if (isNaN(renderViewId))
throw new Error('Invalid value for render_view_id');
}
var renderProcessId = options.render_process_id;
if (typeof renderProcessId == 'string') {
renderProcessId = parseInt(renderProcessId);
if (isNaN(renderProcessId))
throw new Error('Invalid value for render_process_id');
}
chrome.developerPrivate.openDevTools({
extensionId: options.extension_id,
renderProcessId: renderProcessId,
renderViewId: renderViewId,
incognito: options.incognito
}, callback);
});
});
exports.binding = binding.generate();
// 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.
// Custom bindings for the downloads API.
var binding = require('binding').Binding.create('downloads');
var downloadsInternal = require('binding').Binding.create(
'downloadsInternal').generate();
var eventBindings = require('event_bindings');
eventBindings.registerArgumentMassager(
'downloads.onDeterminingFilename',
function massage_determining_filename(args, dispatch) {
var downloadItem = args[0];
// Copy the id so that extensions can't change it.
var downloadId = downloadItem.id;
var suggestable = true;
function isValidResult(result) {
if (result === undefined)
return false;
if (typeof(result) != 'object') {
console.error('Error: Invocation of form suggest(' + typeof(result) +
') doesn\'t match definition suggest({filename: string, ' +
'conflictAction: string})');
return false;
} else if ((typeof(result.filename) != 'string') ||
(result.filename.length == 0)) {
console.error('Error: "filename" parameter to suggest() must be a ' +
'non-empty string');
return false;
} else if ([undefined, 'uniquify', 'overwrite', 'prompt'].indexOf(
result.conflictAction) < 0) {
console.error('Error: "conflictAction" parameter to suggest() must be ' +
'one of undefined, "uniquify", "overwrite", "prompt"');
return false;
}
return true;
}
function suggestCallback(result) {
if (!suggestable) {
console.error('suggestCallback may not be called more than once.');
return;
}
suggestable = false;
if (isValidResult(result)) {
downloadsInternal.determineFilename(
downloadId, result.filename, result.conflictAction || "");
} else {
downloadsInternal.determineFilename(downloadId, "", "");
}
}
try {
var results = dispatch([downloadItem, suggestCallback]);
var async = (results &&
results.results &&
(results.results.length != 0) &&
(results.results[0] === true));
if (suggestable && !async)
suggestCallback();
} catch (e) {
suggestCallback();
throw e;
}
});
exports.binding = binding.generate();
// 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.
// Custom binding for the enterprise.platformKeys API.
// The platformKeys API consists of two major parts:
// - the certificate management.
// - the key generation and crypto operations and
// The former is implemented without custom binding as static functions.
// The latter is exposed by implementing WebCrypto's SubtleCrypto interface.
// The internal API provides the key and crypto operations through static
// functions expecting token IDs and this custom binding adds the SubtleCrypto
// wrapper.
// The Token object holds the token id and the SubtleCrypto member.
var binding = require('binding').Binding.create('enterprise.platformKeys');
var Token = require('enterprise.platformKeys.Token').Token;
var internalAPI = require('enterprise.platformKeys.internalAPI');
binding.registerCustomHook(function(api) {
var apiFunctions = api.apiFunctions;
var ret = apiFunctions.setHandleRequest('getTokens', function(callback) {
internalAPI.getTokens(function(tokenIds) {
callback($Array.map(tokenIds,
function(tokenId) { return new Token(tokenId); }));
});
});
});
exports.binding = binding.generate();
// 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.
var binding = require('binding')
.Binding.create('enterprise.platformKeysInternal')
.generate();
exports.getTokens = binding.getTokens;
exports.generateKey = binding.generateKey;
exports.sign = binding.sign;
// 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.
var utils = require('utils');
var intersect = require('platformKeys.utils').intersect;
var keyModule = require('platformKeys.Key');
var Key = keyModule.Key;
var KeyType = keyModule.KeyType;
var KeyUsage = keyModule.KeyUsage;
/**
* Implementation of WebCrypto.KeyPair used in enterprise.platformKeys.
* @param {ArrayBuffer} publicKeySpki The Subject Public Key Info in DER
* encoding.
* @param {KeyAlgorithm} algorithm The algorithm identifier.
* @param {KeyUsage[]} usages The allowed key usages.
* @constructor
*/
var KeyPairImpl = function(publicKeySpki, algorithm, usages) {
this.publicKey = new Key(KeyType.public,
publicKeySpki,
algorithm,
intersect([KeyUsage.verify], usages),
true /* extractable */);
this.privateKey = new Key(KeyType.private,
publicKeySpki,
algorithm,
intersect([KeyUsage.sign], usages),
false /* not extractable */);
};
exports.KeyPair = utils.expose('KeyPair',
KeyPairImpl,
{readonly:['publicKey', 'privateKey']});
// 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.
var utils = require('utils');
var internalAPI = require('enterprise.platformKeys.internalAPI');
var intersect = require('platformKeys.utils').intersect;
var subtleCryptoModule = require('platformKeys.SubtleCrypto');
var SubtleCrypto = subtleCryptoModule.SubtleCrypto;
var SubtleCryptoImpl = subtleCryptoModule.SubtleCryptoImpl;
var KeyPair = require('enterprise.platformKeys.KeyPair').KeyPair;
var KeyUsage = require('platformKeys.Key').KeyUsage;
var normalizeAlgorithm =
requireNative('platform_keys_natives').NormalizeAlgorithm;
// This error is thrown by the internal and public API's token functions and
// must be rethrown by this custom binding. Keep this in sync with the C++ part
// of this API.
var errorInvalidToken = "The token is not valid.";
// The following errors are specified in WebCrypto.
// TODO(pneubeck): These should be DOMExceptions.
function CreateNotSupportedError() {
return new Error('The algorithm is not supported');
}
function CreateInvalidAccessError() {
return new Error('The requested operation is not valid for the provided key');
}
function CreateDataError() {
return new Error('Data provided to an operation does not meet requirements');
}
function CreateSyntaxError() {
return new Error('A required parameter was missing or out-of-range');
}
function CreateOperationError() {
return new Error('The operation failed for an operation-specific reason');
}
// Catches an |internalErrorInvalidToken|. If so, forwards it to |reject| and
// returns true.
function catchInvalidTokenError(reject) {
if (chrome.runtime.lastError &&
chrome.runtime.lastError.message == errorInvalidToken) {
reject(chrome.runtime.lastError);
return true;
}
return false;
}
// Returns true if |array| is a BigInteger describing the standard public
// exponent 65537. In particular, it ignores leading zeros as required by the
// BigInteger definition in WebCrypto.
function equalsStandardPublicExponent(array) {
var expected = [0x01, 0x00, 0x01];
if (array.length < expected.length)
return false;
for (var i = 0; i < array.length; i++) {
var expectedDigit = 0;
if (i < expected.length) {
// |expected| is symmetric, endianness doesn't matter.
expectedDigit = expected[i];
}
if (array[array.length - 1 - i] !== expectedDigit)
return false;
}
return true;
}
/**
* Implementation of WebCrypto.SubtleCrypto used in enterprise.platformKeys.
* Derived from platformKeys.SubtleCrypto.
* @param {string} tokenId The id of the backing Token.
* @constructor
*/
var EnterpriseSubtleCryptoImpl = function(tokenId) {
SubtleCryptoImpl.call(this, tokenId);
};
EnterpriseSubtleCryptoImpl.prototype =
Object.create(SubtleCryptoImpl.prototype);
EnterpriseSubtleCryptoImpl.prototype.generateKey =
function(algorithm, extractable, keyUsages) {
var subtleCrypto = this;
return new Promise(function(resolve, reject) {
// TODO(pneubeck): Apply the algorithm normalization of the WebCrypto
// implementation.
if (extractable) {
// Note: This deviates from WebCrypto.SubtleCrypto.
throw CreateNotSupportedError();
}
if (intersect(keyUsages, [KeyUsage.sign, KeyUsage.verify]).length !=
keyUsages.length) {
throw CreateDataError();
}
var normalizedAlgorithmParameters =
normalizeAlgorithm(algorithm, 'GenerateKey');
if (!normalizedAlgorithmParameters) {
// TODO(pneubeck): It's not clear from the WebCrypto spec which error to
// throw here.
throw CreateSyntaxError();
}
// normalizeAlgorithm returns an array, but publicExponent should be a
// Uint8Array.
normalizedAlgorithmParameters.publicExponent =
new Uint8Array(normalizedAlgorithmParameters.publicExponent);
if (normalizedAlgorithmParameters.name !== 'RSASSA-PKCS1-v1_5' ||
!equalsStandardPublicExponent(
normalizedAlgorithmParameters.publicExponent)) {
// Note: This deviates from WebCrypto.SubtleCrypto.
throw CreateNotSupportedError();
}
internalAPI.generateKey(subtleCrypto.tokenId,
normalizedAlgorithmParameters.modulusLength,
function(spki) {
if (catchInvalidTokenError(reject))
return;
if (chrome.runtime.lastError) {
reject(CreateOperationError());
return;
}
resolve(new KeyPair(spki, normalizedAlgorithmParameters, keyUsages));
});
});
};
exports.SubtleCrypto =
utils.expose('SubtleCrypto',
EnterpriseSubtleCryptoImpl,
{
superclass: SubtleCrypto,
functions: ['generateKey']
// ['sign', 'exportKey'] are exposed by the base class
});
// 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.
var utils = require('utils');
var SubtleCrypto = require('enterprise.platformKeys.SubtleCrypto').SubtleCrypto;
/**
* Implementation of enterprise.platformKeys.Token.
* @param {string} id The id of the new Token.
* @constructor
*/
var TokenImpl = function(id) {
this.id = id;
this.subtleCrypto = new SubtleCrypto(id);
};
exports.Token =
utils.expose('Token', TokenImpl, {readonly:['id', 'subtleCrypto']});
// 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 feedbackPrivate API.
var binding = require('binding').Binding.create('feedbackPrivate');
var blobNatives = requireNative('blob_natives');
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setUpdateArgumentsPostValidate(
"sendFeedback", function(feedbackInfo, callback) {
var attachedFileBlobUuid = '';
var screenshotBlobUuid = '';
if (feedbackInfo.attachedFile)
attachedFileBlobUuid =
blobNatives.GetBlobUuid(feedbackInfo.attachedFile.data);
if (feedbackInfo.screenshot)
screenshotBlobUuid =
blobNatives.GetBlobUuid(feedbackInfo.screenshot);
feedbackInfo.attachedFileBlobUuid = attachedFileBlobUuid;
feedbackInfo.screenshotBlobUuid = screenshotBlobUuid;
return [feedbackInfo, callback];
});
});
exports.binding = binding.generate();
// 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.
// Custom binding for the fileBrowserHandler API.
var binding = require('binding').Binding.create('fileBrowserHandler');
var eventBindings = require('event_bindings');
var fileBrowserNatives = requireNative('file_browser_handler');
var GetExternalFileEntry = fileBrowserNatives.GetExternalFileEntry;
var fileBrowserHandlerInternal = require('binding').Binding.create(
'fileBrowserHandlerInternal').generate();
eventBindings.registerArgumentMassager('fileBrowserHandler.onExecute',
function(args, dispatch) {
if (args.length < 2) {
dispatch(args);
return;
}
var fileList = args[1].entries;
if (!fileList) {
dispatch(args);
return;
}
// The second parameter for this event's payload is file definition
// dictionary that we used to reconstruct File API's Entry instance
// here.
for (var i = 0; i < fileList.length; i++)
fileList[i] = GetExternalFileEntry(fileList[i]);
dispatch(args);
});
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setHandleRequest('selectFile',
function(selectionParams, callback) {
function internalCallback(externalCallback, internalResult) {
if (!externalCallback)
return;
var result = undefined;
if (internalResult) {
result = { success: internalResult.success, entry: null };
if (internalResult.success)
result.entry = GetExternalFileEntry(internalResult.entry);
}
externalCallback(result);
}
return fileBrowserHandlerInternal.selectFile(
selectionParams, $Function.bind(internalCallback, null, callback));
});
});
exports.binding = binding.generate();
// 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.
// Custom binding for the fileManagerPrivate API.
// Bindings
var binding = require('binding').Binding.create('fileManagerPrivate');
var eventBindings = require('event_bindings');
// Natives
var fileManagerPrivateNatives = requireNative('file_manager_private');
var fileBrowserHandlerNatives = requireNative('file_browser_handler');
// Internals
var fileManagerPrivateInternal =
require('binding').Binding.create('fileManagerPrivateInternal').generate();
// Shorthands
var GetFileSystem = fileManagerPrivateNatives.GetFileSystem;
var GetExternalFileEntry = fileBrowserHandlerNatives.GetExternalFileEntry;
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setCustomCallback('searchDrive',
function(name, request, callback, response) {
if (response && !response.error && response.entries) {
response.entries = response.entries.map(function(entry) {
return GetExternalFileEntry(entry);
});
}
// So |callback| doesn't break if response is not defined.
if (!response)
response = {};
if (callback)
callback(response.entries, response.nextFeed);
});
apiFunctions.setCustomCallback('searchDriveMetadata',
function(name, request, callback, response) {
if (response && !response.error) {
for (var i = 0; i < response.length; i++) {
response[i].entry =
GetExternalFileEntry(response[i].entry);
}
}
// So |callback| doesn't break if response is not defined.
if (!response)
response = {};
if (callback)
callback(response);
});
apiFunctions.setHandleRequest('resolveIsolatedEntries',
function(entries, callback) {
var urls = entries.map(function(entry) {
return fileBrowserHandlerNatives.GetEntryURL(entry);
});
fileManagerPrivateInternal.resolveIsolatedEntries(urls, function(
entryDescriptions) {
callback(entryDescriptions.map(function(description) {
return GetExternalFileEntry(description);
}));
});
});
apiFunctions.setHandleRequest('getEntryProperties',
function(entries, names, callback) {
var urls = entries.map(function(entry) {
return fileBrowserHandlerNatives.GetEntryURL(entry);
});
fileManagerPrivateInternal.getEntryProperties(urls, names, callback);
});
apiFunctions.setHandleRequest('addFileWatch', function(entry, callback) {
var url = fileBrowserHandlerNatives.GetEntryURL(entry);
fileManagerPrivateInternal.addFileWatch(url, callback);
});
apiFunctions.setHandleRequest('removeFileWatch', function(entry, callback) {
var url = fileBrowserHandlerNatives.GetEntryURL(entry);
fileManagerPrivateInternal.removeFileWatch(url, callback);
});
apiFunctions.setHandleRequest('getCustomActions', function(
entries, callback) {
var urls = entries.map(function(entry) {
return fileBrowserHandlerNatives.GetEntryURL(entry);
});
fileManagerPrivateInternal.getCustomActions(urls, callback);
});
apiFunctions.setHandleRequest('executeCustomAction', function(
entries, actionId, callback) {
var urls = entries.map(function(entry) {
return fileBrowserHandlerNatives.GetEntryURL(entry);
});
fileManagerPrivateInternal.executeCustomAction(urls, actionId, callback);
});
apiFunctions.setHandleRequest('computeChecksum', function(entry, callback) {
var url = fileBrowserHandlerNatives.GetEntryURL(entry);
fileManagerPrivateInternal.computeChecksum(url, callback);
});
apiFunctions.setHandleRequest('getMimeType', function(entry, callback) {
var url = fileBrowserHandlerNatives.GetEntryURL(entry);
fileManagerPrivateInternal.getMimeType(url, callback);
});
apiFunctions.setHandleRequest('pinDriveFile', function(entry, pin, callback) {
var url = fileBrowserHandlerNatives.GetEntryURL(entry);
fileManagerPrivateInternal.pinDriveFile(url, pin, callback);
});
apiFunctions.setHandleRequest('executeTask',
function(taskId, entries, callback) {
var urls = entries.map(function(entry) {
return fileBrowserHandlerNatives.GetEntryURL(entry);
});
fileManagerPrivateInternal.executeTask(taskId, urls, callback);
});
apiFunctions.setHandleRequest('setDefaultTask',
function(taskId, entries, mimeTypes, callback) {
var urls = entries.map(function(entry) {
return fileBrowserHandlerNatives.GetEntryURL(entry);
});
fileManagerPrivateInternal.setDefaultTask(
taskId, urls, mimeTypes, callback);
});
apiFunctions.setHandleRequest('getFileTasks', function(entries, callback) {
var urls = entries.map(function(entry) {
return fileBrowserHandlerNatives.GetEntryURL(entry);
});
fileManagerPrivateInternal.getFileTasks(urls, callback);
});
apiFunctions.setHandleRequest('getShareUrl', function(entry, callback) {
var url = fileBrowserHandlerNatives.GetEntryURL(entry);
fileManagerPrivateInternal.getShareUrl(url, callback);
});
apiFunctions.setHandleRequest('getDownloadUrl', function(entry, callback) {
var url = fileBrowserHandlerNatives.GetEntryURL(entry);
fileManagerPrivateInternal.getDownloadUrl(url, callback);
});
apiFunctions.setHandleRequest('requestDriveShare', function(
entry, shareType, callback) {
var url = fileBrowserHandlerNatives.GetEntryURL(entry);
fileManagerPrivateInternal.requestDriveShare(url, shareType, callback);
});
apiFunctions.setHandleRequest('setEntryTag', function(
entry, visibility, key, value, callback) {
var url = fileBrowserHandlerNatives.GetEntryURL(entry);
fileManagerPrivateInternal.setEntryTag(
url, visibility, key, value, callback);
});
apiFunctions.setHandleRequest('cancelFileTransfers', function(
entries, callback) {
var urls = entries.map(function(entry) {
return fileBrowserHandlerNatives.GetEntryURL(entry);
});
fileManagerPrivateInternal.cancelFileTransfers(urls, callback);
});
apiFunctions.setHandleRequest('startCopy', function(
entry, parentEntry, newName, callback) {
var url = fileBrowserHandlerNatives.GetEntryURL(entry);
var parentUrl = fileBrowserHandlerNatives.GetEntryURL(parentEntry);
fileManagerPrivateInternal.startCopy(
url, parentUrl, newName, callback);
});
apiFunctions.setHandleRequest('zipSelection', function(
parentEntry, entries, destName, callback) {
var parentUrl = fileBrowserHandlerNatives.GetEntryURL(parentEntry);
var urls = entries.map(function(entry) {
return fileBrowserHandlerNatives.GetEntryURL(entry);
});
fileManagerPrivateInternal.zipSelection(
parentUrl, urls, destName, callback);
});
apiFunctions.setHandleRequest('validatePathNameLength', function(
entry, name, callback) {
var url = fileBrowserHandlerNatives.GetEntryURL(entry);
fileManagerPrivateInternal.validatePathNameLength(url, name, callback);
});
});
eventBindings.registerArgumentMassager(
'fileManagerPrivate.onDirectoryChanged', function(args, dispatch) {
// Convert the entry arguments into a real Entry object.
args[0].entry = GetExternalFileEntry(args[0].entry);
dispatch(args);
});
exports.binding = binding.generate();
// 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.
var fileSystemNatives = requireNative('file_system_natives');
var GetIsolatedFileSystem = fileSystemNatives.GetIsolatedFileSystem;
var sendRequest = require('sendRequest');
var lastError = require('lastError');
var GetModuleSystem = requireNative('v8_context').GetModuleSystem;
// TODO(sammc): Don't require extension. See http://crbug.com/235689.
var GetExtensionViews = requireNative('runtime').GetExtensionViews;
// For a given |apiName|, generates object with two elements that are used
// in file system relayed APIs:
// * 'bindFileEntryCallback' function that provides mapping between JS objects
// into actual FileEntry|DirectoryEntry objects.
// * 'entryIdManager' object that implements methods for keeping the tracks of
// previously saved file entries.
function getFileBindingsForApi(apiName) {
// Fallback to using the current window if no background page is running.
var backgroundPage = GetExtensionViews(-1, 'BACKGROUND')[0] || window;
var backgroundPageModuleSystem = GetModuleSystem(backgroundPage);
// All windows use the bindFileEntryCallback from the background page so their
// FileEntry objects have the background page's context as their own. This
// allows them to be used from other windows (including the background page)
// after the original window is closed.
if (window == backgroundPage) {
var bindFileEntryCallback = function(functionName, apiFunctions) {
apiFunctions.setCustomCallback(functionName,
function(name, request, callback, response) {
if (callback) {
if (!response) {
callback();
return;
}
var entries = [];
var hasError = false;
var getEntryError = function(fileError) {
if (!hasError) {
hasError = true;
lastError.run(
apiName + '.' + functionName,
'Error getting fileEntry, code: ' + fileError.code,
request.stack,
callback);
}
}
// Loop through the response entries and asynchronously get the
// FileEntry for each. We use hasError to ensure that only the first
// error is reported. Note that an error can occur either during the
// loop or in the asynchronous error callback to getFile.
$Array.forEach(response.entries, function(entry) {
if (hasError)
return;
var fileSystemId = entry.fileSystemId;
var baseName = entry.baseName;
var id = entry.id;
var fs = GetIsolatedFileSystem(fileSystemId);
try {
var getEntryCallback = function(fileEntry) {
if (hasError)
return;
entryIdManager.registerEntry(id, fileEntry);
entries.push(fileEntry);
// Once all entries are ready, pass them to the callback. In the
// event of an error, this condition will never be satisfied so
// the callback will not be called with any entries.
if (entries.length == response.entries.length) {
if (response.multiple) {
sendRequest.safeCallbackApply(
apiName + '.' + functionName, request, callback,
[entries]);
} else {
sendRequest.safeCallbackApply(
apiName + '.' + functionName, request, callback,
[entries[0]]);
}
}
}
// TODO(koz): fs.root.getFile() makes a trip to the browser
// process, but it might be possible avoid that by calling
// WebDOMFileSystem::createV8Entry().
if (entry.isDirectory) {
fs.root.getDirectory(baseName, {}, getEntryCallback,
getEntryError);
} else {
fs.root.getFile(baseName, {}, getEntryCallback, getEntryError);
}
} catch (e) {
if (!hasError) {
hasError = true;
lastError.run(apiName + '.' + functionName,
'Error getting fileEntry: ' + e.stack,
request.stack,
callback);
}
}
});
}
});
};
var entryIdManager = require('entryIdManager');
} else {
// Force the fileSystem API to be loaded in the background page. Using
// backgroundPageModuleSystem.require('fileSystem') is insufficient as
// requireNative is only allowed while lazily loading an API.
backgroundPage.chrome.fileSystem;
var bindFileEntryCallback = backgroundPageModuleSystem.require(
apiName).bindFileEntryCallback;
var entryIdManager = backgroundPageModuleSystem.require('entryIdManager');
}
return {bindFileEntryCallback: bindFileEntryCallback,
entryIdManager: entryIdManager};
}
exports.getFileBindingsForApi = getFileBindingsForApi;
// 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.
// Custom binding for the fileSystem API.
var binding = require('binding').Binding.create('fileSystem');
var sendRequest = require('sendRequest');
var getFileBindingsForApi =
require('fileEntryBindingUtil').getFileBindingsForApi;
var fileBindings = getFileBindingsForApi('fileSystem');
var bindFileEntryCallback = fileBindings.bindFileEntryCallback;
var entryIdManager = fileBindings.entryIdManager;
var fileSystemNatives = requireNative('file_system_natives');
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
var fileSystem = bindingsAPI.compiledApi;
function bindFileEntryFunction(functionName) {
apiFunctions.setUpdateArgumentsPostValidate(
functionName, function(fileEntry, callback) {
var fileSystemName = fileEntry.filesystem.name;
var relativePath = $String.slice(fileEntry.fullPath, 1);
return [fileSystemName, relativePath, callback];
});
}
$Array.forEach(['getDisplayPath', 'getWritableEntry', 'isWritableEntry'],
bindFileEntryFunction);
$Array.forEach(['getWritableEntry', 'chooseEntry', 'restoreEntry'],
function(functionName) {
bindFileEntryCallback(functionName, apiFunctions);
});
apiFunctions.setHandleRequest('retainEntry', function(fileEntry) {
var id = entryIdManager.getEntryId(fileEntry);
if (!id)
return '';
var fileSystemName = fileEntry.filesystem.name;
var relativePath = $String.slice(fileEntry.fullPath, 1);
sendRequest.sendRequest(this.name, [id, fileSystemName, relativePath],
this.definition.parameters, {});
return id;
});
apiFunctions.setHandleRequest('isRestorable',
function(id, callback) {
var savedEntry = entryIdManager.getEntryById(id);
if (savedEntry) {
sendRequest.safeCallbackApply(
'fileSystem.isRestorable',
{},
callback,
[true]);
} else {
sendRequest.sendRequest(
this.name, [id, callback], this.definition.parameters, {});
}
});
apiFunctions.setUpdateArgumentsPostValidate('restoreEntry',
function(id, callback) {
var savedEntry = entryIdManager.getEntryById(id);
if (savedEntry) {
// We already have a file entry for this id so pass it to the callback and
// send a request to the browser to move it to the back of the LRU.
sendRequest.safeCallbackApply(
'fileSystem.restoreEntry',
{},
callback,
[savedEntry]);
return [id, false, null];
} else {
// Ask the browser process for a new file entry for this id, to be passed
// to |callback|.
return [id, true, callback];
}
});
apiFunctions.setCustomCallback('requestFileSystem',
function(name, request, callback, response) {
var fileSystem;
if (response && response.file_system_id) {
fileSystem = fileSystemNatives.GetIsolatedFileSystem(
response.file_system_id, response.file_system_path);
}
sendRequest.safeCallbackApply(
'fileSystem.requestFileSystem',
request,
callback,
[fileSystem]);
});
// TODO(benwells): Remove these deprecated versions of the functions.
fileSystem.getWritableFileEntry = function() {
console.log("chrome.fileSystem.getWritableFileEntry is deprecated");
console.log("Please use chrome.fileSystem.getWritableEntry instead");
$Function.apply(fileSystem.getWritableEntry, this, arguments);
};
fileSystem.isWritableFileEntry = function() {
console.log("chrome.fileSystem.isWritableFileEntry is deprecated");
console.log("Please use chrome.fileSystem.isWritableEntry instead");
$Function.apply(fileSystem.isWritableEntry, this, arguments);
};
fileSystem.chooseFile = function() {
console.log("chrome.fileSystem.chooseFile is deprecated");
console.log("Please use chrome.fileSystem.chooseEntry instead");
$Function.apply(fileSystem.chooseEntry, this, arguments);
};
});
exports.bindFileEntryCallback = bindFileEntryCallback;
exports.binding = binding.generate();
// 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 binding for the fileSystemProvider API.
var binding = require('binding').Binding.create('fileSystemProvider');
var fileSystemProviderInternal =
require('binding').Binding.create('fileSystemProviderInternal').generate();
var eventBindings = require('event_bindings');
/**
* Maximum size of the thumbnail in bytes.
* @type {number}
* @const
*/
var METADATA_THUMBNAIL_SIZE_LIMIT = 32 * 1024 * 1024;
/**
* Regular expression to validate if the thumbnail URI is a valid data URI,
* taking into account allowed formats.
* @type {RegExp}
* @const
*/
var METADATA_THUMBNAIL_FORMAT = new RegExp(
'^data:image/(png|jpeg|webp);', 'i');
/**
* Annotates a date with its serialized value.
* @param {Date} date Input date.
* @return {Date} Date with an extra value attribute.
*/
function annotateDate(date) {
// Copy in case the input date is frozen.
var result = new Date(date.getTime());
result.value = result.toString();
return result;
}
/**
* Verifies if the passed image URI is valid.
* @param {*} uri Image URI.
* @return {boolean} True if valid, valse otherwise.
*/
function verifyImageURI(uri) {
// The URI is specified by a user, so the type may be incorrect.
if (typeof uri != 'string' && !(uri instanceof String))
return false;
return METADATA_THUMBNAIL_FORMAT.test(uri);
}
/**
* Annotates an entry metadata by serializing its modifiedTime value.
* @param {EntryMetadata} metadata Input metadata.
* @return {EntryMetadata} metadata Annotated metadata, which can be passed
* back to the C++ layer.
*/
function annotateMetadata(metadata) {
var result = {
isDirectory: metadata.isDirectory,
name: metadata.name,
size: metadata.size,
modificationTime: annotateDate(metadata.modificationTime)
};
if ('mimeType' in metadata)
result.mimeType = metadata.mimeType;
if ('thumbnail' in metadata)
result.thumbnail = metadata.thumbnail;
return result;
}
/**
* Massages arguments of an event raised by the File System Provider API.
* @param {Array<*>} args Input arguments.
* @param {function(Array<*>)} dispatch Closure to be called with massaged
* arguments.
*/
function massageArgumentsDefault(args, dispatch) {
var executionStart = Date.now();
var options = args[0];
var onSuccessCallback = function(hasNext) {
fileSystemProviderInternal.operationRequestedSuccess(
options.fileSystemId, options.requestId, Date.now() - executionStart);
};
var onErrorCallback = function(error) {
fileSystemProviderInternal.operationRequestedError(
options.fileSystemId, options.requestId, error,
Date.now() - executionStart);
}
dispatch([options, onSuccessCallback, onErrorCallback]);
}
eventBindings.registerArgumentMassager(
'fileSystemProvider.onUnmountRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onGetMetadataRequested',
function(args, dispatch) {
var executionStart = Date.now();
var options = args[0];
var onSuccessCallback = function(metadata) {
var error;
// It is invalid to return a thumbnail when it's not requested. The
// restriction is added in order to avoid fetching the thumbnail while
// it's not needed.
if (!options.thumbnail && metadata.thumbnail)
error = 'Thumbnail data provided, but not requested.';
// Check the format and size. Note, that in the C++ layer, there is
// another sanity check to avoid passing any evil URL.
if ('thumbnail' in metadata && !verifyImageURI(metadata.thumbnail))
error = 'Thumbnail format invalid.';
if ('thumbnail' in metadata &&
metadata.thumbnail.length > METADATA_THUMBNAIL_SIZE_LIMIT) {
error = 'Thumbnail data too large.';
}
if (error) {
console.error(error);
fileSystemProviderInternal.operationRequestedError(
options.fileSystemId, options.requestId, 'FAILED',
Date.now() - executionStart);
return;
}
fileSystemProviderInternal.getMetadataRequestedSuccess(
options.fileSystemId,
options.requestId,
annotateMetadata(metadata),
Date.now() - executionStart);
};
var onErrorCallback = function(error) {
fileSystemProviderInternal.operationRequestedError(
options.fileSystemId, options.requestId, error,
Date.now() - executionStart);
}
dispatch([options, onSuccessCallback, onErrorCallback]);
});
eventBindings.registerArgumentMassager(
'fileSystemProvider.onGetActionsRequested',
function(args, dispatch) {
var executionStart = Date.now();
var options = args[0];
var onSuccessCallback = function(actions) {
fileSystemProviderInternal.getActionsRequestedSuccess(
options.fileSystemId,
options.requestId,
actions,
Date.now() - executionStart);
};
var onErrorCallback = function(error) {
fileSystemProviderInternal.operationRequestedError(
options.fileSystemId, options.requestId, error,
Date.now() - executionStart);
}
dispatch([options, onSuccessCallback, onErrorCallback]);
});
eventBindings.registerArgumentMassager(
'fileSystemProvider.onReadDirectoryRequested',
function(args, dispatch) {
var executionStart = Date.now();
var options = args[0];
var onSuccessCallback = function(entries, hasNext) {
var annotatedEntries = entries.map(annotateMetadata);
// It is invalid to return a thumbnail when it's not requested.
var error;
annotatedEntries.forEach(function(metadata) {
if (metadata.thumbnail) {
var error =
'Thumbnails must not be provided when reading a directory.';
return;
}
});
if (error) {
console.error(error);
fileSystemProviderInternal.operationRequestedError(
options.fileSystemId, options.requestId, 'FAILED',
Date.now() - executionStart);
return;
}
fileSystemProviderInternal.readDirectoryRequestedSuccess(
options.fileSystemId, options.requestId, annotatedEntries, hasNext,
Date.now() - executionStart);
};
var onErrorCallback = function(error) {
fileSystemProviderInternal.operationRequestedError(
options.fileSystemId, options.requestId, error,
Date.now() - executionStart);
}
dispatch([options, onSuccessCallback, onErrorCallback]);
});
eventBindings.registerArgumentMassager(
'fileSystemProvider.onOpenFileRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onCloseFileRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onReadFileRequested',
function(args, dispatch) {
var executionStart = Date.now();
var options = args[0];
var onSuccessCallback = function(data, hasNext) {
fileSystemProviderInternal.readFileRequestedSuccess(
options.fileSystemId, options.requestId, data, hasNext,
Date.now() - executionStart);
};
var onErrorCallback = function(error) {
fileSystemProviderInternal.operationRequestedError(
options.fileSystemId, options.requestId, error,
Date.now() - executionStart);
}
dispatch([options, onSuccessCallback, onErrorCallback]);
});
eventBindings.registerArgumentMassager(
'fileSystemProvider.onCreateDirectoryRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onDeleteEntryRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onCreateFileRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onCopyEntryRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onMoveEntryRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onTruncateRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onWriteFileRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onAbortRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onObserveDirectoryRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onUnobserveEntryRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onAddWatcherRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onRemoveWatcherRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onConfigureRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onExecuteActionRequested',
massageArgumentsDefault);
eventBindings.registerArgumentMassager(
'fileSystemProvider.onMountRequested',
function(args, dispatch) {
var onSuccessCallback = function() {
// TODO(mtomasz): To be implemented.
};
var onErrorCallback = function(error) {
// TODO(mtomasz): To be implemented.
}
dispatch([onSuccessCallback, onErrorCallback]);
});
exports.binding = binding.generate();
// 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 binding for the GCM API.
var binding = require('binding').Binding.create('gcm');
var forEach = require('utils').forEach;
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
var gcm = bindingsAPI.compiledApi;
apiFunctions.setUpdateArgumentsPostValidate(
'send', function(message, callback) {
// Validate message.data.
var payloadSize = 0;
forEach(message.data, function(property, value) {
if (property.length == 0)
throw new Error("One of data keys is empty.");
var lowerCasedProperty = property.toLowerCase();
// Issue an error for forbidden prefixes of property names.
if (lowerCasedProperty.indexOf("goog.") == 0 ||
lowerCasedProperty.indexOf("google") == 0 ||
property.indexOf("collapse_key") == 0) {
throw new Error("Invalid data key: " + property);
}
payloadSize += property.length + value.length;
});
if (payloadSize > gcm.MAX_MESSAGE_SIZE)
throw new Error("Payload exceeded allowed size limit. Payload size is: "
+ payloadSize);
if (payloadSize == 0)
throw new Error("No data to send.");
return arguments;
});
});
exports.binding = binding.generate();
// 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 binding for the Identity API.
var binding = require('binding').Binding.create('identity');
binding.registerCustomHook(function(binding, id, contextType) {
var apiFunctions = binding.apiFunctions;
var identity = binding.compiledApi;
apiFunctions.setHandleRequest('getRedirectURL', function(path) {
if (path === null || path === undefined)
path = '/';
else
path = String(path);
if (path[0] != '/')
path = '/' + path;
return 'https://' + id + '.chromiumapp.org' + path;
});
});
exports.binding = binding.generate();
// 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 binding for the image writer private API.
var binding = require('binding').Binding.create('imageWriterPrivate');
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setUpdateArgumentsPostValidate(
'writeFromFile', function(device, fileEntry, options, callback) {
var fileSystemName = fileEntry.filesystem.name;
var relativePath = $String.slice(fileEntry.fullPath, 1);
return [device, fileSystemName, relativePath, callback];
});
});
exports.binding = binding.generate();
// 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.
// Custom binding for the input ime API. Only injected into the
// v8 contexts for extensions which have permission for the API.
var binding = require('binding').Binding.create('input.ime');
var Event = require('event_bindings').Event;
binding.registerCustomHook(function(api) {
var input_ime = api.compiledApi;
input_ime.onKeyEvent.dispatchToListener = function(callback, args) {
var engineID = args[0];
var keyData = args[1];
var result = false;
try {
result = $Function.call(Event.prototype.dispatchToListener,
this, callback, args);
} catch (e) {
console.error('Error in event handler for onKeyEvent: ' + e.stack);
}
if (!input_ime.onKeyEvent.async) {
input_ime.keyEventHandled(keyData.requestId, result);
}
};
input_ime.onKeyEvent.addListener = function(cb, opt_extraInfo) {
input_ime.onKeyEvent.async = false;
if (opt_extraInfo instanceof Array) {
for (var i = 0; i < opt_extraInfo.length; ++i) {
if (opt_extraInfo[i] == "async") {
input_ime.onKeyEvent.async = true;
}
}
}
$Function.call(Event.prototype.addListener, this, cb);
};
});
exports.binding = binding.generate();
// 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.
// Custom binding for the logPrivate API.
var binding = require('binding').Binding.create('logPrivate');
var sendRequest = require('sendRequest');
var getFileBindingsForApi =
require('fileEntryBindingUtil').getFileBindingsForApi;
var fileBindings = getFileBindingsForApi('logPrivate');
var bindFileEntryCallback = fileBindings.bindFileEntryCallback;
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
var fileSystem = bindingsAPI.compiledApi;
$Array.forEach(['dumpLogs'],
function(functionName) {
bindFileEntryCallback(functionName, apiFunctions);
});
});
exports.bindFileEntryCallback = bindFileEntryCallback;
exports.binding = binding.generate();
// 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.
// Custom binding for the Media Gallery API.
var binding = require('binding').Binding.create('mediaGalleries');
var blobNatives = requireNative('blob_natives');
var mediaGalleriesNatives = requireNative('mediaGalleries');
var blobsAwaitingMetadata = {};
var mediaGalleriesMetadata = {};
function createFileSystemObjectsAndUpdateMetadata(response) {
var result = [];
mediaGalleriesMetadata = {}; // Clear any previous metadata.
if (response) {
for (var i = 0; i < response.length; i++) {
var filesystem = mediaGalleriesNatives.GetMediaFileSystemObject(
response[i].fsid);
$Array.push(result, filesystem);
var metadata = response[i];
delete metadata.fsid;
mediaGalleriesMetadata[filesystem.name] = metadata;
}
}
return result;
}
binding.registerCustomHook(function(bindingsAPI, extensionId) {
var apiFunctions = bindingsAPI.apiFunctions;
// getMediaFileSystems, addUserSelectedFolder, and addScanResults use a
// custom callback so that they can instantiate and return an array of file
// system objects.
apiFunctions.setCustomCallback('getMediaFileSystems',
function(name, request, callback, response) {
var result = createFileSystemObjectsAndUpdateMetadata(response);
if (callback)
callback(result);
});
apiFunctions.setCustomCallback('addScanResults',
function(name, request, callback, response) {
var result = createFileSystemObjectsAndUpdateMetadata(response);
if (callback)
callback(result);
});
apiFunctions.setCustomCallback('addUserSelectedFolder',
function(name, request, callback, response) {
var fileSystems = [];
var selectedFileSystemName = "";
if (response && 'mediaFileSystems' in response &&
'selectedFileSystemIndex' in response) {
fileSystems = createFileSystemObjectsAndUpdateMetadata(
response['mediaFileSystems']);
var selectedFileSystemIndex = response['selectedFileSystemIndex'];
if (selectedFileSystemIndex >= 0) {
selectedFileSystemName = fileSystems[selectedFileSystemIndex].name;
}
}
if (callback)
callback(fileSystems, selectedFileSystemName);
});
apiFunctions.setCustomCallback('dropPermissionForMediaFileSystem',
function(name, request, callback, response) {
var galleryId = response;
if (galleryId) {
for (var key in mediaGalleriesMetadata) {
if (mediaGalleriesMetadata[key].galleryId == galleryId) {
delete mediaGalleriesMetadata[key];
break;
}
}
}
if (callback)
callback();
});
apiFunctions.setHandleRequest('getMediaFileSystemMetadata',
function(filesystem) {
if (filesystem && filesystem.name &&
filesystem.name in mediaGalleriesMetadata) {
return mediaGalleriesMetadata[filesystem.name];
}
return {
'name': '',
'galleryId': '',
'isRemovable': false,
'isMediaDevice': false,
'isAvailable': false,
};
});
apiFunctions.setUpdateArgumentsPostValidate('getMetadata',
function(mediaFile, options, callback) {
var blobUuid = blobNatives.GetBlobUuid(mediaFile)
// Store the blob in a global object to keep its refcount nonzero -- this
// prevents the object from being garbage collected before any metadata
// parsing gets to occur (see crbug.com/415792).
blobsAwaitingMetadata[blobUuid] = mediaFile;
return [blobUuid, options, callback];
});
apiFunctions.setCustomCallback('getMetadata',
function(name, request, callback, response) {
if (response.attachedImagesBlobInfo) {
for (var i = 0; i < response.attachedImagesBlobInfo.length; i++) {
var blobInfo = response.attachedImagesBlobInfo[i];
var blob = blobNatives.TakeBrowserProcessBlob(
blobInfo.blobUUID, blobInfo.type, blobInfo.size);
response.metadata.attachedImages.push(blob);
}
}
if (callback)
callback(response.metadata);
// The UUID was in position 0 in the setUpdateArgumentsPostValidate
// function.
var uuid = request.args[0];
delete blobsAwaitingMetadata[uuid];
});
});
exports.binding = binding.generate();
// 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 notifications API.
//
var binding = require('binding').Binding.create('notifications');
var sendRequest = require('sendRequest').sendRequest;
var exceptionHandler = require('uncaught_exception_handler');
var imageUtil = require('imageUtil');
var lastError = require('lastError');
var notificationsPrivate = requireNative('notifications_private');
function imageDataSetter(context, key) {
var f = function(val) {
this[key] = val;
};
return $Function.bind(f, context);
}
// A URL Spec is an object with the following keys:
// path: The resource to be downloaded.
// width: (optional) The maximum width of the image to be downloaded in device
// pixels.
// height: (optional) The maximum height of the image to be downloaded in
// device pixels.
// callback: A function to be called when the URL is complete. It
// should accept an ImageData object and set the appropriate
// field in |notificationDetails|.
function getUrlSpecs(imageSizes, notificationDetails) {
var urlSpecs = [];
// |iconUrl| might be optional for notification updates.
if (notificationDetails.iconUrl) {
$Array.push(urlSpecs, {
path: notificationDetails.iconUrl,
width: imageSizes.icon.width * imageSizes.scaleFactor,
height: imageSizes.icon.height * imageSizes.scaleFactor,
callback: imageDataSetter(notificationDetails, 'iconBitmap')
});
}
// |appIconMaskUrl| is optional.
if (notificationDetails.appIconMaskUrl) {
$Array.push(urlSpecs, {
path: notificationDetails.appIconMaskUrl,
width: imageSizes.appIconMask.width * imageSizes.scaleFactor,
height: imageSizes.appIconMask.height * imageSizes.scaleFactor,
callback: imageDataSetter(notificationDetails, 'appIconMaskBitmap')
});
}
// |imageUrl| is optional.
if (notificationDetails.imageUrl) {
$Array.push(urlSpecs, {
path: notificationDetails.imageUrl,
width: imageSizes.image.width * imageSizes.scaleFactor,
height: imageSizes.image.height * imageSizes.scaleFactor,
callback: imageDataSetter(notificationDetails, 'imageBitmap')
});
}
// Each button has an optional icon.
var buttonList = notificationDetails.buttons;
if (buttonList && typeof buttonList.length === 'number') {
var numButtons = buttonList.length;
for (var i = 0; i < numButtons; i++) {
if (buttonList[i].iconUrl) {
$Array.push(urlSpecs, {
path: buttonList[i].iconUrl,
width: imageSizes.buttonIcon.width * imageSizes.scaleFactor,
height: imageSizes.buttonIcon.height * imageSizes.scaleFactor,
callback: imageDataSetter(buttonList[i], 'iconBitmap')
});
}
}
}
return urlSpecs;
}
function replaceNotificationOptionURLs(notification_details, callback) {
var imageSizes = notificationsPrivate.GetNotificationImageSizes();
var url_specs = getUrlSpecs(imageSizes, notification_details);
if (!url_specs.length) {
callback(true);
return;
}
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(name, 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;
var stack = exceptionHandler.getExtensionStackTrace();
replaceNotificationOptionURLs(notification_details, function(success) {
if (success) {
sendRequest(that.name,
[id, notification_details, callback],
that.definition.parameters, {stack: stack});
return;
}
lastError.run(name,
'Unable to download all specified images.',
stack,
failure_function, [callback || function() {}, id]);
});
};
}
var handleCreate = genHandle('notifications.create',
function(callback, id) { callback(id); });
var handleUpdate = genHandle('notifications.update',
function(callback, id) { callback(false); });
var notificationsCustomHook = function(bindingsAPI, extensionId) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setHandleRequest('create', handleCreate);
apiFunctions.setHandleRequest('update', handleUpdate);
};
binding.registerCustomHook(notificationsCustomHook);
exports.binding = binding.generate();
// 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.
// Custom binding for the omnibox API. Only injected into the v8 contexts
// for extensions which have permission for the omnibox API.
var binding = require('binding').Binding.create('omnibox');
var eventBindings = require('event_bindings');
var sendRequest = require('sendRequest').sendRequest;
// Remove invalid characters from |text| so that it is suitable to use
// for |AutocompleteMatch::contents|.
function sanitizeString(text, shouldTrim) {
// NOTE: This logic mirrors |AutocompleteMatch::SanitizeString()|.
// 0x2028 = line separator; 0x2029 = paragraph separator.
var kRemoveChars = /(\r|\n|\t|\u2028|\u2029)/gm;
if (shouldTrim)
text = text.trimLeft();
return text.replace(kRemoveChars, '');
}
// Parses the xml syntax supported by omnibox suggestion results. Returns an
// object with two properties: 'description', which is just the text content,
// and 'descriptionStyles', which is an array of style objects in a format
// understood by the C++ backend.
function parseOmniboxDescription(input) {
var domParser = new DOMParser();
// The XML parser requires a single top-level element, but we want to
// support things like 'hello, world!'. So we wrap the
// provided text in generated root level element.
var root = domParser.parseFromString(
'' + input + '', 'text/xml');
// DOMParser has a terrible error reporting facility. Errors come out nested
// inside the returned document.
var error = root.querySelector('parsererror div');
if (error) {
throw new Error(error.textContent);
}
// Otherwise, it's valid, so build up the result.
var result = {
description: '',
descriptionStyles: []
};
// Recursively walk the tree.
function walk(node) {
for (var i = 0, child; child = node.childNodes[i]; i++) {
// Append text nodes to our description.
if (child.nodeType == Node.TEXT_NODE) {
var shouldTrim = result.description.length == 0;
result.description += sanitizeString(child.nodeValue, shouldTrim);
continue;
}
// Process and descend into a subset of recognized tags.
if (child.nodeType == Node.ELEMENT_NODE &&
(child.nodeName == 'dim' || child.nodeName == 'match' ||
child.nodeName == 'url')) {
var style = {
'type': child.nodeName,
'offset': result.description.length
};
$Array.push(result.descriptionStyles, style);
walk(child);
style.length = result.description.length - style.offset;
continue;
}
// Descend into all other nodes, even if they are unrecognized, for
// forward compat.
walk(child);
}
};
walk(root);
return result;
}
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setUpdateArgumentsPreValidate('setDefaultSuggestion',
function(suggestResult) {
if (suggestResult.content != undefined) { // null, etc.
throw new Error(
'setDefaultSuggestion cannot contain the "content" field');
}
return [suggestResult];
});
apiFunctions.setHandleRequest('setDefaultSuggestion', function(details) {
var parseResult = parseOmniboxDescription(details.description);
sendRequest(this.name, [parseResult], this.definition.parameters);
});
apiFunctions.setUpdateArgumentsPostValidate(
'sendSuggestions', function(requestId, userSuggestions) {
var suggestions = [];
for (var i = 0; i < userSuggestions.length; i++) {
var parseResult = parseOmniboxDescription(
userSuggestions[i].description);
parseResult.content = userSuggestions[i].content;
$Array.push(suggestions, parseResult);
}
return [requestId, suggestions];
});
});
eventBindings.registerArgumentMassager('omnibox.onInputChanged',
function(args, dispatch) {
var text = args[0];
var requestId = args[1];
var suggestCallback = function(suggestions) {
chrome.omnibox.sendSuggestions(requestId, suggestions);
};
dispatch([text, suggestCallback]);
});
exports.binding = binding.generate();
// 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.
// Custom binding for the pageAction API.
var binding = require('binding').Binding.create('pageAction');
var setIcon = require('setIcon').setIcon;
var sendRequest = require('sendRequest').sendRequest;
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setHandleRequest('setIcon', function(details, callback) {
setIcon(details, function(args) {
sendRequest(this.name, [args, callback], this.definition.parameters);
}.bind(this));
});
});
exports.binding = binding.generate();
// 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.
// Custom binding for the pageCapture API.
var binding = require('binding').Binding.create('pageCapture');
var handleUncaughtException = require('uncaught_exception_handler').handle;
var pageCaptureNatives = requireNative('page_capture');
var CreateBlob = pageCaptureNatives.CreateBlob;
var SendResponseAck = pageCaptureNatives.SendResponseAck;
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setCustomCallback('saveAsMHTML',
function(name, request, callback, response) {
if (response)
response = CreateBlob(response.mhtmlFilePath, response.mhtmlFileLength);
try {
callback(response);
} catch (e) {
handleUncaughtException(
'Error in chrome.pageCapture.saveAsMHTML callback', e, request.stack);
} finally {
// Notify the browser. Now that the blob is referenced from JavaScript,
// the browser can drop its reference to it.
SendResponseAck(request.id);
}
});
});
exports.binding = binding.generate();
// Copyright 2015 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 binding for the platformKeys API.
var binding = require('binding').Binding.create('platformKeys');
var SubtleCrypto = require('platformKeys.SubtleCrypto').SubtleCrypto;
var getPublicKey = require('platformKeys.getPublicKey').getPublicKey;
var internalAPI = require('platformKeys.internalAPI');
var keyModule = require('platformKeys.Key');
var Key = keyModule.Key;
var KeyType = keyModule.KeyType;
var KeyUsage = keyModule.KeyUsage;
function createPublicKey(publicKeySpki, algorithm) {
return new Key(KeyType.public, publicKeySpki, algorithm, [KeyUsage.verify],
true /* extractable */);
}
function createPrivateKey(publicKeySpki, algorithm) {
return new Key(KeyType.private, publicKeySpki, algorithm, [KeyUsage.sign],
false /* not extractable */);
}
binding.registerCustomHook(function(api) {
var apiFunctions = api.apiFunctions;
var subtleCrypto = new SubtleCrypto('' /* tokenId */);
apiFunctions.setHandleRequest(
'selectClientCertificates', function(details, callback) {
internalAPI.selectClientCertificates(details, function(matches) {
if (chrome.runtime.lastError) {
callback([]);
return;
}
callback($Array.map(matches, function(match) {
// internalAPI.selectClientCertificates returns publicExponent as
// ArrayBuffer, but it should be a Uint8Array.
if (match.keyAlgorithm.publicExponent) {
match.keyAlgorithm.publicExponent =
new Uint8Array(match.keyAlgorithm.publicExponent);
}
return match;
}));
});
});
apiFunctions.setHandleRequest(
'subtleCrypto', function() { return subtleCrypto });
apiFunctions.setHandleRequest(
'getKeyPair', function(cert, params, callback) {
getPublicKey(cert, params, function(publicKey, algorithm) {
if (chrome.runtime.lastError) {
callback();
return;
}
callback(createPublicKey(publicKey, algorithm),
createPrivateKey(publicKey, algorithm));
});
});
});
exports.binding = binding.generate();
// Copyright 2015 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.
var internalAPI = require('platformKeys.internalAPI');
var normalizeAlgorithm =
requireNative('platform_keys_natives').NormalizeAlgorithm;
// Returns the normalized parameters of |importParams|.
// Any unknown parameters will be ignored.
function normalizeImportParams(importParams) {
if (!importParams.name ||
Object.prototype.toString.call(importParams.name) != '[object String]') {
throw new Error('Algorithm: name: Missing or not a String');
}
var filteredParams = { name: importParams.name };
var hashIsNone = false;
if (importParams.hash) {
if (importParams.hash.name.toLowerCase() === 'none') {
hashIsNone = true;
// Temporarily replace |hash| by a valid WebCrypto Hash for normalization.
// This will be reverted to 'none' after normalization.
filteredParams.hash = { name: 'SHA-1' };
} else {
filteredParams.hash = { name: importParams.hash.name }
}
}
// Apply WebCrypto's algorithm normalization.
var resultParams = normalizeAlgorithm(filteredParams, 'ImportKey');
if (!resultParams ) {
throw new Error('A required parameter was missing or out-of-range');
}
if (hashIsNone) {
resultParams.hash = { name: 'none' };
}
return resultParams;
}
function combineAlgorithms(algorithm, importParams) {
// internalAPI.getPublicKey returns publicExponent as ArrayBuffer, but it
// should be a Uint8Array.
if (algorithm.publicExponent) {
algorithm.publicExponent = new Uint8Array(algorithm.publicExponent);
}
algorithm.hash = importParams.hash;
return algorithm;
}
function getPublicKey(cert, importParams, callback) {
importParams = normalizeImportParams(importParams);
internalAPI.getPublicKey(
cert, importParams.name, function(publicKey, algorithm) {
if (chrome.runtime.lastError) {
callback();
return;
}
var combinedAlgorithm = combineAlgorithms(algorithm, importParams);
callback(publicKey, combinedAlgorithm);
});
}
exports.getPublicKey = getPublicKey;
// Copyright 2015 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.
var binding = require('binding')
.Binding.create('platformKeysInternal')
.generate();
exports.selectClientCertificates = binding.selectClientCertificates;
exports.sign = binding.sign;
exports.getPublicKey = binding.getPublicKey;
// 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.
var utils = require('utils');
/**
* Enum of possible key types (subset of WebCrypto.KeyType).
* @enum {string}
*/
var KeyType = {
public: 'public',
private: 'private'
};
/**
* Enum of possible key usages (subset of WebCrypto.KeyUsage).
* @enum {string}
*/
var KeyUsage = {
sign: 'sign',
verify: 'verify'
};
/**
* Implementation of WebCrypto.Key used in enterprise.platformKeys.
* @param {KeyType} type The type of the new key.
* @param {ArrayBuffer} publicKeySpki The Subject Public Key Info in DER
* encoding.
* @param {KeyAlgorithm} algorithm The algorithm identifier.
* @param {KeyUsage[]} usages The allowed key usages.
* @param {boolean} extractable Whether the key is extractable.
* @constructor
*/
var KeyImpl = function(type, publicKeySpki, algorithm, usages, extractable) {
this.type = type;
this.spki = publicKeySpki;
this.algorithm = algorithm;
this.usages = usages;
this.extractable = extractable;
};
var KeyBase = function() {};
Object.defineProperty(KeyBase.prototype, 'algorithm', {
enumerable: true,
get: function() {
return utils.deepCopy(privates(this).impl.algorithm);
}
});
var Key = utils.expose(
'Key',
KeyImpl,
{superclass: KeyBase, readonly: ['extractable', 'type', 'usages']});
/**
* Returns |key|'s Subject Public Key Info. Throws an exception if |key| is not
* a valid Key object.
* @param {Key} key
* @return {ArrayBuffer} The Subject Public Key Info in DER encoding of |key|.
*/
function getSpki(key) {
if (!privates(key))
throw new Error('Invalid key object.');
var keyImpl = privates(key).impl;
if (!keyImpl || !keyImpl.spki)
throw new Error('Invalid key object.');
return keyImpl.spki;
}
exports.Key = Key;
exports.KeyType = KeyType;
exports.KeyUsage = KeyUsage;
exports.getSpki = getSpki;
// Copyright 2015 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.
var utils = require('utils');
var internalAPI = require('platformKeys.internalAPI');
var keyModule = require('platformKeys.Key');
var getSpki = keyModule.getSpki;
var KeyUsage = keyModule.KeyUsage;
var normalizeAlgorithm =
requireNative('platform_keys_natives').NormalizeAlgorithm;
// This error is thrown by the internal and public API's token functions and
// must be rethrown by this custom binding. Keep this in sync with the C++ part
// of this API.
var errorInvalidToken = "The token is not valid.";
// The following errors are specified in WebCrypto.
// TODO(pneubeck): These should be DOMExceptions.
function CreateNotSupportedError() {
return new Error('The algorithm is not supported');
}
function CreateInvalidAccessError() {
return new Error('The requested operation is not valid for the provided key');
}
function CreateDataError() {
return new Error('Data provided to an operation does not meet requirements');
}
function CreateSyntaxError() {
return new Error('A required parameter was missing or out-of-range');
}
function CreateOperationError() {
return new Error('The operation failed for an operation-specific reason');
}
// Catches an |internalErrorInvalidToken|. If so, forwards it to |reject| and
// returns true.
function catchInvalidTokenError(reject) {
if (chrome.runtime.lastError &&
chrome.runtime.lastError.message == errorInvalidToken) {
reject(chrome.runtime.lastError);
return true;
}
return false;
}
/**
* Implementation of WebCrypto.SubtleCrypto used in platformKeys and
* enterprise.platformKeys.
* @param {string} tokenId The id of the backing Token.
* @constructor
*/
var SubtleCryptoImpl = function(tokenId) {
this.tokenId = tokenId;
};
SubtleCryptoImpl.prototype.sign = function(algorithm, key, dataView) {
var subtleCrypto = this;
return new Promise(function(resolve, reject) {
if (key.type != 'private' || key.usages.indexOf(KeyUsage.sign) == -1)
throw CreateInvalidAccessError();
var normalizedAlgorithmParameters =
normalizeAlgorithm(algorithm, 'Sign');
if (!normalizedAlgorithmParameters) {
// TODO(pneubeck): It's not clear from the WebCrypto spec which error to
// throw here.
throw CreateSyntaxError();
}
// Create an ArrayBuffer that equals the dataView. Note that dataView.buffer
// might contain more data than dataView.
var data = dataView.buffer.slice(dataView.byteOffset,
dataView.byteOffset + dataView.byteLength);
internalAPI.sign(subtleCrypto.tokenId,
getSpki(key),
key.algorithm.hash.name,
data,
function(signature) {
if (catchInvalidTokenError(reject))
return;
if (chrome.runtime.lastError) {
reject(CreateOperationError());
return;
}
resolve(signature);
});
});
};
SubtleCryptoImpl.prototype.exportKey = function(format, key) {
return new Promise(function(resolve, reject) {
if (format == 'pkcs8') {
// Either key.type is not 'private' or the key is not extractable. In both
// cases the error is the same.
throw CreateInvalidAccessError();
} else if (format == 'spki') {
if (key.type != 'public')
throw CreateInvalidAccessError();
resolve(getSpki(key));
} else {
// TODO(pneubeck): It should be possible to export to format 'jwk'.
throw CreateNotSupportedError();
}
});
};
// Required for subclassing.
exports.SubtleCryptoImpl = SubtleCryptoImpl
exports.SubtleCrypto =
utils.expose('SubtleCrypto',
SubtleCryptoImpl,
{functions:['sign', 'exportKey']});
// 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.
// Returns the intersection of the arrays |a| and |b|, which do not have to be
// sorted.
function intersect(a, b) {
var result = [];
for (var i = 0; i < a.length; i++) {
if (b.indexOf(a[i]) >= 0)
result.push(a[i]);
}
return result;
};
exports.intersect = intersect;
// 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.
// Custom binding for the syncFileSystem API.
var binding = require('binding').Binding.create('syncFileSystem');
var eventBindings = require('event_bindings');
var fileSystemNatives = requireNative('file_system_natives');
var syncFileSystemNatives = requireNative('sync_file_system');
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
// Functions which take in an [instanceOf=FileEntry].
function bindFileEntryFunction(functionName) {
apiFunctions.setUpdateArgumentsPostValidate(
functionName, function(entry, callback) {
var fileSystemUrl = entry.toURL();
return [fileSystemUrl, callback];
});
}
$Array.forEach(['getFileStatus'], bindFileEntryFunction);
// Functions which take in a FileEntry array.
function bindFileEntryArrayFunction(functionName) {
apiFunctions.setUpdateArgumentsPostValidate(
functionName, function(entries, callback) {
var fileSystemUrlArray = [];
for (var i=0; i < entries.length; i++) {
$Array.push(fileSystemUrlArray, entries[i].toURL());
}
return [fileSystemUrlArray, callback];
});
}
$Array.forEach(['getFileStatuses'], bindFileEntryArrayFunction);
// Functions which take in an [instanceOf=DOMFileSystem].
function bindFileSystemFunction(functionName) {
apiFunctions.setUpdateArgumentsPostValidate(
functionName, function(filesystem, callback) {
var fileSystemUrl = filesystem.root.toURL();
return [fileSystemUrl, callback];
});
}
$Array.forEach(['getUsageAndQuota'], bindFileSystemFunction);
// Functions which return an [instanceOf=DOMFileSystem].
apiFunctions.setCustomCallback('requestFileSystem',
function(name, request, callback, response) {
var result = null;
if (response) {
result = syncFileSystemNatives.GetSyncFileSystemObject(
response.name, response.root);
}
if (callback)
callback(result);
});
// Functions which return an array of FileStatusInfo object
// which has [instanceOf=FileEntry].
apiFunctions.setCustomCallback('getFileStatuses',
function(name, request, callback, response) {
var results = [];
if (response) {
for (var i = 0; i < response.length; i++) {
var result = {};
var entry = response[i].entry;
result.fileEntry = fileSystemNatives.GetFileEntry(
entry.fileSystemType,
entry.fileSystemName,
entry.rootUrl,
entry.filePath,
entry.isDirectory);
result.status = response[i].status;
result.error = response[i].error;
$Array.push(results, result);
}
}
if (callback)
callback(results);
});
});
eventBindings.registerArgumentMassager(
'syncFileSystem.onFileStatusChanged', function(args, dispatch) {
// Make FileEntry object using all the base string fields.
var fileEntry = fileSystemNatives.GetFileEntry(
args[0].fileSystemType,
args[0].fileSystemName,
args[0].rootUrl,
args[0].filePath,
args[0].isDirectory);
// Combine into a single dictionary.
var fileInfo = new Object();
fileInfo.fileEntry = fileEntry;
fileInfo.status = args[1];
if (fileInfo.status == "synced") {
fileInfo.action = args[2];
fileInfo.direction = args[3];
}
dispatch([fileInfo]);
});
exports.binding = binding.generate();
// 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.
// Custom binding for the systemIndicator API.
// TODO(dewittj) Refactor custom binding to reduce redundancy between the
// extension action APIs.
var binding = require('binding').Binding.create('systemIndicator');
var setIcon = require('setIcon').setIcon;
var sendRequest = require('sendRequest').sendRequest;
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
apiFunctions.setHandleRequest('setIcon', function(details, callback) {
setIcon(details, function(args) {
sendRequest(this.name, [args, callback], this.definition.parameters);
}.bind(this));
});
});
exports.binding = binding.generate();
// 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.
// Custom binding for the Tab Capture API.
var binding = require('binding').Binding.create('tabCapture');
binding.registerCustomHook(function(bindingsAPI, extensionId) {
var apiFunctions = bindingsAPI.apiFunctions;
function proxyToGetUserMedia(name, request, callback, response) {
if (!callback)
return;
// TODO(miu): Propagate exceptions and always provide a useful error when
// callback() is invoked with a null argument. http://crbug.com/463679
if (response) {
var options = {};
if (response.audioConstraints)
options.audio = response.audioConstraints;
if (response.videoConstraints)
options.video = response.videoConstraints;
try {
navigator.webkitGetUserMedia(options,
function(stream) { callback(stream); },
function(exception) { callback(null); });
} catch (e) {
callback(null);
}
} else {
callback(null);
}
}
apiFunctions.setCustomCallback('capture', proxyToGetUserMedia);
apiFunctions.setCustomCallback('captureOffscreenTab', proxyToGetUserMedia);
});
exports.binding = binding.generate();
// 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.
// Custom binding for the tabs API.
var binding = require('binding').Binding.create('tabs');
var messaging = require('messaging');
var tabsNatives = requireNative('tabs');
var OpenChannelToTab = tabsNatives.OpenChannelToTab;
var sendRequestIsDisabled = requireNative('process').IsSendRequestDisabled();
var forEach = require('utils').forEach;
binding.registerCustomHook(function(bindingsAPI, extensionId) {
var apiFunctions = bindingsAPI.apiFunctions;
var tabs = bindingsAPI.compiledApi;
apiFunctions.setHandleRequest('connect', function(tabId, connectInfo) {
var name = '';
var frameId = -1;
if (connectInfo) {
name = connectInfo.name || name;
frameId = connectInfo.frameId;
if (typeof frameId == 'undefined' || frameId < 0)
frameId = -1;
}
var portId = OpenChannelToTab(tabId, frameId, extensionId, name);
return messaging.createPort(portId, name);
});
apiFunctions.setHandleRequest('sendRequest',
function(tabId, request, responseCallback) {
if (sendRequestIsDisabled)
throw new Error(sendRequestIsDisabled);
var port = tabs.connect(tabId, {name: messaging.kRequestChannel});
messaging.sendMessageImpl(port, request, responseCallback);
});
apiFunctions.setHandleRequest('sendMessage',
function(tabId, message, options, responseCallback) {
var connectInfo = {
name: messaging.kMessageChannel
};
if (options) {
forEach(options, function(k, v) {
connectInfo[k] = v;
});
}
var port = tabs.connect(tabId, connectInfo);
messaging.sendMessageImpl(port, message, responseCallback);
});
});
exports.binding = binding.generate();
// 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.
function watchForTag(tagName, cb) {
if (!document.body)
return;
function findChildTags(queryNode) {
$Array.forEach(queryNode.querySelectorAll(tagName), function(node) {
cb(node);
});
}
// Query tags already in the document.
findChildTags(document.body);
// Observe the tags added later.
var documentObserver = new MutationObserver(function(mutations) {
$Array.forEach(mutations, function(mutation) {
$Array.forEach(mutation.addedNodes, function(addedNode) {
if (addedNode.nodeType == Node.ELEMENT_NODE) {
if (addedNode.tagName == tagName)
cb(addedNode);
findChildTags(addedNode);
}
});
});
});
documentObserver.observe(document, {subtree: true, childList: true});
}
// Expose a function to watch the |tagName| introduction via mutation observer.
//
// We employee mutation observer to watch on any introduction of |tagName|
// within document so that we may handle it accordingly (either creating it or
// reporting error due to lack of permission).
// Think carefully about when to call this. On one hand, mutation observer
// functions on document, so we need to make sure document is finished
// parsing. To satisfy this, document.readyState has to be "interactive" or
// after. On the other hand, we intend to do this as early as possible so that
// developer would have no chance to bring in any conflicted property. To meet
// this requirement, we choose "readystatechange" event of window and use
// capturing way.
function addTagWatcher(tagName, cb) {
var useCapture = true;
window.addEventListener('readystatechange', function listener(event) {
if (document.readyState == 'loading')
return;
watchForTag(tagName, cb);
window.removeEventListener(event.type, listener, useCapture);
}, useCapture);
}
exports.addTagWatcher = addTagWatcher;
// 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.
// Custom binding for the tts API.
var binding = require('binding').Binding.create('tts');
var idGenerator = requireNative('id_generator');
var sendRequest = require('sendRequest').sendRequest;
var lazyBG = requireNative('lazy_background_page');
binding.registerCustomHook(function(api) {
var apiFunctions = api.apiFunctions;
var tts = api.compiledApi;
var handlers = {};
function ttsEventListener(event) {
var eventHandler = handlers[event.srcId];
if (eventHandler) {
eventHandler({
type: event.type,
charIndex: event.charIndex,
errorMessage: event.errorMessage
});
if (event.isFinalEvent) {
delete handlers[event.srcId];
// Balanced in 'speak' handler.
lazyBG.DecrementKeepaliveCount();
}
}
}
// This file will get run if an extension needs the ttsEngine permission, but
// it doesn't necessarily have the tts permission. If it doesn't, trying to
// add a listener to chrome.tts.onEvent will fail.
// See http://crbug.com/122474.
try {
tts.onEvent.addListener(ttsEventListener);
} catch (e) {}
apiFunctions.setHandleRequest('speak', function() {
var args = arguments;
if (args.length > 1 && args[1] && args[1].onEvent) {
var id = idGenerator.GetNextId();
args[1].srcId = id;
handlers[id] = args[1].onEvent;
// Keep the page alive until the event finishes.
// Balanced in eventHandler.
lazyBG.IncrementKeepaliveCount();
}
sendRequest(this.name, args, this.definition.parameters);
return id;
});
});
exports.binding = binding.generate();
// 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.
// Custom binding for the ttsEngine API.
var binding = require('binding').Binding.create('ttsEngine');
var eventBindings = require('event_bindings');
eventBindings.registerArgumentMassager('ttsEngine.onSpeak',
function(args, dispatch) {
var text = args[0];
var options = args[1];
var requestId = args[2];
var sendTtsEvent = function(event) {
chrome.ttsEngine.sendTtsEvent(requestId, event);
};
dispatch([text, options, sendTtsEvent]);
});
exports.binding = binding.generate();
// Copyright 2015 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 binding for the webrtcDesktopCapturePrivate API.
var binding = require('binding').Binding.create('webrtcDesktopCapturePrivate');
var sendRequest = require('sendRequest').sendRequest;
var idGenerator = requireNative('id_generator');
binding.registerCustomHook(function(bindingsAPI) {
var apiFunctions = bindingsAPI.apiFunctions;
var pendingRequests = {};
function onRequestResult(id, result) {
if (id in pendingRequests) {
var callback = pendingRequests[id];
delete pendingRequests[id];
callback(result);
}
}
apiFunctions.setHandleRequest('chooseDesktopMedia',
function(sources, request, callback) {
var id = idGenerator.GetNextId();
pendingRequests[id] = callback;
sendRequest(this.name,
[id, sources, request, onRequestResult.bind(null, id)],
this.definition.parameters, {});
return id;
});
apiFunctions.setHandleRequest('cancelChooseDesktopMedia', function(id) {
if (id in pendingRequests) {
delete pendingRequests[id];
sendRequest(this.name, [id], this.definition.parameters, {});
}
});
});
exports.binding = binding.generate();
// 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.
// Custom binding for the webstore API.
var webstoreNatives = requireNative('webstore');
var Event = require('event_bindings').Event;
function Installer() {
this._pendingInstall = null;
this.onInstallStageChanged =
new Event(null, [{name: 'stage', type: 'string'}], {unmanaged: true});
this.onDownloadProgress =
new Event(null, [{name: 'progress', type: 'number'}], {unmanaged: true});
}
Installer.prototype.install = function(url, onSuccess, onFailure) {
if (this._pendingInstall)
throw new Error('A Chrome Web Store installation is already pending.');
if (url !== undefined && typeof(url) !== 'string') {
throw new Error(
'The Chrome Web Store item link URL parameter must be a string.');
}
if (onSuccess !== undefined && typeof(onSuccess) !== 'function')
throw new Error('The success callback parameter must be a function.');
if (onFailure !== undefined && typeof(onFailure) !== 'function')
throw new Error('The failure callback parameter must be a function.');
// Since we call Install() with a bool for if we have listeners, listeners
// must be set prior to the inline installation starting (this is also
// noted in the Event documentation in
// chrome/common/extensions/api/webstore.json).
var installId = webstoreNatives.Install(
this.onInstallStageChanged.hasListeners(),
this.onDownloadProgress.hasListeners(),
url,
onSuccess,
onFailure);
if (installId !== undefined) {
this._pendingInstall = {
installId: installId,
onSuccess: onSuccess,
onFailure: onFailure
};
}
};
Installer.prototype.onInstallResponse =
function(installId, success, error, resultCode) {
var pendingInstall = this._pendingInstall;
if (!pendingInstall || pendingInstall.installId != installId) {
// TODO(kalman): should this be an error?
return;
}
try {
if (success && pendingInstall.onSuccess)
pendingInstall.onSuccess();
else if (!success && pendingInstall.onFailure)
pendingInstall.onFailure(error, resultCode);
} catch (e) {
console.error('Exception in chrome.webstore.install response handler: ' +
e.stack);
} finally {
this._pendingInstall = null;
}
};
Installer.prototype.onInstallStageChanged = function(installStage) {
this.onInstallStageChanged.dispatch(installStage);
};
Installer.prototype.onDownloadProgress = function(progress) {
this.onDownloadProgress.dispatch(progress);
};
var installer = new Installer();
var chromeWebstore = {
install: function (url, onSuccess, onFailure) {
installer.install(url, onSuccess, onFailure);
},
onInstallStageChanged: installer.onInstallStageChanged,
onDownloadProgress: installer.onDownloadProgress
};
exports.binding = chromeWebstore;
// Called by webstore_bindings.cc.
exports.onInstallResponse =
Installer.prototype.onInstallResponse.bind(installer);
exports.onInstallStageChanged =
Installer.prototype.onInstallStageChanged.bind(installer);
exports.onDownloadProgress =
Installer.prototype.onDownloadProgress.bind(installer);
// 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.
exports.didCreateDocumentElement = function() {
var root = document.documentElement.createShadowRoot();
root.appendChild(document.createElement('style')).innerText =
// TODO(jeremya): switch this to use automatic inlining once grit
// supports inlining into JS. See http://crbug.com/146319.
"x-titlebar { height: 24px; width: 100%; " +
"position: fixed; left: 0; top: 0; }\n" +
"div { margin-top: 24px; position: absolute; top: 0px; width: 100%; " +
"-webkit-widget-region: region(control rectangle); }\n" +
":-webkit-full-screen * { display: none; }\n" +
":-webkit-full-screen-document * { display: none; }\n" +
"div:-webkit-full-screen, div:-webkit-full-screen-document { " +
"margin-top: 0; }\n" +
"button { -webkit-widget-region: region(control rectangle); }\n" +
"button.close { border: 0; background-color: transparent; " +
"width: 16px; height: 16px; " +
"position: absolute; right: 4px; top: 4px; }\n" +
"button.close { background-image: url(data:image/png;base64," +
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA9ElEQVQ4T7VTQQ6CMBCk0H" +
"AyIfAQbiZ+QHyDL/QLxqvx4MWDB+MvFAWMAuKsacmmSjkQSDbQ2Z3Z3WkQzsBHDOQ7owgs" +
"MdUacTGmi3BeIFYcNycgciGlfFRVtcd3qoojz/PmdV0XOD8RGy1iCoQgT5G8IyREjni7IC" +
"cg58ilwA7A8i4BwgMUxkKIV9M0PggTAoFlJpnwLhO5iEuFapq2s20CyoWIGbpeaRICyrI8" +
"89FtAtqwGxdQ65yYsV8NcwVN5obR/uTJW4mQsfp2fgToGjPqbBjWeoJVfNRsbSskSO7+7B" +
"sAiznZdgu6Qe97lH+htysv+AA10msRAt5JYQAAAABJRU5ErkJggg==); }\n" +
"button.close:hover { background-image: url(data:image/png;base64," +
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABTElEQVQ4T2NkoBAwUqifAc" +
"WA////KwANFAPiV4yMjA+QDcclBzcApCA6Otpz2bJluQkJCf3z58/fDTMEnxyyAWZADQuA" +
"tj4B4ncpKSnbZs+efQjkCqjBmUDmMyD+ADSwD6j2FEgOxQWJiYmuCxYscIYawpWamnr89+" +
"/fHECxbKjmB2VlZbs6OzsvwFyHEQZATXZz5syxAGr4BMR8QCwJDYvn1dXVO1taWi4ihw9G" +
"LID8m5aWZgt0viXUEBaQAUDNh9E1o3gBFuIgA6Be8QKK3QXiLyA5oNMvIDsdph7DC9AASw" +
"cquI9sAJDNk5GRcX769OlHsXoBKapAoQ2KiQcgPwMDkbGrq8sGyP8DChNQwM6aNeswRiAC" +
"DYBF4yOgwnuwAAM5NTMz03rGjBnWsIAFql2ANxqB/l2B7F/kgCUYjUBbyEvKsFAllaY4Nw" +
"IAmJDPEd+LFvYAAAAASUVORK5CYII=); }\n" +
"button.close:active { background-image: url(data:image/png;base64," +
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAZ0lEQVQ4T2NkoBAwUqifge" +
"oG2AFd1AfERUB8CM11WOXQXXAGSROyITDNMGkTGAPdAHSFIENAAOQqGEBxHbYwQDcE2ScY" +
"XsMViNgMwRYuOGOBIgMo8gLFgUi1aCQ7IZGcNaieF0h2AQCMABwRdsuhtQAAAABJRU5Erk" +
"Jggg==); }\n"
var titlebar = root.appendChild(document.createElement('x-titlebar'));
var closeButton = titlebar.appendChild(document.createElement('button'));
closeButton.className = 'close'
closeButton.addEventListener('click', function() { window.close(); });
var container = root.appendChild(document.createElement('div'));
container.appendChild(document.createElement('content'));
}
PNG
IHDR * * o `IDATx^1
C
n.3(A$ހC;ߎDŽ,`N3QlyJ Ag
uJ!C7'*膟.Ms* IENDB`PNG
IHDR J A u IDATx^Qj1@Q!
##K($y ,;ltYvBzO(#DQ6&}ɝE'̲,9VF|۲&ï&~!jY͑aRoFZ7'!fg7m>w%Za--4ȿޤI] IENDB`PNG
IHDR &N: