diff options
author | Kristian Monsen <kristianm@google.com> | 2011-06-28 21:49:31 +0100 |
---|---|---|
committer | Kristian Monsen <kristianm@google.com> | 2011-07-08 17:55:00 +0100 |
commit | ddb351dbec246cf1fab5ec20d2d5520909041de1 (patch) | |
tree | 158e3fb57bdcac07c7f1e767fde3c70687c9fbb1 /chrome/common/extensions | |
parent | 6b92e04f5f151c896e3088e86f70db7081009308 (diff) | |
download | external_chromium-ddb351dbec246cf1fab5ec20d2d5520909041de1.zip external_chromium-ddb351dbec246cf1fab5ec20d2d5520909041de1.tar.gz external_chromium-ddb351dbec246cf1fab5ec20d2d5520909041de1.tar.bz2 |
Merge Chromium at r12.0.742.93: Initial merge by git
Change-Id: Ic5ee2fec31358bbee305f7e915442377bfa6cda6
Diffstat (limited to 'chrome/common/extensions')
143 files changed, 13057 insertions, 1886 deletions
diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index d23f1ae..04389d0 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -191,6 +191,44 @@ } }, { + "name": "isAllowedIncognitoAccess", + "type": "function", + "description": "Retrieves the state of the extension's access to Incognito-mode (as determined by the user-controlled 'Allowed in Incognito' checkbox.", + "min_version": "12.0.706.0", + "parameters": [ + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "isAllowedAccess", + "type": "boolean", + "description": "True if the extension has access to Incognito mode, false otherwise." + } + ] + } + ] + }, + { + "name": "isAllowedFileSchemeAccess", + "type": "function", + "description": "Retrieves the state of the extension's access to the 'file://' scheme (as determined by the user-controlled 'Allow access to File URLs' checkbox.", + "min_version": "12.0.706.0", + "parameters": [ + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "isAllowedAccess", + "type": "boolean", + "description": "True if the extension can access the 'file://' scheme, false otherwise." + } + ] + } + ] + }, + { "name": "setUpdateUrlData", "type": "function", "description": "Sets the value of the ap CGI parameter used in the extension's update URL. This value is ignored for extensions that are hosted in the Chrome Extension Gallery.", @@ -680,8 +718,8 @@ "incognito": {"type": "boolean", "description": "Whether the window is incognito."}, "type": { "type": "string", - "description": "The type of browser window this is.", - "enum": ["normal", "popup", "app"] + "description": "The type of browser window this is. The 'panel' type requires the 'experimental' permission.", + "enum": ["normal", "popup", "panel", "app"] } } } @@ -791,12 +829,13 @@ "top": {"type": "integer", "optional": true, "description": "The number of pixels to position the new window from the top edge of the screen. If not specified, the new window is offset naturally from the last focusd window."}, "width": {"type": "integer", "minimum": 0, "optional": true, "description": "The width in pixels of the new window. If not specified defaults to a natural width."}, "height": {"type": "integer", "minimum": 0, "optional": true, "description": "The height in pixels of the new window. If not specified defaults to a natural height."}, + "focused": {"type": "boolean", "optional": true, "description": "If true, opens an active window. If false, opens an inactive window."}, "incognito": {"type": "boolean", "optional": true, "description": "Whether the new window should be an incognito window."}, "type": { "type": "string", "optional": true, - "description": "Specifies what type of browser window to create.", - "enum": ["normal", "popup"] + "description": "Specifies what type of browser window to create. The 'panel' type requires the 'experimental' permission.", + "enum": ["normal", "popup", "panel"] } }, "optional": true @@ -828,7 +867,7 @@ "top": {"type": "integer", "optional": true, "description": "The offset from the top edge of the screen to move the window to in pixels."}, "width": {"type": "integer", "minimum": 0, "optional": true, "description": "The width to resize the window to in pixels."}, "height": {"type": "integer", "minimum": 0, "optional": true, "description": "The height to resize the window to in pixels."}, - "focused": {"type": "boolean", "optional": true, "description": "It true, brings the window to the front. If false, brings the next window in the z-order to the front."} + "focused": {"type": "boolean", "optional": true, "description": "If true, brings the window to the front. If false, brings the next window in the z-order to the front."} } }, { @@ -1202,7 +1241,7 @@ { "name": "captureVisibleTab", "type": "function", - "description": "Captures the visible area of the currently selected tab in the specified window.", + "description": "Captures the visible area of the currently selected tab in the specified window. You must have <a href='manifest.html#permissions'>host permission</a> for the URL displayed by the tab.", "parameters": [ { "type": "integer", @@ -1215,7 +1254,6 @@ "type": "object", "name": "options", "optional": true, - "min_version": "5.0.372.0", "description": "Set parameters of image capture, such as the format of the resulting image.", "properties": { "format": { @@ -2777,12 +2815,13 @@ "properties": { "type": { "type": "string", + "enum": ["normal", "checkbox", "radio", "separator"], "optional": true, - "description": "The type of menu item - one of 'normal', 'checkbox', 'radio', or 'separator'. Defaults to 'normal' if not specified." + "description": "The type of menu item. Defaults to 'normal' if not specified." }, "title": { "type": "string", - "optional": "true", + "optional": true, "description": "The text to be displayed in the item; this is <em>required</em> unless <em>type</em> is 'separator'. When the context is 'selection', you can use <code>%s</code> within the string to show the selected text. For example, if this parameter's value is \"Translate '%s' to Pig Latin\" and the user selects the word \"cool\", the context menu item for the selection is \"Translate 'cool' to Pig Latin\"." }, "checked": { @@ -2792,10 +2831,13 @@ }, "contexts": { "type": "array", - "items": {"type": "string"}, + "items": { + "type": "string", + "enum": ["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"] + }, "minItems": 1, "optional": true, - "description": "List of contexts this menu item will appear in. Legal values are: 'all', 'page', 'selection', 'link', 'editable', 'image', 'video', and 'audio'. Defaults to ['page']." + "description": "List of contexts this menu item will appear in. Defaults to ['page'] if not specified." }, "onclick": { "type": "function", @@ -2859,11 +2901,12 @@ "properties": { "type": { "type": "string", + "enum": ["normal", "checkbox", "radio", "separator"], "optional": true }, "title": { "type": "string", - "optional": "true" + "optional": true }, "checked": { "type": "boolean", @@ -2871,7 +2914,10 @@ }, "contexts": { "type": "array", - "items": {"type": "string"}, + "items": { + "type": "string", + "enum": ["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"] + }, "minItems": 1, "optional": true }, @@ -3080,6 +3126,39 @@ "events": [] }, { + "namespace": "chromeosInfoPrivate", + "nodoc": "true", + "functions": [ + { + "name": "get", + "description": "Fetches customization values for the given property names. See property names in the declaration of the returned dictionary.", + "type": "function", + "parameters": [ + { + "name": "propertyNames", + "type": "array", + "item": {"type": "string", "description": "Chrome OS Property name"} + }, + { + "name": "callback", + "type": "function", + "parameters": [ + { + "name": "propertiesDictionary", + "type": "object", + "description": "Dictionary which contains all requested properties", + "properties": { + "hwid": {"type": "string", "optional": "true", "description": "Hardware ID"}, + "homeProvider" : {"type": "string", "optional": "true", "description": "Home provider which is used by the cellular device"} + } + } + ] + } + ] + } + ] + }, + { "namespace": "cookies", "types": [ { @@ -3191,6 +3270,7 @@ "type": "function", "name": "callback", "optional": true, + "min_version": "11.0.674.0", "parameters": [ { "name": "cookie", "$ref": "Cookie", "optional": true, "description": "Contains details about the cookie that's been set. If setting failed for any reason, this will be \"null\", and \"chrome.extension.lastError\" will be set." @@ -3218,6 +3298,7 @@ "type": "function", "name": "callback", "optional": true, + "min_version": "11.0.674.0", "parameters": [ { "name": "details", @@ -3255,14 +3336,15 @@ { "name": "onChanged", "type": "function", - "description": "Fired when a cookie is set or removed.", + "description": "Fired when a cookie is set or removed. As a special case, note that updating a cookie's properties is implemented as a two step process: the cookie to be updated is first removed entirely, generating a notification with \"cause\" of \"overwrite\" . Afterwards, a new cookie is written with the updated values, generating a second notification with \"cause\" \"explicit\".", "parameters": [ { "type": "object", "name": "changeInfo", "properties": { "removed": {"type": "boolean", "description": "True if a cookie was removed."}, - "cookie": {"$ref": "Cookie", "description": "Information about the cookie that was set or removed."} + "cookie": {"$ref": "Cookie", "description": "Information about the cookie that was set or removed."}, + "cause": {"min_version": "12.0.707.0", "type": "string", "enum": ["evicted", "expired", "explicit", "expired_overwrite", "overwrite"], "description": "The underlying reason behind the cookie's change. If a cookie was inserted, or removed via an explicit call to \"chrome.cookies.remove\", \"cause\" will be \"explicit\". If a cookie was automatically removed due to expiry, \"cause\" will be \"expired\". If a cookie was removed due to being overwritten with an already-expired expiration date, \"cause\" will be set to \"expired_overwrite\". If a cookie was automatically removed due to garbage collection, \"cause\" will be \"evicted\". If a cookie was automatically removed due to a \"set\" call that overwrote it, \"cause\" will be \"overwrite\". Plan your response accordingly."} } } ] @@ -3337,7 +3419,7 @@ "tabId": {"type": "integer", "description": "The ID of the tab in which the navigation is about to occur."}, "url": {"type": "string"}, "frameId": {"type": "integer", "description": "0 indicates the navigation happens in the tab content window; positive value indicates navigation in a subframe. Frame IDs are unique within a tab."}, - "requestId": {"type": "integer", "description": "The ID of the request to retrieve the document of this navigation. Note that this event is fired prior to the corresponding chrome.experimental.webRequest.onBeforeRequest."}, + "requestId": {"type": "string", "description": "The ID of the request to retrieve the document of this navigation. Note that this event is fired prior to the corresponding chrome.experimental.webRequest.onBeforeRequest."}, "timeStamp": {"type": "number", "description": "The time when the browser was about to start the navigation, in milliseconds since the epoch."} } } @@ -3456,6 +3538,23 @@ "tabId": { "type": "integer", "optional": true }, "windowId": { "type": "integer", "optional": true } } + }, + { + "id": "BlockingResponse", + "type": "object", + "description": "Return value for event handlers that have the 'blocking' extraInfoSpec applied. Allows the event handler to modify network requests.", + "properties": { + "cancel": { + "type": "boolean", + "optional": true, + "description": "If true, the request is cancelled. Used in onBeforeRequest, this prevents the request from being sent." + }, + "redirectUrl": { + "type": "string", + "optional": true, + "description": "If set, the original request is prevented from being sent and is instead redirected to the given URL." + } + } } ], "functions": [ @@ -3479,12 +3578,28 @@ "description": "Array of extra information that should be passed to the listener function.", "items": { "type": "string", - "enum": ["requestLine", "requestHeaders", "statusLine", "responseHeaders", "redirectRequestLine", "redirectRequestHeaders"] + "enum": ["requestLine", "requestHeaders", "statusLine", "responseHeaders", "redirectRequestLine", "redirectRequestHeaders", "blocking"] } }, {"type": "string", "name": "eventName"}, {"type": "string", "name": "subEventName"} ] + }, + { + "name": "eventHandled", + "nodoc": true, + "type": "function", + "description": "Used internally to send a response for a blocked event.", + "parameters": [ + {"type": "string", "name": "eventName"}, + {"type": "string", "name": "subEventName"}, + {"type": "string", "name": "requestId"}, + { + "$ref": "BlockingResponse", + "optional": true, + "name": "response" + } + ] } ], "events": [ @@ -3497,7 +3612,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request. Request IDs are unique within a browser session. As a result, they could be used to relate different events of the same request."}, + "requestId": {"type": "string", "description": "The ID of the request. Request IDs are unique within a browser session. As a result, they could be used to relate different events of the same request."}, "url": {"type": "string"}, "method": {"type": "string", "description": "Standard HTTP method."}, "tabId": {"type": "integer", "description": "The ID of the tab in which the request takes place. Set to null if the request isn't related to a tab."}, @@ -3508,6 +3623,22 @@ ] }, { + "name": "onBeforeSendHeaders", + "type": "function", + "description": "Fires before sending an HTTP request, once the request headers are available.", + "parameters": [ + { + "type": "object", + "name": "details", + "properties": { + "requestId": {"type": "string", "description": "The ID of the request. Request IDs are unique within a browser session. As a result, they could be used to relate different events of the same request."}, + "url": {"type": "string"}, + "timeStamp": {"type": "number", "description": "The time when the browser was about to send headers, in milliseconds since the epoch."} + } + } + ] + }, + { "name": "onRequestSent", "type": "function", "description": "Fires when a request is sent to the server.", @@ -3516,7 +3647,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request."}, + "requestId": {"type": "string", "description": "The ID of the request."}, "url": {"type": "string"}, "ip": {"type": "string", "description": "The server IP address that is actually connected to. Note that it may be a literal IPv6 address."}, "timeStamp": {"type": "number", "description": "The time when the browser finished sending the request, in milliseconds since the epoch."} @@ -3533,7 +3664,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request."}, + "requestId": {"type": "string", "description": "The ID of the request."}, "url": {"type": "string"}, "statusCode": {"type": "integer", "description": "Standard HTTP status code returned by the server."}, "timeStamp": {"type": "number", "description": "The time when the status line and response headers were received, in milliseconds since the epoch."} @@ -3550,7 +3681,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request."}, + "requestId": {"type": "string", "description": "The ID of the request."}, "url": {"type": "string", "description": "The URL of the current request."}, "statusCode": {"type": "integer", "description": "Standard HTTP status code returned by the server."}, "redirectUrl": {"type": "string", "description": "The new URL."}, @@ -3568,7 +3699,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request."}, + "requestId": {"type": "string", "description": "The ID of the request."}, "url": {"type": "string", "description": "The URL of the current request."}, "statusCode": {"type": "integer", "description": "Standard HTTP status code returned by the server."}, "timeStamp": {"type": "number", "description": "The time when the response was received completely, in milliseconds since the epoch."} @@ -3585,7 +3716,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request."}, + "requestId": {"type": "string", "description": "The ID of the request."}, "url": {"type": "string", "description": "The URL of the current request."}, "error": {"type": "string", "description": "The error description."}, "timeStamp": {"type": "number", "description": "The time when the error occurred, in milliseconds since the epoch."} @@ -3760,11 +3891,7 @@ "description": "Proxy settings to be used. The value of this preference is a ProxyConfig object.", "value": [ "proxy", - {"$ref": "ProxyConfig"}, - { - "get": "experimental.proxy.get", - "set": "experimental.proxy.set" - } + {"$ref": "ProxyConfig"} ] } }, @@ -4216,6 +4343,10 @@ "description": "The <a href='manifest.html#version'>version</a> of this extension or app.", "type": "string" }, + "mayDisable": { + "description": "Whether this extension can be disabled or uninstalled by the user.", + "type": "boolean" + }, "enabled": { "description": "Whether it is currently enabled or disabled.", "type": "boolean" @@ -4396,7 +4527,266 @@ } ] }, - + { + "namespace":"fileBrowserHandler", + "nodoc": "true", + "types": [ + { + "id": "FileHandlerExecuteEventDetails", + "type": "object", + "description": "Event details payload for fileBrowserHandler.onExecute event.", + "properties": { + "entries": { + "type": "any", + "description": "Array of Entry instances representing files that are targets of this action (selected in ChromeOS file browser)." + }, + "tab_id" : { + "type": "integer", + "optional": true, + "description": "The ID of the tab that raised this event. Tab IDs are unique within a browser session." + } + } + } + ], + "events": [ + { + "name": "onExecute", + "type": "function", + "description": "Fired when file system action is executed from ChromeOS file browser.", + "parameters": [ + { + "name": "id", + "type": "string", + "description": "File browser action id as specified in the listener component's manifest." + }, + { + "name": "details", + "$ref": "FileHandlerExecuteEventDetails", + "description": "File handler execute event details." + } + ] + } + ] + }, + { + "namespace":"fileBrowserPrivate", + "nodoc": "true", + "types": [ + { + "id": "FileBrowserTask", + "type": "object", + "description": "Represents information about available browser tasks. A task is an abstraction of an operation that the file browser can perform over a selected file set.", + "properties": { + "taskId": {"type": "string", "description": "The unique identifier of the task."}, + "title": {"type": "string", "description": "Task title."}, + "iconUrl": {"type": "string", "description": "Task icon url (from chrome://extension-icon/...)"} + } + }, + { + "id": "VolumeInfo", + "type": "object", + "description": "Mounted disk volume information.", + "properties": { + "mountPath": { + "type": "string", + "description": "Disk volume mount point path. The value corresponds to its Entry.fullPath in File API." + }, + "label": { + "type": "string", + "description": "Volume label." + }, + "deviceType": { + "type": "string", + "enum": ["flash", "hdd", "optical", "undefined"], + "description": "Device type." + }, + "readOnly": { + "type": "boolean", + "description": "Flag that specifies if volume is mounted in read-only mode." + }, + "totalSizeKB": { + "type": "integer", + "description": "Total disk volume size in KBs" + } + } + }, + { + "id": "MountEvent", + "type": "object", + "description": "Payload data for disk mount / unmount event.", + "properties": { + "eventType": { + "type": "string", + "enum": ["added", "removed"], + "description": "Event type that tells listeners which disk volume even was raised." + }, + "volumeInfo": { + "$ref": "VolumeInfo", + "description":"Volume information that this mount event applies to." + } + } + } + ], + "functions": [ + { + "name": "cancelDialog", + "type": "function", + "description": "Cancel file selection.", + "parameters": [] + }, + { + "name": "executeTask", + "description": "Executes file browser task over selected files", + "parameters": [ + { + "name": "taskId", + "type": "string", + "description": "The unique identifier of task to execute." + }, + { + "name": "fileURLs", + "type": "array", + "description": "Array of file URLs", + "items": { "type": "string" } + }, + { + "name": "callback", + "type": "function", + "optional": "false", + "parameters": [ + { + "name": "success", + "type": "boolean", + "optional": "true", + "description": "True of task execution was successfully initiated." + } + ] + } + ] + }, + { + "name": "getFileTasks", + "description": "Gets the of tasks that can be performed over selected files.", + "parameters": [ + { + "name": "fileURLs", + "type": "array", + "description": "Array of selected file URLs", + "items": { "type": "string" } + }, + { + "name": "callback", + "type": "function", + "optional": "false", + "parameters": [ + { + "name": "tasks", + "type": "array", + "items": {"$ref": "FileBrowserTask"}, + "description": "The list of available tasks that can be performed on all of the passed file paths." + } + ] + } + ] + }, + { + "name": "getStrings", + "type": "function", + "description": "Get Strings.", + "parameters": [ + { + "type": "function", + "name": "callback", + "parameters": [ + { + "type": "object", + "properties": {}, + "additionalProperties": {"type": "string"} + } + ] + } + ] + }, + { + "name": "requestLocalFileSystem", + "description": "Requests access to local file system", + "parameters": [ + { + "name": "callback", + "type": "function", + "optional": "false", + "parameters": [ + { + "name" : "fileSystem", + "type": "object", + "optional": "true", + "description": "A DOMFileSystem instance for local file system access. null if the caller has no appropriate permissions." + } + ] + } + ] + }, + { + "name": "selectFiles", + "type": "function", + "description": "Select multiple files.", + "parameters": [ + { + "type": "array", + "description": "Array of selected paths", + "items": {"type": "string"} + } + ] + }, + { + "name": "selectFile", + "type": "function", + "description": "Select a file.", + "parameters": [ + { + "type": "string", + "description": "A selected path" + }, + { + "type": "integer", + "description": "Index of Filter" + } + ] + }, + { + "name": "viewFiles", + "type": "function", + "description": "View multiple files.", + "parameters": [ + { + "name": "fileUrls", + "type": "array", + "description": "Array of selected paths", + "items": {"type": "string"} + }, + { + "name": "id", + "type": "string", + "description": "File browser handler id as for internal tasks." + } + ] + } + ], + "events": [ + { + "name": "onDiskChanged", + "type": "function", + "description": "Fired when disk mount/unmount event is detected.", + "parameters": [ + { + "$ref": "MountEvent", + "name": "fullPath", + "description": "Mount event information." + } + ] + } + ] + }, { "namespace":"webstorePrivate", "nodoc": "true", @@ -4436,6 +4826,44 @@ ] }, { + "name": "beginInstallWithManifest", + "description": "Initiates the install process for the given extension. This must be called during a user gesture.", + "parameters": [ + { + "name": "id", + "type": "string", + "description": "The id of the extension to be installled.", + "minLength": 32, + "maxLength": 32 + }, + { + "name": "icon_data", + "type": "string", + "description": "An icon as a base64-encoded image, displayed in a confirmation dialog." + }, + { + "name": "manifest_json", + "type": "string", + "description": "A string with the contents of the extension's manifest.json file. During the install process, the browser will check that the downloaded extension's manifest matches what was passed in here.", + "minLength": 1 + }, + { + "name": "callback", + "type": "function", + "description": "Called when the user has either accepted/rejected the dialog, or some error occurred (such as invalid manifest or icon image data).", + "optional": "true", + "parameters": [ + { + "name": "result", + "type": "string", + "description": "A string result code, which will be empty upon success. The possible values in the case of errors include 'unknown_error', 'user_cancelled', 'manifest_error', 'icon_error', 'invalid_id', 'permission_denied', and 'no_gesture'." + } + ] + } + ] + + }, + { "name": "completeInstall", "description": "", "parameters": [ @@ -4545,7 +4973,7 @@ "incognito": { "type": "boolean", "optional": true, - "description": "Whether to return the setting that applies to the incognito session only (default false)." + "description": "Whether to return the setting that applies to the incognito session (default false)." } } }, @@ -4563,9 +4991,14 @@ "type": "any" }, "levelOfControl": { - "description": "One of<br>NotControllable = cannot be controlled by any extension<br>ControlledByOtherExtensions = controlled by extensions with higher precedence<br>ControllableByThisExtension = can be controlled by this extension<br>ControlledByThisExtension = controlled by this extension", + "description": "One of<br><var>NotControllable</var>: cannot be controlled by any extension<br><var>ControlledByOtherExtensions</var>: controlled by extensions with higher precedence<br><var>ControllableByThisExtension</var>: can be controlled by this extension<br><var>ControlledByThisExtension</var>: controlled by this extension", "type": "string", "enum": ["NotControllable", "ControlledByOtherExtensions", "ControllableByThisExtension", "ControlledByThisExtension"] + }, + "incognitoSpecific": { + "description": "Whether the effective value is specific to the incognito session.<br>This property will <em>only</em> be present if the <var>incognito</var> property in the <var>details</var> parameter of <code>get()</code> was true.", + "type": "boolean", + "optional": true } } } @@ -4584,7 +5017,7 @@ "description": "What setting to change.", "properties": { "value": { - "description": "The value of the preference.", + "description": "The value of the preference. <br>Note that every preference has a specific value type, which is described together with the preference. An extension should <em>not</em> set a preference value of a different type.", "type": "any" }, "incognito": { @@ -4629,6 +5062,34 @@ } ] } + ], + "events": [ + { + "name": "onChange", + "description": "Fired when the value of the preference changes.", + "parameters": [ + { + "type": "object", + "name": "details", + "properties": { + "value": { + "description": "The value of the preference.", + "type": "any" + }, + "levelOfControl": { + "description": "One of<br><var>NotControllable</var>: cannot be controlled by any extension<br><var>ControlledByOtherExtensions</var>: controlled by extensions with higher precedence<br><var>ControllableByThisExtension</var>: can be controlled by this extension<br><var>ControlledByThisExtension</var>: controlled by this extension", + "type": "string", + "enum": ["NotControllable", "ControlledByOtherExtensions", "ControllableByThisExtension", "ControlledByThisExtension"] + }, + "incognitoSpecific": { + "description": "Whether the value that has changed is specific to the incognito session.<br>This property will <em>only</em> be present if the user has enabled the extension in incognito mode.", + "type": "boolean", + "optional": true + } + } + } + ] + } ] } ] @@ -4640,7 +5101,140 @@ "$ref": "Preference", "value": ["blockThirdPartyCookies", {"type": "boolean"}], "description": "Whether third party cookies should be blocked. The value of this preference is of type boolean." + }, + "enableReferrers": { + "$ref": "Preference", + "value": ["enableReferrers", {"type":"boolean"}], + "description": "Whether referrers should be enabled. The value of this preference is of type boolean." + }, + "enableHyperlinkAuditing": { + "$ref": "Preference", + "value": ["enableHyperlinkAuditing", {"type":"boolean"}], + "description": "Whether to enable hyperlink auditing (\"<a ping>\"). The value of this preference is of type boolean." } } + }, + { + "namespace": "experimental.debugger", + "functions": [ + { + "name": "attach", + "type": "function", + "description": "Attaches debugger to the tab with given id.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "description": "The id of the tab to which you want to attach." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [], + "description": "If an error occurs while attaching to the tab, the callback will be called with no arguments and <a href='extension.html#property-lastError'>chrome.extension.lastError</a> will be set to the error message." + } + ] + }, + { + "name": "detach", + "type": "function", + "description": "Detaches debugger from a tab with given id.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "description": "The id of the tab from which you want to detach." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [], + "description": "If an error occurs while detaching from the tab, the callback will be called with no arguments and <a href='extension.html#property-lastError'>chrome.extension.lastError</a> will be set to the error message." + } + ] + }, + { + "name": "sendRequest", + "type": "function", + "description": "Send given request to the debugger.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "description": "The id of the tab to which you want to send debugging request." + }, + { + "type": "string", + "name": "method", + "description": "Method name." + }, + { + "type": "object", + "name": "params", + "optional": true, + "properties": {}, + "additionalProperties": { "type": "any" }, + "description": "JSON object matching Developer Tools / Web Inspector Protocol scheme." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [ + { + "type": "object", + "name": "result", + "optional": true, + "properties": {}, + "additionalProperties": { "type": "any" }, + "description": "JSON object with the request response." + } + ], + "description": "Request response body. If an error occurs while posting the message, the callback will be called with no arguments and <a href='extension.html#property-lastError'>chrome.extension.lastError</a> will be set to the error message." + } + ] + } + ], + "events": [ + { + "name": "onEvent", + "type": "function", + "description": "Fired whenever debugger issues instrumentation event.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "description": "The id of the tab that generated debug evet." + }, + { + "type": "string", + "name": "method", + "description": "Method name." + }, + { + "type": "object", + "name": "params", + "optional": true, + "properties": {}, + "additionalProperties": { "type": "any" }, + "description": "JSON object matching Developer Tools / Web Inspector Protocol event data scheme." + } + ] + }, + { + "name": "onDetach", + "type": "function", + "description": "Fired when browser terminates debugging session for the tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "description": "The id of the tab that was detached." + } + ] + } + ] } ] diff --git a/chrome/common/extensions/docs/api_index.html b/chrome/common/extensions/docs/api_index.html index fcfd737..8ac3789 100644 --- a/chrome/common/extensions/docs/api_index.html +++ b/chrome/common/extensions/docs/api_index.html @@ -348,23 +348,26 @@ Here are the supported chrome.* APIs: <h2 id="experimental">Experimental APIs</h2> <p> -Google Chrome also has some +Google Chrome also has <a href="experimental.html">experimental APIs</a>, -which give you access to -functionality such as process information and history. +some of which will become supported APIs +in future releases of Chrome. </p> <h2 id="conventions">API conventions</h2> <p> Unless the doc says otherwise, -methods in the chrome.* APIs are asynchronous: +methods in the chrome.* APIs are <b>asynchronous</b>: they return immediately, without waiting for the operation to finish. If you need to know the outcome of an operation, then you pass a callback function into the method. -For more information, see the video -<a href="http://www.youtube.com/watch?v=bmxr75CV36A&feature=PlayList&p=CA101D6A85FE9D4B&index=2">Extension API Design</a>. +For more information, watch this video: +</p> + +<p> +<iframe title="YouTube video player" width="640" height="390" src="http://www.youtube.com/embed/bmxr75CV36A?rel=0" frameborder="0" allowfullscreen=""></iframe> </p> </div> diff --git a/chrome/common/extensions/docs/apps.html b/chrome/common/extensions/docs/apps.html index 8130ad6..e5f48ae 100644 --- a/chrome/common/extensions/docs/apps.html +++ b/chrome/common/extensions/docs/apps.html @@ -381,12 +381,14 @@ instead of a hosted app if any of the following are true: </ul> <p> -The following articles can help you understand +To learn more about the differences between web apps and websites, -extensions and packaged apps, and packaged apps and hosted apps: +extensions and packaged apps, and packaged apps and hosted apps, +read these: </p> <ul> + <li> <a href="http://code.google.com/chrome/webstore/docs/choosing.html">Choosing an App Type</a> </li> <li> <a href="http://code.google.com/chrome/apps/articles/thinking_in_web_apps.html">Thinking in Web Apps</a> </li> <li> <a href="http://code.google.com/chrome/webstore/articles/apps_vs_extensions.html">Extensions, Packaged Apps, and Hosted Apps in the Chrome Web Store</a> </li> </ul> @@ -415,11 +417,7 @@ Here is a typical manifest for a packaged app: "icons": { "16": "icon_16.png", "128": "icon_128.png" - }, - "permissions": [ - "unlimitedStorage", - "notifications" - ] + } } </pre> diff --git a/chrome/common/extensions/docs/bookmarks.html b/chrome/common/extensions/docs/bookmarks.html index 71ff4c2..2937e60 100644 --- a/chrome/common/extensions/docs/bookmarks.html +++ b/chrome/common/extensions/docs/bookmarks.html @@ -391,8 +391,8 @@ For example:</p> <p> Bookmarks are organized in a tree, where each node in the tree -is either a bookmark or a group -(a folder that can contain nodes). +is either a bookmark or a folder +(sometimes called a <em>group</em>). Each node in the tree is represented by a <a href="#type-BookmarkTreeNode"><code>BookmarkTreeNode</code></a> object. @@ -410,6 +410,9 @@ See <a href="#type-BookmarkTreeNode"><code>BookmarkTreeNode</code></a> for information about the properties a node can have. </p> +<p class="note"><b>Note:</b> You cannot use this API to add or remove entries +in the root folder. You also cannot rename, move, or remove the special +"Bookmarks Bar" and "Other Bookmarks" folders.</p> <h2 id="overview-examples">Examples</h2> diff --git a/chrome/common/extensions/docs/build/directory.py b/chrome/common/extensions/docs/build/directory.py index a58ea87..055948d 100644 --- a/chrome/common/extensions/docs/build/directory.py +++ b/chrome/common/extensions/docs/build/directory.py @@ -225,7 +225,8 @@ class SamplesManifest(object): Args: path: The path to write the samples manifest file to. """ - manifest_text = json.dumps(self._manifest_data, indent=2, sort_keys=True) + manifest_text = json.dumps(self._manifest_data, indent=2, + sort_keys=True, separators=(',', ': ')) output_path = os.path.realpath(path) try: output_file = open(output_path, 'w') diff --git a/chrome/common/extensions/docs/content_scripts.html b/chrome/common/extensions/docs/content_scripts.html index e044dca..1097608 100644 --- a/chrome/common/extensions/docs/content_scripts.html +++ b/chrome/common/extensions/docs/content_scripts.html @@ -792,7 +792,7 @@ The first video describes content scripts and isolated worlds. </p> <p> -<object width="560" height="340"><param name="movie" value="http://www.youtube.com/v/laLudeUmXHM&hl=en_US&fs=1&"><param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="always"><embed src="http://www.youtube.com/v/laLudeUmXHM&hl=en_US&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"></object> +<iframe title="YouTube video player" width="640" height="390" src="http://www.youtube.com/embed/laLudeUmXHM?rel=0" frameborder="0" allowfullscreen=""></iframe> </p> <p> @@ -802,7 +802,7 @@ sending a request to its parent extension. </p> <p> -<object width="560" height="340"><param name="movie" value="http://www.youtube.com/v/B4M_a7xejYI&hl=en_US&fs=1&"><param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="always"><embed src="http://www.youtube.com/v/B4M_a7xejYI&hl=en_US&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"></object> +<iframe title="YouTube video player" width="640" height="390" src="http://www.youtube.com/embed/B4M_a7xejYI?rel=0" frameborder="0" allowfullscreen=""></iframe> </p> </div> diff --git a/chrome/common/extensions/docs/contextMenus.html b/chrome/common/extensions/docs/contextMenus.html index f76f8c3..6ccc1b5 100644 --- a/chrome/common/extensions/docs/contextMenus.html +++ b/chrome/common/extensions/docs/contextMenus.html @@ -481,7 +481,7 @@ You can find samples of this API on the <div style="display:inline"> ( <span class="optional">optional</span> - <span class="enum" style="display: none; ">enumerated</span> + <span class="enum">enumerated</span> <span id="typeTemplate"> <span style="display: none; "> <a> Type</a> @@ -491,7 +491,7 @@ You can find samples of this API on the array of <span><span></span></span> </span> <span>string</span> - <span style="display: none; "></span> + <span>["normal", "checkbox", "radio", "separator"]</span> </span> </span> ) @@ -502,7 +502,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The type of menu item - one of 'normal', 'checkbox', 'radio', or 'separator'. Defaults to 'normal' if not specified.</dd> + <dd>The type of menu item. Defaults to 'normal' if not specified.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -701,7 +701,7 @@ You can find samples of this API on the array of <span><span></span></span> </span> <span>string</span> - <span style="display: none; "></span> + <span>["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"]</span> </span> </span></span> </span> @@ -717,7 +717,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>List of contexts this menu item will appear in. Legal values are: 'all', 'page', 'selection', 'link', 'editable', 'image', 'video', and 'audio'. Defaults to ['page'].</dd> + <dd>List of contexts this menu item will appear in. Defaults to ['page'] if not specified.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1855,7 +1855,7 @@ You can find samples of this API on the <div style="display:inline"> ( <span class="optional">optional</span> - <span class="enum" style="display: none; ">enumerated</span> + <span class="enum">enumerated</span> <span id="typeTemplate"> <span style="display: none; "> <a> Type</a> @@ -1865,7 +1865,7 @@ You can find samples of this API on the array of <span><span></span></span> </span> <span>string</span> - <span style="display: none; "></span> + <span>["normal", "checkbox", "radio", "separator"]</span> </span> </span> ) @@ -2081,7 +2081,7 @@ You can find samples of this API on the array of <span><span></span></span> </span> <span>string</span> - <span style="display: none; "></span> + <span>["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"]</span> </span> </span></span> </span> diff --git a/chrome/common/extensions/docs/cookies.html b/chrome/common/extensions/docs/cookies.html index 14c8cf6..f7bcea4 100644 --- a/chrome/common/extensions/docs/cookies.html +++ b/chrome/common/extensions/docs/cookies.html @@ -2137,9 +2137,9 @@ see <a href="samples.html">Samples</a>. <dd style="display: none; "> Description of this parameter from the json schema. </dd> - <dd style="display: none; "> + <dd> This parameter was added in version - <b><span></span></b>. + <b><span>11.0.674.0</span></b>. You must omit this parameter in earlier versions, and you may omit it in any version. If you require this parameter, the manifest key @@ -3212,9 +3212,9 @@ see <a href="samples.html">Samples</a>. <dd style="display: none; "> Description of this parameter from the json schema. </dd> - <dd style="display: none; "> + <dd> This parameter was added in version - <b><span></span></b>. + <b><span>11.0.674.0</span></b>. You must omit this parameter in earlier versions, and you may omit it in any version. If you require this parameter, the manifest key @@ -3378,7 +3378,7 @@ see <a href="samples.html">Samples</a>. <div class="description"> <p class="todo" style="display: none; ">Undocumented.</p> - <p>Fired when a cookie is set or removed.</p> + <p>Fired when a cookie is set or removed. As a special case, note that updating a cookie's properties is implemented as a two step process: the cookie to be updated is first removed entirely, generating a notification with "cause" of "overwrite" . Afterwards, a new cookie is written with the updated values, generating a second notification with "cause" "explicit".</p> <!-- PARAMETERS --> <div> @@ -3567,6 +3567,74 @@ see <a href="samples.html">Samples</a>. </dd> </div> + </div><div> + <div> + <dt> + <var>cause</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span>["evicted", "expired", "explicit", "expired_overwrite", "overwrite"]</span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The underlying reason behind the cookie's change. If a cookie was inserted, or removed via an explicit call to "chrome.cookies.remove", "cause" will be "explicit". If a cookie was automatically removed due to expiry, "cause" will be "expired". If a cookie was removed due to being overwritten with an already-expired expiration date, "cause" will be set to "expired_overwrite". If a cookie was automatically removed due to garbage collection, "cause" will be "evicted". If a cookie was automatically removed due to a "set" call that overwrote it, "cause" will be "overwrite". Plan your response accordingly.</dd> + <dd> + This parameter was added in version + <b><span>12.0.707.0</span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> </div> </dl> </dd> diff --git a/chrome/common/extensions/docs/docs.html b/chrome/common/extensions/docs/docs.html index 81fce56..d61024b 100644 --- a/chrome/common/extensions/docs/docs.html +++ b/chrome/common/extensions/docs/docs.html @@ -319,8 +319,7 @@ This documentation tells you how to write extensions and packaged apps for the <a href="http://www.google.com/chrome">Google Chrome browser</a>. Because extensions came first, -the APIs and these docs say <em>extension</em> everywhere, -even though almost everything applies to packaged apps, as well. +the APIs and these docs say <em>extension</em> everywhere. </p> <p class="caution"> diff --git a/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess.zip b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess.zip Binary files differnew file mode 100644 index 0000000..9855524 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess.zip diff --git a/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/manifest.json b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/manifest.json new file mode 100644 index 0000000..a75a5fe --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/manifest.json @@ -0,0 +1,15 @@ +{ + "name" : "`extension.isAllowed???Access` Example", + "version" : "1.0.0", + "description" : "Demonstrates the `extension.isAllowed???Access` APIs", + "permissions" : [ "file://*" ], + "browser_action" : { + "default_popup": "popup.html", + "default_icon" : "sample-19.png" + }, + "icons" : { + "16" : "sample-16.png", + "48" : "sample-48.png", + "128" : "sample-128.png" + } +} diff --git a/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/popup.html b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/popup.html new file mode 100644 index 0000000..1c3da61 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/popup.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<!-- + * Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> +<html> + <head> + <title>extension.isAllowedAccess Sample</title> + <link rel="stylesheet" href="./sample.css"> + </head> + <body> + <h1>extension.isAllowedAccess Sample</h1> + <section> + <ol> + <li><p> + <span>1</span> chrome.extension.isAllowedFileSchemeAccess: + <code id="file">unknown</code> (unpacked extensions always have + file scheme access, you'll need to install this as a packed + extension to toggle it properly) + </p></li> + <li><p> + <span>2</span> chrome.extension.isAllowedIncognitoAccess: + <code id="incognito">unknown</code> + </p></li> + </ol> + </section> + <script> + chrome.extension.isAllowedFileSchemeAccess(function(state) { + var el = document.getElementById('file'); + el.textContent = el.className = state ? 'true': 'false'; + }); + chrome.extension.isAllowedIncognitoAccess(function(state) { + var el = document.getElementById('incognito'); + el.textContent = el.className = state ? 'true': 'false'; + }); + </script> + </body> +</html> diff --git a/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-128.png b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-128.png Binary files differnew file mode 100644 index 0000000..d733b1e --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-128.png diff --git a/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-16.png b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-16.png Binary files differnew file mode 100644 index 0000000..dcc5c14 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-16.png diff --git a/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-19.png b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-19.png Binary files differnew file mode 100644 index 0000000..01e0aa8 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-19.png diff --git a/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-48.png b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-48.png Binary files differnew file mode 100644 index 0000000..3af1eb8 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample-48.png diff --git a/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample.css b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample.css new file mode 100644 index 0000000..2f35d4a --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample.css @@ -0,0 +1,61 @@ +body { + margin: 5px 10px 10px; +} + +h1 { + color: #53637D; + font: 26px/1.2 Helvetica, sans-serif; + font-size: 200%; + margin: 0; + padding-bottom: 4px; + text-shadow: white 0 1px 2px; +} + +body > section { + border-radius: 5px; + background: -webkit-linear-gradient(rgba(234, 238, 243, 0.2), #EAEEF3), + -webkit-linear-gradient(left, #EAEEF3, #EAEEF3 97%, #D3D7DB); + font: 14px/1 Arial,Sans Serif; + padding: 10px; + width: 563px; + max-height: 400px; + overflow-y: auto; + box-shadow: inset 0px 2px 5px rgba(0,0,0,0.5); +} + +body > section > ol { + padding: 0; + margin: 0; + list-style: none inside; +} + +body > section > ol > li { + position: relative; + margin: 0.5em 0 0.5em 40px; +} + +code { + word-wrap: break-word; + background: rgba(255,255,0, 0.5); +} + code.true { + background: rgba(0, 255, 0, 0.5); + } + code.false { + background: rgba(255, 0, 0, 0.5); + } + +li > p > span:first-child { + position: absolute; + top: 0px; + left: -40px; + width: 30px; + text-align: right; + font: 30px/1 Helvetica, sans-serif; + font-weight: 700; +} + +p { + min-height: 30px; + line-height: 1.2; +} diff --git a/chrome/common/extensions/docs/examples/api/preferences/enableReferrer.zip b/chrome/common/extensions/docs/examples/api/preferences/enableReferrer.zip Binary files differnew file mode 100644 index 0000000..3669d90 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/preferences/enableReferrer.zip diff --git a/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/advicedog.jpg b/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/advicedog.jpg Binary files differnew file mode 100644 index 0000000..9274fbd --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/advicedog.jpg diff --git a/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/manifest.json b/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/manifest.json new file mode 100644 index 0000000..03b0e5a --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/manifest.json @@ -0,0 +1,10 @@ +{
+ "name" : "Enable/disable referrers API example extension",
+ "version" : "0.1",
+ "description" : "Sample extension which demonstrates how to access a preference.",
+ "permissions": [ "experimental", "contentSettings" ],
+ "browser_action": {
+ "default_icon": "advicedog.jpg",
+ "popup": "popup.html"
+ }
+}
diff --git a/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/popup.html b/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/popup.html new file mode 100644 index 0000000..43b5dae --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/popup.html @@ -0,0 +1,129 @@ +<!DOCTYPE html> +<html> +<head> + <script> +var cs = chrome.experimental.contentSettings; +var pref = cs.misc.enableReferrers; + +/** + * Returns whether the |levelOfControl| means that the extension can change the + * preference value. + * + * @param levelOfControl{string} + */ +function settingIsControllable(levelOfControl) { + return (levelOfControl == "ControllableByThisExtension" || + levelOfControl == "ControlledByThisExtension"); +} + +/** + * Updates the UI to reflect the state of the preference. + * + * @param settings{object} A settings object, as returned from |get()| or the + * |onchange| event. + */ +function updateUI(settings) { + var disableUI = !settingIsControllable(settings.levelOfControl); + document.getElementById("regularValue").disabled = disableUI; + document.getElementById("useSeparateIncognitoSettings").disabled = disableUI; + if (settings.hasOwnProperty('incognitoSpecific')) { + var hasIncognitoValue = settings.incognitoSpecific; + document.getElementById("useSeparateIncognitoSettings").checked = + hasIncognitoValue; + document.getElementById("incognitoValue").disabled = + disableUI || !hasIncognitoValue; + document.getElementById("incognitoValue").checked = settings.value; + } else { + document.getElementById("regularValue").checked = settings.value; + } +} + +/** + * Wrapper for |updateUI| which is used as callback for the |get()| method and + * which logs the result. + * If there was an error getting the preference, does nothing. + * + * @param settings{object} A settings object, as returned from |get()|. + */ +function updateUIFromGet(settings) { + if (settings) { + console.log('pref.get result:' + JSON.stringify(settings)); + updateUI(settings); + } +} + +/** + * Wrapper for |updateUI| which is used as handler for the |onchange| event + * and which logs the result. + * + * @param settings{object} A settings object, as returned from the |onchange| + * event. + */ +function updateUIFromOnChange(settings) { + console.log('pref.onChange event:' + JSON.stringify(settings)); + updateUI(settings); +} + +/* + * Initializes the UI. + */ +function init() { + chrome.extension.isAllowedIncognitoAccess(function(allowed) { + if (allowed) { + pref.get({'incognito': true}, updateUIFromGet); + document.getElementById("incognito").style.display = "block"; + document.getElementById("incognito-forbidden").style.display = "none"; + } + }); + pref.get({}, updateUIFromGet); + pref.onChange.addListener(updateUIFromOnChange); +} + +/** + * Called from the UI to change the preference value. + * + * @param enabled{boolean} The new preference value. + * @param incognito{boolean} Whether the value is specific to incognito mode. + */ +function setPrefValue(enabled, incognito) { + pref.set({'value':enabled, 'incognito': incognito}); +} + +/** + * Called from the UI to change whether to use separate settings for + * incognito mode. + * + * @param value{boolean} whether to use separate settings for + * incognito mode. + */ +function setUseSeparateIncognitoSettings(value) { + if (!value) { + pref.clear({'incognito': true}); + } else { + // Explicitly set the value for incognito mode. + pref.get({'incognito': true}, function(settings) { + pref.set({'incognito': true, 'value': settings.value}); + }); + } + document.getElementById("incognitoValue").disabled = !value; +} + + </script> +</head> +<body onload="init()"> + +<div style="width: 300px"> +<input type="checkbox" onclick="setPrefValue(this.checked)" id="regularValue" /> Enable referrers + +<div id="incognito" style="display:none"> +<input type="checkbox" onclick="setUseSeparateIncognitoSettings(this.checked)" id="useSeparateIncognitoSettings" /> Use separate setting for incognito mode: +<br> +<input type="checkbox" onclick="setPrefValue(this.checked, true)" id="incognitoValue" disabled="disabled"/> Enable referrers in incognito sessions +</div> +<div id="incognito-forbidden"> +Select "Allow in incognito" to access incognito preferences +</div> +</div> + +</body> +</html>
\ No newline at end of file diff --git a/chrome/common/extensions/docs/examples/api/tabs/pin.zip b/chrome/common/extensions/docs/examples/api/tabs/pin.zip Binary files differnew file mode 100644 index 0000000..63f48d2 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/pin.zip diff --git a/chrome/common/extensions/docs/examples/api/tabs/pin/README b/chrome/common/extensions/docs/examples/api/tabs/pin/README new file mode 100644 index 0000000..d9d5c65 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/pin/README @@ -0,0 +1,2 @@ +Demo Chrome Extension that uses the Tab Pinning API. Enables a new keyboard +shortcut (Ctrl + Shift + P) to toggle pinning and unpinning of the current tab. diff --git a/chrome/common/extensions/docs/examples/api/tabs/pin/background.html b/chrome/common/extensions/docs/examples/api/tabs/pin/background.html new file mode 100644 index 0000000..47aa9fa --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/pin/background.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<!-- + * Copyright (c) 2011 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. +--> +<head> +<title>Keyboard Pin</title> +<script> +chrome.extension.onRequest.addListener( + function(request, sender, sendResponse) { + if (request.toggle_pin) { + // Get the currently selected tab + chrome.tabs.getSelected(null, function(tab) { + // Toggle the pinned status + chrome.tabs.update(tab.id, {'pinned': !tab.pinned}); + }); + } + } +); +</script> +</head> diff --git a/chrome/common/extensions/docs/examples/api/tabs/pin/inject.js b/chrome/common/extensions/docs/examples/api/tabs/pin/inject.js new file mode 100644 index 0000000..3117e40 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/pin/inject.js @@ -0,0 +1,13 @@ +// Copyright (c) 2011 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. +window.addEventListener("keydown", function(event) { + // Bind to both command (for Mac) and control (for Win/Linux) + var modifier = event.ctrlKey || event.metaKey; + if (modifier && event.shiftKey && event.keyCode == 80) { + // Send message to background page to toggle tab + chrome.extension.sendRequest({toggle_pin: true}, function(response) { + // Do stuff on successful response + }); + } +}, false); diff --git a/chrome/common/extensions/docs/examples/api/tabs/pin/manifest.json b/chrome/common/extensions/docs/examples/api/tabs/pin/manifest.json new file mode 100644 index 0000000..e7969cd --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/tabs/pin/manifest.json @@ -0,0 +1,15 @@ +{ + "name": "Keyboard Pin", + "version": "0.1", + "description": "Creates a keyboard shortcut (C + Shift + P) to toggle the pinned state of the currently selected tab", + "permissions": [ + "tabs" + ], + "background_page": "background.html", + "content_scripts": [ + { + "matches": ["<all_urls>"], + "js": ["inject.js"] + } + ] +} diff --git a/chrome/common/extensions/docs/examples/api/webNavigation/basic.zip b/chrome/common/extensions/docs/examples/api/webNavigation/basic.zip Binary files differnew file mode 100644 index 0000000..496714e --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/webNavigation/basic.zip diff --git a/chrome/common/extensions/docs/examples/api/webNavigation/basic/_locales/en/messages.json b/chrome/common/extensions/docs/examples/api/webNavigation/basic/_locales/en/messages.json new file mode 100644 index 0000000..704131d --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/webNavigation/basic/_locales/en/messages.json @@ -0,0 +1,52 @@ +{ + "extName": { + "message": "WebNavigation Tech Demo", + "description": "The extension name." + }, + "extDescription": { + "message": "Demonstration of the WebNavigation extension API.", + "description": "The extension description." + }, + + "navigationDescription": { + "message": ", requested $NUM$ times. Loaded in an average of $LOAD$ miliseconds.", + "description": "The message posted in the popup for each stored navigation.", + "placeholders": { + "NUM": { + "content": "$1", + "example": "4 (The number of times this URL was accessed.)" + }, + "LOAD": { + "content": "$2", + "example": "12.345 (The average load time in miliseconds.)" + } + } + }, + + "inHandler": { + "message": "In webNavigation[`%s`] handler: %o", + "description": "Notification displayed for each webNavigation event." + }, + + "inHandlerError": { + "message": "In webNavigation[`%s`] handler: No data!", + "description": "Notification displayed in a webNavigation event handler without data!" + }, + + "errorCommittedWithoutPending": { + "message": "Wha? `onCommitted` for `%s` called, though it's not pending: %o", + "description": "Error logged when `onCommitted` is triggered on a non-pending request." + }, + "errorCommittedWithoutPending": { + "message": "Wha? `onCompleted` for `%s` called, though it's not pending: %o", + "description": "Error logged when `onCompleted` is triggered on a non-pending request." + }, + "errorErrorOccurredWithoutPending": { + "message": "Wha? `onErrorOccurred` for `%s` called, though it's not pending: %o", + "description": "Error logged when `onErrorOccurred` is triggered on a non-pending request." + }, + "errorCommittedWithoutPending": { + "message": "Wha? `onCompleted` for `%s` called, though it's not pending: %o", + "description": "Error logged when `onCompleted` is triggered on a non-pending request." + } +} diff --git a/chrome/common/extensions/docs/examples/api/webNavigation/basic/background.html b/chrome/common/extensions/docs/examples/api/webNavigation/basic/background.html new file mode 100644 index 0000000..138e8d0 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/webNavigation/basic/background.html @@ -0,0 +1,29 @@ +<!doctype html> +<!-- + * Copyright (c) 2011 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> +<html> + <head> + <title>WebNavigation Sample Extension Background Page</title> + </head> + <body> + <script src='./navigation_collector.js'></script> + <script> + var nav = new NavigationCollector(); + + var eventList = ['onBeforeNavigate', 'onBeforeRetarget', 'onCommitted', + 'onCompleted', 'onDOMContentLoaded', 'onErrorOccurred']; + + eventList.forEach(function(e) { + chrome.experimental.webNavigation[e].addListener(function(data) { + if (typeof data) + console.log(chrome.i18n.getMessage('inHandler'), e, data); + else + console.error(chrome.i18n.getMessage('inHandlerError'), e); + }); + }); + </script> + </body> +</html> diff --git a/chrome/common/extensions/docs/examples/api/webNavigation/basic/icon.png b/chrome/common/extensions/docs/examples/api/webNavigation/basic/icon.png Binary files differnew file mode 100644 index 0000000..103ff36 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/webNavigation/basic/icon.png diff --git a/chrome/common/extensions/docs/examples/api/webNavigation/basic/manifest.json b/chrome/common/extensions/docs/examples/api/webNavigation/basic/manifest.json new file mode 100644 index 0000000..538417e --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/webNavigation/basic/manifest.json @@ -0,0 +1,16 @@ +{ + "name": "__MSG_extName__", + "version": "0.1", + "description": "__MSG_extDescription__", + "default_locale": "en", + "background_page": "background.html", + "browser_action": { + "default_icon": "icon.png", + "popup": "popup.html" + }, + "permissions": [ + "extension", + "experimental", + "webNavigation" + ] +} diff --git a/chrome/common/extensions/docs/examples/api/webNavigation/basic/navigation_collector.js b/chrome/common/extensions/docs/examples/api/webNavigation/basic/navigation_collector.js new file mode 100644 index 0000000..9a7b608 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/webNavigation/basic/navigation_collector.js @@ -0,0 +1,371 @@ +// Copyright (c) 2011 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. + +/** + * Implements the NavigationCollector object that powers the extension. + * + * @author mkwst@google.com (Mike West) + */ + +/** + * Collects navigation events, and provides a list of successful requests + * that you can do interesting things with. Calling the constructor will + * automatically bind handlers to the relevant webnavigation API events, + * and to a `getMostRequestedUrls` extension message for internal + * communication between background pages and popups. + * + * @constructor + */ +function NavigationCollector() { + /** + * A list of currently pending requests, implemented as a hash of each + * request's tab ID, frame ID, and URL in order to ensure uniqueness. + * + * @type {Object.<string, {start: number}>} + * @private + */ + this.pending_ = {}; + + /** + * A list of completed requests, implemented as a hash of each + * request's tab ID, frame ID, and URL in order to ensure uniqueness. + * + * @type {Object.<string, Array.<NavigationCollector.Request>>} + * @private + */ + this.completed_ = {}; + + /** + * A list of requests that errored off, implemented as a hash of each + * request's tab ID, frame ID, and URL in order to ensure uniqueness. + * + * @type {Object.<string, Array.<NavigationCollector.Request>>} + * @private + */ + this.errored_ = {}; + + // Bind handlers to the 'webNavigation' events that we're interested + // in handling in order to build up a complete picture of the whole + // navigation event. + chrome.experimental.webNavigation.onBeforeRetarget.addListener( + this.onBeforeRetargetListener_.bind(this)); + chrome.experimental.webNavigation.onBeforeNavigate.addListener( + this.onBeforeNavigateListener_.bind(this)); + chrome.experimental.webNavigation.onCompleted.addListener( + this.onCompletedListener_.bind(this)); + chrome.experimental.webNavigation.onCommitted.addListener( + this.onCommittedListener_.bind(this)); + chrome.experimental.webNavigation.onErrorOccurred.addListener( + this.onErrorOccurredListener_.bind(this)); + + // Bind handler to extension messages for communication from popup. + chrome.extension.onRequest.addListener(this.onRequestListener_.bind(this)); +} + +/////////////////////////////////////////////////////////////////////////////// + +/** + * The possible transition types that explain how the navigation event + * was generated (i.e. "The user clicked on a link." or "The user submitted + * a form"). + * + * @see http://code.google.com/chrome/extensions/trunk/history.html + * @enum {string} + */ +NavigationCollector.NavigationType = { + AUTO_BOOKMARK: 'auto_bookmark', + AUTO_SUBFRAME: 'auto_subframe', + FORM_SUBMIT: 'form_submit', + GENERATED: 'generated', + KEYWORD: 'keyword', + KEYWORD_GENERATED: 'keyword_generated', + LINK: 'link', + MANUAL_SUBFRAME: 'manual_subframe', + RELOAD: 'reload', + START_PAGE: 'start_page', + TYPED: 'typed' +}; + +/** + * The possible transition qualifiers: + * + * * CLIENT_REDIRECT: Redirects caused by JavaScript, or a refresh meta tag + * on a page. + * + * * SERVER_REDIRECT: Redirected by the server via a 301/302 response. + * + * * FORWARD_BACK: User used the forward or back buttons to navigate through + * her browsing history. + * + * @enum {string} + */ +NavigationCollector.NavigationQualifier = { + CLIENT_REDIRECT: 'client_redirect', + FORWARD_BACK: 'forward_back', + SERVER_REDIRECT: 'server_redirect' +}; + +/** + * @typedef {{url: string, transitionType: NavigationCollector.NavigationType, + * transitionQualifier: Array.<NavigationCollector.NavigationQualifier>, + * openedInNewTab: boolean, sourceUrl: ?string, duration: number}} + */ +NavigationCollector.Request; + +/////////////////////////////////////////////////////////////////////////////// + +NavigationCollector.prototype = { + /** + * Returns a somewhat unique ID for a given WebNavigation request. + * + * @param {!{tabId: number, frameId: number, url: string}} data Information + * about the navigation event we'd like an ID for. + * @return {!string} ID created by combining the tab ID and frame ID (as the + * API ensures that these will be unique across a single navigation + * event) + * @private + */ + parseId_: function(data) { + return data.tabId + '-' + data.frameId; + }, + + + /** + * Creates an empty entry in the pending array, and prepopulates the + * errored and completed arrays for ease of insertion later. + * + * @param {!string} id The request's ID, as produced by parseId_. + * @param {!string} url The request's URL. + */ + prepareDataStorage_: function(id, url) { + this.pending_[id] = this.pending_[id] || { + openedInNewTab: false, + sourceUrl: null, + start: null, + transitionQualifiers: [], + transitionType: null + }; + this.completed_[url] = this.completed_[url] || []; + this.errored_[url] = this.errored_[url] || []; + }, + + + /** + * Handler for the 'onBeforeRetarget' event. Updates the pending request + * with a sourceUrl, and notes that it was opened in a new tab. + * + * Pushes the request onto the + * 'pending_' object, and stores it for later use. + * + * @param {!Object} data The event data generated for this request. + * @private + */ + onBeforeRetargetListener_: function(data) { + var id = this.parseId_(data); + this.prepareDataStorage_(id, data.url); + this.pending_[id].openedInNewTab = true; + this.pending_[id].sourceUrl = data.sourceUrl; + this.pending_[id].start = data.timeStamp; + }, + + + /** + * Handler for the 'onBeforeNavigate' event. Pushes the request onto the + * 'pending_' object, and stores it for later use. + * + * @param {!Object} data The event data generated for this request. + * @private + */ + onBeforeNavigateListener_: function(data) { + var id = this.parseId_(data); + this.prepareDataStorage_(id, data.url); + this.pending_[id].start = this.pending_[id].start || data.timeStamp; + }, + + + /** + * Handler for the 'onCommitted' event. Updates the pending request with + * transition information. + * + * Pushes the request onto the + * 'pending_' object, and stores it for later use. + * + * @param {!Object} data The event data generated for this request. + * @private + */ + onCommittedListener_: function(data) { + var id = this.parseId_(data); + if (!this.pending_[id]) { + console.warn( + chrome.i18n.getMessage('errorCommittedWithoutPending'), + data.url, + data); + } else { + this.pending_[id].transitionType = data.transitionType; + this.pending_[id].transitionQualifiers = + data.transitionQualifiers; + } + }, + + + /** + * Handler for the 'onCompleted` event. Pulls the request's data from the + * 'pending_' object, combines it with the completed event's data, and pushes + * a new NavigationCollector.Request object onto 'completed_'. + * + * @param {!Object} data The event data generated for this request. + * @private + */ + onCompletedListener_: function(data) { + var id = this.parseId_(data); + if (!this.pending_[id]) { + console.warn( + chrome.i18n.getMessage('errorCompletedWithoutPending'), + data.url, + data); + } else { + this.completed_[data.url].push({ + duration: (data.timeStamp - this.pending_[id].start), + openedInNewWindow: this.pending_[id].openedInNewWindow, + sourceUrl: this.pending_[id].sourceUrl, + transitionQualifiers: this.pending_[id].transitionQualifiers, + transitionType: this.pending_[id].transitionType, + url: data.url + }); + delete this.pending_[id]; + } + }, + + + /** + * Handler for the 'onErrorOccurred` event. Pulls the request's data from the + * 'pending_' object, combines it with the completed event's data, and pushes + * a new NavigationCollector.Request object onto 'errored_'. + * + * @param {!Object} data The event data generated for this request. + * @private + */ + onErrorOccurredListener_: function(data) { + var id = this.parseId_(data); + if (!this.pending_[id]) { + console.error( + chrome.i18n.getMessage('errorErrorOccurredWithoutPending'), + data.url, + data); + } else { + this.errored_[data.url].push({ + duration: (data.timeStamp - this.pending_[id].start), + openedInNewWindow: this.pending_[id].openedInNewWindow, + sourceUrl: this.pending_[id].sourceUrl, + transitionQualifiers: this.pending_[id].transitionQualifiers, + transitionType: this.pending_[id].transitionType, + url: data.url + }); + delete this.pending_[id]; + } + }, + + /** + * Handle request messages from the popup. + * + * @param {!{type:string}} request The external request to answer. + * @param {!MessageSender} sender Info about the script context that sent + * the request. + * @param {!function} sendResponse Function to call to send a response. + * @private + */ + onRequestListener_: function(request, sender, sendResponse) { + if (request.type === 'getMostRequestedUrls') + sendResponse({result: this.getMostRequestedUrls(request.num)}); + else + sendResponse({}); + }, + +/////////////////////////////////////////////////////////////////////////////// + + /** + * @return {Object.<string, NavigationCollector.Request>} The complete list of + * successful navigation requests. + */ + get completed() { + return this.completed_; + }, + + + /** + * @return {Object.<string, Navigationcollector.Request>} The complete list of + * unsuccessful navigation requests. + */ + get errored() { + return this.errored_; + }, + + + /** + * Get a list of the X most requested URLs. + * + * @param {number=} num The number of successful navigation requests to + * return. If 0 is passed in, or the argument left off entirely, all + * successful requests are returned. + * @return {Object.<string, NavigationCollector.Request>} The list of + * successful navigation requests, sorted in decending order of frequency. + */ + getMostRequestedUrls: function(num) { + return this.getMostFrequentUrls_(this.completed, num); + }, + + + /** + * Get a list of the X most errored URLs. + * + * @param {number=} num The number of unsuccessful navigation requests to + * return. If 0 is passed in, or the argument left off entirely, all + * successful requests are returned. + * @return {Object.<string, NavigationCollector.Request>} The list of + * unsuccessful navigation requests, sorted in decending order + * of frequency. + */ + getMostErroredUrls: function(num) { + return this.getMostErroredUrls_(this.errored, num); + }, + + + /** + * Get a list of the most frequent URLs in a list. + * + * @param {NavigationCollector.Request} list A list of URLs to parse. + * @param {number=} num The number of navigation requests to return. If + * 0 is passed in, or the argument left off entirely, all requests + * are returned. + * @return {Object.<string, NavigationCollector.Request>} The list of + * navigation requests, sorted in decending order of frequency. + * @private + */ + getMostFrequentUrls_: function(list, num) { + var result = []; + var avg; + // Convert the 'completed_' object to an array. + for (var x in list) { + avg = 0; + if (list.hasOwnProperty(x)) { + list[x].forEach(function(o) { + avg += o.duration; + }); + avg = avg / list[x].length; + result.push({ + url: x, + numRequests: list[x].length, + requestList: list[x], + average: avg + }); + } + } + // Sort the array. + result.sort(function(a, b) { + return b.numRequests - a.numRequests; + }); + // Return the requested number of results. + return num ? result.slice(0, num) : result; + } +}; diff --git a/chrome/common/extensions/docs/examples/api/webNavigation/basic/popup.html b/chrome/common/extensions/docs/examples/api/webNavigation/basic/popup.html new file mode 100644 index 0000000..55956aa --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/webNavigation/basic/popup.html @@ -0,0 +1,103 @@ +<!doctype html> +<!-- + * Copyright (c) 2011 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> +<html> + <head> + <title>WebNavigation Tech Demo Popup</title> + <style> + body { + margin: 5px 10px 10px; + } + + h1 { + color: #53637D; + font: 26px/1.2 Helvetica, sans-serif; + font-size: 200%; + margin: 0; + padding-bottom: 4px; + text-shadow: white 0 1px 2px; + } + + body > section { + border-radius: 5px; + background: -webkit-linear-gradient(rgba(234, 238, 243, 0.2), #EAEEF3), + -webkit-linear-gradient( + left, #EAEEF3, #EAEEF3 97%, #D3D7DB); + font: 14px/1 Arial,Sans Serif; + padding: 10px; + width: 563px; + max-height: 400px; + overflow-y: auto; + box-shadow: inset 0px 2px 5px rgba(0,0,0,0.5); + } + + body > section > ol { + padding: 0; + margin: 0; + list-style: none inside; + } + + body > section > ol > li { + position: relative; + margin: 0.5em 0 0.5em 40px; + } + + code { + word-wrap: break-word; + background: rgba(255,255,0, 0.5); + } + + em { + position: absolute; + top: 0px; + left: -40px; + width: 30px; + text-align: right; + font: 30px/1 Helvetica, sans-serif; + font-weight: 700; + } + + p { + min-height: 30px; + line-height: 1.2; + } + </style> + </head> + <body> + <h1>Most Requested URLs</h1> + <section></section> + <script> + chrome.extension.sendRequest( + {'type': 'getMostRequestedUrls'}, + function generateList(response) { + var section = document.querySelector('body>section'); + var results = response.result; + var ol = document.createElement('ol'); + var li, p, em, code, text; + var i; + for (i = 0; i < results.length; i++ ) { + li = document.createElement('li'); + p = document.createElement('p'); + em = document.createElement('em'); + em.textContent = i + 1; + code = document.createElement('code'); + code.textContent = results[i].url; + text = document.createTextNode( + chrome.i18n.getMessage('navigationDescription', + [results[i].numRequests, + results[i].average])); + p.appendChild(em); + p.appendChild(code); + p.appendChild(text); + li.appendChild(p); + ol.appendChild(li); + } + section.innerHTML = ''; + section.appendChild(ol); + }); + </script> + </body> +</html> diff --git a/chrome/common/extensions/docs/examples/extensions/imageinfo.zip b/chrome/common/extensions/docs/examples/extensions/imageinfo.zip Binary files differindex 7d14906..4a61c1d 100644 --- a/chrome/common/extensions/docs/examples/extensions/imageinfo.zip +++ b/chrome/common/extensions/docs/examples/extensions/imageinfo.zip diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration.zip b/chrome/common/extensions/docs/examples/extensions/proxy_configuration.zip Binary files differnew file mode 100644 index 0000000..5b7cb0a --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration.zip diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/_locales/en/messages.json b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/_locales/en/messages.json new file mode 100644 index 0000000..b05ac21 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/_locales/en/messages.json @@ -0,0 +1,50 @@ +{ + "extName": { + "message": "Proxy Settings", + "description": "The extension name." + }, + "extDescription": { + "message": "Set Chrome-specific proxies; a demonstration of Chrome's Proxy API", + "description": "The extension description." + }, + "headerDirectConnection": { + "message": "Direct Connection", + "description": "Header for 'Direct Connection' configuration `fieldset`." + }, + "errorNoExtensionAccess": { + "message": "Sorry. This browser's proxy settings cannot be controlled via extensions.", + "description": "Error message displayed when `levelOfControl` is 'NotControllable'." + }, + "errorOtherExtensionControls": { + "message": "Sorry. This browser's proxy settings are being controlled by another extension. Please visit chrome://extensions for details.", + "description": "Error message displayed when `levelOfControl` is 'ControlledByOtherExtensions'." + }, + "errorSettingRegularProxy": { + "message": "Setting regular proxy settings failed. Sorry!", + "description": "Error message, displayed when failing to set regular proxy settings." + }, + "errorSettingIncognitoProxy": { + "message": "Setting incognito proxy settings failed. Sorry!", + "description": "Error message, displayed when failing to set incognito proxy settings." + }, + "successfullySetProxy": { + "message": "Your proxy settings have been saved successfully; this window will close automagically. Have a nice day!", + "description": "Success message, displayed after proxy settings have been written." + }, + "errorPopupTitle": { + "message": "Error: $1", + "description": "Error message used as popup title." + }, + "errorProxyError": { + "message": "ProxyError: $1", + "description": "Error message displayed in popup when an error occurs." + }, + "errorIdNotFound": { + "message": "Element with ID `$1` doesn't exist in the document", + "description": "Error message thrown when the given `id` doesn't exist" + }, + "errorIdNotForm": { + "message": "Element with ID `$1` isn't a form element.", + "description": "Error message thrown when the given `id` isn't a form element." + } +} diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/background.html b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/background.html new file mode 100644 index 0000000..870c505 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/background.html @@ -0,0 +1,29 @@ +<!doctype html> +<html> + <head> + <title>Proxy Configuration Extension Background Page</title> + </head> + <body> + <!-- + Here, we'll hook into the proxy extension API's `onProxyError` + event, and use it to set a warning badge on the browser action's icon. + Additionally, we'll store proxy settings locally, and reset them when the + background page initializes. This is essential, as incognito settings + are wiped on restart. + --> + <script src="./proxy_form_controller.js"></script> + <script src="./proxy_error_handler.js"></script> + <script> + var errorHandler = new ProxyErrorHandler(); + + // If this extension has already set the proxy settings, then reset it + // once as the background page initializes. This is essential, as + // incognito settings are wiped on restart. + var persistedSettings = ProxyFormController.getPersistedSettings(); + if (persistedSettings !== null) { + chrome.experimental.proxy.settings.set( + {'value': persistedSettings.regular}); + } + </script> + </body> +</html> diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon128.png b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon128.png Binary files differnew file mode 100644 index 0000000..0e4f441 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon128.png diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon16.png b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon16.png Binary files differnew file mode 100644 index 0000000..ddab866 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon16.png diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon32.png b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon32.png Binary files differnew file mode 100644 index 0000000..29504c0 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon32.png diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon48.png b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon48.png Binary files differnew file mode 100644 index 0000000..b85ae2e --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/icon48.png diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/manifest.json b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/manifest.json new file mode 100644 index 0000000..9bf0834 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "__MSG_extName__", + "version": "0.2", + "description": "__MSG_extDescription__", + "default_locale": "en", + "browser_action": { + "default_icon": "icon16.png", + "popup": "popup.html" + }, + "icons": { + "16": "icon16.png", + "32": "icon32.png", + "48": "icon48.png", + "128": "icon128.png" + }, + "background_page": "background.html", + "permissions": [ + "experimental", + "proxy" + ] +} diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/popup.html b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/popup.html new file mode 100644 index 0000000..9152e24 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/popup.html @@ -0,0 +1,300 @@ +<!doctype html> +<html> +<head> + <title>Popup for Proxy API Test</title> + <style> + body { + margin: 5px 10px 10px; + } + + h1 { + color: #53637D; + font: 26px/1.2 Helvetica, sans-serif; + font-size: 200%; + margin: 0; + padding-bottom: 4px; + text-shadow: white 0 1px 2px; + } + + div[role='main'] { + border-radius: 5px; + background: #EAEEF3; + font: 14px/1 Arial,Sans Serif; + padding: 10px; + width: 563px; + box-shadow: inset 0px 2px 5px rgba(0,0,0,0.5); + -webkit-transition: background-color 0.5s ease-out; + overflow: hidden; + } + + div[role='main'].incognito { + background: #496281 url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAAhCAYAAABeD2IVAAAFz0lEQVRYw8VYC0xcRRQtUEprERQ1VkohSGNpwaCYYNBUjS2K+KEVTUkDiqY0hWD5xxhDINgQqIZviRAkSiiWNDRxWypNpaatEQk/CwvlWyks3+XPAssu7I7nbmabx+suPzfrTU7yljfz5sy95947w5YtpjcLwBLYBuzgsAGs+DuzGZGwBh4BHIDdwHPA8xxuwBOcnIWpPUAL7wQeB54CngYcAWdO4kXgDeBoWFhYSk5OTtmFCxeuxsbGpuBv3sCTwFZTkrLmXtgH+AJ+QAAQCBzz8/P7MiUl5aebN29Kh4eHFVNTU2xoaIgNDAywe/fusYyMjO858R2mJEUe2ldZWfljd3f3P+3t7bK2trZBuVyuYCJbXFxkXV1drLOzU4e7d++yzMzMasz3AexMSYq89CoRYuuwvr6+B6Q6OjpYUVFRG+Yf4iE0iZYoc5woVK2trUPrIUXh05Mir9XW1qow/33gmf8idj0ZypjHuIgj5ubm1OshpdFoVoTwzp07zMbGJsTb23s/16fFZlKcyDjY29u7+fr6HoJFV1dXt7EN2MjIyANSUqmUubq6Jtna2r7OvWW7kUy04IR2+fj4HDx+/HhUYmJiIQQuhZ5mN0JKqVSuEHtWVlbd6dOns+3s7N7h9YtKynbuhFWNQuYQGBh4ODs7u2B0dHRauNDMzIx2eXmZabXadRG7f/++jhSylV25ckXOk2D4yJEjSVjnNV7ntq9Fily6Jzg4+CTEOqf/OJFoampSFRQUjDc3Ny+QF9RqtU47hozGE3mUDIbxOtTX1ytQt5T6MQipFGu9zDPbYq0i+ey5c+dyxAvBSxrsXHXixIlmVOsOaGykp6dnYXZ2lt49BHhIWVdXN1VaWipLS0trCQ8P/xseUwrD6+HhEUJO4BFa3VMREREx4+PjCkNhunjx4iAWuI0x16KjowlVYkA7uncYczUyMpJ+3z579mzXwsLCA9fimcXHx6djvb3cGatmHrnTBxr4jSZShaYwCQnC89PQXFNUVNSvMTExv6C/XYqLi6sg0DMISRISEq7h+QY81dHY2DgpDq9CoWCYm8Fbl8FmLaxLROoA8OGpU6dKL1++LCMdGdIPEkF569atIYlE0oNNdGFsNz03NDTI0QOVhrRG31GpVCR4JcrEV1jnFWOZaMVrBx07vIDDQBiQil3XQENDMplseWlpiW3WBOLXovWoy8vLBxA+CTT6NdZ5gR9vtooFvis1NfWTioqKn6uqqv6ihius4NCYZn5+Xuf2zRAi4OSgQQIsT05OrnA71qrmR6BtQlL0wwVpL1nPIqQzCgFpzlB50HuFxlF2TkxMUKiXxBKgb4Dooqen5zHeX63FWUfl/22k/aix+qM3IkEeS05Obs7Pz6+vqakZgAcUY2Njur8T2cHBwSXobbSwsPCPgICAAny7QJgstAaVjry8vEquKwexpiz5WccrJCTkDO1yLWIILcP474DPgQ8oMahpA9+C0FxJSUk7nr/h2vwMSAfhJWFJQAKN8Xku4tCJveWP3d8gb6zWToh0aGjoJYz/FHgToGYbTMmBUM0RnJyccvE7GkiEVlv1cylhpqenGbI7l4v8UWNVfRsn5efs7Jxx/vz5fr0+VvGWuqysrMbNzS0R88KDgoLyEMo+/XvyDI4tcmHC6GsUSkgL5rxFCWbsxGDFY/oSdn+mt7d3gj5w/fp15VrENmokbvRANTbyBdbbz8/tBgsn1agD/v7+cUh7lfAj2KmGmdAoI9PT0//kOnQ01vesedF8t7+/X27oQ0hbrSmJoXBKucD3GAsd3VS8cDIoY2YykgM6RS4/Zj8kckt+sfTDeaeHmdFwHxzj98Y94qKpOz+5u7ufZGY28haOyOX89rzCW9SZ3YuLi39g/4Mhy4ex/kHejFeQ2tvS0vI79ShqE+YEmjxLSkoK5aQsheGjW6snv8EeBT42Az4C3uP/l3DhyWYhvuPZ84PWbt6tzQFH7pCdvCzoSP0LtBi6oflBr2wAAAAASUVORK5CYII=') no-repeat 533px bottom; + } + + form { + width: 563px; + -webkit-transition: -webkit-transform 0.25s ease; + } + + form.offscreen { + -webkit-transform: translateX(-600px); + } + + fieldset { + border: 0; + margin: 0; + padding: 0; + position: relative; + } + + legend { + position: absolute; + left: -999em; + } + + form > fieldset { + border-radius: 5px; + border: 1px solid transparent; + padding: 10px 10px 10px 30px; + margin: 5px 0; + -webkit-transition: all 0.5s ease; + } + + form > fieldset:hover { + background: rgba(255,255,255,0.1); + border-color: rgba(0,0,0,0.1); + } + + form > fieldset.active { + background: rgba(255,255,255,0.25); + border-color: rgba(0,0,0,0.25); + } + + form > fieldset > input { + margin-left: -20px; + } + + section { + margin: 5px 0 0; + } + + section fieldset:not(:first-child):not(:last-child) { + -webkit-transition: all 0.5s ease; + overflow: hidden; + max-height: 1.6em; + } + + section.single fieldset:not(:first-child):not(:last-child) { + max-height: 0px; + } + + section fieldset:last-child { + margin-top: 5px; + } + + section fieldset:last-child label { + display: block; + } + + section fieldset:last-child textarea { + width: 412px; + } + + section > fieldset { + position: relative; + padding-left: 60px; + } + + section > fieldset > legend { + left: 0; + top: 4px; + width: 53px; + text-align: right; + } + + input[type='url']:invalid:not(:active):not(:focus) { + border-color: rgba(255,0,0,0.5); + background: rgba(255,0,0,0.25); + } + + input:invalid:not(:active):not(:focus):after { + content: "This isn't a valid URL!"; + display:block; + } + + input[type="checkbox"] { + margin: 5px 0 5px 35px; + } + + input[type="text"] { + width: 200px; + margin: 0 10px 0 0; + } + + input[type="number"] { + width: 50px; + margin: 2px 10px 0 5px; + } + + section label, section legend { + color: #999; + -webkit-transition: color 0.5s ease; + } + + .incognito section label, .incognito section legend { + color: #BBB; + } + + .active section label, .active section legend, form > fieldset > label { + color: #000; + -webkit-transition: color 0.5s ease; + } + + .incognito .active section label, .incognito .active section legend, .incognito form > fieldset > label { + color: #FFF; + } + + input[type="submit"], button { + border-radius: 2px; + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1); + -webkit-user-select: none; + background: -webkit-linear-gradient(#FAFAFA, #F4F4F4 40%, #E5E5E5); + border: 1px solid #AAA; + color: #444; + margin-bottom: 0; + min-width: 4em; + padding: 3px 12px; + margin-top: 0; + font-size: 1.1em; + } + + .overlay { + display: block; + text-align: center; + position: absolute; + left: 50%; + top: 50%; + width: 350px; + padding: 20px; + margin: -80px 0 0 -195px; + opacity: 0; + background: rgba(0, 0, 0, 0.75); + border-radius: 5px; + color: #FFF; + font: 1.5em/1.2 Helvetica Neue, sans-serif; + -webkit-transition: all 1.0s ease; + } + .overlay a { + color: #FFF; + } + + .overlay.visible { + opacity: 1; + } + </style> +</head> +<body> + <h1>Proxy Configuration</h1> + <div role="main"> + <form id="proxyForm"> + <fieldset id="system"> + <legend>System Settings</legend> + <input type="radio" name="proxyType" id="proxyTypeSystem" value="system"> + <label for="proxyTypeSystem">Use the <em>system's proxy settings</em>.</label> + </fieldset> + <fieldset id="direct"> + <legend>Direct Connection</legend> + <input type="radio" name="proxyType" id="proxyTypeDirect" value="direct"> + <label for="proxyTypeDirect">Your computer is <em>directly connected</em> to the internet; no need for a proxy.</label> + </fieldset> + <fieldset id="pac_script"> + <legend>Automatic Configuration</legend> + <input type="radio" name="proxyType" id="proxyTypeAutoconfig" value="autoconfig"> + <label for="proxyTypeAutoconfig">Your proxy supports <em>automatic configuration</em>.</label> + + <section> + <label for="autoconfigURL">Autoconfiguration URL (PAC file)</label> + <input type="url" name="autoconfigURL" id="autoconfigURL"> + <input type="hidden" name="autoconfigData" id="autoconfigData"> + </section> + </fieldset> + <fieldset id="fixed_servers"> + <legend>Manual Proxy</legend> + <input type="radio" name="proxyType" id="proxyTypeManual" value="manual"> + <label for="proxyTypeManual">Configure your proxy settings <em>manually</em>.</label> + <section> + <fieldset> + <legend>HTTP</legend> + <label for="proxyHostHttp">Host</label> + <select id="proxySchemeHttp" name="proxySchemeHttp"> + <option selected value="http">http://</option> + <option value="https">https://</option> + <option value="socks4">socks4://</option> + <option value="socks5">socks5://</option> + </select> + <input type="text" name="proxyHostHttp" id="proxyHostHttp"> + + <label for="proxyPortHttp">Port</label> + <input type="number" min="1" step="1" name="proxyPortHttp" id="proxyPortHttp"> + + <input type="checkbox" name="singleProxyForEverything" id="singleProxyForEverything"> + <label for="singleProxyForEverything">Use the same proxy server for all protocols</label> + </fieldset> + <fieldset> + <legend>HTTPS</legend> + <label for="proxyHostHttps">Host</label> + <select id="proxySchemeHttps" name="proxySchemeHttps"> + <option selected value="http">http://</option> + <option value="https">https://</option> + <option value="socks4">socks4://</option> + <option value="socks5">socks5://</option> + </select> + <input type="text" name="proxyHostHttps" id="proxyHostHttps"> + + <label for="proxyPortHttps">Port</label> + <input type="number" min="1" step="1" name="proxyPortHttps" id="proxyPortHttps"> + </fieldset> + <fieldset> + <legend>FTP</legend> + <label for="proxyHostFtp">Host</label> + <select id="proxySchemeFtp" name="proxySchemeFtp"> + <option selected value="http">http://</option> + <option value="https">https://</option> + <option value="socks4">socks4://</option> + <option value="socks5">socks5://</option> + </select> + <input type="text" name="proxyHostFtp" id="proxyHostFtp"> + + <label for="proxyPortFtp">Port</label> + <input type="number" min="1" step="1" name="proxyPortFtp" id="proxyPortFtp"> + </fieldset> + <fieldset> + <legend>Fallback</legend> + <label for="proxyHostFallback">Host</label> + <select id="proxySchemeFallback" name="proxySchemeFallback"> + <option selected value="http">http://</option> + <option value="https">https://</option> + <option value="socks4">socks4://</option> + <option value="socks5">socks5://</option> + </select> + <input type="text" name="proxyHostFallback" id="proxyHostFallback"> + + <label for="proxyPortFallback">Port</label> + <input type="number" min="1" step="1" name="proxyPortFallback" id="proxyPortFallback"> + </fieldset> + <fieldset> + <label for="bypassList">Bypass proxy for these hosts:</label> + <textarea id="bypassList" name="bypassList" placeholder="<local>,192.168.1.1/16, .example.com"></textarea> + </fieldset> + </section> + </fieldset> + <input type="submit" value="Save proxy settings"> + <button value="incognito">Configure incognito window settings.</button> + </form> + </div> + <script src="./proxy_form_controller.js"></script> + <script> + var c = new ProxyFormController( 'proxyForm' ); + </script> +</body> +</html> diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/proxy_error_handler.js b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/proxy_error_handler.js new file mode 100644 index 0000000..bf6d80a --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/proxy_error_handler.js @@ -0,0 +1,105 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview This file implements the ProxyErrorHandler class, which will + * flag proxy errors in a visual way for the extension's user. + * + * @author Mike West <mkwst@google.com> + */ + + +/** + * The proxy error handling object. Binds to the 'onProxyError' event, and + * changes the extensions badge to reflect the error state (yellow for + * non-fatal errors, red for fatal). + * + * @constructor + */ +function ProxyErrorHandler() { + // Handle proxy error events. + chrome.experimental.proxy.onProxyError.addListener( + this.handleError_.bind(this)); + + // Handle message events from popup. + chrome.extension.onRequest.addListener( + this.handleOnRequest_.bind(this)); +}; + +/////////////////////////////////////////////////////////////////////////////// + +/** + * @typedef {{fatal: boolean, error: string, details: string}} + */ +ProxyErrorHandler.ErrorDetails; + +/////////////////////////////////////////////////////////////////////////////// + +ProxyErrorHandler.prototype = { + /** + * Details of the most recent error. + * @type {?ProxyErrorHandler.ErrorDetails} + * @private + */ + lastError_: null, + + /** + * Handle request messages from the popup. + * + * @param {!{type:string}} request The external request to answer. + * @param {!MessageSender} sender Info about the script context that sent + * the request. + * @param {!function} sendResponse Function to call to send a response. + * @private + */ + handleOnRequest_: function(request, sender, sendResponse) { + if (request.type === 'getError') { + sendResponse({result: this.getErrorDetails()}); + } else if (request.type === 'clearError') { + this.clearErrorDetails(); + sendResponse({result: true}); + } + }, + + /** + * Handles the error event, storing the error details for later use, and + * badges the browser action icon. + * + * @param {!ProxyErrorHandler.ErrorDetails} details The error details. + * @private + */ + handleError_: function(details) { + var RED = [255, 0, 0, 255]; + var YELLOW = [255, 205, 0, 255]; + + // Badge the popup icon. + var color = details.fatal ? RED : YELLOW; + chrome.browserAction.setBadgeBackgroundColor({color: color}); + chrome.browserAction.setBadgeText({text: 'X'}); + chrome.browserAction.setTitle({ + 'title': chrome.i18n.getMessage('errorPopupTitle', details.error) + }); + + // Store the error for display in the popup. + this.lastError_ = JSON.stringify(details); + }, + + + /** + * Returns details of the last error handled. + * + * @return {?ProxyErrorHandler.ErrorDetails} + */ + getErrorDetails: function() { + return this.lastError_; + }, + + + /** + * Clears last handled error. + */ + clearErrorDetails: function() { + this.lastError_ = null; + } +} diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/proxy_form_controller.js b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/proxy_form_controller.js new file mode 100644 index 0000000..6b4fc09 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/proxy_form_controller.js @@ -0,0 +1,778 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview This file implements the ProxyFormController class, which + * wraps a form element with logic that enables implementation of proxy + * settings. + * + * @author mkwst@google.com (Mike West) + */ + +/** + * Wraps the proxy configuration form, binding proper handlers to its various + * `change`, `click`, etc. events in order to take appropriate action in + * response to user events. + * + * @param {string} id The form's DOM ID. + * @constructor + */ +var ProxyFormController = function(id) { + /** + * The wrapped form element + * @type {Node} + * @private + */ + this.form_ = document.getElementById(id); + + // Throw an error if the element either doesn't exist, or isn't a form. + if (!this.form_) + throw chrome.i18n.getMessage('errorIdNotFound', id); + else if (this.form_.nodeName !== 'FORM') + throw chrome.i18n.getMessage('errorIdNotForm', id); + + /** + * Cached references to the `fieldset` groups that define the configuration + * options presented to the user. + * + * @type {NodeList} + * @private + */ + this.configGroups_ = document.querySelectorAll('#' + id + ' > fieldset'); + + this.bindEventHandlers_(); + this.readCurrentState_(); + + // Handle errors + this.handleProxyErrors_(); +}; + +/////////////////////////////////////////////////////////////////////////////// + +/** + * The proxy types we're capable of handling. + * @enum {string} + */ +ProxyFormController.ProxyTypes = { + AUTO: 'auto_detect', + PAC: 'pac_script', + DIRECT: 'direct', + FIXED: 'fixed_servers', + SYSTEM: 'system' +}; + +/** + * The window types we're capable of handling. + * @enum {int} + */ +ProxyFormController.WindowTypes = { + REGULAR: 1, + INCOGNITO: 2 +}; + +/** + * The extension's level of control of Chrome's roxy setting + * @enum {string} + */ +ProxyFormController.LevelOfControl = { + NOT_CONTROLLABLE: 'NotControllable', + OTHER_EXTENSION: 'ControlledByOtherExtension', + AVAILABLE: 'ControllableByThisExtension', + CONTROLLING: 'ControlledByThisExtension' +}; + +/** + * The response type from 'proxy.settings.get' + * + * @typedef {{value: ProxyConfig, + * levelOfControl: ProxyFormController.LevelOfControl}} + */ +ProxyFormController.WrappedProxyConfig; + +/////////////////////////////////////////////////////////////////////////////// + +/** + * Retrieves proxy settings that have been persisted across restarts. + * + * @return {?ProxyConfig} The persisted proxy configuration, or null if no + * value has been persisted. + * @static + */ +ProxyFormController.getPersistedSettings = function() { + var result = JSON.parse(window.localStorage['proxyConfig']); + return result ? result : null; +}; + + +/** + * Persists proxy settings across restarts. + * + * @param {!ProxyConfig} config The proxy config to persist. + * @static + */ +ProxyFormController.setPersistedSettings = function(config) { + window.localStorage['proxyConfig'] = JSON.stringify(config); +}; + +/////////////////////////////////////////////////////////////////////////////// + +ProxyFormController.prototype = { + /** + * The form's current state. + * @type {regular: ?ProxyConfig, incognito: ?ProxyConfig} + * @private + */ + config_: {regular: null, incognito: null}, + + /** + * Do we have access to incognito mode? + * @type {boolean} + * @private + */ + isAllowedIncognitoAccess_: false, + + /** + * @return {string} The PAC file URL (or an empty string). + */ + get pacURL() { + return document.getElementById('autoconfigURL').value; + }, + + + /** + * @param {!string} value The PAC file URL. + */ + set pacURL(value) { + document.getElementById('autoconfigURL').value = value; + }, + + + /** + * @return {string} The PAC file data (or an empty string). + */ + get manualPac() { + return document.getElementById('autoconfigData').value; + }, + + + /** + * @param {!string} value The PAC file data. + */ + set manualPac(value) { + document.getElementById('autoconfigData').value = value; + }, + + + /** + * @return {Array.<string>} A list of hostnames that should bypass the proxy. + */ + get bypassList() { + return document.getElementById('bypassList').value.split(/\s*(?:,|^)\s*/m); + }, + + + /** + * @param {?Array.<string>} data A list of hostnames that should bypass + * the proxy. If empty, the bypass list is emptied. + */ + set bypassList(data) { + if (!data) + data = []; + document.getElementById('bypassList').value = data.join(', '); + }, + + + /** + * @see http://code.google.com/chrome/extensions/trunk/experimental.proxy.html + * @return {?ProxyServer} An object containing the proxy server host, port, + * and scheme. If null, there is no single proxy. + */ + get singleProxy() { + var checkbox = document.getElementById('singleProxyForEverything'); + return checkbox.checked ? this.httpProxy : null; + }, + + + /** + * @see http://code.google.com/chrome/extensions/trunk/experimental.proxy.html + * @param {?ProxyServer} data An object containing the proxy server host, + * port, and scheme. If null, the single proxy checkbox will be unchecked. + */ + set singleProxy(data) { + var checkbox = document.getElementById('singleProxyForEverything'); + checkbox.checked = !!data; + + if (data) + this.httpProxy = data; + + if (checkbox.checked) + checkbox.parentNode.parentNode.classList.add('single'); + else + checkbox.parentNode.parentNode.classList.remove('single'); + }, + + /** + * @return {?ProxyServer} An object containing the proxy server host, port + * and scheme. + */ + get httpProxy() { + return this.getProxyImpl_('Http'); + }, + + + /** + * @param {?ProxyServer} data An object containing the proxy server host, + * port, and scheme. If empty, empties the proxy setting. + */ + set httpProxy(data) { + this.setProxyImpl_('Http', data); + }, + + + /** + * @return {?ProxyServer} An object containing the proxy server host, port + * and scheme. + */ + get httpsProxy() { + return this.getProxyImpl_('Https'); + }, + + + /** + * @param {?ProxyServer} data An object containing the proxy server host, + * port, and scheme. If empty, empties the proxy setting. + */ + set httpsProxy(data) { + this.setProxyImpl_('Https', data); + }, + + + /** + * @return {?ProxyServer} An object containing the proxy server host, port + * and scheme. + */ + get ftpProxy() { + return this.getProxyImpl_('Ftp'); + }, + + + /** + * @param {?ProxyServer} data An object containing the proxy server host, + * port, and scheme. If empty, empties the proxy setting. + */ + set ftpProxy(data) { + this.setProxyImpl_('Ftp', data); + }, + + + /** + * @return {?ProxyServer} An object containing the proxy server host, port + * and scheme. + */ + get fallbackProxy() { + return this.getProxyImpl_('Fallback'); + }, + + + /** + * @param {?ProxyServer} data An object containing the proxy server host, + * port, and scheme. If empty, empties the proxy setting. + */ + set fallbackProxy(data) { + this.setProxyImpl_('Fallback', data); + }, + + + /** + * @param {string} type The type of proxy that's being set ("Http", + * "Https", etc.). + * @return {?ProxyServer} An object containing the proxy server host, + * port, and scheme. + * @private + */ + getProxyImpl_: function(type) { + var result = { + scheme: document.getElementById('proxyScheme' + type).value, + host: document.getElementById('proxyHost' + type).value, + port: parseInt(document.getElementById('proxyPort' + type).value, 10) + }; + return (result.scheme && result.host && result.port) ? result : undefined; + }, + + + /** + * A generic mechanism for setting proxy data. + * + * @see http://code.google.com/chrome/extensions/trunk/experimental.proxy.html + * @param {string} type The type of proxy that's being set ("Http", + * "Https", etc.). + * @param {?ProxyServer} data An object containing the proxy server host, + * port, and scheme. If empty, empties the proxy setting. + * @private + */ + setProxyImpl_: function(type, data) { + if (!data) + data = {scheme: 'http', host: '', port: ''}; + + document.getElementById('proxyScheme' + type).value = data.scheme; + document.getElementById('proxyHost' + type).value = data.host; + document.getElementById('proxyPort' + type).value = data.port; + }, + +/////////////////////////////////////////////////////////////////////////////// + + /** + * Calls the proxy API to read the current settings, and populates the form + * accordingly. + * + * @private + */ + readCurrentState_: function() { + chrome.extension.isAllowedIncognitoAccess( + this.handleIncognitoAccessResponse_.bind(this)); + }, + + /** + * Handles the respnse from `chrome.extension.isAllowedIncognitoAccess` + * We can't render the form until we know what our access level is, so + * we wait until we have confirmed incognito access levels before + * asking for the proxy state. + * + * @param {boolean} state The state of incognito access. + * @private + */ + handleIncognitoAccessResponse_: function(state) { + this.isAllowedIncognitoAccess_ = state; + chrome.experimental.proxy.settings.get({incognito: false}, + this.handleRegularState_.bind(this)); + if (this.isAllowedIncognitoAccess_) { + chrome.experimental.proxy.settings.get({incognito: true}, + this.handleIncognitoState_.bind(this)); + } + }, + + /** + * Handles the response from 'proxy.settings.get' for regular settings. + * + * @param {ProxyFormController.WrappedProxyConfig} c The proxy data and + * extension's level of control thereof. + * @private + */ + handleRegularState_: function(c) { + if (c.levelOfControl === ProxyFormController.LevelOfControl.AVAILABLE || + c.levelOfControl === ProxyFormController.LevelOfControl.CONTROLLING) { + this.recalcFormValues_(c.value); + this.config_.regular = c.value; + } else { + this.handleLackOfControl_(c.levelOfControl); + } + }, + + /** + * Handles the response from 'proxy.settings.get' for incognito settings. + * + * @param {ProxyFormController.WrappedProxyConfig} c The proxy data and + * extension's level of control thereof. + * @private + */ + handleIncognitoState_: function(c) { + if (c.levelOfControl === ProxyFormController.LevelOfControl.AVAILABLE || + c.levelOfControl === ProxyFormController.LevelOfControl.CONTROLLING) { + if (this.isIncognitoMode_()) + this.recalcFormValues_(c.value); + + this.config_.incognito = c.value; + } else { + this.handleLackOfControl_(c.levelOfControl); + } + }, + + /** + * Binds event handlers for the various bits and pieces of the form that + * are interesting to the controller. + * + * @private + */ + bindEventHandlers_: function() { + this.form_.addEventListener('click', this.dispatchFormClick_.bind(this)); + }, + + + /** + * When a `click` event is triggered on the form, this function handles it by + * analyzing the context, and dispatching the click to the correct handler. + * + * @param {Event} e The event to be handled. + * @private + * @return {boolean} True if the event should bubble, false otherwise. + */ + dispatchFormClick_: function(e) { + var t = e.target; + + // Case 1: "Apply" + if (t.nodeName === 'INPUT' && t.getAttribute('type') === 'submit') { + return this.applyChanges_(e); + + // Case 2: "Use the same proxy for all protocols" in an active section + } else if (t.nodeName === 'INPUT' && + t.getAttribute('type') === 'checkbox' && + t.parentNode.parentNode.parentNode.classList.contains('active') + ) { + return this.toggleSingleProxyConfig_(e); + + // Case 3: "Flip to incognito mode." + } else if (t.nodeName === 'BUTTON') { + return this.toggleIncognitoMode_(e); + + // Case 4: Click on something random: maybe changing active config group? + } else { + // Walk up the tree until we hit `form > fieldset` or fall off the top + while (t && (t.nodeName !== 'FIELDSET' || + t.parentNode.nodeName !== 'FORM')) { + t = t.parentNode; + } + if (t) { + this.changeActive_(t); + return false; + } + } + return true; + }, + + + /** + * Sets the form's active config group. + * + * @param {DOMElement} fieldset The configuration group to activate. + * @private + */ + changeActive_: function(fieldset) { + for (var i = 0; i < this.configGroups_.length; i++) { + var el = this.configGroups_[i]; + var radio = el.querySelector("input[type='radio']"); + if (el === fieldset) { + el.classList.add('active'); + radio.checked = true; + } else { + el.classList.remove('active'); + } + } + this.recalcDisabledInputs_(); + }, + + + /** + * Recalculates the `disabled` state of the form's input elements, based + * on the currently active group, and that group's contents. + * + * @private + */ + recalcDisabledInputs_: function() { + var i, j; + for (i = 0; i < this.configGroups_.length; i++) { + var el = this.configGroups_[i]; + var inputs = el.querySelectorAll( + "input:not([type='radio']), select, textarea"); + if (el.classList.contains('active')) { + for (j = 0; j < inputs.length; j++) { + inputs[j].removeAttribute('disabled'); + } + } else { + for (j = 0; j < inputs.length; j++) { + inputs[j].setAttribute('disabled', 'disabled'); + } + } + } + }, + + + /** + * Handler called in response to click on form's submission button. Generates + * the proxy configuration and passes it to `useCustomProxySettings`, or + * handles errors in user input. + * + * @param {Event} e DOM event generated by the user's click. + * @private + */ + applyChanges_: function(e) { + e.preventDefault(); + e.stopPropagation(); + + if (this.isIncognitoMode_()) + this.config_.incognito = this.generateProxyConfig_(); + else + this.config_.regular = this.generateProxyConfig_(); + + chrome.experimental.proxy.settings.set( + {value: this.config_.regular, incognito: false}, + this.callbackForRegularSettings_.bind(this)); + }, + + /** + * Called in response to setting a regular window's proxy settings: checks + * for `lastError`, and then sets incognito settings (if they exist). + * + * @private + */ + callbackForRegularSettings_: function() { + if (chrome.extension.lastError) { + this.generateAlert_(chrome.i18n.getMessage('errorSettingRegularProxy')); + return; + } + if (this.config_.incognito) { + chrome.experimental.proxy.settings.set( + {value: this.config_.incognito, incognito: true}, + this.callbackForIncognitoSettings_.bind(this)); + } else { + ProxyFormController.setPersistedSettings(this.config_); + this.generateAlert_(chrome.i18n.getMessage('successfullySetProxy')); + } + }, + + /** + * Called in response to setting an incognito window's proxy settings: checks + * for `lastError` and sets a success message. + * + * @private + */ + callbackForIncognitoSettings_: function() { + if (chrome.extension.lastError) { + this.generateAlert_(chrome.i18n.getMessage('errorSettingIncognitoProxy')); + return; + } + ProxyFormController.setPersistedSettings(this.config_); + this.generateAlert_( + chrome.i18n.getMessage('successfullySetProxy')); + }, + + /** + * Generates an alert overlay inside the proxy's popup, then closes the popup + * after a short delay. + * + * @param {string} msg The message to be displayed in the overlay. + * @param {?boolean} close Should the window be closed? Defaults to true. + * @private + */ + generateAlert_: function(msg, close) { + var success = document.createElement('div'); + success.classList.add('overlay'); + success.setAttribute('role', 'alert'); + success.textContent = msg; + document.body.appendChild(success); + + setTimeout(function() { success.classList.add('visible'); }, 10); + setTimeout(function() { + if (close === false) + success.classList.remove('visible'); + else + window.close(); + }, 3000); + }, + + + /** + * Parses the proxy configuration form, and generates a ProxyConfig object + * that can be passed to `useCustomProxyConfig`. + * + * @see http://code.google.com/chrome/extensions/trunk/experimental.proxy.html + * @return {ProxyConfig} The proxy configuration represented by the form. + * @private + */ + generateProxyConfig_: function() { + var active = document.getElementsByClassName('active')[0]; + switch (active.id) { + case ProxyFormController.ProxyTypes.SYSTEM: + return {mode: 'system'}; + case ProxyFormController.ProxyTypes.DIRECT: + return {mode: 'direct'}; + case ProxyFormController.ProxyTypes.PAC: + var pacScriptURL = this.pacURL; + var pacManual = this.manualPac; + if (pacScriptURL) + return {mode: 'pac_script', pacScript: {url: pacScriptURL}}; + else if (pacManual) + return {mode: 'pac_script', pacScript: {data: pacManual}}; + else + return {mode: 'auto_detect'}; + case ProxyFormController.ProxyTypes.FIXED: + var config = {mode: 'fixed_servers'}; + if (this.singleProxy) { + config.rules = {singleProxy: this.singleProxy}; + } else { + config.rules = { + proxyForHttp: this.httpProxy, + proxyForHttps: this.httpsProxy, + proxyForFtp: this.ftpProxy, + fallbackProxy: this.fallbackProxy, + bypassList: this.bypassList + }; + } + return config; + } + }, + + + /** + * Sets the proper display classes based on the "Use the same proxy server + * for all protocols" checkbox. Expects to be called as an event handler + * when that field is clicked. + * + * @param {Event} e The `click` event to respond to. + * @private + */ + toggleSingleProxyConfig_: function(e) { + var checkbox = e.target; + if (checkbox.nodeName === 'INPUT' && + checkbox.getAttribute('type') === 'checkbox') { + if (checkbox.checked) + checkbox.parentNode.parentNode.classList.add('single'); + else + checkbox.parentNode.parentNode.classList.remove('single'); + } + }, + + + /** + * Returns the form's current incognito status. + * + * @return {boolean} True if the form is in incognito mode, false otherwise. + * @private + */ + isIncognitoMode_: function(e) { + return this.form_.parentNode.classList.contains('incognito'); + }, + + + /** + * Toggles the form's incognito mode. Saves the current state to an object + * property for later use, clears the form, and toggles the appropriate state. + * + * @param {Event} e The `click` event to respond to. + * @private + */ + toggleIncognitoMode_: function(e) { + var div = this.form_.parentNode; + var button = document.getElementsByTagName('button')[0]; + + // Cancel the button click. + e.preventDefault(); + e.stopPropagation(); + + // If we can't access Incognito settings, throw a message and return. + if (!this.isAllowedIncognitoAccess_) { + var msg = "I'm sorry, Dave, I'm afraid I can't do that. Give me access " + + "to Incognito settings by checking the checkbox labeled " + + "'Allow in Incognito mode', which is visible at " + + "chrome://extensions."; + this.generateAlert_(msg, false); + return; + } + + if (this.isIncognitoMode_()) { + // In incognito mode, switching to cognito. + this.config_.incognito = this.generateProxyConfig_(); + div.classList.remove('incognito'); + this.recalcFormValues_(this.config_.regular); + button.innerText = 'Configure incognito window settings.'; + } else { + // In cognito mode, switching to incognito. + this.config_.regular = this.generateProxyConfig_(); + div.classList.add('incognito'); + this.recalcFormValues_(this.config_.incognito); + button.innerText = 'Configure regular window settings.'; + } + }, + + + /** + * Sets the form's values based on a ProxyConfig. + * + * @param {!ProxyConfig} c The ProxyConfig object. + * @private + */ + recalcFormValues_: function(c) { + // Normalize `auto_detect` + if (c.mode === 'auto_detect') + c.mode = 'pac_script'; + // Activate one of the groups, based on `mode`. + this.changeActive_(document.getElementById(c.mode)); + // Populate the PAC script + if (c.pacScript) { + if (c.pacScript.url) + this.pacURL = c.pacScript.url; + } else { + this.pacURL = ''; + } + // Evaluate the `rules` + if (c.rules) { + var rules = c.rules; + if (rules.singleProxy) { + this.singleProxy = rules.singleProxy; + } else { + this.singleProxy = null; + this.httpProxy = rules.proxyForHttp; + this.httpsProxy = rules.proxyForHttps; + this.ftpProxy = rules.proxyForFtp; + this.fallbackProxy = rules.fallbackProxy; + } + this.bypassList = rules.bypassList; + } else { + this.singleProxy = null; + this.httpProxy = null; + this.httpsProxy = null; + this.ftpProxy = null; + this.fallbackProxy = null; + this.bypassList = ''; + } + }, + + + /** + * Handles the case in which this extension doesn't have the ability to + * control the Proxy settings, either because of an overriding policy + * or an extension with higher priority. + * + * @param {ProxyFormController.LevelOfControl} l The level of control this + * extension has over the proxy settings. + * @private + */ + handleLackOfControl_: function(l) { + var msg; + if (l === ProxyFormController.LevelOfControl.NO_ACCESS) + msg = chrome.i18n.getMessage('errorNoExtensionAccess'); + else if (l === ProxyFormController.LevelOfControl.OTHER_EXTENSION) + msg = chrome.i18n.getMessage('errorOtherExtensionControls'); + this.generateAlert_(msg); + }, + + + /** + * Handle the case in which errors have been generated outside the context + * of this popup. + * + * @private + */ + handleProxyErrors_: function() { + chrome.extension.sendRequest( + {type: 'getError'}, + this.handleProxyErrorHandlerResponse_.bind(this)); + }, + + /** + * Handles response from ProxyErrorHandler + * + * @param {{result: !string}} response The message sent in response to this + * popup's request. + */ + handleProxyErrorHandlerResponse_: function(response) { + if (response.result !== null) { + var error = response.result; + console.error(error); // TODO(mkwst): Do something more interesting + this.generateAlert_( + chrome.i18n.getMessage('errorProxyError', error.error), + false); + } + chrome.extension.sendRequest({type: 'clearError'}); + } +}; diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/jsunittest.js b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/jsunittest.js new file mode 100644 index 0000000..8e1eab0 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/jsunittest.js @@ -0,0 +1,964 @@ +/* Jsunittest, version 0.6.0 + * (c) 2008 Dr Nic Williams + * + * Jsunittest is freely distributable under + * the terms of an MIT-style license. + * For details, see the web site: http://jsunittest.rubyforge.org + * + *--------------------------------------------------------------------------*/ + +var JsUnitTest = { + Version: '0.6.0', +}; + +var DrNicTest = { + Unit: {}, + inspect: function(object) { + try { + if (typeof object == "undefined") return 'undefined'; + if (object === null) return 'null'; + if (typeof object == "string") { + var useDoubleQuotes = arguments[1]; + var escapedString = this.gsub(object, /[\x00-\x1f\\]/, function(match) { + var character = String.specialChar[match[0]]; + return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + }; + return String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + }, + $: function(element) { + if (arguments.length > 1) { + for (var i = 0, elements = [], length = arguments.length; i < length; i++) + elements.push(this.$(arguments[i])); + return elements; + } + if (typeof element == "string") + element = document.getElementById(element); + return element; + }, + gsub: function(source, pattern, replacement) { + var result = '', match; + replacement = arguments.callee.prepareReplacement(replacement); + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += DrNicTest.String.interpret(replacement(match)); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + }, + scan: function(source, pattern, iterator) { + this.gsub(source, pattern, iterator); + return String(source); + }, + escapeHTML: function(data) { + return data.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + }, + arrayfromargs: function(args) { + var myarray = new Array(); + var i; + + for (i=0;i<args.length;i++) + myarray[i] = args[i]; + + return myarray; + }, + hashToSortedArray: function(hash) { + var results = []; + for (key in hash) { + results.push([key, hash[key]]); + } + return results.sort(); + }, + flattenArray: function(array) { + var results = arguments[1] || []; + for (var i=0; i < array.length; i++) { + var object = array[i]; + if (object != null && typeof object == "object" && + 'splice' in object && 'join' in object) { + this.flattenArray(object, results); + } else { + results.push(object); + } + }; + return results; + }, + selectorMatch: function(expression, element) { + var tokens = []; + var patterns = { + // combinators must be listed first + // (and descendant needs to be last combinator) + laterSibling: /^\s*~\s*/, + child: /^\s*>\s*/, + adjacent: /^\s*\+\s*/, + descendant: /^\s/, + + // selectors follow + tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, + id: /^#([\w\-\*]+)(\b|$)/, + className: /^\.([\w\-\*]+)(\b|$)/, + pseudo: + /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, + attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/, + attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ + }; + + var assertions = { + tagName: function(element, matches) { + return matches[1].toUpperCase() == element.tagName.toUpperCase(); + }, + + className: function(element, matches) { + return Element.hasClassName(element, matches[1]); + }, + + id: function(element, matches) { + return element.id === matches[1]; + }, + + attrPresence: function(element, matches) { + return Element.hasAttribute(element, matches[1]); + }, + + attr: function(element, matches) { + var nodeValue = Element.readAttribute(element, matches[1]); + return nodeValue && operators[matches[2]](nodeValue, matches[5] || matches[6]); + } + }; + var e = this.expression, ps = patterns, as = assertions; + var le, p, m; + + while (e && le !== e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + p = ps[i]; + if (m = e.match(p)) { + // use the Selector.assertions methods unless the selector + // is too complex. + if (as[i]) { + tokens.push([i, Object.clone(m)]); + e = e.replace(m[0], ''); + } + } + } + } + + var match = true, name, matches; + for (var i = 0, token; token = tokens[i]; i++) { + name = token[0], matches = token[1]; + if (!assertions[name](element, matches)) { + match = false; break; + } + } + + return match; + }, + toQueryParams: function(query, separator) { + var query = query || window.location.search; + var match = query.replace(/^\s+/, '').replace(/\s+$/, '').match(/([^?#]*)(#.*)?$/); + if (!match) return { }; + + var hash = {}; + var parts = match[1].split(separator || '&'); + for (var i=0; i < parts.length; i++) { + var pair = parts[i].split('='); + if (pair[0]) { + var key = decodeURIComponent(pair.shift()); + var value = pair.length > 1 ? pair.join('=') : pair[0]; + if (value != undefined) value = decodeURIComponent(value); + + if (key in hash) { + var object = hash[key]; + var isArray = object != null && typeof object == "object" && + 'splice' in object && 'join' in object + if (!isArray) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + }; + return hash; + }, + + String: { + interpret: function(value) { + return value == null ? '' : String(value); + } + } +}; + +DrNicTest.gsub.prepareReplacement = function(replacement) { + if (typeof replacement == "function") return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; +}; + +DrNicTest.Template = function(template, pattern) { + this.template = template; //template.toString(); + this.pattern = pattern || DrNicTest.Template.Pattern; +}; + +DrNicTest.Template.prototype.evaluate = function(object) { + if (typeof object.toTemplateReplacements == "function") + object = object.toTemplateReplacements(); + + return DrNicTest.gsub(this.template, this.pattern, function(match) { + if (object == null) return ''; + + var before = match[1] || ''; + if (before == '\\') return match[2]; + + var ctx = object, expr = match[3]; + var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + match = pattern.exec(expr); + if (match == null) return before; + + while (match != null) { + var comp = (match[1].indexOf('[]') === 0) ? match[2].gsub('\\\\]', ']') : match[1]; + ctx = ctx[comp]; + if (null == ctx || '' == match[3]) break; + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); + match = pattern.exec(expr); + } + + return before + DrNicTest.String.interpret(ctx); + }); +} + +DrNicTest.Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; +DrNicTest.Event = {}; +// written by Dean Edwards, 2005 +// with input from Tino Zijdel, Matthias Miller, Diego Perini +// namespaced by Dr Nic Williams 2008 + +// http://dean.edwards.name/weblog/2005/10/add-event/ +// http://dean.edwards.name/weblog/2005/10/add-event2/ +DrNicTest.Event.addEvent = function(element, type, handler) { + if (element.addEventListener) { + element.addEventListener(type, handler, false); + } else { + // assign each event handler a unique ID + if (!handler.$$guid) handler.$$guid = addEvent.guid++; + // create a hash table of event types for the element + if (!element.events) element.events = {}; + // create a hash table of event handlers for each element/event pair + var handlers = element.events[type]; + if (!handlers) { + handlers = element.events[type] = {}; + // store the existing event handler (if there is one) + if (element["on" + type]) { + handlers[0] = element["on" + type]; + } + } + // store the event handler in the hash table + handlers[handler.$$guid] = handler; + // assign a global event handler to do all the work + element["on" + type] = handleEvent; + } +}; +// a counter used to create unique IDs +DrNicTest.Event.addEvent.guid = 1; + +DrNicTest.Event.removeEvent = function(element, type, handler) { + if (element.removeEventListener) { + element.removeEventListener(type, handler, false); + } else { + // delete the event handler from the hash table + if (element.events && element.events[type]) { + delete element.events[type][handler.$$guid]; + } + } +}; + +DrNicTest.Event.handleEvent = function(event) { + var returnValue = true; + // grab the event object (IE uses a global event object) + event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); + // get a reference to the hash table of event handlers + var handlers = this.events[event.type]; + // execute each event handler + for (var i in handlers) { + this.$$handleEvent = handlers[i]; + if (this.$$handleEvent(event) === false) { + returnValue = false; + } + } + return returnValue; +}; + +DrNicTest.Event.fixEvent = function(event) { + // add W3C standard event methods + event.preventDefault = fixEvent.preventDefault; + event.stopPropagation = fixEvent.stopPropagation; + return event; +}; +DrNicTest.Event.fixEvent.preventDefault = function() { + this.returnValue = false; +}; +DrNicTest.Event.fixEvent.stopPropagation = function() { + this.cancelBubble = true; +}; + +DrNicTest.Unit.Logger = function(element) { + this.element = DrNicTest.$(element); + if (this.element) this._createLogTable(); +}; + +DrNicTest.Unit.Logger.prototype.start = function(testName) { + if (!this.element) return; + var tbody = this.element.getElementsByTagName('tbody')[0]; + tbody.innerHTML = tbody.innerHTML + '<tr><td>' + testName + '</td><td></td><td></td></tr>'; +}; + +DrNicTest.Unit.Logger.prototype.setStatus = function(status) { + var logline = this.getLastLogLine(); + logline.className = status; + var statusCell = logline.getElementsByTagName('td')[1]; + statusCell.innerHTML = status; +}; + +DrNicTest.Unit.Logger.prototype.finish = function(status, summary) { + if (!this.element) return; + this.setStatus(status); + this.message(summary); +}; + +DrNicTest.Unit.Logger.prototype.message = function(message) { + if (!this.element) return; + var cell = this.getMessageCell(); + cell.innerHTML = this._toHTML(message); +}; + +DrNicTest.Unit.Logger.prototype.summary = function(summary) { + if (!this.element) return; + var div = this.element.getElementsByTagName('div')[0]; + div.innerHTML = this._toHTML(summary); +}; + +DrNicTest.Unit.Logger.prototype.getLastLogLine = function() { + var tbody = this.element.getElementsByTagName('tbody')[0]; + var loglines = tbody.getElementsByTagName('tr'); + return loglines[loglines.length - 1]; +}; + +DrNicTest.Unit.Logger.prototype.getMessageCell = function() { + var logline = this.getLastLogLine(); + return logline.getElementsByTagName('td')[2]; +}; + +DrNicTest.Unit.Logger.prototype._createLogTable = function() { + var html = '<div class="logsummary">running...</div>' + + '<table class="logtable">' + + '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' + + '<tbody class="loglines"></tbody>' + + '</table>'; + this.element.innerHTML = html; +}; + +DrNicTest.Unit.Logger.prototype.appendActionButtons = function(actions) { + // actions = $H(actions); + // if (!actions.any()) return; + // var div = new Element("div", {className: 'action_buttons'}); + // actions.inject(div, function(container, action) { + // var button = new Element("input").setValue(action.key).observe("click", action.value); + // button.type = "button"; + // return container.insert(button); + // }); + // this.getMessageCell().insert(div); +}; + +DrNicTest.Unit.Logger.prototype._toHTML = function(txt) { + return DrNicTest.escapeHTML(txt).replace(/\n/g,"<br/>"); +}; +DrNicTest.Unit.MessageTemplate = function(string) { + var parts = []; + var str = DrNicTest.scan((string || ''), /(?=[^\\])\?|(?:\\\?|[^\?])+/, function(part) { + parts.push(part[0]); + }); + this.parts = parts; +}; + +DrNicTest.Unit.MessageTemplate.prototype.evaluate = function(params) { + var results = []; + for (var i=0; i < this.parts.length; i++) { + var part = this.parts[i]; + var result = (part == '?') ? DrNicTest.inspect(params.shift()) : part.replace(/\\\?/, '?'); + results.push(result); + }; + return results.join(''); +}; +// A generic function for performming AJAX requests +// It takes one argument, which is an object that contains a set of options +// All of which are outline in the comments, below +// From John Resig's book Pro JavaScript Techniques +// published by Apress, 2006-8 +DrNicTest.ajax = function( options ) { + + // Load the options object with defaults, if no + // values were provided by the user + options = { + // The type of HTTP Request + type: options.type || "POST", + + // The URL the request will be made to + url: options.url || "", + + // How long to wait before considering the request to be a timeout + timeout: options.timeout || 5000, + + // Functions to call when the request fails, succeeds, + // or completes (either fail or succeed) + onComplete: options.onComplete || function(){}, + onError: options.onError || function(){}, + onSuccess: options.onSuccess || function(){}, + + // The data type that'll be returned from the server + // the default is simply to determine what data was returned from the + // and act accordingly. + data: options.data || "" + }; + + // Create the request object + var xml = new XMLHttpRequest(); + + // Open the asynchronous POST request + xml.open(options.type, options.url, true); + + // We're going to wait for a request for 5 seconds, before giving up + var timeoutLength = 5000; + + // Keep track of when the request has been succesfully completed + var requestDone = false; + + // Initalize a callback which will fire 5 seconds from now, cancelling + // the request (if it has not already occurred). + setTimeout(function(){ + requestDone = true; + }, timeoutLength); + + // Watch for when the state of the document gets updated + xml.onreadystatechange = function(){ + // Wait until the data is fully loaded, + // and make sure that the request hasn't already timed out + if ( xml.readyState == 4 && !requestDone ) { + + // Check to see if the request was successful + if ( httpSuccess( xml ) ) { + + // Execute the success callback with the data returned from the server + options.onSuccess( httpData( xml, options.type ) ); + + // Otherwise, an error occurred, so execute the error callback + } else { + options.onError(); + } + + // Call the completion callback + options.onComplete(); + + // Clean up after ourselves, to avoid memory leaks + xml = null; + } + }; + + // Establish the connection to the server + xml.send(); + + // Determine the success of the HTTP response + function httpSuccess(r) { + try { + // If no server status is provided, and we're actually + // requesting a local file, then it was successful + return !r.status && location.protocol == "file:" || + + // Any status in the 200 range is good + ( r.status >= 200 && r.status < 300 ) || + + // Successful if the document has not been modified + r.status == 304 || + + // Safari returns an empty status if the file has not been modified + navigator.userAgent.indexOf("Safari") >= 0 && typeof r.status == "undefined"; + } catch(e){} + + // If checking the status failed, then assume that the request failed too + return false; + } + + // Extract the correct data from the HTTP response + function httpData(r,type) { + // Get the content-type header + var ct = r.getResponseHeader("content-type"); + + // If no default type was provided, determine if some + // form of XML was returned from the server + var data = !type && ct && ct.indexOf("xml") >= 0; + + // Get the XML Document object if XML was returned from + // the server, otherwise return the text contents returned by the server + data = type == "xml" || data ? r.responseXML : r.responseText; + + // If the specified type is "script", execute the returned text + // response as if it was JavaScript + if ( type == "script" ) + eval.call( window, data ); + + // Return the response data (either an XML Document or a text string) + return data; + } + +} +DrNicTest.Unit.Assertions = { + buildMessage: function(message, template) { + var args = DrNicTest.arrayfromargs(arguments).slice(2); + return (message ? message + '\n' : '') + + new DrNicTest.Unit.MessageTemplate(template).evaluate(args); + }, + + flunk: function(message) { + this.assertBlock(message || 'Flunked', function() { return false }); + }, + + assertBlock: function(message, block) { + try { + block.call(this) ? this.pass() : this.fail(message); + } catch(e) { this.error(e) } + }, + + assert: function(expression, message) { + message = this.buildMessage(message || 'assert', 'got <?>', expression); + this.assertBlock(message, function() { return expression }); + }, + + assertEqual: function(expected, actual, message) { + message = this.buildMessage(message || 'assertEqual', 'expected <?>, actual: <?>', expected, actual); + this.assertBlock(message, function() { return expected == actual }); + }, + + assertNotEqual: function(expected, actual, message) { + message = this.buildMessage(message || 'assertNotEqual', 'expected <?>, actual: <?>', expected, actual); + this.assertBlock(message, function() { return expected != actual }); + }, + + assertEnumEqual: function(expected, actual, message) { + message = this.buildMessage(message || 'assertEnumEqual', 'expected <?>, actual: <?>', expected, actual); + var expected_array = DrNicTest.flattenArray(expected); + var actual_array = DrNicTest.flattenArray(actual); + this.assertBlock(message, function() { + if (expected_array.length == actual_array.length) { + for (var i=0; i < expected_array.length; i++) { + if (expected_array[i] != actual_array[i]) return false; + }; + return true; + } + return false; + }); + }, + + assertEnumNotEqual: function(expected, actual, message) { + message = this.buildMessage(message || 'assertEnumNotEqual', '<?> was the same as <?>', expected, actual); + var expected_array = DrNicTest.flattenArray(expected); + var actual_array = DrNicTest.flattenArray(actual); + this.assertBlock(message, function() { + if (expected_array.length == actual_array.length) { + for (var i=0; i < expected_array.length; i++) { + if (expected_array[i] != actual_array[i]) return true; + }; + return false; + } + return true; + }); + }, + + assertHashEqual: function(expected, actual, message) { + message = this.buildMessage(message || 'assertHashEqual', 'expected <?>, actual: <?>', expected, actual); + var expected_array = DrNicTest.flattenArray(DrNicTest.hashToSortedArray(expected)); + var actual_array = DrNicTest.flattenArray(DrNicTest.hashToSortedArray(actual)); + var block = function() { + if (expected_array.length == actual_array.length) { + for (var i=0; i < expected_array.length; i++) { + if (expected_array[i] != actual_array[i]) return false; + }; + return true; + } + return false; + }; + this.assertBlock(message, block); + }, + + assertHashNotEqual: function(expected, actual, message) { + message = this.buildMessage(message || 'assertHashNotEqual', '<?> was the same as <?>', expected, actual); + var expected_array = DrNicTest.flattenArray(DrNicTest.hashToSortedArray(expected)); + var actual_array = DrNicTest.flattenArray(DrNicTest.hashToSortedArray(actual)); + // from now we recursively zip & compare nested arrays + var block = function() { + if (expected_array.length == actual_array.length) { + for (var i=0; i < expected_array.length; i++) { + if (expected_array[i] != actual_array[i]) return true; + }; + return false; + } + return true; + }; + this.assertBlock(message, block); + }, + + assertIdentical: function(expected, actual, message) { + message = this.buildMessage(message || 'assertIdentical', 'expected <?>, actual: <?>', expected, actual); + this.assertBlock(message, function() { return expected === actual }); + }, + + assertNotIdentical: function(expected, actual, message) { + message = this.buildMessage(message || 'assertNotIdentical', 'expected <?>, actual: <?>', expected, actual); + this.assertBlock(message, function() { return expected !== actual }); + }, + + assertNull: function(obj, message) { + message = this.buildMessage(message || 'assertNull', 'got <?>', obj); + this.assertBlock(message, function() { return obj === null }); + }, + + assertNotNull: function(obj, message) { + message = this.buildMessage(message || 'assertNotNull', 'got <?>', obj); + this.assertBlock(message, function() { return obj !== null }); + }, + + assertUndefined: function(obj, message) { + message = this.buildMessage(message || 'assertUndefined', 'got <?>', obj); + this.assertBlock(message, function() { return typeof obj == "undefined" }); + }, + + assertNotUndefined: function(obj, message) { + message = this.buildMessage(message || 'assertNotUndefined', 'got <?>', obj); + this.assertBlock(message, function() { return typeof obj != "undefined" }); + }, + + assertNullOrUndefined: function(obj, message) { + message = this.buildMessage(message || 'assertNullOrUndefined', 'got <?>', obj); + this.assertBlock(message, function() { return obj == null }); + }, + + assertNotNullOrUndefined: function(obj, message) { + message = this.buildMessage(message || 'assertNotNullOrUndefined', 'got <?>', obj); + this.assertBlock(message, function() { return obj != null }); + }, + + assertMatch: function(expected, actual, message) { + message = this.buildMessage(message || 'assertMatch', 'regex <?> did not match <?>', expected, actual); + this.assertBlock(message, function() { return new RegExp(expected).exec(actual) }); + }, + + assertNoMatch: function(expected, actual, message) { + message = this.buildMessage(message || 'assertNoMatch', 'regex <?> matched <?>', expected, actual); + this.assertBlock(message, function() { return !(new RegExp(expected).exec(actual)) }); + }, + + assertHidden: function(element, message) { + message = this.buildMessage(message || 'assertHidden', '? isn\'t hidden.', element); + this.assertBlock(message, function() { return element.style.display == 'none' }); + }, + + assertInstanceOf: function(expected, actual, message) { + message = this.buildMessage(message || 'assertInstanceOf', '<?> was not an instance of the expected type', actual); + this.assertBlock(message, function() { return actual instanceof expected }); + }, + + assertNotInstanceOf: function(expected, actual, message) { + message = this.buildMessage(message || 'assertNotInstanceOf', '<?> was an instance of the expected type', actual); + this.assertBlock(message, function() { return !(actual instanceof expected) }); + }, + + assertRespondsTo: function(method, obj, message) { + message = this.buildMessage(message || 'assertRespondsTo', 'object doesn\'t respond to <?>', method); + this.assertBlock(message, function() { return (method in obj && typeof obj[method] == 'function') }); + }, + + assertRaise: function(exceptionName, method, message) { + message = this.buildMessage(message || 'assertRaise', '<?> exception expected but none was raised', exceptionName); + var block = function() { + try { + method(); + return false; + } catch(e) { + if (e.name == exceptionName) return true; + else throw e; + } + }; + this.assertBlock(message, block); + }, + + assertNothingRaised: function(method, message) { + try { + method(); + this.assert(true, "Expected nothing to be thrown"); + } catch(e) { + message = this.buildMessage(message || 'assertNothingRaised', '<?> was thrown when nothing was expected.', e); + this.flunk(message); + } + }, + + _isVisible: function(element) { + element = DrNicTest.$(element); + if(!element.parentNode) return true; + this.assertNotNull(element); + if(element.style && element.style.display == 'none') + return false; + + return arguments.callee.call(this, element.parentNode); + }, + + assertVisible: function(element, message) { + message = this.buildMessage(message, '? was not visible.', element); + this.assertBlock(message, function() { return this._isVisible(element) }); + }, + + assertNotVisible: function(element, message) { + message = this.buildMessage(message, '? was not hidden and didn\'t have a hidden parent either.', element); + this.assertBlock(message, function() { return !this._isVisible(element) }); + }, + + assertElementsMatch: function() { + var pass = true, expressions = DrNicTest.arrayfromargs(arguments); + var elements = expressions.shift(); + if (elements.length != expressions.length) { + message = this.buildMessage('assertElementsMatch', 'size mismatch: ? elements, ? expressions (?).', elements.length, expressions.length, expressions); + this.flunk(message); + pass = false; + } + for (var i=0; i < expressions.length; i++) { + var expression = expressions[i]; + var element = DrNicTest.$(elements[i]); + if (DrNicTest.selectorMatch(expression, element)) { + pass = true; + break; + } + message = this.buildMessage('assertElementsMatch', 'In index <?>: expected <?> but got ?', index, expression, element); + this.flunk(message); + pass = false; + }; + this.assert(pass, "Expected all elements to match."); + }, + + assertElementMatches: function(element, expression, message) { + this.assertElementsMatch([element], expression); + } +}; +DrNicTest.Unit.Runner = function(testcases) { + var argumentOptions = arguments[1] || {}; + var options = this.options = {}; + options.testLog = ('testLog' in argumentOptions) ? argumentOptions.testLog : 'testlog'; + options.resultsURL = this.queryParams.resultsURL; + options.testLog = DrNicTest.$(options.testLog); + + this.tests = this.getTests(testcases); + this.currentTest = 0; + this.logger = new DrNicTest.Unit.Logger(options.testLog); + + var self = this; + DrNicTest.Event.addEvent(window, "load", function() { + setTimeout(function() { + self.runTests(); + }, 0.1); + }); +}; + +DrNicTest.Unit.Runner.prototype.queryParams = DrNicTest.toQueryParams(); + +DrNicTest.Unit.Runner.prototype.portNumber = function() { + if (window.location.search.length > 0) { + var matches = window.location.search.match(/\:(\d{3,5})\//); + if (matches) { + return parseInt(matches[1]); + } + } + return null; +}; + +DrNicTest.Unit.Runner.prototype.getTests = function(testcases) { + var tests = [], options = this.options; + if (this.queryParams.tests) tests = this.queryParams.tests.split(','); + else if (options.tests) tests = options.tests; + else if (options.test) tests = [option.test]; + else { + for (testname in testcases) { + if (testname.match(/^test/)) tests.push(testname); + } + } + var results = []; + for (var i=0; i < tests.length; i++) { + var test = tests[i]; + if (testcases[test]) + results.push( + new DrNicTest.Unit.Testcase(test, testcases[test], testcases.setup, testcases.teardown) + ); + }; + return results; +}; + +DrNicTest.Unit.Runner.prototype.getResult = function() { + var results = { + tests: this.tests.length, + assertions: 0, + failures: 0, + errors: 0 + }; + + for (var i=0; i < this.tests.length; i++) { + var test = this.tests[i]; + results.assertions += test.assertions; + results.failures += test.failures; + results.errors += test.errors; + }; + return results; +}; + +DrNicTest.Unit.Runner.prototype.postResults = function() { + if (this.options.resultsURL) { + // new Ajax.Request(this.options.resultsURL, + // { method: 'get', parameters: this.getResult(), asynchronous: false }); + var results = this.getResult(); + var url = this.options.resultsURL + "?"; + url += "assertions="+ results.assertions + "&"; + url += "failures=" + results.failures + "&"; + url += "errors=" + results.errors; + DrNicTest.ajax({ + url: url, + type: 'GET' + }) + } +}; + +DrNicTest.Unit.Runner.prototype.runTests = function() { + var test = this.tests[this.currentTest], actions; + + if (!test) return this.finish(); + if (!test.isWaiting) this.logger.start(test.name); + test.run(); + var self = this; + if(test.isWaiting) { + this.logger.message("Waiting for " + test.timeToWait + "ms"); + // setTimeout(this.runTests.bind(this), test.timeToWait || 1000); + setTimeout(function() { + self.runTests(); + }, test.timeToWait || 1000); + return; + } + + this.logger.finish(test.status(), test.summary()); + if (actions = test.actions) this.logger.appendActionButtons(actions); + this.currentTest++; + // tail recursive, hopefully the browser will skip the stackframe + this.runTests(); +}; + +DrNicTest.Unit.Runner.prototype.finish = function() { + this.postResults(); + this.logger.summary(this.summary()); +}; + +DrNicTest.Unit.Runner.prototype.summary = function() { + return new DrNicTest.Template('#{tests} tests, #{assertions} assertions, #{failures} failures, #{errors} errors').evaluate(this.getResult()); +}; +DrNicTest.Unit.Testcase = function(name, test, setup, teardown) { + this.name = name; + this.test = test || function() {}; + this.setup = setup || function() {}; + this.teardown = teardown || function() {}; + this.messages = []; + this.actions = {}; +}; +// import DrNicTest.Unit.Assertions + +for (method in DrNicTest.Unit.Assertions) { + DrNicTest.Unit.Testcase.prototype[method] = DrNicTest.Unit.Assertions[method]; +} + +DrNicTest.Unit.Testcase.prototype.isWaiting = false; +DrNicTest.Unit.Testcase.prototype.timeToWait = 1000; +DrNicTest.Unit.Testcase.prototype.assertions = 0; +DrNicTest.Unit.Testcase.prototype.failures = 0; +DrNicTest.Unit.Testcase.prototype.errors = 0; +// DrNicTest.Unit.Testcase.prototype.isRunningFromRake = window.location.port == 4711; +DrNicTest.Unit.Testcase.prototype.isRunningFromRake = window.location.port; + +DrNicTest.Unit.Testcase.prototype.wait = function(time, nextPart) { + this.isWaiting = true; + this.test = nextPart; + this.timeToWait = time; +}; + +DrNicTest.Unit.Testcase.prototype.run = function(rethrow) { + try { + try { + if (!this.isWaiting) this.setup(); + this.isWaiting = false; + this.test(); + } finally { + if(!this.isWaiting) { + this.teardown(); + } + } + } + catch(e) { + if (rethrow) throw e; + this.error(e, this); + } +}; + +DrNicTest.Unit.Testcase.prototype.summary = function() { + var msg = '#{assertions} assertions, #{failures} failures, #{errors} errors\n'; + return new DrNicTest.Template(msg).evaluate(this) + + this.messages.join("\n"); +}; + +DrNicTest.Unit.Testcase.prototype.pass = function() { + this.assertions++; +}; + +DrNicTest.Unit.Testcase.prototype.fail = function(message) { + this.failures++; + var line = ""; + try { + throw new Error("stack"); + } catch(e){ + line = (/\.html:(\d+)/.exec(e.stack || '') || ['',''])[1]; + } + this.messages.push("Failure: " + message + (line ? " Line #" + line : "")); +}; + +DrNicTest.Unit.Testcase.prototype.info = function(message) { + this.messages.push("Info: " + message); +}; + +DrNicTest.Unit.Testcase.prototype.error = function(error, test) { + this.errors++; + this.actions['retry with throw'] = function() { test.run(true) }; + this.messages.push(error.name + ": "+ error.message + "(" + DrNicTest.inspect(error) + ")"); +}; + +DrNicTest.Unit.Testcase.prototype.status = function() { + if (this.failures > 0) return 'failed'; + if (this.errors > 0) return 'error'; + return 'passed'; +}; + +DrNicTest.Unit.Testcase.prototype.benchmark = function(operation, iterations) { + var startAt = new Date(); + (iterations || 1).times(operation); + var timeTaken = ((new Date())-startAt); + this.info((arguments[2] || 'Operation') + ' finished ' + + iterations + ' iterations in ' + (timeTaken/1000)+'s' ); + return timeTaken; +}; + +Test = DrNicTest diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/proxy_form_controller_test.html b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/proxy_form_controller_test.html new file mode 100644 index 0000000..096e709 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/proxy_form_controller_test.html @@ -0,0 +1,113 @@ +<!doctype html> +<html> +<head> + <title>Popup for Proxy API Test</title> + <link rel="stylesheet" type="text/css" href="./unittest.css"> + <script src="./jsunittest.js"></script> + <script src="../proxy_form_controller.js"></script> +</head> +<body> + <h1>Proxy Configuration Unit Tests</h1> + + <h2>ProxyFormController</h2> + <div id="proxyformcontrollerlog"></div> + + <div id="fixture"> + <form id="proxyForm"> + <fieldset id="system"> + <legend>System Settings</legend> + <input type="radio" name="proxyType" id="proxyTypeSystem" value="system"> + <label for="proxyTypeSystem">Use the <em>system's proxy settings</em>.</label> + </fieldset> + <fieldset id="direct"> + <legend>Direct Connection</legend> + <input type="radio" name="proxyType" id="proxyTypeDirect" value="direct"> + <label for="proxyTypeDirect">Your computer is <em>directly connected</em> to the internet; no need for a proxy.</label> + </fieldset> + <fieldset id="pac_script"> + <legend>Automatic Configuration</legend> + <input type="radio" name="proxyType" id="proxyTypeAutoconfig" value="autoconfig"> + <label for="proxyTypeAutoconfig">Your proxy supports <em>automatic configuration</em>.</label> + + <section> + <label for="autoconfigURL">Autoconfiguration URL (PAC file)</label> + <input type="url" name="autoconfigURL" id="autoconfigURL"> + <input type="hidden" name="autoconfigData" id="autoconfigData"> + </section> + </fieldset> + <fieldset id="fixed_servers"> + <legend>Manual Proxy</legend> + <input type="radio" name="proxyType" id="proxyTypeManual" value="manual"> + <label for="proxyTypeManual">Configure your proxy settings <em>manually</em>.</label> + <section> + <fieldset> + <legend>HTTP</legend> + <label for="proxyHostHttp">Host</label> + <select id="proxySchemeHttp" name="proxySchemeHttp"> + <option selected value="http">http://</option> + <option value="https">https://</option> + <option value="socks4">socks4://</option> + <option value="socks5">socks5://</option> + </select> + <input type="text" name="proxyHostHttp" id="proxyHostHttp"> + + <label for="proxyPortHttp">Port</label> + <input type="number" min="1" step="1" name="proxyPortHttp" id="proxyPortHttp"> + + <input type="checkbox" name="singleProxyForEverything" id="singleProxyForEverything"> + <label for="singleProxyForEverything">Use the same proxy server for all protocols</label> + </fieldset> + <fieldset> + <legend>HTTPS</legend> + <label for="proxyHostHttps">Host</label> + <select id="proxySchemeHttps" name="proxySchemeHttps"> + <option selected value="http">http://</option> + <option value="https">https://</option> + <option value="socks4">socks4://</option> + <option value="socks5">socks5://</option> + </select> + <input type="text" name="proxyHostHttps" id="proxyHostHttps"> + + <label for="proxyPortHttps">Port</label> + <input type="number" min="1" step="1" name="proxyPortHttps" id="proxyPortHttps"> + </fieldset> + <fieldset> + <legend>FTP</legend> + <label for="proxyHostFtp">Host</label> + <select id="proxySchemeFtp" name="proxySchemeFtp"> + <option selected value="http">http://</option> + <option value="https">https://</option> + <option value="socks4">socks4://</option> + <option value="socks5">socks5://</option> + </select> + <input type="text" name="proxyHostFtp" id="proxyHostFtp"> + + <label for="proxyPortFtp">Port</label> + <input type="number" min="1" step="1" name="proxyPortFtp" id="proxyPortFtp"> + </fieldset> + <fieldset> + <legend>Fallback</legend> + <label for="proxyHostFallback">Host</label> + <select id="proxySchemeFallback" name="proxySchemeFallback"> + <option selected value="http">http://</option> + <option value="https">https://</option> + <option value="socks4">socks4://</option> + <option value="socks5">socks5://</option> + </select> + <input type="text" name="proxyHostFallback" id="proxyHostFallback"> + + <label for="proxyPortFallback">Port</label> + <input type="number" min="1" step="1" name="proxyPortFallback" id="proxyPortFallback"> + </fieldset> + <fieldset> + <label for="bypassList">Bypass proxy for these hosts:</label> + <textarea id="bypassList" name="bypassList" placeholder="localhost,192.168.1.1/16, .example.com"></textarea> + </fieldset> + </section> + </fieldset> + <input type="submit" value="Save proxy settings"> + </form> + </div> + <script src="./proxy_form_controller_test.js"></script> +</body> +</html> diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/proxy_form_controller_test.js b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/proxy_form_controller_test.js new file mode 100644 index 0000000..b577cb6 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/proxy_form_controller_test.js @@ -0,0 +1,519 @@ +// Copyright (c) 2011 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. + +// Stub out the `chrome.experimental.proxy` API +chrome.experimental = chrome.experimental || { + proxy: { + settings: { + get: function() {}, + clear: function() {}, + set: function() {} + } + } +}; +// Stub out i18n +chrome.i18n = chrome.i18n || { + getMessage: function(x) { return x; } +}; +// Stub out messaging and access. +chrome.extension = chrome.extension || { + sendRequest: function() {}, + isAllowedIncognitoAccess: function(funk) { + funk(true); + } +}; +var fixture = document.getElementById('fixture'); +var baselineHTML = fixture.innerHTML; +var groupIDs = [ProxyFormController.ProxyTypes.DIRECT, + ProxyFormController.ProxyTypes.SYSTEM, + ProxyFormController.ProxyTypes.PAC, + ProxyFormController.ProxyTypes.FIXED]; + +var mockFunctionFactory = function(returnValue, logging) { + var called = []; + returnValue = returnValue || null; + + var funky = function() { + called.push(arguments); + if (arguments[1] && typeof(arguments[1]) === 'function') { + var funk = arguments[1]; + funk(returnValue); + } + return returnValue; + }; + funky.getCallList = function() { return called; }; + funky.getValue = function() { return returnValue; }; + return funky; +}; + +var chrome = chrome || {}; + +var proxyform = new Test.Unit.Runner({ + setup: function() { + fixture.innerHTML = baselineHTML; + this.controller_ = new ProxyFormController('proxyForm'); + this.clickEvent_ = document.createEvent('MouseEvents'); + this.clickEvent_.initMouseEvent('click', true, true, window, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + // Reset mock functions. + chrome.experimental = { + proxy: { + settings: { + get: mockFunctionFactory({ + value: {mode: 'system' }, + levelOfControl: 'ControllableByThisExtension' }), + clear: mockFunctionFactory({ + value: {mode: 'system' }, + levelOfControl: 'ControllableByThisExtension' }), + set: mockFunctionFactory({ + value: {mode: 'system' }, + levelOfControl: 'ControllableByThisExtension' }) + } + } + }; + }, + + teardown: function() { + fixture.removeChild(fixture.childNodes[0]); + delete(this.controller_); + }, + + // Clicking on various bits of the interface should set correct classes, + // and select correct radio buttons. + testActivationClicks: function() { + var self = this; + var i; + groupIDs.forEach(function(id) { + var group = document.getElementById(id); + var all = group.querySelectorAll('*'); + for (i = 0; i < all.length; i++) { + group.classList.remove('active'); + all[i].dispatchEvent(self.clickEvent_); + self.assert(group.classList.contains('active')); + } + }); + }, + + // Elements inside an active group should not be disabled, and vice versa + testDisabledElements: function() { + var self = this; + var i, j; + groupIDs.forEach(function(id) { + var group = document.getElementById(id); + var all = group.querySelectorAll('*'); + // First, check that activating a group enables its form elements + for (i = 0; i < all.length; i++) { + group.classList.remove('active'); + var inputs = group.querySelectorAll('input:not([type="radio"]),select'); + for (j = 0; j < inputs.length; j++) { + inputs[j].setAttribute('disabled', 'disabled'); + } + all[i].dispatchEvent(self.clickEvent_); + for (j = 0; j < inputs.length; j++) { + self.assert(!inputs[j].hasAttribute('disabled')); + } + } + }); + }, + + // Clicking the "Use single proxy" checkbox should set the correct + // classes on the form. + testSingleProxyToggle: function() { + var group = document.getElementById( + ProxyFormController.ProxyTypes.FIXED); + var checkbox = document.getElementById('singleProxyForEverything'); + var section = checkbox.parentNode.parentNode; + // Checkbox only works in active group, `testActivationClicks` tests + // the inactive click behavior. + group.classList.add('active'); + + checkbox.checked = false; + checkbox.dispatchEvent(this.clickEvent_); + this.assert(section.classList.contains('single')); + checkbox.dispatchEvent(this.clickEvent_); + this.assert(!section.classList.contains('single')); + }, + + // On instantiation, ProxyFormController should read the current state + // from `chrome.experimental.getCurrentProxySettings`, and react + // accordingly. Let's see if that happens with the next four sets of + // assertsions. + testSetupFormSystem: function() { + chrome.experimental.proxy.settings.get = mockFunctionFactory({ + value: {mode: 'system'}, + levelOfControl: 'ControllableByThisExtension' + }); + + fixture.innerHTML = baselineHTML; + this.controller_ = new ProxyFormController('proxyForm'); + // Wait for async calls to fire + this.wait(100, function() { + this.assertEqual( + 2, + chrome.experimental.proxy.settings.get.getCallList().length); + this.assert( + document.getElementById(ProxyFormController.ProxyTypes.SYSTEM) + .classList.contains('active')); + }); + }, + + testSetupFormDirect: function() { + chrome.experimental.proxy.settings.get = + mockFunctionFactory({value: {mode: 'direct'}, + levelOfControl: 'ControllableByThisExtension'}, true); + + fixture.innerHTML = baselineHTML; + this.controller_ = new ProxyFormController('proxyForm'); + // Wait for async calls to fire + this.wait(100, function() { + this.assertEqual( + 2, + chrome.experimental.proxy.settings.get.getCallList().length); + this.assert( + document.getElementById(ProxyFormController.ProxyTypes.DIRECT) + .classList.contains('active')); + }); + }, + + testSetupFormPac: function() { + chrome.experimental.proxy.settings.get = + mockFunctionFactory({value: {mode: 'pac_script' }, + levelOfControl: 'ControllableByThisExtension'}); + + fixture.innerHTML = baselineHTML; + this.controller_ = new ProxyFormController('proxyForm'); + // Wait for async calls to fire + this.wait(100, function() { + this.assertEqual( + 2, + chrome.experimental.proxy.settings.get.getCallList().length); + this.assert( + document.getElementById(ProxyFormController.ProxyTypes.PAC) + .classList.contains('active')); + }); + }, + + testSetupFormFixed: function() { + chrome.experimental.proxy.settings.get = + mockFunctionFactory({value: {mode: 'fixed_servers' }, + levelOfControl: 'ControllableByThisExtension'}); + + fixture.innerHTML = baselineHTML; + this.controller_ = new ProxyFormController('proxyForm'); + // Wait for async calls to fire + this.wait(100, function() { + this.assertEqual( + 2, + chrome.experimental.proxy.settings.get.getCallList().length); + this.assert( + document.getElementById(ProxyFormController.ProxyTypes.FIXED) + .classList.contains('active')); + }); + }, + + // Test that `recalcFormValues_` correctly sets DOM field values when + // given a `ProxyConfig` structure + testRecalcFormValuesGroups: function() { + // Test `AUTO` normalization to `PAC` + this.controller_.recalcFormValues_({ + mode: ProxyFormController.ProxyTypes.AUTO, + rules: {}, + pacScript: '' + }); + this.assert( + document.getElementById(ProxyFormController.ProxyTypes.PAC) + .classList.contains('active')); + + // DIRECT + this.controller_.recalcFormValues_({ + mode: ProxyFormController.ProxyTypes.DIRECT, + rules: {}, + pacScript: '' + }); + this.assert( + document.getElementById(ProxyFormController.ProxyTypes.DIRECT) + .classList.contains('active')); + + // FIXED + this.controller_.recalcFormValues_({ + mode: ProxyFormController.ProxyTypes.FIXED, + rules: {}, + pacScript: '' + }); + this.assert( + document.getElementById(ProxyFormController.ProxyTypes.FIXED) + .classList.contains('active')); + + // PAC + this.controller_.recalcFormValues_({ + mode: ProxyFormController.ProxyTypes.PAC, + rules: {}, + pacScript: '' + }); + this.assert( + document.getElementById(ProxyFormController.ProxyTypes.PAC) + .classList.contains('active')); + + // SYSTEM + this.controller_.recalcFormValues_({ + mode: ProxyFormController.ProxyTypes.SYSTEM, + rules: {}, + pacScript: '' + }); + this.assert( + document.getElementById(ProxyFormController.ProxyTypes.SYSTEM) + .classList.contains('active')); + }, + + testRecalcFormValuesFixedSingle: function() { + this.controller_.recalcFormValues_({ + mode: ProxyFormController.ProxyTypes.FIXED, + rules: { + singleProxy: { + scheme: 'socks5', + host: 'singleproxy.example.com', + port: '1234' + } + } + }); + var single = this.controller_.singleProxy; + this.assertEqual('socks5', single.scheme); + this.assertEqual('singleproxy.example.com', single.host); + this.assertEqual(1234, single.port); + }, + + testRecalcFormValuesPacScript: function() { + this.controller_.recalcFormValues_({ + mode: ProxyFormController.ProxyTypes.PAC, + rules: {}, + pacScript: {url: 'http://example.com/this/is/a/pac.script'} + }); + this.assertEqual( + 'http://example.com/this/is/a/pac.script', + document.getElementById('autoconfigURL').value); + }, + + testRecalcFormValuesSingle: function() { + this.controller_.recalcFormValues_({ + mode: ProxyFormController.ProxyTypes.FIXED, + rules: { + singleProxy: { + scheme: 'https', + host: 'example.com', + port: 80 + } + } + }); + // Single! + this.assert( + document.querySelector('#' + ProxyFormController.ProxyTypes.FIXED + + ' > section').classList.contains('single')); + + var single = this.controller_.singleProxy; + this.assertEqual('https', single.scheme); + this.assertEqual('example.com', single.host); + this.assertEqual(80, single.port); + }, + + testRecalcFormValuesMultiple: function() { + this.controller_.recalcFormValues_({ + mode: ProxyFormController.ProxyTypes.FIXED, + rules: { + proxyForHttp: { + scheme: 'http', + host: 'http.example.com', + port: 1 + }, + proxyForHttps: { + scheme: 'https', + host: 'https.example.com', + port: 2 + }, + proxyForFtp: { + scheme: 'socks4', + host: 'socks4.example.com', + port: 3 + }, + fallbackProxy: { + scheme: 'socks5', + host: 'socks5.example.com', + port: 4 + } + } + }); + // Not Single! + this.assert( + !document.querySelector('#' + ProxyFormController.ProxyTypes.FIXED + + ' > section').classList.contains('single')); + var server = this.controller_.singleProxy; + this.assertNull(server); + + server = this.controller_.httpProxy; + this.assertEqual('http', server.scheme); + this.assertEqual('http.example.com', server.host); + this.assertEqual(1, server.port); + + server = this.controller_.httpsProxy; + this.assertEqual('https', server.scheme); + this.assertEqual('https.example.com', server.host); + this.assertEqual(2, server.port); + + server = this.controller_.ftpProxy; + this.assertEqual('socks4', server.scheme); + this.assertEqual('socks4.example.com', server.host); + this.assertEqual(3, server.port); + + server = this.controller_.fallbackProxy; + this.assertEqual('socks5', server.scheme); + this.assertEqual('socks5.example.com', server.host); + this.assertEqual(4, server.port); + }, + + testBypassList: function() { + this.controller_.bypassList = ['1.example.com', + '2.example.com', + '3.example.com']; + this.assertEnumEqual( + document.getElementById('bypassList').value, + '1.example.com, 2.example.com, 3.example.com'); + this.assertEnumEqual( + this.controller_.bypassList, + ['1.example.com', '2.example.com', '3.example.com']); + }, + + // Test that "system" rules are correctly generated + testProxyRulesGenerationSystem: function() { + this.controller_.changeActive_( + document.getElementById(ProxyFormController.ProxyTypes.SYSTEM)); + + this.assertHashEqual( + {mode: 'system'}, + this.controller_.generateProxyConfig_()); + }, + + // Test that "direct" rules are correctly generated + testProxyRulesGenerationDirect: function() { + this.controller_.changeActive_( + document.getElementById(ProxyFormController.ProxyTypes.DIRECT)); + + this.assertHashEqual( + {mode: 'direct'}, + this.controller_.generateProxyConfig_()); + }, + + // Test that auto detection rules are correctly generated when "automatic" + // is selected, and no PAC file URL is given + testProxyRulesGenerationAuto: function() { + this.controller_.changeActive_( + document.getElementById(ProxyFormController.ProxyTypes.PAC)); + + this.assertHashEqual( + {mode: 'auto_detect'}, + this.controller_.generateProxyConfig_()); + }, + + // Test that PAC URL rules are correctly generated when "automatic" + // is selected, and a PAC file URL is given + testProxyRulesGenerationPacURL: function() { + this.controller_.changeActive_( + document.getElementById(ProxyFormController.ProxyTypes.PAC)); + this.controller_.pacURL = 'http://example.com/pac.pac'; + var result = this.controller_.generateProxyConfig_(); + this.assertEqual('pac_script', result.mode); + this.assertEqual('http://example.com/pac.pac', result.pacScript.url); + }, + + // Manual PAC definitions + testProxyRulesGenerationPacData: function() { + var pacData = 'function FindProxyForURL(url,host) { return "DIRECT"; }'; + this.controller_.changeActive_( + document.getElementById(ProxyFormController.ProxyTypes.PAC)); + this.controller_.manualPac = pacData; + var result = this.controller_.generateProxyConfig_(); + this.assertEqual('pac_script', result.mode); + this.assertEqual(pacData, result.pacScript.data); + }, + + // PAC URLs override manual PAC definitions + testProxyRulesGenerationPacURLOverridesData: function() { + this.controller_.changeActive_( + document.getElementById(ProxyFormController.ProxyTypes.PAC)); + this.controller_.pacURL = 'http://example.com/pac.pac'; + this.controller_.manualPac = + 'function FindProxyForURL(url,host) { return "DIRECT"; }'; + var result = this.controller_.generateProxyConfig_(); + this.assertEqual('pac_script', result.mode); + this.assertEqual('http://example.com/pac.pac', result.pacScript.url); + }, + + // Test that fixed, manual servers are correctly transformed into a + // `ProxyRules` structure. + testProxyRulesGenerationSingle: function() { + this.controller_.changeActive_( + document.getElementById(ProxyFormController.ProxyTypes.FIXED)); + + this.controller_.singleProxy = { + scheme: 'http', + host: 'example.com', + port: '80' + }; + + var result = this.controller_.generateProxyConfig_(); + this.assertEqual('fixed_servers', result.mode); + this.assertEqual('http', result.rules.singleProxy.scheme); + this.assertEqual('example.com', result.rules.singleProxy.host); + this.assertEqual(80, result.rules.singleProxy.port); + this.assertEqual(undefined, result.rules.proxyForHttp); + this.assertEqual(undefined, result.rules.proxyForHttps); + this.assertEqual(undefined, result.rules.proxyForFtp); + this.assertEqual(undefined, result.rules.fallbackProxy); + }, + + // Test that proxy configuration rules are correctly generated + // for separate manually entered servers. + testProxyRulesGenerationSeparate: function() { + this.controller_.changeActive_( + document.getElementById(ProxyFormController.ProxyTypes.FIXED)); + + this.controller_.singleProxy = false; + this.controller_.httpProxy = { + scheme: 'http', + host: 'http.example.com', + port: 80 + }; + this.controller_.httpsProxy = { + scheme: 'https', + host: 'https.example.com', + port: 443 + }; + this.controller_.ftpProxy = { + scheme: 'socks4', + host: 'ftp.example.com', + port: 80 + }; + this.controller_.fallbackProxy = { + scheme: 'socks5', + host: 'fallback.example.com', + port: 80 + }; + + var result = this.controller_.generateProxyConfig_(); + this.assertEqual('fixed_servers', result.mode); + this.assertEqual(undefined, result.rules.singleProxy); + this.assertEqual('http', result.rules.proxyForHttp.scheme); + this.assertEqual('http.example.com', result.rules.proxyForHttp.host); + this.assertEqual('80', result.rules.proxyForHttp.port); + this.assertEqual('https', result.rules.proxyForHttps.scheme); + this.assertEqual('https.example.com', result.rules.proxyForHttps.host); + this.assertEqual('443', result.rules.proxyForHttps.port); + this.assertEqual('socks4', result.rules.proxyForFtp.scheme); + this.assertEqual('ftp.example.com', result.rules.proxyForFtp.host); + this.assertEqual('80', result.rules.proxyForFtp.port); + this.assertEqual('socks5', result.rules.fallbackProxy.scheme); + this.assertEqual('fallback.example.com', result.rules.fallbackProxy.host); + this.assertEqual('80', result.rules.fallbackProxy.port); + } +}, { testLog: 'proxyformcontrollerlog' }); + +var c = new ProxyFormController('proxyForm'); diff --git a/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/unittest.css b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/unittest.css new file mode 100644 index 0000000..d62b1c5 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/unittest.css @@ -0,0 +1,58 @@ +body, div, p, h1, h2, h3, ul, ol, span, a, table, td, form, img, li { + font-family: sans-serif; +} + +body { + font-size:0.8em; +} + +#log { + padding-bottom: 1em; + border-bottom: 2px solid #000; + margin-bottom: 2em; +} + +.logsummary { + margin-top: 1em; + margin-bottom: 1em; + padding: 1ex; + border: 1px solid #000; + font-weight: bold; +} + +.logtable { + width:100%; + border-collapse: collapse; + border: 1px dotted #666; +} + +.logtable td, .logtable th { + text-align: left; + padding: 3px 8px; + border: 1px dotted #666; +} + +.logtable .passed { + background-color: #cfc; +} + +.logtable .failed, .logtable .error { + background-color: #fcc; +} + +.logtable .warning { + background-color: #FC6; +} + +.logtable td div.action_buttons { + display: inline; +} + +.logtable td div.action_buttons input { + margin: 0 5px; + font-size: 10px; +} + +#fixture { + display: none; +} diff --git a/chrome/common/extensions/docs/experimental.contentSettings.misc.html b/chrome/common/extensions/docs/experimental.contentSettings.misc.html index 61631d8..dc1ce80 100644 --- a/chrome/common/extensions/docs/experimental.contentSettings.misc.html +++ b/chrome/common/extensions/docs/experimental.contentSettings.misc.html @@ -273,6 +273,10 @@ <ol> <li> <a href="#property-blockThirdPartyCookies">blockThirdPartyCookies</a> + </li><li> + <a href="#property-enableReferrers">enableReferrers</a> + </li><li> + <a href="#property-enableHyperlinkAuditing">enableHyperlinkAuditing</a> </li> </ol> </li> @@ -398,6 +402,154 @@ </dd> </div> + </div><div> + <a name="property-enableReferrers"></a> + <h4>enableReferrers</h4> + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.experimental.contentSettings.misc.</span><span>enableReferrers</span> + </div> + <div> + <dt> + <var>enableReferrers</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span> + <a href="experimental.extension.html#type-Preference">Preference</a> + </span> + <span style="display: none; "> + <span> + array of <span><span></span></span> + </span> + <span>paramType</span> + <span></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Whether referrers should be enabled. The value of this preference is of type boolean.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <a name="property-enableHyperlinkAuditing"></a> + <h4>enableHyperlinkAuditing</h4> + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.experimental.contentSettings.misc.</span><span>enableHyperlinkAuditing</span> + </div> + <div> + <dt> + <var>enableHyperlinkAuditing</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span> + <a href="experimental.extension.html#type-Preference">Preference</a> + </span> + <span style="display: none; "> + <span> + array of <span><span></span></span> + </span> + <span>paramType</span> + <span></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Whether to enable hyperlink auditing ("<a ping>"). The value of this preference is of type boolean.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> </div> </div> <!-- /apiGroup --> diff --git a/chrome/common/extensions/docs/experimental.debugger.html b/chrome/common/extensions/docs/experimental.debugger.html new file mode 100644 index 0000000..46036a2 --- /dev/null +++ b/chrome/common/extensions/docs/experimental.debugger.html @@ -0,0 +1,1559 @@ +<!DOCTYPE html><!-- This page is a placeholder for generated extensions api doc. Note: + 1) The <head> information in this page is significant, should be uniform + across api docs and should be edited only with knowledge of the + templating mechanism. + 3) All <body>.innerHTML is genereated as an rendering step. If viewed in a + browser, it will be re-generated from the template, json schema and + authored overview content. + 4) The <body>.innerHTML is also generated by an offline step so that this + page may easily be indexed by search engines. +--><html xmlns="http://www.w3.org/1999/xhtml"><head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <link href="css/ApiRefStyles.css" rel="stylesheet" type="text/css"> + <link href="css/print.css" rel="stylesheet" type="text/css" media="print"> + <script type="text/javascript" src="../../../third_party/jstemplate/jstemplate_compiled.js"> + </script> + <script type="text/javascript" src="js/api_page_generator.js"></script> + <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> + <title>chrome.experimental.debugger - Google Chrome Extensions - Google Code</title></head> + <body> <div id="gc-container" class="labs"> + <div id="devModeWarning"> + You are viewing extension docs in chrome via the 'file:' scheme: are you expecting to see local changes when you refresh? You'll need run chrome with --allow-file-access-from-files. + </div> + <!-- SUBTEMPLATES: DO NOT MOVE FROM THIS LOCATION --> + <!-- In particular, sub-templates that recurse, must be used by allowing + jstemplate to make a copy of the template in this section which + are not operated on by way of the jsskip="true" --> + <div style="display:none"> + + <!-- VALUE --> + <div id="valueTemplate"> + <dt> + <var>paramName</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum">enumerated</span> + <span id="typeTemplate"> + <span> + <a> Type</a> + </span> + <span> + <span> + array of <span><span></span></span> + </span> + <span>paramType</span> + <span></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd> + Description of this parameter from the json schema. + </dd> + <dd> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd> + <div></div> + </dd> + + </div> <!-- /VALUE --> + + <div id="functionParametersTemplate"> + <h5>Parameters</h5> + <dl> + <div> + <div> + </div> + </div> + </dl> + </div> + </div> <!-- /SUBTEMPLATES --> + + <a id="top"></a> + <div id="skipto"> + <a href="#gc-pagecontent">Skip to page content</a> + <a href="#gc-toc">Skip to main navigation</a> + </div> + <!-- API HEADER --> + <table id="header" width="100%" cellspacing="0" border="0"> + <tbody><tr> + <td valign="middle"><a href="http://code.google.com/"><img src="images/code_labs_logo.gif" height="43" width="161" alt="Google Code Labs" style="border:0; margin:0;"></a></td> + <td valign="middle" width="100%" style="padding-left:0.6em;"> + <form action="http://www.google.com/cse" id="cse" style="margin-top:0.5em"> + <div id="gsc-search-box"> + <input type="hidden" name="cx" value="002967670403910741006:61_cvzfqtno"> + <input type="hidden" name="ie" value="UTF-8"> + <input type="text" name="q" value="" size="55"> + <input class="gsc-search-button" type="submit" name="sa" value="Search"> + <br> + <span class="greytext">e.g. "page action" or "tabs"</span> + </div> + </form> + + <script type="text/javascript" src="http://www.google.com/jsapi"></script> + <script type="text/javascript">google.load("elements", "1", {packages: "transliteration"});</script> + <script type="text/javascript" src="http://www.google.com/coop/cse/t13n?form=cse&t13n_langs=en"></script> + <script type="text/javascript" src="http://www.google.com/coop/cse/brand?form=cse&lang=en"></script> + </td> + </tr> + </tbody></table> + + <div id="codesiteContent" class=""> + + <a id="gc-topnav-anchor"></a> + <div id="gc-topnav"> + <h1>Google Chrome Extensions (<a href="http://code.google.com/labs/">Labs</a>)</h1> + <ul id="home" class="gc-topnav-tabs"> + <li id="home_link"> + <a href="index.html" title="Google Chrome Extensions home page">Home</a> + </li> + <li id="docs_link"> + <a href="docs.html" title="Official Google Chrome Extensions documentation">Docs</a> + </li> + <li id="faq_link"> + <a href="faq.html" title="Answers to frequently asked questions about Google Chrome Extensions">FAQ</a> + </li> + <li id="samples_link"> + <a href="samples.html" title="Sample extensions (with source code)">Samples</a> + </li> + <li id="group_link"> + <a href="http://groups.google.com/a/chromium.org/group/chromium-extensions" title="Google Chrome Extensions developer forum">Group</a> + </li> + </ul> + </div> <!-- end gc-topnav --> + + <div class="g-section g-tpl-170"> + <!-- SIDENAV --> + <div class="g-unit g-first" id="gc-toc"> + <ul> + <li><a href="getstarted.html">Getting Started</a></li> + <li><a href="overview.html">Overview</a></li> + <li><a href="whats_new.html">What's New?</a></li> + <li><h2><a href="devguide.html">Developer's Guide</a></h2> + <ul> + <li>Browser UI + <ul> + <li><a href="browserAction.html">Browser Actions</a></li> + <li><a href="contextMenus.html">Context Menus</a></li> + <li><a href="notifications.html">Desktop Notifications</a></li> + <li><a href="omnibox.html">Omnibox</a></li> + <li><a href="options.html">Options Pages</a></li> + <li><a href="override.html">Override Pages</a></li> + <li><a href="pageAction.html">Page Actions</a></li> + </ul> + </li> + <li>Browser Interaction + <ul> + <li><a href="bookmarks.html">Bookmarks</a></li> + <li><a href="cookies.html">Cookies</a></li> + <li><a href="events.html">Events</a></li> + <li><a href="history.html">History</a></li> + <li><a href="management.html">Management</a></li> + <li><a href="tabs.html">Tabs</a></li> + <li><a href="windows.html">Windows</a></li> + </ul> + </li> + <li>Implementation + <ul> + <li><a href="a11y.html">Accessibility</a></li> + <li><a href="background_pages.html">Background Pages</a></li> + <li><a href="content_scripts.html">Content Scripts</a></li> + <li><a href="xhr.html">Cross-Origin XHR</a></li> + <li><a href="idle.html">Idle</a></li> + <li><a href="i18n.html">Internationalization</a></li> + <li><a href="messaging.html">Message Passing</a></li> + <li><a href="npapi.html">NPAPI Plugins</a></li> + </ul> + </li> + <li>Finishing + <ul> + <li><a href="hosting.html">Hosting</a></li> + <li><a href="external_extensions.html">Other Deployment Options</a></li> + </ul> + </li> + </ul> + </li> + <li><h2><a href="apps.html">Packaged Apps</a></h2></li> + <li><h2><a href="tutorials.html">Tutorials</a></h2> + <ul> + <li><a href="tut_debugging.html">Debugging</a></li> + <li><a href="tut_analytics.html">Google Analytics</a></li> + <li><a href="tut_oauth.html">OAuth</a></li> + </ul> + </li> + <li><h2>Reference</h2> + <ul> + <li>Formats + <ul> + <li><a href="manifest.html">Manifest Files</a></li> + <li><a href="match_patterns.html">Match Patterns</a></li> + </ul> + </li> + <li><a href="permission_warnings.html">Permission Warnings</a></li> + <li><a href="api_index.html">chrome.* APIs</a></li> + <li><a href="api_other.html">Other APIs</a></li> + </ul> + </li> + <li><h2><a href="samples.html">Samples</a></h2></li> + <div class="line"> </div> + <li><h2>More</h2> + <ul> + <li><a href="http://code.google.com/chrome/webstore/docs/index.html">Chrome Web Store</a></li> + <li><a href="http://code.google.com/chrome/apps/docs/developers_guide.html">Hosted Apps</a></li> + <li><a href="themes.html">Themes</a></li> + </ul> + </li> + </ul> + </div> + <script> + initToggles(); + </script> + + <div class="g-unit" id="gc-pagecontent"> + <div id="pageTitle"> + <h1 class="page_title">chrome.experimental.debugger</h1> + </div> + <!-- TABLE OF CONTENTS --> + <div id="toc"> + <h2>Contents</h2> + <ol> + <li> + <a href="#H2-0">Notes</a> + <ol> + <li style="display: none; "> + <a>h3Name</a> + </li> + </ol> + </li> + <li> + <a href="#apiReference">API reference: chrome.experimental.debugger</a> + <ol> + <li style="display: none; "> + <a href="#properties">Properties</a> + <ol> + <li> + <a href="#property-anchor">propertyName</a> + </li> + </ol> + </li> + <li> + <a href="#global-methods">Methods</a> + <ol> + <li> + <a href="#method-attach">attach</a> + </li><li> + <a href="#method-detach">detach</a> + </li><li> + <a href="#method-sendRequest">sendRequest</a> + </li> + </ol> + </li> + <li> + <a href="#global-events">Events</a> + <ol> + <li> + <a href="#event-onDetach">onDetach</a> + </li><li> + <a href="#event-onEvent">onEvent</a> + </li> + </ol> + </li> + <li style="display: none; "> + <a href="#types">Types</a> + <ol> + <li> + <a href="#id-anchor">id</a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </div> + <!-- /TABLE OF CONTENTS --> + + <!-- Standard content lead-in for experimental API pages --> + <p id="classSummary"> + For information on how to use experimental APIs, see the <a href="experimental.html">chrome.experimental.* APIs</a> page. + </p> + + <!-- STATIC CONTENT PLACEHOLDER --> + <div id="static"><!-- BEGIN AUTHORED CONTENT --> + + +<a name="H2-0"></a><h2>Notes</h2> + +<p> +Debugger API exposes Google Chrome debugging interface to the extensions. +Debugging messages that are being sent and received are all JSON objects +with the structure defined by the Developer Tools / Web Inspector Protocol. +We are currently drafting this protocol, it is by no means ready and stable, +but we can already expose the way to attach to a given tab and instrument it. +Although not finalized, this API is feature rich. There is a proof of concept +implementation of the Chrome Developer Tools front-end running as an extension +using the debugger API. +</p> + +<p> +Note that attaching to the page by means of the debugger API and using embedded +Chrome Developer Tools with it are mutually exclusive. If extension is attached +to the page and user invokes Developer Tools, debugging session is being +terminated. Extension can then re-establish it via attaching to a tab later. +</p> + +<!-- END AUTHORED CONTENT --> +</div> + + <!-- API PAGE --> + <div class="apiPage"> + <a name="apiReference"></a> + <h2>API reference: chrome.experimental.debugger</h2> + + <!-- PROPERTIES --> + <div class="apiGroup" style="display: none; "> + <a name="properties"></a> + <h3 id="properties">Properties</h3> + + <div> + <a></a> + <h4>getLastError</h4> + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.extension</span><span>lastError</span> + </div> + <div> + </div> + </div> + + </div> <!-- /apiGroup --> + + <!-- METHODS --> + <div id="methodsTemplate" class="apiGroup"> + <a name="global-methods"></a> + <h3>Methods</h3> + + <!-- iterates over all functions --> + <div class="apiItem"> + <a name="method-attach"></a> <!-- method-anchor --> + <h4>attach</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.experimental.debugger.attach</span>(<span class="null"><span style="display: none; ">, </span><span>integer</span> + <var><span>tabId</span></var></span><span class="optional"><span>, </span><span>function</span> + <var><span>callback</span></var></span>)</div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Attaches debugger to the tab with given id.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>tabId</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>integer</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The id of the tab to which you want to attach.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>callback</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>function</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>If an error occurs while attaching to the tab, the callback will be called with no arguments and <a href="extension.html#property-lastError">chrome.extension.lastError</a> will be set to the error message.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + + <!-- RETURNS --> + <h4 style="display: none; ">Returns</h4> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div> + <div> + <h4>Callback function</h4> + <p style="display: none; "> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span></span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p style="display: none; "> + This function was added in version <b><span></span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + + </div><div class="apiItem"> + <a name="method-detach"></a> <!-- method-anchor --> + <h4>detach</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.experimental.debugger.detach</span>(<span class="null"><span style="display: none; ">, </span><span>integer</span> + <var><span>tabId</span></var></span><span class="optional"><span>, </span><span>function</span> + <var><span>callback</span></var></span>)</div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Detaches debugger from a tab with given id.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>tabId</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>integer</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The id of the tab from which you want to detach.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>callback</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>function</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>If an error occurs while detaching from the tab, the callback will be called with no arguments and <a href="extension.html#property-lastError">chrome.extension.lastError</a> will be set to the error message.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + + <!-- RETURNS --> + <h4 style="display: none; ">Returns</h4> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div> + <div> + <h4>Callback function</h4> + <p style="display: none; "> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span></span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p style="display: none; "> + This function was added in version <b><span></span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + + </div><div class="apiItem"> + <a name="method-sendRequest"></a> <!-- method-anchor --> + <h4>sendRequest</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.experimental.debugger.sendRequest</span>(<span class="null"><span style="display: none; ">, </span><span>integer</span> + <var><span>tabId</span></var></span><span class="null"><span>, </span><span>string</span> + <var><span>method</span></var></span><span class="optional"><span>, </span><span>object</span> + <var><span>params</span></var></span><span class="optional"><span>, </span><span>function</span> + <var><span>callback</span></var></span>)</div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Send given request to the debugger.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>tabId</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>integer</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The id of the tab to which you want to send debugging request.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>method</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Method name.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>params</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>object</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>JSON object matching Developer Tools / Web Inspector Protocol scheme.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>callback</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>function</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Request response body. If an error occurs while posting the message, the callback will be called with no arguments and <a href="extension.html#property-lastError">chrome.extension.lastError</a> will be set to the error message.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + + <!-- RETURNS --> + <h4 style="display: none; ">Returns</h4> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div> + <div> + <h4>Callback function</h4> + <p style="display: none; "> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span>object result</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + <dt> + <var>result</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>object</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>JSON object with the request response.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p style="display: none; "> + This function was added in version <b><span></span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + + </div> <!-- /apiItem --> + + </div> <!-- /apiGroup --> + + <!-- EVENTS --> + <div id="eventsTemplate" class="apiGroup"> + <a name="global-events"></a> + <h3>Events</h3> + <!-- iterates over all events --> + <div class="apiItem"> + <a name="event-onDetach"></a> + <h4>onDetach</h4> + + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span class="subdued">chrome.experimental.debugger.</span><span>onDetach</span><span class="subdued">.addListener</span>(function(<span>integer tabId</span>) <span class="subdued">{...}</span>); + </div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Fired when browser terminates debugging session for the tab.</p> + + <!-- PARAMETERS --> + <div> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>tabId</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>integer</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The id of the tab that was detached.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + </div> + </div> <!-- /decription --> + + </div><div class="apiItem"> + <a name="event-onEvent"></a> + <h4>onEvent</h4> + + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span class="subdued">chrome.experimental.debugger.</span><span>onEvent</span><span class="subdued">.addListener</span>(function(<span>integer tabId, string method, object params</span>) <span class="subdued">{...}</span>); + </div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Fired whenever debugger issues instrumentation event.</p> + + <!-- PARAMETERS --> + <div> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>tabId</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>integer</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The id of the tab that generated debug evet.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>method</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Method name.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>params</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>object</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>JSON object matching Developer Tools / Web Inspector Protocol event data scheme.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + </div> + </div> <!-- /decription --> + + </div> <!-- /apiItem --> + + </div> <!-- /apiGroup --> + + <!-- TYPES --> + <div class="apiGroup" style="display: none; "> + <a name="types"></a> + <h3 id="types">Types</h3> + + <!-- iterates over all types --> + <div class="apiItem"> + <a></a> + <h4>type name</h4> + + <div> + </div> + + </div> <!-- /apiItem --> + + </div> <!-- /apiGroup --> + + </div> <!-- /apiPage --> + </div> <!-- /gc-pagecontent --> + </div> <!-- /g-section --> + </div> <!-- /codesiteContent --> + <div id="gc-footer" --=""> + <div class="text"> + <p> + Except as otherwise <a href="http://code.google.com/policies.html#restrictions">noted</a>, + the content of this page is licensed under the <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons + Attribution 3.0 License</a>, and code samples are licensed under the + <a rel="license" href="http://code.google.com/google_bsd_license.html">BSD License</a>. + </p> + <p> + ©2011 Google + </p> + +<!-- begin analytics --> +<script src="http://www.google-analytics.com/urchin.js" type="text/javascript"></script> +<script src="http://www.google-analytics.com/ga.js" type="text/javascript"></script> + +<script type="text/javascript"> + // chrome doc tracking + try { + var engdocs = _gat._getTracker("YT-10763712-2"); + engdocs._trackPageview(); + } catch(err) {} + + // code.google.com site-wide tracking + try { + _uacct="UA-18071-1"; + _uanchor=1; + _uff=0; + urchinTracker(); + } + catch(e) {/* urchinTracker not available. */} +</script> +<!-- end analytics --> + </div> + </div> <!-- /gc-footer --> + </div> <!-- /gc-container --> +</body></html> diff --git a/chrome/common/extensions/docs/experimental.extension.html b/chrome/common/extensions/docs/experimental.extension.html index 4d203ae..542e85c 100644 --- a/chrome/common/extensions/docs/experimental.extension.html +++ b/chrome/common/extensions/docs/experimental.extension.html @@ -873,7 +873,7 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Whether to return the setting that applies to the incognito session only (default false).</dd> + <dd>Whether to return the setting that applies to the incognito session (default false).</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1172,7 +1172,75 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>One of<br>NotControllable = cannot be controlled by any extension<br>ControlledByOtherExtensions = controlled by extensions with higher precedence<br>ControllableByThisExtension = can be controlled by this extension<br>ControlledByThisExtension = controlled by this extension</dd> + <dd>One of<br><var>NotControllable</var>: cannot be controlled by any extension<br><var>ControlledByOtherExtensions</var>: controlled by extensions with higher precedence<br><var>ControllableByThisExtension</var>: can be controlled by this extension<br><var>ControlledByThisExtension</var>: controlled by this extension</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>incognitoSpecific</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Whether the effective value is specific to the incognito session.<br>This property will <em>only</em> be present if the <var>incognito</var> property in the <var>details</var> parameter of <code>get()</code> was true.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1336,7 +1404,7 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The value of the preference.</dd> + <dd>The value of the preference. <br>Note that every preference has a specific value type, which is described together with the preference. An extension should <em>not</em> set a preference value of a different type.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1580,10 +1648,309 @@ </dd> <!-- OBJECT EVENT FIELDS --> + <dd> + <div class="apiGroup" style=""> + <a name="global-Preference-events"></a> + <h3>Events of Preference</h3> + <!-- iterates over all events --> + <div class="apiItem"> + <a name="event-Preference-onChange"></a> + <h4>onChange</h4> + + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span class="subdued">preference.</span><span>onChange</span><span class="subdued">.addListener</span>(function(<span>object details</span>) <span class="subdued">{...}</span>); + </div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Fired when the value of the preference changes.</p> + + <!-- PARAMETERS --> + <div> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>details</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>object</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd> + <dl> + <div> + <div> + <dt> + <var>value</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>any</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The value of the preference.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>levelOfControl</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span>["NotControllable", "ControlledByOtherExtensions", "ControllableByThisExtension", "ControlledByThisExtension"]</span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>One of<br><var>NotControllable</var>: cannot be controlled by any extension<br><var>ControlledByOtherExtensions</var>: controlled by extensions with higher precedence<br><var>ControllableByThisExtension</var>: can be controlled by this extension<br><var>ControlledByThisExtension</var>: controlled by this extension</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>incognitoSpecific</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Whether the value that has changed is specific to the incognito session.<br>This property will <em>only</em> be present if the user has enabled the extension in incognito mode.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> <dd style="display: none; "> <div></div> </dd> + </div> + </div> + </dl> + </div> + </div> <!-- /decription --> + + </div> <!-- /apiItem --> + + </div> + </dd> + <!-- FUNCTION PARAMETERS --> <dd style="display: none; "> <div></div> diff --git a/chrome/common/extensions/docs/experimental.html b/chrome/common/extensions/docs/experimental.html index b7af23f..7c542b6 100644 --- a/chrome/common/extensions/docs/experimental.html +++ b/chrome/common/extensions/docs/experimental.html @@ -323,12 +323,17 @@ on the following experimental APIs: <li> <a href="experimental.clipboard.html">experimental.clipboard</a></li><li> <a href="experimental.contentSettings.misc.html">experimental.contentSettings.misc</a></li><li> + <a href="experimental.debugger.html">experimental.debugger</a></li><li> <a href="experimental.extension.html">experimental.extension</a></li><li> <a href="experimental.infobars.html">experimental.infobars</a></li><li> <a href="experimental.processes.html">experimental.processes</a></li><li> <a href="experimental.proxy.html">experimental.proxy</a></li><li> <a href="experimental.sidebar.html">experimental.sidebar</a></li><li> <a href="experimental.tts.html">experimental.tts</a></li><li> + <a href="experimental.webInspector.audits.html">experimental.webInspector.audits</a></li><li> + <a href="experimental.webInspector.inspectedWindow.html">experimental.webInspector.inspectedWindow</a></li><li> + <a href="experimental.webInspector.panels.html">experimental.webInspector.panels</a></li><li> + <a href="experimental.webInspector.resources.html">experimental.webInspector.resources</a></li><li> <a href="experimental.webNavigation.html">experimental.webNavigation</a></li><li> <a href="experimental.webRequest.html">experimental.webRequest</a></li> </ul> diff --git a/chrome/common/extensions/docs/experimental.webInspector.audits.html b/chrome/common/extensions/docs/experimental.webInspector.audits.html index 2520d1c..ff20c3a 100644 --- a/chrome/common/extensions/docs/experimental.webInspector.audits.html +++ b/chrome/common/extensions/docs/experimental.webInspector.audits.html @@ -257,10 +257,10 @@ <div id="toc"> <h2>Contents</h2> <ol> - <li style="display: none; "> - <a>h2Name</a> + <li> + <a href="#H2-0">Notes</a> <ol> - <li> + <li style="display: none; "> <a>h3Name</a> </li> </ol> @@ -320,7 +320,64 @@ </p> <!-- STATIC CONTENT PLACEHOLDER --> - <div id="static"></div> + <div id="static"><!-- BEGIN AUTHORED CONTENT --> +<p id="classSummary"> +Use the <code>experimental.webInspector.audits</code> module to add new audit +categories and rules to WebInspector's Audit panel. +</p><p> +See <a href="experimental.webInspector.html">WebInspector API summary</a> for +general introduction to using WebInspector API. +</p> + +<a name="H2-0"></a><h2>Notes</h2> + +<p> +Each audit category is represented by a line on <em>Select audits to run</em> +screen in the Audits panel. The following example adds a category named +<em>Readability</em>:</p> +<pre>var category = webInspector.audits.addCategory("Readability", 2); +</pre> +<img src="images/devtools-audits-category.png" style="margin-left: 20px" width="489" height="342" alt="Extension audit category on the launch screen of Audits panel"> +<p> +If the category's checkbox is checked, the <code>onAuditStarted</code> event of +that category will be fired when user clicks the <em>Run</em> button. +</p> +<p>The event handler in your extension receives <code>AuditResults</code> +as an argument and should add one or more results using <code>addResult()</code> +method. This may be done asynchronously, i.e. after the handler returns. The +run of the category is considered to be complete once the extension adds the +number of results declared when adding the category with +<code>experimental.webInspector.audits.addCategory()</code> or +calls AuditResult's <code>done()</code> method. +</p> +<p>The results may include additional details visualized as an expandable +tree by the Audits panel. You may build the details tree using +<code>createResult()</code> and <code>addChild()</code> methods. The child node +may include specially formatted fragments created by +<code>auditResults.snippet()</code> or <code>auditResults.url()</code>. +</p> +The following example adds a handler for onAuditStarted event that creates two +audit results and populates one of them with the additional details: + +<pre>category.onAuditStarted.addListener(function(results) { + var details = results.createResult("Details..."); + var styles = details.addChild("2 styles with small font"); + var elements = details.addChild("3 elements with small font"); + + results.addResult("Font Size (5)", + "5 elements use font size below 10pt", + results.Severity.Severe, + details); + results.addResult("Contrast", + "Text should stand out from background", + results.Severity.Info); +}); +</pre> +<p>The audit result tree produced by the snippet above will look like this: +</p> +<img src="images/devtools-audits-results.png" style="margin-left: 20px" width="330" height="169" alt="Audit results example"> +<!-- END AUTHORED CONTENT --> +</div> <!-- API PAGE --> <div class="apiPage"> diff --git a/chrome/common/extensions/docs/experimental.webInspector.html b/chrome/common/extensions/docs/experimental.webInspector.html new file mode 100644 index 0000000..6169522 --- /dev/null +++ b/chrome/common/extensions/docs/experimental.webInspector.html @@ -0,0 +1,586 @@ +<!DOCTYPE html><!-- This page is a placeholder for generated extensions api doc. Note: + 1) The <head> information in this page is significant, should be uniform + across api docs and should be edited only with knowledge of the + templating mechanism. + 3) All <body>.innerHTML is genereated as an rendering step. If viewed in a + browser, it will be re-generated from the template, json schema and + authored overview content. + 4) The <body>.innerHTML is also generated by an offline step so that this + page may easily be indexed by search engines. +--><html xmlns="http://www.w3.org/1999/xhtml"><head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <link href="css/ApiRefStyles.css" rel="stylesheet" type="text/css"> + <link href="css/print.css" rel="stylesheet" type="text/css" media="print"> + <script type="text/javascript" src="../../../third_party/jstemplate/jstemplate_compiled.js"> + </script> + <script type="text/javascript" src="js/api_page_generator.js"></script> + <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> + <title>experimental.webInspector.* APIs - Google Chrome Extensions - Google Code</title></head> + <body> <div id="gc-container" class="labs"> + <div id="devModeWarning"> + You are viewing extension docs in chrome via the 'file:' scheme: are you expecting to see local changes when you refresh? You'll need run chrome with --allow-file-access-from-files. + </div> + <!-- SUBTEMPLATES: DO NOT MOVE FROM THIS LOCATION --> + <!-- In particular, sub-templates that recurse, must be used by allowing + jstemplate to make a copy of the template in this section which + are not operated on by way of the jsskip="true" --> + <div style="display:none"> + + <!-- VALUE --> + <div id="valueTemplate"> + <dt> + <var>paramName</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum">enumerated</span> + <span id="typeTemplate"> + <span> + <a> Type</a> + </span> + <span> + <span> + array of <span><span></span></span> + </span> + <span>paramType</span> + <span></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd> + Description of this parameter from the json schema. + </dd> + <dd> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd> + <div></div> + </dd> + + </div> <!-- /VALUE --> + + <div id="functionParametersTemplate"> + <h5>Parameters</h5> + <dl> + <div> + <div> + </div> + </div> + </dl> + </div> + </div> <!-- /SUBTEMPLATES --> + + <a id="top"></a> + <div id="skipto"> + <a href="#gc-pagecontent">Skip to page content</a> + <a href="#gc-toc">Skip to main navigation</a> + </div> + <!-- API HEADER --> + <table id="header" width="100%" cellspacing="0" border="0"> + <tbody><tr> + <td valign="middle"><a href="http://code.google.com/"><img src="images/code_labs_logo.gif" height="43" width="161" alt="Google Code Labs" style="border:0; margin:0;"></a></td> + <td valign="middle" width="100%" style="padding-left:0.6em;"> + <form action="http://www.google.com/cse" id="cse" style="margin-top:0.5em"> + <div id="gsc-search-box"> + <input type="hidden" name="cx" value="002967670403910741006:61_cvzfqtno"> + <input type="hidden" name="ie" value="UTF-8"> + <input type="text" name="q" value="" size="55"> + <input class="gsc-search-button" type="submit" name="sa" value="Search"> + <br> + <span class="greytext">e.g. "page action" or "tabs"</span> + </div> + </form> + + <script type="text/javascript" src="http://www.google.com/jsapi"></script> + <script type="text/javascript">google.load("elements", "1", {packages: "transliteration"});</script> + <script type="text/javascript" src="http://www.google.com/coop/cse/t13n?form=cse&t13n_langs=en"></script> + <script type="text/javascript" src="http://www.google.com/coop/cse/brand?form=cse&lang=en"></script> + </td> + </tr> + </tbody></table> + + <div id="codesiteContent" class=""> + + <a id="gc-topnav-anchor"></a> + <div id="gc-topnav"> + <h1>Google Chrome Extensions (<a href="http://code.google.com/labs/">Labs</a>)</h1> + <ul id="home" class="gc-topnav-tabs"> + <li id="home_link"> + <a href="index.html" title="Google Chrome Extensions home page">Home</a> + </li> + <li id="docs_link"> + <a href="docs.html" title="Official Google Chrome Extensions documentation">Docs</a> + </li> + <li id="faq_link"> + <a href="faq.html" title="Answers to frequently asked questions about Google Chrome Extensions">FAQ</a> + </li> + <li id="samples_link"> + <a href="samples.html" title="Sample extensions (with source code)">Samples</a> + </li> + <li id="group_link"> + <a href="http://groups.google.com/a/chromium.org/group/chromium-extensions" title="Google Chrome Extensions developer forum">Group</a> + </li> + </ul> + </div> <!-- end gc-topnav --> + + <div class="g-section g-tpl-170"> + <!-- SIDENAV --> + <div class="g-unit g-first" id="gc-toc"> + <ul> + <li><a href="getstarted.html">Getting Started</a></li> + <li><a href="overview.html">Overview</a></li> + <li><a href="whats_new.html">What's New?</a></li> + <li><h2><a href="devguide.html">Developer's Guide</a></h2> + <ul> + <li>Browser UI + <ul> + <li><a href="browserAction.html">Browser Actions</a></li> + <li><a href="contextMenus.html">Context Menus</a></li> + <li><a href="notifications.html">Desktop Notifications</a></li> + <li><a href="omnibox.html">Omnibox</a></li> + <li><a href="options.html">Options Pages</a></li> + <li><a href="override.html">Override Pages</a></li> + <li><a href="pageAction.html">Page Actions</a></li> + </ul> + </li> + <li>Browser Interaction + <ul> + <li><a href="bookmarks.html">Bookmarks</a></li> + <li><a href="cookies.html">Cookies</a></li> + <li><a href="events.html">Events</a></li> + <li><a href="history.html">History</a></li> + <li><a href="management.html">Management</a></li> + <li><a href="tabs.html">Tabs</a></li> + <li><a href="windows.html">Windows</a></li> + </ul> + </li> + <li>Implementation + <ul> + <li><a href="a11y.html">Accessibility</a></li> + <li><a href="background_pages.html">Background Pages</a></li> + <li><a href="content_scripts.html">Content Scripts</a></li> + <li><a href="xhr.html">Cross-Origin XHR</a></li> + <li><a href="idle.html">Idle</a></li> + <li><a href="i18n.html">Internationalization</a></li> + <li><a href="messaging.html">Message Passing</a></li> + <li><a href="npapi.html">NPAPI Plugins</a></li> + </ul> + </li> + <li>Finishing + <ul> + <li><a href="hosting.html">Hosting</a></li> + <li><a href="external_extensions.html">Other Deployment Options</a></li> + </ul> + </li> + </ul> + </li> + <li><h2><a href="apps.html">Packaged Apps</a></h2></li> + <li><h2><a href="tutorials.html">Tutorials</a></h2> + <ul> + <li><a href="tut_debugging.html">Debugging</a></li> + <li><a href="tut_analytics.html">Google Analytics</a></li> + <li><a href="tut_oauth.html">OAuth</a></li> + </ul> + </li> + <li><h2>Reference</h2> + <ul> + <li>Formats + <ul> + <li><a href="manifest.html">Manifest Files</a></li> + <li><a href="match_patterns.html">Match Patterns</a></li> + </ul> + </li> + <li><a href="permission_warnings.html">Permission Warnings</a></li> + <li><a href="api_index.html">chrome.* APIs</a></li> + <li><a href="api_other.html">Other APIs</a></li> + </ul> + </li> + <li><h2><a href="samples.html">Samples</a></h2></li> + <div class="line"> </div> + <li><h2>More</h2> + <ul> + <li><a href="http://code.google.com/chrome/webstore/docs/index.html">Chrome Web Store</a></li> + <li><a href="http://code.google.com/chrome/apps/docs/developers_guide.html">Hosted Apps</a></li> + <li><a href="themes.html">Themes</a></li> + </ul> + </li> + </ul> + </div> + <script> + initToggles(); + </script> + + <div class="g-unit" id="gc-pagecontent"> + <div id="pageTitle"> + <h1 class="page_title">experimental.webInspector.* APIs</h1> + </div> + <!-- TABLE OF CONTENTS --> + <div id="toc" style="display: none; "> + <h2>Contents</h2> + <ol> + <li> + <a>h2Name</a> + <ol> + <li> + <a>h3Name</a> + </li> + </ol> + </li> + <li> + <a href="#apiReference">API reference</a> + <ol> + <li> + <a href="#properties">Properties</a> + <ol> + <li> + <a href="#property-anchor">propertyName</a> + </li> + </ol> + </li> + <li> + <a>Methods</a> + <ol> + <li> + <a href="#method-anchor">methodName</a> + </li> + </ol> + </li> + <li> + <a>Events</a> + <ol> + <li> + <a href="#event-anchor">eventName</a> + </li> + </ol> + </li> + <li> + <a href="#types">Types</a> + <ol> + <li> + <a href="#id-anchor">id</a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </div> + <!-- /TABLE OF CONTENTS --> + + <!-- Standard content lead-in for experimental API pages --> + <p id="classSummary" style="display: none; "> + For information on how to use experimental APIs, see the <a href="experimental.html">chrome.experimental.* APIs</a> page. + </p> + + <!-- STATIC CONTENT PLACEHOLDER --> + <div id="static"><div id="pageData-name" class="pageData">experimental.webInspector.* APIs</div> + +<p> +The following API modules provide support for extending +Chrome Development Tools (aka WebInspector): +</p> + +<a name="api-list"></a> +<ul> + <li> + <a href="experimental.webInspector.audits.html">experimental.webInspector.audits</a></li><li> + <a href="experimental.webInspector.inspectedWindow.html">experimental.webInspector.inspectedWindow</a></li><li> + <a href="experimental.webInspector.panels.html">experimental.webInspector.panels</a></li><li> + <a href="experimental.webInspector.resources.html">experimental.webInspector.resources</a></li> +</ul> + +<p class="warning"> +<b>Caution:</b> +Don't depend on these experimental APIs. They might disappear, and they +<em>will</em> change. +Also, the Chrome Developer Dashboard doesn't allow you to +upload extensions that use experimental APIs. +</p> + +<h2 id="using">How to use WebInspector APIs</h2> + +<ol> + <li> + WebInspector APIs are currently experimental, so please start with + <a href="experimental.html">the steps for using experimental extension + APIs</a>. + </li> + <li> + Specify the "devtools_page" field in your extension's manifest and make + sure you have "experimental" permission: +<pre>{ + "name": ... + "version": "1.0", + "minimum_chrome_version": "10.0", + <b>"devtools_page": "devtools.html"</b>, + "permissions": [ <b>"experimental"</b> ... ], + ... +} +</pre> + </li> + <li> + An instance of the devtools_page specified in your extension's manifest + will be created for every Developer Tools window opened. The page may add + other extension pages as panels and sidebars to the Developer Tools window + using <a href="experimental.webInspector.panels">experimental.webInspector.panels</a> + API. + </li> + <li>The APIs available to extension pages within the Developer Tools + window include all <a href="#api-list">experimental.webInspector modules + listed above</a> and <a href="extension.html">chrome.extension</a> API. + Other extension APIs are not available to the Developer Tools pages, but + you may invoke them by sending a request to the background page of your + extension, similarly to how it's done in the + <a href="overview.html#contentScripts">content scripts</a>. + </li><li>Please note that, unlike other Chrome Extension APIs, the WebInspector + APIs lack "chrome" prefix. This is because the APIs, as the WebInspector + itself, are a part of WebKit and may eventually appear in other browsers. + </li> + <li> + <a href="http://groups.google.com/group/google-chrome-developer-tools/topics">Give us feedback!</a> + Your comments and suggestions help us improve the APIs and decide which + ones should move from experimental to supported. + </li> +</ol> + +<h2 id="other">More information</h2> + +<p> +For information on the standard APIs that extensions can use, see +<a href="api_index.html">chrome.* APIs</a> and +<a href="api_other.html">Other APIs</a>. +</p> +</div> + + <!-- API PAGE --> + <div class="apiPage" style="display: none; "> + <a name="apiReference"></a> + <h2>API reference: chrome.apiname </h2> + + <!-- PROPERTIES --> + <div class="apiGroup"> + <a name="properties"></a> + <h3 id="properties">Properties</h3> + + <div> + <a></a> + <h4>getLastError</h4> + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.extension</span><span>lastError</span> + </div> + <div> + </div> + </div> + + </div> <!-- /apiGroup --> + + <!-- METHODS --> + <div id="methodsTemplate" class="apiGroup"> + <a></a> + <h3>Methods</h3> + + <!-- iterates over all functions --> + <div class="apiItem"> + <a></a> <!-- method-anchor --> + <h4>method name</h4> + + <div class="summary"><span>void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.module.methodName</span>(<span><span>, </span><span></span> + <var><span></span></var></span>)</div> + + <div class="description"> + <p class="todo">Undocumented.</p> + <p> + A description from the json schema def of the function goes here. + </p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + </div> + </div> + </dl> + + <!-- RETURNS --> + <h4>Returns</h4> + <dl> + <div> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div> + <div> + <h4>Callback function</h4> + <p> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p> + This function was added in version <b><span></span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + + </div> <!-- /apiItem --> + + </div> <!-- /apiGroup --> + + <!-- EVENTS --> + <div id="eventsTemplate" class="apiGroup"> + <a></a> + <h3>Events</h3> + <!-- iterates over all events --> + <div class="apiItem"> + <a></a> + <h4>event name</h4> + + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span class="subdued">chrome.bookmarks</span><span>onEvent</span><span class="subdued">.addListener</span>(function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>); + </div> + + <div class="description"> + <p class="todo">Undocumented.</p> + <p> + A description from the json schema def of the event goes here. + </p> + + <!-- PARAMETERS --> + <div> + <h4>Parameters</h4> + <dl> + <div> + <div> + </div> + </div> + </dl> + </div> + </div> <!-- /decription --> + + </div> <!-- /apiItem --> + + </div> <!-- /apiGroup --> + + <!-- TYPES --> + <div class="apiGroup"> + <a name="types"></a> + <h3 id="types">Types</h3> + + <!-- iterates over all types --> + <div class="apiItem"> + <a></a> + <h4>type name</h4> + + <div> + </div> + + </div> <!-- /apiItem --> + + </div> <!-- /apiGroup --> + + </div> <!-- /apiPage --> + </div> <!-- /gc-pagecontent --> + </div> <!-- /g-section --> + </div> <!-- /codesiteContent --> + <div id="gc-footer" --=""> + <div class="text"> + <p> + Except as otherwise <a href="http://code.google.com/policies.html#restrictions">noted</a>, + the content of this page is licensed under the <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons + Attribution 3.0 License</a>, and code samples are licensed under the + <a rel="license" href="http://code.google.com/google_bsd_license.html">BSD License</a>. + </p> + <p> + ©2011 Google + </p> + +<!-- begin analytics --> +<script src="http://www.google-analytics.com/urchin.js" type="text/javascript"></script> +<script src="http://www.google-analytics.com/ga.js" type="text/javascript"></script> + +<script type="text/javascript"> + // chrome doc tracking + try { + var engdocs = _gat._getTracker("YT-10763712-2"); + engdocs._trackPageview(); + } catch(err) {} + + // code.google.com site-wide tracking + try { + _uacct="UA-18071-1"; + _uanchor=1; + _uff=0; + urchinTracker(); + } + catch(e) {/* urchinTracker not available. */} +</script> +<!-- end analytics --> + </div> + </div> <!-- /gc-footer --> + </div> <!-- /gc-container --> +</body></html> diff --git a/chrome/common/extensions/docs/experimental.webInspector.panels.html b/chrome/common/extensions/docs/experimental.webInspector.panels.html index 29a1295..8ee8763 100644 --- a/chrome/common/extensions/docs/experimental.webInspector.panels.html +++ b/chrome/common/extensions/docs/experimental.webInspector.panels.html @@ -257,10 +257,17 @@ <div id="toc"> <h2>Contents</h2> <ol> - <li style="display: none; "> - <a>h2Name</a> + <li> + <a href="#H2-0">Notes</a> <ol> - <li> + <li style="display: none; "> + <a>h3Name</a> + </li> + </ol> + </li><li> + <a href="#overview-examples">Examples</a> + <ol> + <li style="display: none; "> <a>h3Name</a> </li> </ol> @@ -320,7 +327,52 @@ </p> <!-- STATIC CONTENT PLACEHOLDER --> - <div id="static"></div> + <div id="static"><!-- BEGIN AUTHORED CONTENT --> +<p id="classSummary"> +Use the <code>experimental.webInspector.panels</code> module to integrate your +extension into Developer Tools window UI: create your own panels, access +existing panels and add sidebars. +</p><p> +See <a href="experimental.webInspector.html">WebInspector API summary</a> for +general introduction to using WebInspector API. +</p> + +<a name="H2-0"></a><h2>Notes</h2> + +<p> +Each extension panel and sidebar is displayed as a separate HTML page. All +extension pages displayed in the Developer Tools window have access to all +modules in <code>experimental.webInspector</code> API, as well as to +<a href="extension.html">chrome.extension</a> API. Other extension APIs are not +available to the pages within Developer Tools window, but you may invoke them +by sending a request to the background page of your extension, similarly to how +it's done in the <a href="overview.html#contentScripts">content scripts</a>. +</p> + +<h2 id="overview-examples">Examples</h2> +<p>The following code adds a panel contained in <code>Panel.html</code>, +represented by <code>FontPicker.png</code> on the Developer Tools toolbar +and labeled as <em>Font Picker</em>:</p> + +<pre>webInspector.panels.create("Font Picker", "FontPicker.png", "Panel.html"); +</pre> +<p>The following code adds a sidebar pane contained in +<code>Sidebar.html</code> and titled <em>Font Properties</em> to the Elements +panel, then sets its height to <code>8ex</code>: +</p><pre>webInspector.panels.elements.createSidebarPane("Font Properties", "Sidebar.html", + function(sidebar) { + sidebar.setHeight("8ex"); + } +})); +</pre> +<p> +This screenshot demonstrates the effect the above examples would have on +Developer Tools window: + +<img src="images/devtools-panels.png" style="margin-left: 20px" width="686" height="289" alt="Extension icon panel on DevTools toolbar"> +</p> +<!-- END AUTHORED CONTENT --> +</div> <!-- API PAGE --> <div class="apiPage"> diff --git a/chrome/common/extensions/docs/experimental.webInspector.resources.html b/chrome/common/extensions/docs/experimental.webInspector.resources.html index 60036e2..50b031a 100644 --- a/chrome/common/extensions/docs/experimental.webInspector.resources.html +++ b/chrome/common/extensions/docs/experimental.webInspector.resources.html @@ -257,10 +257,17 @@ <div id="toc"> <h2>Contents</h2> <ol> - <li style="display: none; "> - <a>h2Name</a> + <li> + <a href="#H2-0">Notes</a> <ol> - <li> + <li style="display: none; "> + <a>h3Name</a> + </li> + </ol> + </li><li> + <a href="#overview-examples">Examples</a> + <ol> + <li style="display: none; "> <a>h3Name</a> </li> </ol> @@ -312,7 +319,49 @@ </p> <!-- STATIC CONTENT PLACEHOLDER --> - <div id="static"></div> + <div id="static"><!-- BEGIN AUTHORED CONTENT --> +<p id="classSummary"> +Use the <code>experimental.webInspector.resources</code> module to retrieve the +information about network resources displayed by DevTools' Network panel. +</p><p> +See <a href="experimental.webInspector.html">WebInspector API summary</a> for +general introduction to using WebInspector API. +</p> + +<a name="H2-0"></a><h2>Notes</h2> + +<p> +Network resource information is represented in HTTP Archive format +(<em>HAR</em>). The description of HAR is outside of scope of this document, +please refer to +<a href="http://groups.google.com/group/http-archive-specification/web/har-1-2-spec"> +HAR v1.2 Specification</a>. +</p><p> +In terms of HAR, the <code>webInspector.resources.getHAR()</code> method +returns entire <em>HAR log</em>, while +<code>webInspector.resources.onFinish</code> event provides <em>HAR entry</em> +as an argument to the event callback. +</p> +<p>Note that resource content is not provided as part of HAR for efficieny +reasons. You may call resource's <code>getContent()</code> method to retrieve +content. +</p><p>Some resources may be missing in the array of entries returned by <code> +getHAR()</code> in case WebInspector was opened after the page was loaded — +reload the page to get all resources. In general, the list of resources returned +by <code>getHAR()</code> should match that displayed by the Network panel. +</p><h2 id="overview-examples">Examples</h2> + +<p>The following code logs URLs of all images larger than 40KB as they are +loaded:</p> + +<pre>experimental.webInspector.resources.onFinished.addListener(function(resource) { + if (resource.response.bodySize > 40*1024) + webInspector.log("Large image: " + resource.request.url); +}); +</pre> + +<!-- END AUTHORED CONTENT --> +</div> <!-- API PAGE --> <div class="apiPage"> diff --git a/chrome/common/extensions/docs/experimental.webNavigation.html b/chrome/common/extensions/docs/experimental.webNavigation.html index 7d733d1..c3041ff 100644 --- a/chrome/common/extensions/docs/experimental.webNavigation.html +++ b/chrome/common/extensions/docs/experimental.webNavigation.html @@ -16,7 +16,7 @@ <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> <script type="text/javascript" src="js/sidebar.js"></script> - <title>chrome.experimental.webNavigation - Google Chrome Extensions - Google Code</title></head> + <title>WebNavigation API - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> You are viewing extension docs in chrome via the 'file:' scheme: are you expecting to see local changes when you refresh? You'll need run chrome with --allow-file-access-from-files. @@ -251,16 +251,16 @@ <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> - <h1 class="page_title">chrome.experimental.webNavigation</h1> + <h1 class="page_title">WebNavigation API</h1> </div> <!-- TABLE OF CONTENTS --> <div id="toc"> <h2>Contents</h2> <ol> - <li style="display: none; "> - <a>h2Name</a> + <li> + <a href="#H2-0">A note about timestamps</a> <ol> - <li> + <li style="display: none; "> <a>h3Name</a> </li> </ol> @@ -317,12 +317,35 @@ <!-- /TABLE OF CONTENTS --> <!-- Standard content lead-in for experimental API pages --> - <p id="classSummary"> + <p id="classSummary" style="display: none; "> For information on how to use experimental APIs, see the <a href="experimental.html">chrome.experimental.* APIs</a> page. </p> <!-- STATIC CONTENT PLACEHOLDER --> - <div id="static"></div> + <div id="static"><div id="pageData-name" class="pageData">WebNavigation API</div> + +<!-- BEGIN AUTHORED CONTENT --> +<p id="classSummary"> +Use the <code>chrome.experimental.webNavigation</code> module to recieve +notifications about the status of navigations requests in-flight. This +module is still very much experimental. For information on how to use +experimental APIs, see the <a href="experimental.html">chrome.experimental.* +APIs</a> page. +</p> + +<a name="H2-0"></a><h2>A note about timestamps</h2> +<p> +It's important to note that some technical oddities in the OS's handling +of distinct Chrome processes can cause the clock to be skewed between the +browser itself and extension processes. That means that WebNavigation's events' +<code>timeStamp</code> property is only guaranteed to be <i>internally</i> +consistent. Comparing one event to another event will give you the correct +offset between them, but comparing them to the current time inside the +extension (via <code>(new Date()).getTime()</code>, for instance) might give +unexpected results. +</p> +<!-- END AUTHORED CONTENT --> +</div> <!-- API PAGE --> <div class="apiPage"> @@ -717,7 +740,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> diff --git a/chrome/common/extensions/docs/experimental.webRequest.html b/chrome/common/extensions/docs/experimental.webRequest.html index 0e11c39..65f7ce6 100644 --- a/chrome/common/extensions/docs/experimental.webRequest.html +++ b/chrome/common/extensions/docs/experimental.webRequest.html @@ -16,7 +16,7 @@ <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> <script type="text/javascript" src="js/sidebar.js"></script> - <title>chrome.experimental.webRequest - Google Chrome Extensions - Google Code</title></head> + <title>WebRequest API - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> You are viewing extension docs in chrome via the 'file:' scheme: are you expecting to see local changes when you refresh? You'll need run chrome with --allow-file-access-from-files. @@ -251,16 +251,16 @@ <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> - <h1 class="page_title">chrome.experimental.webRequest</h1> + <h1 class="page_title">WebRequest API</h1> </div> <!-- TABLE OF CONTENTS --> <div id="toc"> <h2>Contents</h2> <ol> - <li style="display: none; "> - <a>h2Name</a> + <li> + <a href="#H2-0">A note about timestamps</a> <ol> - <li> + <li style="display: none; "> <a>h3Name</a> </li> </ol> @@ -281,6 +281,8 @@ <ol> <li style="display: none; "> <a href="#method-anchor">methodName</a> + </li><li style="display: none; "> + <a href="#method-anchor">methodName</a> </li> </ol> </li> @@ -292,6 +294,8 @@ </li><li> <a href="#event-onBeforeRequest">onBeforeRequest</a> </li><li> + <a href="#event-onBeforeSendHeaders">onBeforeSendHeaders</a> + </li><li> <a href="#event-onCompleted">onCompleted</a> </li><li> <a href="#event-onErrorOccurred">onErrorOccurred</a> @@ -307,6 +311,8 @@ <ol> <li> <a href="#type-RequestFilter">RequestFilter</a> + </li><li> + <a href="#type-BlockingResponse">BlockingResponse</a> </li> </ol> </li> @@ -317,12 +323,34 @@ <!-- /TABLE OF CONTENTS --> <!-- Standard content lead-in for experimental API pages --> - <p id="classSummary"> + <p id="classSummary" style="display: none; "> For information on how to use experimental APIs, see the <a href="experimental.html">chrome.experimental.* APIs</a> page. </p> <!-- STATIC CONTENT PLACEHOLDER --> - <div id="static"></div> + <div id="static"><div id="pageData-name" class="pageData">WebRequest API</div> + +<!-- BEGIN AUTHORED CONTENT --> +<p id="classSummary"> +Use the <code>chrome.experimental.webRequest</code> module to intercept, block, +or modify requests in-flight. This module is still very much experimental. For +information on how to use experimental APIs, see the +<a href="experimental.html">chrome.experimental.* APIs</a> page. +</p> + +<a name="H2-0"></a><h2>A note about timestamps</h2> +<p> +It's important to note that some technical oddities in the OS's handling +of distinct Chrome processes can cause the clock to be skewed between the +browser itself and extension processes. That means that WebRequest's events' +<code>timeStamp</code> property is only guaranteed to be <i>internally</i> +consistent. Comparing one event to another event will give you the correct +offset between them, but comparing them to the current time inside the +extension (via <code>(new Date()).getTime()</code>, for instance) might give +unexpected results. +</p> +<!-- END AUTHORED CONTENT --> +</div> <!-- API PAGE --> <div class="apiPage"> @@ -419,6 +447,72 @@ </p> </div> <!-- /description --> + </div><div class="apiItem" style="display: none; "> + <a></a> <!-- method-anchor --> + <h4>method name</h4> + + <div class="summary"><span>void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.module.methodName</span>(<span><span>, </span><span></span> + <var><span></span></var></span>)</div> + + <div class="description"> + <p class="todo">Undocumented.</p> + <p> + A description from the json schema def of the function goes here. + </p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + </div> + </div> + </dl> + + <!-- RETURNS --> + <h4>Returns</h4> + <dl> + <div> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div> + <div> + <h4>Callback function</h4> + <p> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p> + This function was added in version <b><span></span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + </div> <!-- /apiItem --> </div> <!-- /apiGroup --> @@ -511,7 +605,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -940,7 +1034,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -1357,6 +1451,301 @@ </div> <!-- /decription --> </div><div class="apiItem"> + <a name="event-onBeforeSendHeaders"></a> + <h4>onBeforeSendHeaders</h4> + + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span class="subdued">chrome.experimental.webRequest.</span><span>onBeforeSendHeaders</span><span class="subdued">.addListener</span>(function(<span>object details</span>) <span class="subdued">{...}</span>); + </div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Fires before sending an HTTP request, once the request headers are available.</p> + + <!-- PARAMETERS --> + <div> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>details</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>object</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd> + <dl> + <div> + <div> + <dt> + <var>requestId</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The ID of the request. Request IDs are unique within a browser session. As a result, they could be used to relate different events of the same request.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>url</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>timeStamp</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>number</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The time when the browser was about to send headers, in milliseconds since the epoch.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + </div> + </div> <!-- /decription --> + + </div><div class="apiItem"> <a name="event-onCompleted"></a> <h4>onCompleted</h4> @@ -1439,7 +1828,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -1800,7 +2189,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -2161,7 +2550,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -2524,7 +2913,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -3181,6 +3570,211 @@ </div> + </div><div class="apiItem"> + <a name="type-BlockingResponse"></a> + <h4>BlockingResponse</h4> + + <div> + <dt> + <var style="display: none; ">paramName</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>object</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Return value for event handlers that have the 'blocking' extraInfoSpec applied. Allows the event handler to modify network requests.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd> + <dl> + <div> + <div> + <dt> + <var>cancel</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>If true, the request is cancelled. Used in onBeforeRequest, this prevents the request from being sent.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>redirectUrl</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>If set, the original request is prevented from being sent and is instead redirected to the given URL.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> <!-- /apiItem --> </div> <!-- /apiGroup --> diff --git a/chrome/common/extensions/docs/extension.html b/chrome/common/extensions/docs/extension.html index 9e99f0e..c8d6378 100644 --- a/chrome/common/extensions/docs/extension.html +++ b/chrome/common/extensions/docs/extension.html @@ -294,6 +294,10 @@ </li><li> <a href="#method-getViews">getViews</a> </li><li> + <a href="#method-isAllowedFileSchemeAccess">isAllowedFileSchemeAccess</a> + </li><li> + <a href="#method-isAllowedIncognitoAccess">isAllowedIncognitoAccess</a> + </li><li> <a href="#method-sendRequest">sendRequest</a> </li><li> <a href="#method-setUpdateUrlData">setUpdateUrlData</a> @@ -1813,6 +1817,398 @@ For details, see </div> <!-- /description --> </div><div class="apiItem"> + <a name="method-isAllowedFileSchemeAccess"></a> <!-- method-anchor --> + <h4>isAllowedFileSchemeAccess</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.extension.isAllowedFileSchemeAccess</span>(<span class="null"><span style="display: none; ">, </span><span>function</span> + <var><span>callback</span></var></span>)</div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Retrieves the state of the extension's access to the 'file://' scheme (as determined by the user-controlled 'Allow access to File URLs' checkbox.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>callback</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>function</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + + <!-- RETURNS --> + <h4 style="display: none; ">Returns</h4> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div> + <div> + <h4>Callback function</h4> + <p> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p style="display: none; "> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span>boolean isAllowedAccess</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + <dt> + <var>isAllowedAccess</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>True if the extension can access the 'file://' scheme, false otherwise.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p> + This function was added in version <b><span>12.0.706.0</span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + + </div><div class="apiItem"> + <a name="method-isAllowedIncognitoAccess"></a> <!-- method-anchor --> + <h4>isAllowedIncognitoAccess</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.extension.isAllowedIncognitoAccess</span>(<span class="null"><span style="display: none; ">, </span><span>function</span> + <var><span>callback</span></var></span>)</div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Retrieves the state of the extension's access to Incognito-mode (as determined by the user-controlled 'Allowed in Incognito' checkbox.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>callback</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>function</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + + <!-- RETURNS --> + <h4 style="display: none; ">Returns</h4> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div> + <div> + <h4>Callback function</h4> + <p> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p style="display: none; "> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span>boolean isAllowedAccess</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + <dt> + <var>isAllowedAccess</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>True if the extension has access to Incognito mode, false otherwise.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p> + This function was added in version <b><span>12.0.706.0</span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + + </div><div class="apiItem"> <a name="method-sendRequest"></a> <!-- method-anchor --> <h4>sendRequest</h4> diff --git a/chrome/common/extensions/docs/i18n.html b/chrome/common/extensions/docs/i18n.html index fc3351f..086f828 100644 --- a/chrome/common/extensions/docs/i18n.html +++ b/chrome/common/extensions/docs/i18n.html @@ -623,8 +623,8 @@ padding-right: 1.5em; <h2 id="overview-locales">Locales</h2> <p> -Extensions can use all the locales that Google Chrome supports, -plus a few (such as <code>en</code>) +You can choose from many locales, +including some (such as <code>en</code>) that let a single translation support multiple variations of a language (such as <code>en_GB</code> and <code>en_US</code>). </p> @@ -633,12 +633,8 @@ that let a single translation support multiple variations of a language <h3 id="locales-supported">Supported locales</h3> <p> -Your extension can use any of the following locales: -</p> - -<p> -<code>am ar bg bn ca cs da de el en en_GB en_US es es_419 et fi fil fr gu he hi hr hu id it ja kn ko lt -lv ml mr nb nl or pl pt pt_BR pt_PT ro ru sk sl sr sv sw ta te th tr uk vi zh zh_CN zh_TW</code> +Extensions can use any of the +<a href="http://code.google.com/chrome/webstore/docs/i18n.html#localeTable">locales that the Chrome Web Store supports</a>. </p> diff --git a/chrome/common/extensions/docs/images/devtools-audits-category.png b/chrome/common/extensions/docs/images/devtools-audits-category.png Binary files differnew file mode 100644 index 0000000..b05fc7d --- /dev/null +++ b/chrome/common/extensions/docs/images/devtools-audits-category.png diff --git a/chrome/common/extensions/docs/images/devtools-audits-results.png b/chrome/common/extensions/docs/images/devtools-audits-results.png Binary files differnew file mode 100644 index 0000000..9185055 --- /dev/null +++ b/chrome/common/extensions/docs/images/devtools-audits-results.png diff --git a/chrome/common/extensions/docs/images/devtools-panels.png b/chrome/common/extensions/docs/images/devtools-panels.png Binary files differnew file mode 100644 index 0000000..93cbdfa --- /dev/null +++ b/chrome/common/extensions/docs/images/devtools-panels.png diff --git a/chrome/common/extensions/docs/images/index/gmail.png b/chrome/common/extensions/docs/images/index/gmail.png Binary files differdeleted file mode 100644 index dda74da..0000000 --- a/chrome/common/extensions/docs/images/index/gmail.png +++ /dev/null diff --git a/chrome/common/extensions/docs/images/index/news.png b/chrome/common/extensions/docs/images/index/news.png Binary files differdeleted file mode 100644 index 0dedda4..0000000 --- a/chrome/common/extensions/docs/images/index/news.png +++ /dev/null diff --git a/chrome/common/extensions/docs/images/index/rss.png b/chrome/common/extensions/docs/images/index/rss.png Binary files differdeleted file mode 100644 index 304e8ff..0000000 --- a/chrome/common/extensions/docs/images/index/rss.png +++ /dev/null diff --git a/chrome/common/extensions/docs/images/intermediate/README.txt b/chrome/common/extensions/docs/images/intermediate/README.txt index bae6a9b..271d373 100644 --- a/chrome/common/extensions/docs/images/intermediate/README.txt +++ b/chrome/common/extensions/docs/images/intermediate/README.txt @@ -2,6 +2,13 @@ This directory contains files that we've used to create images. If you need to change images, edit these files instead of the gif/png files that are derived from them. +Most of the images in this directory are either OmniGraffle files or +full-size images that we reduced or combined to produce the images +that we display in the doc. + +NOTE: No page should point to images/intermediate/<any file> + + overview-arch.graffle ----- An OmniGraffle file used to create the arch-* figures in overview.html. diff --git a/chrome/common/extensions/docs/images/arch-1.gif b/chrome/common/extensions/docs/images/overview/arch-1.gif Binary files differindex 16d4a84..16d4a84 100644 --- a/chrome/common/extensions/docs/images/arch-1.gif +++ b/chrome/common/extensions/docs/images/overview/arch-1.gif diff --git a/chrome/common/extensions/docs/images/arch-2.gif b/chrome/common/extensions/docs/images/overview/arch-2.gif Binary files differindex e6281c5..e6281c5 100644 --- a/chrome/common/extensions/docs/images/arch-2.gif +++ b/chrome/common/extensions/docs/images/overview/arch-2.gif diff --git a/chrome/common/extensions/docs/images/arch-3.gif b/chrome/common/extensions/docs/images/overview/arch-3.gif Binary files differindex 968f388..968f388 100644 --- a/chrome/common/extensions/docs/images/arch-3.gif +++ b/chrome/common/extensions/docs/images/overview/arch-3.gif diff --git a/chrome/common/extensions/docs/images/arch-cs.gif b/chrome/common/extensions/docs/images/overview/arch-cs.gif Binary files differindex 619ea2a..619ea2a 100644 --- a/chrome/common/extensions/docs/images/arch-cs.gif +++ b/chrome/common/extensions/docs/images/overview/arch-cs.gif diff --git a/chrome/common/extensions/docs/images/overview/browser-action-with-popup.png b/chrome/common/extensions/docs/images/overview/browser-action-with-popup.png Binary files differnew file mode 100644 index 0000000..b1ca45e --- /dev/null +++ b/chrome/common/extensions/docs/images/overview/browser-action-with-popup.png diff --git a/chrome/common/extensions/docs/images/overview/browser-action.png b/chrome/common/extensions/docs/images/overview/browser-action.png Binary files differnew file mode 100644 index 0000000..8bf54df --- /dev/null +++ b/chrome/common/extensions/docs/images/overview/browser-action.png diff --git a/chrome/common/extensions/docs/images/index/flashapp.png b/chrome/common/extensions/docs/images/overview/flash-app.png Binary files differindex c3052b4..c3052b4 100644 --- a/chrome/common/extensions/docs/images/index/flashapp.png +++ b/chrome/common/extensions/docs/images/overview/flash-app.png diff --git a/chrome/common/extensions/docs/images/overview/page-action.png b/chrome/common/extensions/docs/images/overview/page-action.png Binary files differnew file mode 100644 index 0000000..257a233 --- /dev/null +++ b/chrome/common/extensions/docs/images/overview/page-action.png diff --git a/chrome/common/extensions/docs/index.html b/chrome/common/extensions/docs/index.html index 3888f88..8b84fd9 100644 --- a/chrome/common/extensions/docs/index.html +++ b/chrome/common/extensions/docs/index.html @@ -420,7 +420,7 @@ For more information, see the </p> <p> -<object width="300" height="250"><param name="movie" value="http://www.youtube.com/p/38DF05697DE372B1&hl=en_US&fs=1"><param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="always"><embed src="http://www.youtube.com/p/38DF05697DE372B1&hl=en_US&fs=1" type="application/x-shockwave-flash" width="300" height="250" allowscriptaccess="always" allowfullscreen="true"></object> +<iframe title="YouTube video player" width="300" height="199" src="http://www.youtube.com/embed/wRDPTnY3yO8?rel=0" frameborder="0" allowfullscreen=""></iframe> </p> </td> </tr> diff --git a/chrome/common/extensions/docs/js/api_page_generator.js b/chrome/common/extensions/docs/js/api_page_generator.js index 185aafc..b5afc6f 100644 --- a/chrome/common/extensions/docs/js/api_page_generator.js +++ b/chrome/common/extensions/docs/js/api_page_generator.js @@ -1,3 +1,7 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + /** * @fileoverview This file is the controller for generating extension * doc pages. @@ -14,12 +18,13 @@ * */ -var USE_DEVTOOLS_SCHEMA = /\.webInspector\.[^/]*\.html/.test(location.href); var API_TEMPLATE = "template/api_template.html"; var WEBKIT_PATH = "../../../../third_party/WebKit"; -var SCHEMA = USE_DEVTOOLS_SCHEMA ? - WEBKIT_PATH + "/Source/WebCore/inspector/front-end/ExtensionAPISchema.json" - : "../api/extension_api.json"; +var SCHEMA = "../api/extension_api.json"; +var DEVTOOLS_SCHEMA = WEBKIT_PATH + + "/Source/WebCore/inspector/front-end/ExtensionAPISchema.json"; +var USE_DEVTOOLS_SCHEMA = + /\.webInspector[^/]*\.html/.test(location.pathname); var API_MODULE_PREFIX = USE_DEVTOOLS_SCHEMA ? "" : "chrome."; var SAMPLES = "samples.json"; var REQUEST_TIMEOUT = 2000; @@ -101,7 +106,6 @@ function fetchStatic() { fetchContent(staticResource(pageBase), function(overviewContent) { document.getElementById("static").innerHTML = overviewContent; fetchSchema(); - }, function(error) { // Not fatal. Some api pages may not have matching static content. fetchSchema(); @@ -111,16 +115,34 @@ function fetchStatic() { function fetchSchema() { // Now the page is composed with the authored content, we fetch the schema // and populate the templates. - fetchContent(SCHEMA, function(schemaContent) { - schema = JSON.parse(schemaContent); + var is_experimental_index = /\/experimental\.html$/.test(location.pathname); + + var schemas_to_retrieve = []; + if (!USE_DEVTOOLS_SCHEMA || is_experimental_index) + schemas_to_retrieve.push(SCHEMA); + if (USE_DEVTOOLS_SCHEMA || is_experimental_index) + schemas_to_retrieve.push(DEVTOOLS_SCHEMA); + + var schemas_retrieved = 0; + schema = []; + + function onSchemaContent(content) { + schema = schema.concat(JSON.parse(content)); + if (++schemas_retrieved < schemas_to_retrieve.length) + return; if (pageName.toLowerCase() == "samples") { fetchSamples(); } else { renderTemplate(); } - }, function(error) { - alert("Failed to load " + SCHEMA); - }); + } + + for (var i = 0; i < schemas_to_retrieve.length; ++i) { + var schema_path = schemas_to_retrieve[i]; + fetchContent(schema_path, onSchemaContent, function(error) { + alert("Failed to load " + schema_path); + }); + } } function fetchSamples() { @@ -325,6 +347,14 @@ function experimentalAPIs() { }).sort(); } +function webInspectorAPIs() { + return schema.filter(function(module) { + return !module.nodoc && module.namespace.indexOf("webInspector.") !== 0; + }).map(function(module) { + return module.namespace; + }).sort(); +} + function getDataFromPageHTML(id) { var node = document.getElementById(id); if (!node) diff --git a/chrome/common/extensions/docs/management.html b/chrome/common/extensions/docs/management.html index 5312d8c..a7dc6f2 100644 --- a/chrome/common/extensions/docs/management.html +++ b/chrome/common/extensions/docs/management.html @@ -2440,6 +2440,74 @@ For example:</p> </div><div> <div> <dt> + <var>mayDisable</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Whether this extension can be disabled or uninstalled by the user.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> <var>enabled</var> <em> diff --git a/chrome/common/extensions/docs/override.html b/chrome/common/extensions/docs/override.html index f2aa1dd..ba947fc 100644 --- a/chrome/common/extensions/docs/override.html +++ b/chrome/common/extensions/docs/override.html @@ -16,7 +16,7 @@ <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> <script type="text/javascript" src="js/sidebar.js"></script> - <title>Override - Google Chrome Extensions - Google Code</title></head> + <title>Override Pages - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> You are viewing extension docs in chrome via the 'file:' scheme: are you expecting to see local changes when you refresh? You'll need run chrome with --allow-file-access-from-files. @@ -251,7 +251,7 @@ <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> - <h1 class="page_title">Override</h1> + <h1 class="page_title">Override Pages</h1> </div> <!-- TABLE OF CONTENTS --> <div id="toc"> @@ -326,7 +326,10 @@ </p> <!-- STATIC CONTENT PLACEHOLDER --> - <div id="static"><style> + <div id="static"><div id="pageData-name" class="pageData">Override Pages</div> +<div id="pageData-showTOC" class="pageData">true</div> + +<style> #pics { margin:2em 1em 1.5em; } @@ -346,9 +349,6 @@ } </style> -<div id="pageData-title" class="pageData">Override Pages</div> -<div id="pageData-showTOC" class="pageData">true</div> - <p> Override pages are a way to substitute an HTML file from your extension for a page that Google Chrome normally provides. @@ -388,19 +388,23 @@ An extension can replace any one of the following pages: <p class="note"> <b>Note:</b> A single extension can override -only one page. +<b>only one page</b>. +For example, an extension can't override both +the Bookmark Manager and History pages. </p> -<p class="note"> -<b>Note:</b> -If you want to override the page in incognito windows as well, make sure to -specify "spanning" mode for the <a href="manifest.html#incognito">incognito</a> -manifest property. -</p> - -<p class="note"> -<b>Note:</b> -You cannot override the New Tab page in incognito windows. +<p> +Incognito windows are treated specially. +New Tab pages cannot be overridden in incognito windows. +Other override pages work in incognito windows +as long as the +<a href="manifest.html#incognito">incognito</a> +manifest property is set to "spanning" +(which is the default value for extensions +but not for packaged apps). +See <a href="overview.html#incognito">Saving data and incognito mode</a> +in the Overview for more details on how you should treat +incognito windows. </p> <p> @@ -502,15 +506,9 @@ For an effective override page, follow these guidelines: <h2 id="examples"> Examples </h2> <p> -You can find simple examples of defining override pages in the -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/override/">examples/api/override</a> -directory. -For other examples and for help in viewing the source code, see -<a href="samples.html">Samples</a>. +See the +<a href="samples.html#chrome_url_overrides">override samples</a>. </p> - - -<!-- [PENDING: Maybe have a gallery of NTPs?] --> </div> <!-- API PAGE --> diff --git a/chrome/common/extensions/docs/overview.html b/chrome/common/extensions/docs/overview.html index 89a18cb..f91d3aa 100644 --- a/chrome/common/extensions/docs/overview.html +++ b/chrome/common/extensions/docs/overview.html @@ -279,7 +279,9 @@ <a href="#arch">Architecture</a> <ol> <li> - <a href="#pages">Pages</a> + <a href="#background_page">The background page</a> + </li><li> + <a href="#pages">UI pages</a> </li><li> <a href="#contentScripts">Content scripts</a> </li> @@ -408,14 +410,14 @@ depending on the page. <table class="columns"> <tbody><tr> - <td> - <img src="images/index/gmail.png" width="150" height="79" alt="screenshot"> + <td width="33%"> + <img src="images/overview/browser-action.png" width="147" height="100" alt="screenshot"> </td> - <td> - <img src="images/index/news.png" width="150" height="79" alt="screenshot"> + <td width="33%"> + <img src="images/overview/page-action.png" width="147" height="100" alt="screenshot"> </td> <td> - <img src="images/index/rss.png" width="150" height="79" alt="screenshot"> + <img src="images/overview/browser-action-with-popup.png" width="147" height="100" alt="screenshot"> </td> </tr> @@ -426,18 +428,18 @@ depending on the page. (icon in the toolbar). </td> <td> - This <a href="samples.html#news">news reader extension</a> - features a browser action that, - when clicked, - shows a <em>popup</em>. - </td> - <td> This <a href="samples.html#mappy">map extension</a> uses a <em>page action</em> (icon in the address bar) and <em>content script</em> (code injected into a web page). </td> + <td> + This <a href="samples.html#news">news extension</a> + features a browser action that, + when clicked, + shows a <em>popup</em>. + </td> </tr> </tbody></table> @@ -462,7 +464,7 @@ For example, the following packaged app displays a Flash file within an HTML page. </p> -<img src="images/index/flashapp.png" width="372" height="300" alt="screenshot"> +<img src="images/overview/flash-app.png" width="372" height="300" alt="screenshot"> <p> For more information, @@ -568,38 +570,89 @@ For details, see <h2 id="arch">Architecture</h2> <p> -Most extensions have a -<a href="background_pages.html">background page</a>, +Many extensions have a <em>background page</em>, an invisible page that holds the main logic of the extension. +An extension can also contain other pages +that present the extension's UI. +If an extension needs to interact with web pages that the user loads +(as opposed to pages that are included in the extension), +then the extension must use a content script. </p> -<img src="images/arch-1.gif" width="232" height="168" alt="Two windows and a box representing a background page (background.html). One window has a yellow icon; the other has both a yellow icon and a blue icon. The yellow icons are connected to the background page."> + +<h3 id="background_page">The background page</h3> <p> -The preceding figure shows a browser +The following figure shows a browser that has at least two extensions installed: a browser action (yellow icon) and a page action (blue icon). -The browser action's background page, -which is defined by an HTML file -(<code>background.html</code>), -has JavaScript code that controls +Both the browser action and the page action +have background pages defined by HTML files. +This figure shows the browser action's background page, +which is defined by <code>background.html</code> +and has JavaScript code that controls the behavior of the browser action in both windows. </p> +<img src="images/overview/arch-1.gif" width="232" height="168" alt="Two windows and a box representing a background page (background.html). One window has a yellow icon; the other has both a yellow icon and a blue icon. The yellow icons are connected to the background page."> -<h3 id="pages">Pages</h3> +<p> +Although background pages can be useful, +don't use one if you don't need it. +Background pages are always open, +so when a user installs many extensions that have background pages, +Chrome's performance can suffer. +</p> + +<!-- PENDING: Perhaps show a picture of many background page processes. + This could build on a figure that shows the process architecture, + and perhaps the differences between packaged apps and extensions. --> + +<p> +Here are some examples of extensions that usually +<b>do not need</b> a background page: +</p> + +<ul> + <li> An extension with a browser action that + presents its UI solely through a popup + (and perhaps an options page). + </li> + <li> + An extension that provides an <em>override page</em>—a + page that replaces a standard Chrome page. + </li> + <li> + An extension with a content script + that doesn't use cross-origin XMLHttpRequests or localStorage, + and that doesn't need to use + <a href="api_index.html">extension APIs</a>. + </li> + <li> + An extension that has no UI except for an options page. + </li> +</ul> <p> -The background page isn't the only HTML page -that an extension can have. +See <a href="background_pages.html">Background Pages</a> +for more details. +</p> + +<h3 id="pages">UI pages</h3> + +<p> +Extensions can contain ordinary HTML pages that display the extension's UI. For example, a browser action can have a popup, which is implemented by an HTML file. -Extensions can also -use <a href="tabs.html#Method-create"><code>chrome.tabs.create()</code></a> +Any extension can have an options page, +which lets users customize how the extension works. +Another type of special page is the override page. +And finally, you can +use <a href="tabs.html#method-create"><code>chrome.tabs.create()</code></a> or <code>window.open()</code> -to display HTML files that are in the extension. +to display any other HTML files that are in the extension. </p> <p> @@ -608,23 +661,30 @@ have complete access to each other's DOMs, and they can invoke functions on each other. </p> +<!-- PENDING: Change the following example and figure +to use something that's not a popup? +(It might lead people to think that popups need background pages.) --> + <p> The following figure shows the architecture of a browser action's popup. The popup's contents are a web page defined by an HTML file (<code>popup.html</code>). +This extension also happens to have a background page +(<code>background.html</code>). The popup doesn't need to duplicate code that's in the background page -(<code>background.html</code>) because the popup can invoke functions on the background page. </p> -<img src="images/arch-2.gif" width="256" height="168" alt="A browser window containing a browser action that's displaying a popup. The popup's HTML file (popup.html) can communicate with the extension's background page (background.html)."> +<img src="images/overview/arch-2.gif" width="256" height="168" alt="A browser window containing a browser action that's displaying a popup. The popup's HTML file (popup.html) can communicate with the extension's background page (background.html)."> <p> -See the <a href="browserAction.html">Browser Actions</a> page and -the <a href="#pageComm">Communication between pages</a> section +See <a href="browserAction.html">Browser Actions</a>, +<a href="options.html">Options</a>, +<a href="override.html">Override Pages</a>, +and the <a href="#pageComm">Communication between pages</a> section for more details. </p> @@ -661,7 +721,7 @@ the DOM for the displayed web page. It cannot, however, modify the DOM of its parent extension's background page. </p> -<img src="images/arch-3.gif" width="238" height="169" alt="A browser window with a browser action (controlled by background.html) and a content script (controlled by contentscript.js)."> +<img src="images/overview/arch-3.gif" width="238" height="169" alt="A browser window with a browser action (controlled by background.html) and a content script (controlled by contentscript.js)."> <p> Content scripts aren't completely cut off from their parent extensions. @@ -673,7 +733,7 @@ Or a background page might send a message asking a content script to change the appearance of its browser page. </p> -<img src="images/arch-cs.gif" width="238" height="194" alt="Like the previous figure, but showing more of the parent extension's files, as well as a communication path between the content script and the parent extension."> +<img src="images/overview/arch-cs.gif" width="238" height="194" alt="Like the previous figure, but showing more of the parent extension's files, as well as a communication path between the content script and the parent extension."> <!-- [PENDING: Add overview of message passing.] --> diff --git a/chrome/common/extensions/docs/permission_warnings.html b/chrome/common/extensions/docs/permission_warnings.html index 66b96dd..4ba491f 100644 --- a/chrome/common/extensions/docs/permission_warnings.html +++ b/chrome/common/extensions/docs/permission_warnings.html @@ -338,7 +338,7 @@ <!-- NOTE: When this doc is updated, the online help should also be updated: -http://www.google.com/support/chrome/bin/answer.py?hl=en&answer=186213 +http://www.google.com/support/chrome_webstore/bin/answer.py?hl=en&answer=186213 We should periodically look at http://src.chromium.org/viewvc/chrome/trunk/src/chrome/app/generated_resources.grd?view=markup @@ -535,17 +535,24 @@ that trigger them. <!-- HasEffectiveAccessToAllHosts() --> Any of the following: <ul> - <li> "proxy" permission (experimental) </li> + <li> "proxy" permission </li> + <li> "debugger" permission </li> <li> A match pattern in the "permissions" field that matches all hosts </li> <li> A "content_scripts" field with a "matches" entry that matches all hosts </li> + <li> "devtools_page" (experimental) </li> </ul> </td> <td> <p> The "proxy" permission is required by the - <a href="http://code.google.com/chrome/extensions/dev/experimental.proxy.html">experimental proxy</a> module. + <a href="experimental.proxy.html">experimental proxy</a> module. + </p> + + <p> + The "debugger" permission is required by the + <a href="experimental.debugger.html">experimental debugger</a> module. </p> <p> diff --git a/chrome/common/extensions/docs/samples.html b/chrome/common/extensions/docs/samples.html index bda40f4..07ee87b 100644 --- a/chrome/common/extensions/docs/samples.html +++ b/chrome/common/extensions/docs/samples.html @@ -313,7 +313,7 @@ <!-- STATIC CONTENT PLACEHOLDER --> <div id="static"><link rel="stylesheet" href="css/samples.css"> -<script>var search_data = {"0262260daf0c8f7b28feff2ef23b05e7abf9d1e0":"A BROWSER ACTION WHICH CHANGES ITS ICON WHEN CLICKED. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON","ea2894c41cb8e80a4433a3e6c5772dadce9be90d":"A BROWSER ACTION WITH A POPUP THAT CHANGES THE PAGE COLOR. BROWSER_ACTION POPUP TABS CHROME.TABS.EXECUTESCRIPT","ede3c47b7757245be42ec33fd5ca63df4b490066":"A BROWSER ACTION WITH NO ICON THAT MAKES THE PAGE RED BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.EXECUTESCRIPT","fbf0aa1a09a15ff8cc4fc7de4fd176d6c663d07a":"ACCEPTLANGUAGE RETURNS ACCEPT LANGUAGES OF THE BROWSER BROWSER_ACTION POPUP CHROME.I18N.GETACCEPTLANGUAGES CHROME.I18N.GETMESSAGE","9a6e4ec46997fb92b324974afa08a3d007e2537f":"ANIMATED PAGE ACTION THIS EXTENSION ADDS AN ANIMATED BROWSER ACTION TO THE TOOLBAR. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.ONCLICKED CHROME.PAGEACTION.SETICON CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED","a1f7cf79dd555b04fa8d603247a040e644996293":"APP LAUNCHER BROWSER_ACTION MANAGEMENT CHROME.EXTENSION.GETURL CHROME.MANAGEMENT.GET CHROME.MANAGEMENT.GETALL CHROME.MANAGEMENT.LAUNCHAPP CHROME.TABS.CREATE","9747e3d6a3eab39bc7c17f11a80573c62d44c7e5":"BLANK NEW TAB PAGE CHROME_URL_OVERRIDES","903e7277139e1e6caec123d3319cab295d8d1b3a":"CHROME SOUNDS ENJOY A MORE MAGICAL AND IMMERSIVE EXPERIENCE WHEN BROWSING THE WEB USING THE POWER OF SOUND. BACKGROUND_PAGE BOOKMARKS OPTIONS_PAGE TABS CHROME.BOOKMARKS.ONCREATED CHROME.BOOKMARKS.ONMOVED CHROME.BOOKMARKS.ONREMOVED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.TABS.GET CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED","0e790e035a4a00b6f1def5ef9a7d7be1bce95ab5":"CHROMIUM BUILDBOT MONITOR DISPLAYS THE STATUS OF THE CHROMIUM BUILDBOT IN THE TOOLBAR. CLICK TO SEE MORE DETAILED STATUS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION NOTIFICATIONS OPTIONS_PAGE POPUP CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETURL","ac31228200b41a87982e386cc90d3a6eee4ad885":"CHROMIUM SEARCH ADD SUPPORT TO THE OMNIBOX TO SEARCH THE CHROMIUM SOURCE CODE. BACKGROUND_PAGE TABS CHROME.OMNIBOX.ONINPUTCANCELLED CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED CHROME.OMNIBOX.ONINPUTSTARTED CHROME.OMNIBOX.SETDEFAULTSUGGESTION CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE","7d5d6cf195bc25480256618e360aa38c6e6fba82":"CLD DISPLAYS THE LANGUAGE OF A TAB BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.DETECTLANGUAGE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED","5d81304a17cf7ac2887484f730fbd2b01e51e166":"CONTEXT MENUS SAMPLE SHOWS SOME OF THE FEATURES OF THE CONTEXT MENUS API BACKGROUND_PAGE CONTEXTMENUS CHROME.CONTEXTMENUS.CREATE","4daa6becd0899a54776d9cf7f09613ed1a9f4d77":"COOKIE API TEST EXTENSION TESTING COOKIE API BACKGROUND_PAGE BROWSER_ACTION COOKIES TABS CHROME.BROWSERACTION.ONCLICKED CHROME.COOKIES.GET CHROME.COOKIES.GETALL CHROME.COOKIES.ONCHANGED CHROME.COOKIES.REMOVE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL","6871d09f4a96bf9d4b6cc724d00e909cee0f3902":"CROSS-DOMAIN XMLHTTPREQUEST FROM A CONTENT SCRIPT DEMONSTRATES A METHOD TO MAKE A CROSS-DOMAIN XMLHTTPREQUEST FETCH FROM A CONTENT SCRIPT. THIS EXTENSION FETCHES THE CURRENT TRENDING TOPICS FROM TWITTER AND INSERTS THEM IN AN OVERLAY AT THE TOP OF GOOGLE NEWS. VISIT HTTP://NEWS.GOOGLE.COM TO TEST THIS EXTENSION. BACKGROUND_PAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","028eb5364924344029bcbe1d527f132fc72b34e5":"EMAIL THIS PAGE (BY GOOGLE) THIS EXTENSION ADDS AN EMAIL BUTTON TO THE TOOLBAR WHICH ALLOWS YOU TO EMAIL THE PAGE LINK USING YOUR DEFAULT MAIL CLIENT OR GMAIL. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.CONNECT CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.UPDATE","763a08e9b06595d785568a8d392b95a2f3700258":"EVENT TRACKING WITH GOOGLE ANALYTICS A SAMPLE EXTENSION WHICH USES GOOGLE ANALYTICS TO TRACK USAGE. BACKGROUND_PAGE BROWSER_ACTION POPUP","e3df888a89e35bdeb9c8bc8d03be5e1851b97c68":"EXTENSION DOCS SEARCH SEARCH THE CHROME EXTENSIONS DOCUMENTATION. TO USE, TYPE CRDOC PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE TABS CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.ONREMOVED CHROME.TABS.UPDATE","8b0dd31216235941bdd8eb33fda915ef5cf79a82":"GOOGLE CALENDAR CHECKER (BY GOOGLE) QUICKLY SEE THE TIME UNTIL YOUR NEXT MEETING FROM ANY OF YOUR CALENDARS. CLICK ON THE BUTTON TO BE TAKEN TO YOUR CALENDAR. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE","4e35caa9742fb82dbd628892d23a781614f6eff6":"GOOGLE DOCUMENT LIST VIEWER DEMONSTRATES HOW TO USE OAUTH TO CONNECT THE GOOGLE DOCUMENTS LIST DATA API. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","bb57f7a0132cbeb36ad7e7bb0ab75c21704234ca":"GOOGLE MAIL CHECKER DISPLAYS THE NUMBER OF UNREAD MESSAGES IN YOUR GOOGLE MAIL INBOX. YOU CAN ALSO CLICK THE BUTTON TO OPEN YOUR INBOX. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE","1682e05ea9a1bde985123b04f6f8ac50a8a64033":"GOOGLE WAVE NOTIFIER FIND OUT WHEN YOU HAVE NEW WAVES AND PREVIEW THEM FAST. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","14b9651fda4e57b2a5914ba73a779812201b750a":"HELLO WORLD THE FIRST EXTENSION THAT I MADE. BROWSER_ACTION POPUP","2020d72f2577f53caf8e94e3dbac0fb849ceaa4d":"IDLE - SIMPLE EXAMPLE DEMONSTRATES THE IDLE API BACKGROUND_PAGE BROWSER_ACTION IDLE CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.IDLE.ONSTATECHANGED CHROME.IDLE.QUERYSTATE","0ea1588bd07b20338fc21f725de1542a5fdf9726":"IGOOGLE NEW TAB PAGE CHROME_URL_OVERRIDES","646325c25f572a1d15edc73d057f821d847a4fbe":"IMAGEINFO GET IMAGE INFO FOR IMAGES, INCLUDING EXIF DATA BACKGROUND_PAGE CONTEXTMENUS TABS CHROME.CONTEXTMENUS.CREATE CHROME.TABS.GET CHROME.TABS.GETCURRENT CHROME.WINDOWS.CREATE CHROME.WINDOWS.UPDATE","ec97ec20ca2f095d081e39f1565fc12af09ef067":"MAPPY FINDS ADDRESSES IN THE WEB PAGE YOURE ON AND POPS UP A MAP WINDOW. BACKGROUND_PAGE PAGE_ACTION POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.SENDREQUEST","b2f5f8a790e16f091a7e4e0a39b2d0a6d32e3a6d":"MERGE WINDOWS MERGES ALL OF THE BROWSERS WINDOWS INTO THE CURRENT WINDOW BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.MOVE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT","51a83d2ba3a32e3ff1bdb624d4e18ccec4c4038e":"MESSAGE TIMER TIMES HOW LONG IT TAKES TO SEND A MESSAGE TO A CONTENT SCRIPT AND BACK. BROWSER_ACTION POPUP TABS CHROME.EXTENSION.ONCONNECT CHROME.EXTENSION.ONREQUEST CHROME.TABS.CONNECT CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.SENDREQUEST","4f6785ec4f937add6728615682dd37c9a42d9548":"MY BOOKMARKS A BROWSER ACTION WITH A POPUP DUMP OF ALL BOOKMARKS, INCLUDING SEARCH, ADD, EDIT AND DELETE. BOOKMARKS BROWSER_ACTION POPUP TABS CHROME.BOOKMARKS.CREATE CHROME.BOOKMARKS.GET CHROME.BOOKMARKS.GETTREE CHROME.BOOKMARKS.REMOVE CHROME.BOOKMARKS.UPDATE CHROME.TABS.CREATE","597015d3bcce3da693b02314afd607bec4f55291":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE","6444e5c8ae112a6a433909c5e770669cd16e2e5f":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE","3aea027164cb9b732ba4a8c51cb93708891726ef":"NEWS READER (BY GOOGLE) DISPLAYS THE LATEST STORIES FROM GOOGLE NEWS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETURL CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE","f799e26ceef2367cf836f24bcb47df4398b0df58":"NOTIFICATION DEMO SHOWS OFF DESKTOP NOTIFICATIONS, WHICH ARE TOAST WINDOWS THAT POP UP ON THE DESKTOP. BACKGROUND_PAGE NOTIFICATIONS OPTIONS_PAGE TABS CHROME.TABS.CREATE","e787b322bddbc6289bb31b7d7550b1bf6456a80b":"OMNIBOX EXAMPLE TO USE, TYPE OMNIX PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED","8d0a50b57c26bb498be592e871001ffed91541b4":"PAGE ACTION BY CONTENT SHOWS A PAGE ACTION FOR HTML PAGES CONTAINING THE WORD SANDWICH BACKGROUND_PAGE PAGE_ACTION CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.PAGEACTION.SHOW","80b86ccc6e8520660fa591caa565826f0ed1b12c":"PAGE ACTION BY URL SHOWS A PAGE ACTION FOR URLS WHICH HAVE THE LETTER G IN THEM. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.SHOW CHROME.TABS.ONUPDATED","d74c3c18a1c1dd18b035149105a306f837c8823e":"PAGE BENCHMARKER CHROMIUM PAGE BENCHMARKER. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.CONNECT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETEXTENSIONTABS CHROME.EXTENSION.GETURL CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETCURRENT","e6ae17ab4ccfd7e059c8c01f25760ca5d894c7fd":"PRINT THIS PAGE ADDS A PRINT BUTTON TO THE BROWSER. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.UPDATE","beff6ecd9677dea0a7c648c5042165b48bb66f09":"PROCESS MONITOR ADDS A BROWSER ACTION THAT MONITORS RESOURCE USAGE OF ALL BROWSER PROCESSES. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.ONUPDATED","56a8d2ac24ca7bba78fd88ad57f43fc13c784497":"SAMPLE - OAUTH CONTACTS USES OAUTH TO CONNECT TO GOOGLES CONTACTS SERVICE AND DISPLAY A LIST OF YOUR CONTACTS. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","38f6e1e17756ede38b1364c7114a738ca717dcbb":"SANDWICHBAR SHOWS AN INFOBAR ON PAGES WHICH CONTAIN THE WORD SANDWICH BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.INFOBARS.SHOW CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","fc89b35755483af30b66cd72cefa34a43a3e8312":"SHOW TABS IN PROCESS ADDS A BROWSER ACTION SHOWING WHICH TABS SHARE THE CURRENT TABS PROCESS. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.GETPROCESSIDFORTAB CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.UPDATE","230463f2d5c3d4d0ca13c230e1f00f2aae0a8a64":"TAB INSPECTOR UTILITY FOR WORKING WITH THE EXTENSION TABS API BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.MOVE CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.CREATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.GETLASTFOCUSED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED CHROME.WINDOWS.REMOVE CHROME.WINDOWS.UPDATE","e1697cacebad05218798bf3e8a0f724517f0e8c3":"TEST SCREENSHOT EXTENSION DEMONSTRATE SCREENSHOT FUNCTIONALITY IN THE CHROME.TABS API. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.EXTENSION.GETVIEWS CHROME.TABS.CAPTUREVISIBLETAB CHROME.TABS.CREATE CHROME.TABS.ONUPDATED","b3de91ab04b7d7a2670ca7ee9d740eb42cead0b6":"TYPED URL HISTORY READS YOUR HISTORY, AND SHOWS THE TOP TEN PAGES YOU GO TO BY TYPING THE URL. BROWSER_ACTION HISTORY TABS CHROME.HISTORY.GETVISITS CHROME.HISTORY.SEARCH CHROME.TABS.CREATE"}</script> +<script>var search_data = {"0262260daf0c8f7b28feff2ef23b05e7abf9d1e0":"A BROWSER ACTION WHICH CHANGES ITS ICON WHEN CLICKED. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON","ea2894c41cb8e80a4433a3e6c5772dadce9be90d":"A BROWSER ACTION WITH A POPUP THAT CHANGES THE PAGE COLOR. BROWSER_ACTION POPUP TABS CHROME.TABS.EXECUTESCRIPT","ede3c47b7757245be42ec33fd5ca63df4b490066":"A BROWSER ACTION WITH NO ICON THAT MAKES THE PAGE RED BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.EXECUTESCRIPT","fbf0aa1a09a15ff8cc4fc7de4fd176d6c663d07a":"ACCEPTLANGUAGE RETURNS ACCEPT LANGUAGES OF THE BROWSER BROWSER_ACTION POPUP CHROME.I18N.GETACCEPTLANGUAGES CHROME.I18N.GETMESSAGE","9a6e4ec46997fb92b324974afa08a3d007e2537f":"ANIMATED PAGE ACTION THIS EXTENSION ADDS AN ANIMATED BROWSER ACTION TO THE TOOLBAR. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.ONCLICKED CHROME.PAGEACTION.SETICON CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED","a1f7cf79dd555b04fa8d603247a040e644996293":"APP LAUNCHER BROWSER_ACTION MANAGEMENT CHROME.EXTENSION.GETURL CHROME.MANAGEMENT.GET CHROME.MANAGEMENT.GETALL CHROME.MANAGEMENT.LAUNCHAPP CHROME.TABS.CREATE","9747e3d6a3eab39bc7c17f11a80573c62d44c7e5":"BLANK NEW TAB PAGE CHROME_URL_OVERRIDES","903e7277139e1e6caec123d3319cab295d8d1b3a":"CHROME SOUNDS ENJOY A MORE MAGICAL AND IMMERSIVE EXPERIENCE WHEN BROWSING THE WEB USING THE POWER OF SOUND. BACKGROUND_PAGE BOOKMARKS OPTIONS_PAGE TABS CHROME.BOOKMARKS.ONCREATED CHROME.BOOKMARKS.ONMOVED CHROME.BOOKMARKS.ONREMOVED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.TABS.GET CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED","0e790e035a4a00b6f1def5ef9a7d7be1bce95ab5":"CHROMIUM BUILDBOT MONITOR DISPLAYS THE STATUS OF THE CHROMIUM BUILDBOT IN THE TOOLBAR. CLICK TO SEE MORE DETAILED STATUS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION NOTIFICATIONS OPTIONS_PAGE POPUP CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETURL","ac31228200b41a87982e386cc90d3a6eee4ad885":"CHROMIUM SEARCH ADD SUPPORT TO THE OMNIBOX TO SEARCH THE CHROMIUM SOURCE CODE. BACKGROUND_PAGE TABS CHROME.OMNIBOX.ONINPUTCANCELLED CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED CHROME.OMNIBOX.ONINPUTSTARTED CHROME.OMNIBOX.SETDEFAULTSUGGESTION CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE","7d5d6cf195bc25480256618e360aa38c6e6fba82":"CLD DISPLAYS THE LANGUAGE OF A TAB BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.DETECTLANGUAGE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED","5d81304a17cf7ac2887484f730fbd2b01e51e166":"CONTEXT MENUS SAMPLE SHOWS SOME OF THE FEATURES OF THE CONTEXT MENUS API BACKGROUND_PAGE CONTEXTMENUS CHROME.CONTEXTMENUS.CREATE","4daa6becd0899a54776d9cf7f09613ed1a9f4d77":"COOKIE API TEST EXTENSION TESTING COOKIE API BACKGROUND_PAGE BROWSER_ACTION COOKIES TABS CHROME.BROWSERACTION.ONCLICKED CHROME.COOKIES.GET CHROME.COOKIES.GETALL CHROME.COOKIES.ONCHANGED CHROME.COOKIES.REMOVE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL","6871d09f4a96bf9d4b6cc724d00e909cee0f3902":"CROSS-DOMAIN XMLHTTPREQUEST FROM A CONTENT SCRIPT DEMONSTRATES A METHOD TO MAKE A CROSS-DOMAIN XMLHTTPREQUEST FETCH FROM A CONTENT SCRIPT. THIS EXTENSION FETCHES THE CURRENT TRENDING TOPICS FROM TWITTER AND INSERTS THEM IN AN OVERLAY AT THE TOP OF GOOGLE NEWS. VISIT HTTP://NEWS.GOOGLE.COM TO TEST THIS EXTENSION. BACKGROUND_PAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","028eb5364924344029bcbe1d527f132fc72b34e5":"EMAIL THIS PAGE (BY GOOGLE) THIS EXTENSION ADDS AN EMAIL BUTTON TO THE TOOLBAR WHICH ALLOWS YOU TO EMAIL THE PAGE LINK USING YOUR DEFAULT MAIL CLIENT OR GMAIL. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.CONNECT CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.UPDATE","0bf0930a32829dfb77170535ecb27adc2de3998a":"ENABLE/DISABLE REFERRERS API EXAMPLE EXTENSION SAMPLE EXTENSION WHICH DEMONSTRATES HOW TO ACCESS A PREFERENCE. BROWSER_ACTION CONTENTSETTINGS EXPERIMENTAL POPUP CHROME.EXTENSION.ISALLOWEDINCOGNITOACCESS","763a08e9b06595d785568a8d392b95a2f3700258":"EVENT TRACKING WITH GOOGLE ANALYTICS A SAMPLE EXTENSION WHICH USES GOOGLE ANALYTICS TO TRACK USAGE. BACKGROUND_PAGE BROWSER_ACTION POPUP","e3df888a89e35bdeb9c8bc8d03be5e1851b97c68":"EXTENSION DOCS SEARCH SEARCH THE CHROME EXTENSIONS DOCUMENTATION. TO USE, TYPE CRDOC PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE TABS CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.ONREMOVED CHROME.TABS.UPDATE","8b0dd31216235941bdd8eb33fda915ef5cf79a82":"GOOGLE CALENDAR CHECKER (BY GOOGLE) QUICKLY SEE THE TIME UNTIL YOUR NEXT MEETING FROM ANY OF YOUR CALENDARS. CLICK ON THE BUTTON TO BE TAKEN TO YOUR CALENDAR. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE","4e35caa9742fb82dbd628892d23a781614f6eff6":"GOOGLE DOCUMENT LIST VIEWER DEMONSTRATES HOW TO USE OAUTH TO CONNECT THE GOOGLE DOCUMENTS LIST DATA API. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","bb57f7a0132cbeb36ad7e7bb0ab75c21704234ca":"GOOGLE MAIL CHECKER DISPLAYS THE NUMBER OF UNREAD MESSAGES IN YOUR GOOGLE MAIL INBOX. YOU CAN ALSO CLICK THE BUTTON TO OPEN YOUR INBOX. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE","1682e05ea9a1bde985123b04f6f8ac50a8a64033":"GOOGLE WAVE NOTIFIER FIND OUT WHEN YOU HAVE NEW WAVES AND PREVIEW THEM FAST. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","14b9651fda4e57b2a5914ba73a779812201b750a":"HELLO WORLD THE FIRST EXTENSION THAT I MADE. BROWSER_ACTION POPUP","2020d72f2577f53caf8e94e3dbac0fb849ceaa4d":"IDLE - SIMPLE EXAMPLE DEMONSTRATES THE IDLE API BACKGROUND_PAGE BROWSER_ACTION IDLE CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.IDLE.ONSTATECHANGED CHROME.IDLE.QUERYSTATE","0ea1588bd07b20338fc21f725de1542a5fdf9726":"IGOOGLE NEW TAB PAGE CHROME_URL_OVERRIDES","646325c25f572a1d15edc73d057f821d847a4fbe":"IMAGEINFO GET IMAGE INFO FOR IMAGES, INCLUDING EXIF DATA BACKGROUND_PAGE CONTEXTMENUS TABS CHROME.CONTEXTMENUS.CREATE CHROME.TABS.GET CHROME.TABS.GETCURRENT CHROME.WINDOWS.CREATE CHROME.WINDOWS.UPDATE","65b2fe595b7ac38dddd89cca50f5efd0017b0bd4":"KEYBOARD PIN CREATES A KEYBOARD SHORTCUT (C + SHIFT + P) TO TOGGLE THE PINNED STATE OF THE CURRENTLY SELECTED TAB BACKGROUND_PAGE TABS CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE","ec97ec20ca2f095d081e39f1565fc12af09ef067":"MAPPY FINDS ADDRESSES IN THE WEB PAGE YOURE ON AND POPS UP A MAP WINDOW. BACKGROUND_PAGE PAGE_ACTION POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.SENDREQUEST","b2f5f8a790e16f091a7e4e0a39b2d0a6d32e3a6d":"MERGE WINDOWS MERGES ALL OF THE BROWSERS WINDOWS INTO THE CURRENT WINDOW BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.MOVE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT","51a83d2ba3a32e3ff1bdb624d4e18ccec4c4038e":"MESSAGE TIMER TIMES HOW LONG IT TAKES TO SEND A MESSAGE TO A CONTENT SCRIPT AND BACK. BROWSER_ACTION POPUP TABS CHROME.EXTENSION.ONCONNECT CHROME.EXTENSION.ONREQUEST CHROME.TABS.CONNECT CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.SENDREQUEST","4f6785ec4f937add6728615682dd37c9a42d9548":"MY BOOKMARKS A BROWSER ACTION WITH A POPUP DUMP OF ALL BOOKMARKS, INCLUDING SEARCH, ADD, EDIT AND DELETE. BOOKMARKS BROWSER_ACTION POPUP TABS CHROME.BOOKMARKS.CREATE CHROME.BOOKMARKS.GET CHROME.BOOKMARKS.GETTREE CHROME.BOOKMARKS.REMOVE CHROME.BOOKMARKS.UPDATE CHROME.TABS.CREATE","597015d3bcce3da693b02314afd607bec4f55291":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE","6444e5c8ae112a6a433909c5e770669cd16e2e5f":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE","3aea027164cb9b732ba4a8c51cb93708891726ef":"NEWS READER (BY GOOGLE) DISPLAYS THE LATEST STORIES FROM GOOGLE NEWS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETURL CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE","f799e26ceef2367cf836f24bcb47df4398b0df58":"NOTIFICATION DEMO SHOWS OFF DESKTOP NOTIFICATIONS, WHICH ARE TOAST WINDOWS THAT POP UP ON THE DESKTOP. BACKGROUND_PAGE NOTIFICATIONS OPTIONS_PAGE TABS CHROME.TABS.CREATE","e787b322bddbc6289bb31b7d7550b1bf6456a80b":"OMNIBOX EXAMPLE TO USE, TYPE OMNIX PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED","8d0a50b57c26bb498be592e871001ffed91541b4":"PAGE ACTION BY CONTENT SHOWS A PAGE ACTION FOR HTML PAGES CONTAINING THE WORD SANDWICH BACKGROUND_PAGE PAGE_ACTION CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.PAGEACTION.SHOW","80b86ccc6e8520660fa591caa565826f0ed1b12c":"PAGE ACTION BY URL SHOWS A PAGE ACTION FOR URLS WHICH HAVE THE LETTER G IN THEM. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.SHOW CHROME.TABS.ONUPDATED","d74c3c18a1c1dd18b035149105a306f837c8823e":"PAGE BENCHMARKER CHROMIUM PAGE BENCHMARKER. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.CONNECT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETEXTENSIONTABS CHROME.EXTENSION.GETURL CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETCURRENT","e6ae17ab4ccfd7e059c8c01f25760ca5d894c7fd":"PRINT THIS PAGE ADDS A PRINT BUTTON TO THE BROWSER. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.UPDATE","beff6ecd9677dea0a7c648c5042165b48bb66f09":"PROCESS MONITOR ADDS A BROWSER ACTION THAT MONITORS RESOURCE USAGE OF ALL BROWSER PROCESSES. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.ONUPDATED","3e8e226d87e431296bb110b4f6eb7eec2ca7a826":"PROXY SETTINGS SET CHROME-SPECIFIC PROXIES; A DEMONSTRATION OF CHROMES PROXY API BACKGROUND_PAGE BROWSER_ACTION EXPERIMENTAL POPUP PROXY CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXPERIMENTAL.PROXY.ONPROXYERROR CHROME.EXTENSION.ISALLOWEDINCOGNITOACCESS CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.I18N.GETMESSAGE","56a8d2ac24ca7bba78fd88ad57f43fc13c784497":"SAMPLE - OAUTH CONTACTS USES OAUTH TO CONNECT TO GOOGLES CONTACTS SERVICE AND DISPLAY A LIST OF YOUR CONTACTS. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","38f6e1e17756ede38b1364c7114a738ca717dcbb":"SANDWICHBAR SHOWS AN INFOBAR ON PAGES WHICH CONTAIN THE WORD SANDWICH BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.INFOBARS.SHOW CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","fc89b35755483af30b66cd72cefa34a43a3e8312":"SHOW TABS IN PROCESS ADDS A BROWSER ACTION SHOWING WHICH TABS SHARE THE CURRENT TABS PROCESS. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.GETPROCESSIDFORTAB CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.UPDATE","230463f2d5c3d4d0ca13c230e1f00f2aae0a8a64":"TAB INSPECTOR UTILITY FOR WORKING WITH THE EXTENSION TABS API BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.MOVE CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.CREATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.GETLASTFOCUSED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED CHROME.WINDOWS.REMOVE CHROME.WINDOWS.UPDATE","e1697cacebad05218798bf3e8a0f724517f0e8c3":"TEST SCREENSHOT EXTENSION DEMONSTRATE SCREENSHOT FUNCTIONALITY IN THE CHROME.TABS API. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.EXTENSION.GETVIEWS CHROME.TABS.CAPTUREVISIBLETAB CHROME.TABS.CREATE CHROME.TABS.ONUPDATED","b3de91ab04b7d7a2670ca7ee9d740eb42cead0b6":"TYPED URL HISTORY READS YOUR HISTORY, AND SHOWS THE TOP TEN PAGES YOU GO TO BY TYPING THE URL. BROWSER_ACTION HISTORY TABS CHROME.HISTORY.GETVISITS CHROME.HISTORY.SEARCH CHROME.TABS.CREATE","a3c674f7eb93621c590562ad3fa04de4f03f53a0":"WEBNAVIGATION TECH DEMO DEMONSTRATION OF THE WEBNAVIGATION EXTENSION API. BACKGROUND_PAGE BROWSER_ACTION EXPERIMENTAL EXTENSION POPUP WEBNAVIGATION CHROME.EXPERIMENTAL.WEBNAVIGATION.ONBEFORENAVIGATE CHROME.EXPERIMENTAL.WEBNAVIGATION.ONBEFORERETARGET CHROME.EXPERIMENTAL.WEBNAVIGATION.ONCOMMITTED CHROME.EXPERIMENTAL.WEBNAVIGATION.ONCOMPLETED CHROME.EXPERIMENTAL.WEBNAVIGATION.ONERROROCCURRED CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.I18N.GETMESSAGE","0fa4abe6545f0316fea7cfa07c53aa48c4f3d018":"`EXTENSION.ISALLOWED???ACCESS` EXAMPLE DEMONSTRATES THE `EXTENSION.ISALLOWED???ACCESS` APIS BROWSER_ACTION CHROME.EXTENSION.ISALLOWEDFILESCHEMEACCESS CHROME.EXTENSION.ISALLOWEDINCOGNITOACCESS"}</script> <script src="js/sample_search.js"></script> @@ -1091,6 +1091,48 @@ <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/email_this_page/">Browse source</a> - <a href="examples/extensions/email_this_page.zip">Download source</a> </div> +</div><div class="sample" id="0bf0930a32829dfb77170535ecb27adc2de3998a"> + <img class="icon" style="display: none; "> + <img class="icon" src="images/sample-default-icon.png"> + <h2 class="name"> + <a href="#0bf0930a32829dfb77170535ecb27adc2de3998a">Enable/disable referrers API example extension</a> + </h2> + <p class="metadata features">Uses + <span> + <strong>browser_action</strong><span>, </span> + <span style="display: none; "> and</span> + </span><span> + <strong>contentSettings</strong><span>, </span> + <span style="display: none; "> and</span> + </span><span> + <strong>experimental</strong><span style="display: none; ">, </span> + <span> and</span> + </span><span> + <strong>popup</strong><span style="display: none; ">, </span> + <span style="display: none; "> and</span> + </span> + </p> + <p>Sample extension which demonstrates how to access a preference.</p> + <div class="apicalls"><strong>Calls:</strong> + <ul> + <li> + <code><a href="extension.html#method-isAllowedIncognitoAccess">chrome.extension.isAllowedIncognitoAccess</a></code> + </li> + </ul> + </div> + <div class="sourcefiles"><strong>Source files:</strong> + <ul> + <li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/manifest.json?content-type=text/plain">manifest.json</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/popup.html?content-type=text/plain">popup.html</a></code> + </li> + </ul> + </div> + <div> + <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/preferences/enableReferrer/">Browse source</a> + - <a href="examples/api/preferences/enableReferrer.zip">Download source</a> + </div> </div><div class="sample" id="763a08e9b06595d785568a8d392b95a2f3700258"> <img class="icon" src="examples/tutorials/analytics/analytics-extension-icon-128.png"> <img class="icon" src="images/sample-default-icon.png" style="display: none; "> @@ -1717,6 +1759,52 @@ <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/imageinfo/">Browse source</a> - <a href="examples/extensions/imageinfo.zip">Download source</a> </div> +</div><div class="sample" id="65b2fe595b7ac38dddd89cca50f5efd0017b0bd4"> + <img class="icon" style="display: none; "> + <img class="icon" src="images/sample-default-icon.png"> + <h2 class="name"> + <a href="#65b2fe595b7ac38dddd89cca50f5efd0017b0bd4">Keyboard Pin</a> + </h2> + <p class="metadata features">Uses + <span> + <strong>background_page</strong><span style="display: none; ">, </span> + <span> and</span> + </span><span> + <strong>tabs</strong><span style="display: none; ">, </span> + <span style="display: none; "> and</span> + </span> + </p> + <p>Creates a keyboard shortcut (C + Shift + P) to toggle the pinned state of the currently selected tab</p> + <div class="apicalls"><strong>Calls:</strong> + <ul> + <li> + <code><a href="extension.html#event-onRequest">chrome.extension.onRequest</a></code> + </li><li> + <code><a href="extension.html#method-sendRequest">chrome.extension.sendRequest</a></code> + </li><li> + <code><a href="tabs.html#method-get">chrome.tabs.get</a></code> + </li><li> + <code><a href="tabs.html#method-getSelected">chrome.tabs.getSelected</a></code> + </li><li> + <code><a href="tabs.html#method-update">chrome.tabs.update</a></code> + </li> + </ul> + </div> + <div class="sourcefiles"><strong>Source files:</strong> + <ul> + <li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/tabs/pin/background.html?content-type=text/plain">background.html</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/tabs/pin/inject.js?content-type=text/plain">inject.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/tabs/pin/manifest.json?content-type=text/plain">manifest.json</a></code> + </li> + </ul> + </div> + <div> + <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/tabs/pin/">Browse source</a> + - <a href="examples/api/tabs/pin.zip">Download source</a> + </div> </div><div class="sample" id="ec97ec20ca2f095d081e39f1565fc12af09ef067"> <img class="icon" src="examples/extensions/mappy/icon.png"> <img class="icon" src="images/sample-default-icon.png" style="display: none; "> @@ -2441,6 +2529,81 @@ <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/processes/process_monitor/">Browse source</a> - <a href="examples/api/processes/process_monitor.zip">Download source</a> </div> +</div><div class="sample" id="3e8e226d87e431296bb110b4f6eb7eec2ca7a826"> + <img class="icon" src="examples/extensions/proxy_configuration/icon128.png"> + <img class="icon" src="images/sample-default-icon.png" style="display: none; "> + <h2 class="name"> + <a href="#3e8e226d87e431296bb110b4f6eb7eec2ca7a826">Proxy Settings</a> + </h2> + <p class="metadata features">Uses + <span> + <strong>background_page</strong><span>, </span> + <span style="display: none; "> and</span> + </span><span> + <strong>browser_action</strong><span>, </span> + <span style="display: none; "> and</span> + </span><span> + <strong>experimental</strong><span>, </span> + <span style="display: none; "> and</span> + </span><span> + <strong>popup</strong><span style="display: none; ">, </span> + <span> and</span> + </span><span> + <strong>proxy</strong><span style="display: none; ">, </span> + <span style="display: none; "> and</span> + </span> + </p> + <p>Set Chrome-specific proxies; a demonstration of Chrome's Proxy API</p> + <div class="apicalls"><strong>Calls:</strong> + <ul> + <li> + <code><a href="browserAction.html#method-setBadgeBackgroundColor">chrome.browserAction.setBadgeBackgroundColor</a></code> + </li><li> + <code><a href="browserAction.html#method-setBadgeText">chrome.browserAction.setBadgeText</a></code> + </li><li> + <code><a href="browserAction.html#method-setTitle">chrome.browserAction.setTitle</a></code> + </li><li> + <code><a href="experimental.proxy.html#event-onProxyError">chrome.experimental.proxy.onProxyError</a></code> + </li><li> + <code><a href="extension.html#method-isAllowedIncognitoAccess">chrome.extension.isAllowedIncognitoAccess</a></code> + </li><li> + <code><a href="extension.html#event-onRequest">chrome.extension.onRequest</a></code> + </li><li> + <code><a href="extension.html#method-sendRequest">chrome.extension.sendRequest</a></code> + </li><li> + <code><a href="i18n.html#method-getMessage">chrome.i18n.getMessage</a></code> + </li> + </ul> + </div> + <div class="sourcefiles"><strong>Source files:</strong> + <ul> + <li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/proxy_configuration/_locales/en/messages.json?content-type=text/plain">_locales/en/messages.json</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/proxy_configuration/background.html?content-type=text/plain">background.html</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/proxy_configuration/manifest.json?content-type=text/plain">manifest.json</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/proxy_configuration/popup.html?content-type=text/plain">popup.html</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/proxy_configuration/proxy_error_handler.js?content-type=text/plain">proxy_error_handler.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/proxy_configuration/proxy_form_controller.js?content-type=text/plain">proxy_form_controller.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/jsunittest.js?content-type=text/plain">test/jsunittest.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/proxy_form_controller_test.html?content-type=text/plain">test/proxy_form_controller_test.html</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/proxy_form_controller_test.js?content-type=text/plain">test/proxy_form_controller_test.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/proxy_configuration/test/unittest.css?content-type=text/plain">test/unittest.css</a></code> + </li> + </ul> + </div> + <div> + <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/proxy_configuration/">Browse source</a> + - <a href="examples/extensions/proxy_configuration.zip">Download source</a> + </div> </div><div class="sample" id="56a8d2ac24ca7bba78fd88ad57f43fc13c784497"> <img class="icon" src="examples/extensions/oauth_contacts/img/icon-128.png"> <img class="icon" src="images/sample-default-icon.png" style="display: none; "> @@ -2795,6 +2958,111 @@ <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/history/showHistory/">Browse source</a> - <a href="examples/api/history/showHistory.zip">Download source</a> </div> +</div><div class="sample" id="a3c674f7eb93621c590562ad3fa04de4f03f53a0"> + <img class="icon" style="display: none; "> + <img class="icon" src="images/sample-default-icon.png"> + <h2 class="name"> + <a href="#a3c674f7eb93621c590562ad3fa04de4f03f53a0">WebNavigation Tech Demo</a> + </h2> + <p class="metadata features">Uses + <span> + <strong>background_page</strong><span>, </span> + <span style="display: none; "> and</span> + </span><span> + <strong>browser_action</strong><span>, </span> + <span style="display: none; "> and</span> + </span><span> + <strong>experimental</strong><span>, </span> + <span style="display: none; "> and</span> + </span><span> + <strong>extension</strong><span>, </span> + <span style="display: none; "> and</span> + </span><span> + <strong>popup</strong><span style="display: none; ">, </span> + <span> and</span> + </span><span> + <strong>webNavigation</strong><span style="display: none; ">, </span> + <span style="display: none; "> and</span> + </span> + </p> + <p>Demonstration of the WebNavigation extension API.</p> + <div class="apicalls"><strong>Calls:</strong> + <ul> + <li> + <code><a href="experimental.webNavigation.html#event-onBeforeNavigate">chrome.experimental.webNavigation.onBeforeNavigate</a></code> + </li><li> + <code><a href="experimental.webNavigation.html#event-onBeforeRetarget">chrome.experimental.webNavigation.onBeforeRetarget</a></code> + </li><li> + <code><a href="experimental.webNavigation.html#event-onCommitted">chrome.experimental.webNavigation.onCommitted</a></code> + </li><li> + <code><a href="experimental.webNavigation.html#event-onCompleted">chrome.experimental.webNavigation.onCompleted</a></code> + </li><li> + <code><a href="experimental.webNavigation.html#event-onErrorOccurred">chrome.experimental.webNavigation.onErrorOccurred</a></code> + </li><li> + <code><a href="extension.html#event-onRequest">chrome.extension.onRequest</a></code> + </li><li> + <code><a href="extension.html#method-sendRequest">chrome.extension.sendRequest</a></code> + </li><li> + <code><a href="i18n.html#method-getMessage">chrome.i18n.getMessage</a></code> + </li> + </ul> + </div> + <div class="sourcefiles"><strong>Source files:</strong> + <ul> + <li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/webNavigation/basic/_locales/en/messages.json?content-type=text/plain">_locales/en/messages.json</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/webNavigation/basic/background.html?content-type=text/plain">background.html</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/webNavigation/basic/manifest.json?content-type=text/plain">manifest.json</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/webNavigation/basic/navigation_collector.js?content-type=text/plain">navigation_collector.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/webNavigation/basic/popup.html?content-type=text/plain">popup.html</a></code> + </li> + </ul> + </div> + <div> + <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/webNavigation/basic/">Browse source</a> + - <a href="examples/api/webNavigation/basic.zip">Download source</a> + </div> +</div><div class="sample" id="0fa4abe6545f0316fea7cfa07c53aa48c4f3d018"> + <img class="icon" src="examples/api/extension/isAllowedAccess/sample-128.png"> + <img class="icon" src="images/sample-default-icon.png" style="display: none; "> + <h2 class="name"> + <a href="#0fa4abe6545f0316fea7cfa07c53aa48c4f3d018">`extension.isAllowed???Access` Example</a> + </h2> + <p class="metadata features">Uses + <span> + <strong>browser_action</strong><span style="display: none; ">, </span> + <span style="display: none; "> and</span> + </span> + </p> + <p>Demonstrates the `extension.isAllowed???Access` APIs</p> + <div class="apicalls"><strong>Calls:</strong> + <ul> + <li> + <code><a href="extension.html#method-isAllowedFileSchemeAccess">chrome.extension.isAllowedFileSchemeAccess</a></code> + </li><li> + <code><a href="extension.html#method-isAllowedIncognitoAccess">chrome.extension.isAllowedIncognitoAccess</a></code> + </li> + </ul> + </div> + <div class="sourcefiles"><strong>Source files:</strong> + <ul> + <li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/manifest.json?content-type=text/plain">manifest.json</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/popup.html?content-type=text/plain">popup.html</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/sample.css?content-type=text/plain">sample.css</a></code> + </li> + </ul> + </div> + <div> + <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/extension/isAllowedAccess/">Browse source</a> + - <a href="examples/api/extension/isAllowedAccess.zip">Download source</a> + </div> </div> <div id="noresults" style="display:none"> diff --git a/chrome/common/extensions/docs/samples.json b/chrome/common/extensions/docs/samples.json index 9a62535..c3e1ff6 100644 --- a/chrome/common/extensions/docs/samples.json +++ b/chrome/common/extensions/docs/samples.json @@ -1,1582 +1,1743 @@ { "api": { - "chrome.bookmarks.create": "bookmarks.html#method-create", - "chrome.bookmarks.export": "bookmarks.html#method-export", - "chrome.bookmarks.get": "bookmarks.html#method-get", - "chrome.bookmarks.getChildren": "bookmarks.html#method-getChildren", - "chrome.bookmarks.getRecent": "bookmarks.html#method-getRecent", - "chrome.bookmarks.getTree": "bookmarks.html#method-getTree", - "chrome.bookmarks.import": "bookmarks.html#method-import", - "chrome.bookmarks.move": "bookmarks.html#method-move", - "chrome.bookmarks.onChanged": "bookmarks.html#event-onChanged", - "chrome.bookmarks.onChildrenReordered": "bookmarks.html#event-onChildrenReordered", - "chrome.bookmarks.onCreated": "bookmarks.html#event-onCreated", - "chrome.bookmarks.onImportBegan": "bookmarks.html#event-onImportBegan", - "chrome.bookmarks.onImportEnded": "bookmarks.html#event-onImportEnded", - "chrome.bookmarks.onMoved": "bookmarks.html#event-onMoved", - "chrome.bookmarks.onRemoved": "bookmarks.html#event-onRemoved", - "chrome.bookmarks.remove": "bookmarks.html#method-remove", - "chrome.bookmarks.removeTree": "bookmarks.html#method-removeTree", - "chrome.bookmarks.search": "bookmarks.html#method-search", - "chrome.bookmarks.update": "bookmarks.html#method-update", - "chrome.browserAction.onClicked": "browserAction.html#event-onClicked", - "chrome.browserAction.setBadgeBackgroundColor": "browserAction.html#method-setBadgeBackgroundColor", - "chrome.browserAction.setBadgeText": "browserAction.html#method-setBadgeText", - "chrome.browserAction.setIcon": "browserAction.html#method-setIcon", - "chrome.browserAction.setPopup": "browserAction.html#method-setPopup", - "chrome.browserAction.setTitle": "browserAction.html#method-setTitle", - "chrome.contextMenus.create": "contextMenus.html#method-create", - "chrome.contextMenus.remove": "contextMenus.html#method-remove", - "chrome.contextMenus.removeAll": "contextMenus.html#method-removeAll", - "chrome.contextMenus.update": "contextMenus.html#method-update", - "chrome.cookies.get": "cookies.html#method-get", - "chrome.cookies.getAll": "cookies.html#method-getAll", - "chrome.cookies.getAllCookieStores": "cookies.html#method-getAllCookieStores", - "chrome.cookies.onChanged": "cookies.html#event-onChanged", - "chrome.cookies.remove": "cookies.html#method-remove", - "chrome.cookies.set": "cookies.html#method-set", - "chrome.experimental.clipboard.executeCopy": "experimental.clipboard.html#method-executeCopy", - "chrome.experimental.clipboard.executeCut": "experimental.clipboard.html#method-executeCut", - "chrome.experimental.clipboard.executePaste": "experimental.clipboard.html#method-executePaste", - "chrome.experimental.infobars.show": "experimental.infobars.html#method-show", - "chrome.experimental.processes.getProcessIdForTab": "experimental.processes.html#method-getProcessIdForTab", - "chrome.experimental.processes.onUpdated": "experimental.processes.html#event-onUpdated", - "chrome.experimental.proxy.onProxyError": "experimental.proxy.html#event-onProxyError", - "chrome.experimental.sidebar.collapse": "experimental.sidebar.html#method-collapse", - "chrome.experimental.sidebar.expand": "experimental.sidebar.html#method-expand", - "chrome.experimental.sidebar.getState": "experimental.sidebar.html#method-getState", - "chrome.experimental.sidebar.hide": "experimental.sidebar.html#method-hide", - "chrome.experimental.sidebar.navigate": "experimental.sidebar.html#method-navigate", - "chrome.experimental.sidebar.onStateChanged": "experimental.sidebar.html#event-onStateChanged", - "chrome.experimental.sidebar.setBadgeText": "experimental.sidebar.html#method-setBadgeText", - "chrome.experimental.sidebar.setIcon": "experimental.sidebar.html#method-setIcon", - "chrome.experimental.sidebar.setTitle": "experimental.sidebar.html#method-setTitle", - "chrome.experimental.sidebar.show": "experimental.sidebar.html#method-show", - "chrome.experimental.tts.isSpeaking": "experimental.tts.html#method-isSpeaking", - "chrome.experimental.tts.onSpeak": "experimental.tts.html#event-onSpeak", - "chrome.experimental.tts.onStop": "experimental.tts.html#event-onStop", - "chrome.experimental.tts.speak": "experimental.tts.html#method-speak", - "chrome.experimental.tts.speakCompleted": "experimental.tts.html#method-speakCompleted", - "chrome.experimental.tts.stop": "experimental.tts.html#method-stop", - "chrome.experimental.webNavigation.onBeforeNavigate": "experimental.webNavigation.html#event-onBeforeNavigate", - "chrome.experimental.webNavigation.onBeforeRetarget": "experimental.webNavigation.html#event-onBeforeRetarget", - "chrome.experimental.webNavigation.onCommitted": "experimental.webNavigation.html#event-onCommitted", - "chrome.experimental.webNavigation.onCompleted": "experimental.webNavigation.html#event-onCompleted", - "chrome.experimental.webNavigation.onDOMContentLoaded": "experimental.webNavigation.html#event-onDOMContentLoaded", - "chrome.experimental.webNavigation.onErrorOccurred": "experimental.webNavigation.html#event-onErrorOccurred", - "chrome.experimental.webRequest.addEventListener": "experimental.webRequest.html#method-addEventListener", - "chrome.experimental.webRequest.onBeforeRedirect": "experimental.webRequest.html#event-onBeforeRedirect", - "chrome.experimental.webRequest.onBeforeRequest": "experimental.webRequest.html#event-onBeforeRequest", - "chrome.experimental.webRequest.onCompleted": "experimental.webRequest.html#event-onCompleted", - "chrome.experimental.webRequest.onErrorOccurred": "experimental.webRequest.html#event-onErrorOccurred", - "chrome.experimental.webRequest.onHeadersReceived": "experimental.webRequest.html#event-onHeadersReceived", - "chrome.experimental.webRequest.onRequestSent": "experimental.webRequest.html#event-onRequestSent", - "chrome.extension.connect": "extension.html#method-connect", - "chrome.extension.getBackgroundPage": "extension.html#method-getBackgroundPage", - "chrome.extension.getExtensionTabs": "extension.html#method-getExtensionTabs", - "chrome.extension.getToolstrips": "extension.html#method-getToolstrips", - "chrome.extension.getURL": "extension.html#method-getURL", - "chrome.extension.getViews": "extension.html#method-getViews", - "chrome.extension.onConnect": "extension.html#event-onConnect", - "chrome.extension.onConnectExternal": "extension.html#event-onConnectExternal", - "chrome.extension.onRequest": "extension.html#event-onRequest", - "chrome.extension.onRequestExternal": "extension.html#event-onRequestExternal", - "chrome.extension.sendRequest": "extension.html#method-sendRequest", - "chrome.extension.setUpdateUrlData": "extension.html#method-setUpdateUrlData", - "chrome.history.addUrl": "history.html#method-addUrl", - "chrome.history.deleteAll": "history.html#method-deleteAll", - "chrome.history.deleteRange": "history.html#method-deleteRange", - "chrome.history.deleteUrl": "history.html#method-deleteUrl", - "chrome.history.getVisits": "history.html#method-getVisits", - "chrome.history.onVisitRemoved": "history.html#event-onVisitRemoved", - "chrome.history.onVisited": "history.html#event-onVisited", - "chrome.history.search": "history.html#method-search", - "chrome.i18n.getAcceptLanguages": "i18n.html#method-getAcceptLanguages", - "chrome.i18n.getMessage": "i18n.html#method-getMessage", - "chrome.idle.onStateChanged": "idle.html#event-onStateChanged", - "chrome.idle.queryState": "idle.html#method-queryState", - "chrome.management.get": "management.html#method-get", - "chrome.management.getAll": "management.html#method-getAll", - "chrome.management.launchApp": "management.html#method-launchApp", - "chrome.management.onDisabled": "management.html#event-onDisabled", - "chrome.management.onEnabled": "management.html#event-onEnabled", - "chrome.management.onInstalled": "management.html#event-onInstalled", - "chrome.management.onUninstalled": "management.html#event-onUninstalled", - "chrome.management.setEnabled": "management.html#method-setEnabled", - "chrome.management.uninstall": "management.html#method-uninstall", - "chrome.omnibox.onInputCancelled": "omnibox.html#event-onInputCancelled", - "chrome.omnibox.onInputChanged": "omnibox.html#event-onInputChanged", - "chrome.omnibox.onInputEntered": "omnibox.html#event-onInputEntered", - "chrome.omnibox.onInputStarted": "omnibox.html#event-onInputStarted", - "chrome.omnibox.sendSuggestions": "omnibox.html#method-sendSuggestions", - "chrome.omnibox.setDefaultSuggestion": "omnibox.html#method-setDefaultSuggestion", - "chrome.pageAction.hide": "pageAction.html#method-hide", - "chrome.pageAction.onClicked": "pageAction.html#event-onClicked", - "chrome.pageAction.setIcon": "pageAction.html#method-setIcon", - "chrome.pageAction.setPopup": "pageAction.html#method-setPopup", - "chrome.pageAction.setTitle": "pageAction.html#method-setTitle", - "chrome.pageAction.show": "pageAction.html#method-show", - "chrome.tabs.captureVisibleTab": "tabs.html#method-captureVisibleTab", - "chrome.tabs.connect": "tabs.html#method-connect", - "chrome.tabs.create": "tabs.html#method-create", - "chrome.tabs.detectLanguage": "tabs.html#method-detectLanguage", - "chrome.tabs.executeScript": "tabs.html#method-executeScript", - "chrome.tabs.get": "tabs.html#method-get", - "chrome.tabs.getAllInWindow": "tabs.html#method-getAllInWindow", - "chrome.tabs.getCurrent": "tabs.html#method-getCurrent", - "chrome.tabs.getSelected": "tabs.html#method-getSelected", - "chrome.tabs.insertCSS": "tabs.html#method-insertCSS", - "chrome.tabs.move": "tabs.html#method-move", - "chrome.tabs.onAttached": "tabs.html#event-onAttached", - "chrome.tabs.onCreated": "tabs.html#event-onCreated", - "chrome.tabs.onDetached": "tabs.html#event-onDetached", - "chrome.tabs.onMoved": "tabs.html#event-onMoved", - "chrome.tabs.onRemoved": "tabs.html#event-onRemoved", - "chrome.tabs.onSelectionChanged": "tabs.html#event-onSelectionChanged", - "chrome.tabs.onUpdated": "tabs.html#event-onUpdated", - "chrome.tabs.remove": "tabs.html#method-remove", - "chrome.tabs.sendRequest": "tabs.html#method-sendRequest", - "chrome.tabs.update": "tabs.html#method-update", - "chrome.windows.create": "windows.html#method-create", - "chrome.windows.get": "windows.html#method-get", - "chrome.windows.getAll": "windows.html#method-getAll", - "chrome.windows.getCurrent": "windows.html#method-getCurrent", - "chrome.windows.getLastFocused": "windows.html#method-getLastFocused", - "chrome.windows.onCreated": "windows.html#event-onCreated", - "chrome.windows.onFocusChanged": "windows.html#event-onFocusChanged", - "chrome.windows.onRemoved": "windows.html#event-onRemoved", - "chrome.windows.remove": "windows.html#method-remove", + "chrome.bookmarks.create": "bookmarks.html#method-create", + "chrome.bookmarks.export": "bookmarks.html#method-export", + "chrome.bookmarks.get": "bookmarks.html#method-get", + "chrome.bookmarks.getChildren": "bookmarks.html#method-getChildren", + "chrome.bookmarks.getRecent": "bookmarks.html#method-getRecent", + "chrome.bookmarks.getTree": "bookmarks.html#method-getTree", + "chrome.bookmarks.import": "bookmarks.html#method-import", + "chrome.bookmarks.move": "bookmarks.html#method-move", + "chrome.bookmarks.onChanged": "bookmarks.html#event-onChanged", + "chrome.bookmarks.onChildrenReordered": "bookmarks.html#event-onChildrenReordered", + "chrome.bookmarks.onCreated": "bookmarks.html#event-onCreated", + "chrome.bookmarks.onImportBegan": "bookmarks.html#event-onImportBegan", + "chrome.bookmarks.onImportEnded": "bookmarks.html#event-onImportEnded", + "chrome.bookmarks.onMoved": "bookmarks.html#event-onMoved", + "chrome.bookmarks.onRemoved": "bookmarks.html#event-onRemoved", + "chrome.bookmarks.remove": "bookmarks.html#method-remove", + "chrome.bookmarks.removeTree": "bookmarks.html#method-removeTree", + "chrome.bookmarks.search": "bookmarks.html#method-search", + "chrome.bookmarks.update": "bookmarks.html#method-update", + "chrome.browserAction.onClicked": "browserAction.html#event-onClicked", + "chrome.browserAction.setBadgeBackgroundColor": "browserAction.html#method-setBadgeBackgroundColor", + "chrome.browserAction.setBadgeText": "browserAction.html#method-setBadgeText", + "chrome.browserAction.setIcon": "browserAction.html#method-setIcon", + "chrome.browserAction.setPopup": "browserAction.html#method-setPopup", + "chrome.browserAction.setTitle": "browserAction.html#method-setTitle", + "chrome.contextMenus.create": "contextMenus.html#method-create", + "chrome.contextMenus.remove": "contextMenus.html#method-remove", + "chrome.contextMenus.removeAll": "contextMenus.html#method-removeAll", + "chrome.contextMenus.update": "contextMenus.html#method-update", + "chrome.cookies.get": "cookies.html#method-get", + "chrome.cookies.getAll": "cookies.html#method-getAll", + "chrome.cookies.getAllCookieStores": "cookies.html#method-getAllCookieStores", + "chrome.cookies.onChanged": "cookies.html#event-onChanged", + "chrome.cookies.remove": "cookies.html#method-remove", + "chrome.cookies.set": "cookies.html#method-set", + "chrome.experimental.clipboard.executeCopy": "experimental.clipboard.html#method-executeCopy", + "chrome.experimental.clipboard.executeCut": "experimental.clipboard.html#method-executeCut", + "chrome.experimental.clipboard.executePaste": "experimental.clipboard.html#method-executePaste", + "chrome.experimental.debugger.attach": "experimental.debugger.html#method-attach", + "chrome.experimental.debugger.detach": "experimental.debugger.html#method-detach", + "chrome.experimental.debugger.onDetach": "experimental.debugger.html#event-onDetach", + "chrome.experimental.debugger.onEvent": "experimental.debugger.html#event-onEvent", + "chrome.experimental.debugger.sendRequest": "experimental.debugger.html#method-sendRequest", + "chrome.experimental.infobars.show": "experimental.infobars.html#method-show", + "chrome.experimental.processes.getProcessIdForTab": "experimental.processes.html#method-getProcessIdForTab", + "chrome.experimental.processes.onUpdated": "experimental.processes.html#event-onUpdated", + "chrome.experimental.proxy.onProxyError": "experimental.proxy.html#event-onProxyError", + "chrome.experimental.sidebar.collapse": "experimental.sidebar.html#method-collapse", + "chrome.experimental.sidebar.expand": "experimental.sidebar.html#method-expand", + "chrome.experimental.sidebar.getState": "experimental.sidebar.html#method-getState", + "chrome.experimental.sidebar.hide": "experimental.sidebar.html#method-hide", + "chrome.experimental.sidebar.navigate": "experimental.sidebar.html#method-navigate", + "chrome.experimental.sidebar.onStateChanged": "experimental.sidebar.html#event-onStateChanged", + "chrome.experimental.sidebar.setBadgeText": "experimental.sidebar.html#method-setBadgeText", + "chrome.experimental.sidebar.setIcon": "experimental.sidebar.html#method-setIcon", + "chrome.experimental.sidebar.setTitle": "experimental.sidebar.html#method-setTitle", + "chrome.experimental.sidebar.show": "experimental.sidebar.html#method-show", + "chrome.experimental.tts.isSpeaking": "experimental.tts.html#method-isSpeaking", + "chrome.experimental.tts.onSpeak": "experimental.tts.html#event-onSpeak", + "chrome.experimental.tts.onStop": "experimental.tts.html#event-onStop", + "chrome.experimental.tts.speak": "experimental.tts.html#method-speak", + "chrome.experimental.tts.speakCompleted": "experimental.tts.html#method-speakCompleted", + "chrome.experimental.tts.stop": "experimental.tts.html#method-stop", + "chrome.experimental.webNavigation.onBeforeNavigate": "experimental.webNavigation.html#event-onBeforeNavigate", + "chrome.experimental.webNavigation.onBeforeRetarget": "experimental.webNavigation.html#event-onBeforeRetarget", + "chrome.experimental.webNavigation.onCommitted": "experimental.webNavigation.html#event-onCommitted", + "chrome.experimental.webNavigation.onCompleted": "experimental.webNavigation.html#event-onCompleted", + "chrome.experimental.webNavigation.onDOMContentLoaded": "experimental.webNavigation.html#event-onDOMContentLoaded", + "chrome.experimental.webNavigation.onErrorOccurred": "experimental.webNavigation.html#event-onErrorOccurred", + "chrome.experimental.webRequest.addEventListener": "experimental.webRequest.html#method-addEventListener", + "chrome.experimental.webRequest.eventHandled": "experimental.webRequest.html#method-eventHandled", + "chrome.experimental.webRequest.onBeforeRedirect": "experimental.webRequest.html#event-onBeforeRedirect", + "chrome.experimental.webRequest.onBeforeRequest": "experimental.webRequest.html#event-onBeforeRequest", + "chrome.experimental.webRequest.onBeforeSendHeaders": "experimental.webRequest.html#event-onBeforeSendHeaders", + "chrome.experimental.webRequest.onCompleted": "experimental.webRequest.html#event-onCompleted", + "chrome.experimental.webRequest.onErrorOccurred": "experimental.webRequest.html#event-onErrorOccurred", + "chrome.experimental.webRequest.onHeadersReceived": "experimental.webRequest.html#event-onHeadersReceived", + "chrome.experimental.webRequest.onRequestSent": "experimental.webRequest.html#event-onRequestSent", + "chrome.extension.connect": "extension.html#method-connect", + "chrome.extension.getBackgroundPage": "extension.html#method-getBackgroundPage", + "chrome.extension.getExtensionTabs": "extension.html#method-getExtensionTabs", + "chrome.extension.getToolstrips": "extension.html#method-getToolstrips", + "chrome.extension.getURL": "extension.html#method-getURL", + "chrome.extension.getViews": "extension.html#method-getViews", + "chrome.extension.isAllowedFileSchemeAccess": "extension.html#method-isAllowedFileSchemeAccess", + "chrome.extension.isAllowedIncognitoAccess": "extension.html#method-isAllowedIncognitoAccess", + "chrome.extension.onConnect": "extension.html#event-onConnect", + "chrome.extension.onConnectExternal": "extension.html#event-onConnectExternal", + "chrome.extension.onRequest": "extension.html#event-onRequest", + "chrome.extension.onRequestExternal": "extension.html#event-onRequestExternal", + "chrome.extension.sendRequest": "extension.html#method-sendRequest", + "chrome.extension.setUpdateUrlData": "extension.html#method-setUpdateUrlData", + "chrome.history.addUrl": "history.html#method-addUrl", + "chrome.history.deleteAll": "history.html#method-deleteAll", + "chrome.history.deleteRange": "history.html#method-deleteRange", + "chrome.history.deleteUrl": "history.html#method-deleteUrl", + "chrome.history.getVisits": "history.html#method-getVisits", + "chrome.history.onVisitRemoved": "history.html#event-onVisitRemoved", + "chrome.history.onVisited": "history.html#event-onVisited", + "chrome.history.search": "history.html#method-search", + "chrome.i18n.getAcceptLanguages": "i18n.html#method-getAcceptLanguages", + "chrome.i18n.getMessage": "i18n.html#method-getMessage", + "chrome.idle.onStateChanged": "idle.html#event-onStateChanged", + "chrome.idle.queryState": "idle.html#method-queryState", + "chrome.management.get": "management.html#method-get", + "chrome.management.getAll": "management.html#method-getAll", + "chrome.management.launchApp": "management.html#method-launchApp", + "chrome.management.onDisabled": "management.html#event-onDisabled", + "chrome.management.onEnabled": "management.html#event-onEnabled", + "chrome.management.onInstalled": "management.html#event-onInstalled", + "chrome.management.onUninstalled": "management.html#event-onUninstalled", + "chrome.management.setEnabled": "management.html#method-setEnabled", + "chrome.management.uninstall": "management.html#method-uninstall", + "chrome.omnibox.onInputCancelled": "omnibox.html#event-onInputCancelled", + "chrome.omnibox.onInputChanged": "omnibox.html#event-onInputChanged", + "chrome.omnibox.onInputEntered": "omnibox.html#event-onInputEntered", + "chrome.omnibox.onInputStarted": "omnibox.html#event-onInputStarted", + "chrome.omnibox.sendSuggestions": "omnibox.html#method-sendSuggestions", + "chrome.omnibox.setDefaultSuggestion": "omnibox.html#method-setDefaultSuggestion", + "chrome.pageAction.hide": "pageAction.html#method-hide", + "chrome.pageAction.onClicked": "pageAction.html#event-onClicked", + "chrome.pageAction.setIcon": "pageAction.html#method-setIcon", + "chrome.pageAction.setPopup": "pageAction.html#method-setPopup", + "chrome.pageAction.setTitle": "pageAction.html#method-setTitle", + "chrome.pageAction.show": "pageAction.html#method-show", + "chrome.tabs.captureVisibleTab": "tabs.html#method-captureVisibleTab", + "chrome.tabs.connect": "tabs.html#method-connect", + "chrome.tabs.create": "tabs.html#method-create", + "chrome.tabs.detectLanguage": "tabs.html#method-detectLanguage", + "chrome.tabs.executeScript": "tabs.html#method-executeScript", + "chrome.tabs.get": "tabs.html#method-get", + "chrome.tabs.getAllInWindow": "tabs.html#method-getAllInWindow", + "chrome.tabs.getCurrent": "tabs.html#method-getCurrent", + "chrome.tabs.getSelected": "tabs.html#method-getSelected", + "chrome.tabs.insertCSS": "tabs.html#method-insertCSS", + "chrome.tabs.move": "tabs.html#method-move", + "chrome.tabs.onAttached": "tabs.html#event-onAttached", + "chrome.tabs.onCreated": "tabs.html#event-onCreated", + "chrome.tabs.onDetached": "tabs.html#event-onDetached", + "chrome.tabs.onMoved": "tabs.html#event-onMoved", + "chrome.tabs.onRemoved": "tabs.html#event-onRemoved", + "chrome.tabs.onSelectionChanged": "tabs.html#event-onSelectionChanged", + "chrome.tabs.onUpdated": "tabs.html#event-onUpdated", + "chrome.tabs.remove": "tabs.html#method-remove", + "chrome.tabs.sendRequest": "tabs.html#method-sendRequest", + "chrome.tabs.update": "tabs.html#method-update", + "chrome.windows.create": "windows.html#method-create", + "chrome.windows.get": "windows.html#method-get", + "chrome.windows.getAll": "windows.html#method-getAll", + "chrome.windows.getCurrent": "windows.html#method-getCurrent", + "chrome.windows.getLastFocused": "windows.html#method-getLastFocused", + "chrome.windows.onCreated": "windows.html#event-onCreated", + "chrome.windows.onFocusChanged": "windows.html#event-onFocusChanged", + "chrome.windows.onRemoved": "windows.html#event-onRemoved", + "chrome.windows.remove": "windows.html#method-remove", "chrome.windows.update": "windows.html#method-update" - }, + }, "samples": [ { "api_calls": [ - "chrome.browserAction.onClicked", + "chrome.browserAction.onClicked", "chrome.browserAction.setIcon" - ], - "description": "", + ], + "description": "", "features": [ - "background_page", - "browser_action", + "background_page", + "browser_action", "tabs" - ], - "icon": null, - "id": "0262260daf0c8f7b28feff2ef23b05e7abf9d1e0", - "name": "A browser action which changes its icon when clicked.", - "path": "examples/api/browserAction/set_icon_path/", + ], + "icon": null, + "id": "0262260daf0c8f7b28feff2ef23b05e7abf9d1e0", + "name": "A browser action which changes its icon when clicked.", + "path": "examples/api/browserAction/set_icon_path/", "protocols": [ "http://" - ], - "search_string": "A BROWSER ACTION WHICH CHANGES ITS ICON WHEN CLICKED. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON", + ], + "search_string": "A BROWSER ACTION WHICH CHANGES ITS ICON WHEN CLICKED. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON", "source_files": [ - "background.html", + "background.html", "manifest.json" - ], - "source_hash": "c5752555642e89340c57657b48440b0dcd74ee99", + ], + "source_hash": "c5752555642e89340c57657b48440b0dcd74ee99", "zip_path": "examples/api/browserAction/set_icon_path.zip" - }, + }, { "api_calls": [ "chrome.tabs.executeScript" - ], - "description": "", + ], + "description": "", "features": [ - "browser_action", - "popup", + "browser_action", + "popup", "tabs" - ], - "icon": null, - "id": "ea2894c41cb8e80a4433a3e6c5772dadce9be90d", - "name": "A browser action with a popup that changes the page color.", - "path": "examples/api/browserAction/set_page_color/", + ], + "icon": null, + "id": "ea2894c41cb8e80a4433a3e6c5772dadce9be90d", + "name": "A browser action with a popup that changes the page color.", + "path": "examples/api/browserAction/set_page_color/", "protocols": [ - "http://", + "http://", "https://" - ], - "search_string": "A BROWSER ACTION WITH A POPUP THAT CHANGES THE PAGE COLOR. BROWSER_ACTION POPUP TABS CHROME.TABS.EXECUTESCRIPT", + ], + "search_string": "A BROWSER ACTION WITH A POPUP THAT CHANGES THE PAGE COLOR. BROWSER_ACTION POPUP TABS CHROME.TABS.EXECUTESCRIPT", "source_files": [ - "manifest.json", + "manifest.json", "popup.html" - ], - "source_hash": "c8d14b6893e75a62f3bd150d5d2cc5bb785bc411", + ], + "source_hash": "c8d14b6893e75a62f3bd150d5d2cc5bb785bc411", "zip_path": "examples/api/browserAction/set_page_color.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", - "chrome.browserAction.setBadgeBackgroundColor", - "chrome.browserAction.setBadgeText", + "chrome.browserAction.onClicked", + "chrome.browserAction.setBadgeBackgroundColor", + "chrome.browserAction.setBadgeText", "chrome.tabs.executeScript" - ], - "description": "", + ], + "description": "", "features": [ - "background_page", - "browser_action", + "background_page", + "browser_action", "tabs" - ], - "icon": null, - "id": "ede3c47b7757245be42ec33fd5ca63df4b490066", - "name": "A browser action with no icon that makes the page red", - "path": "examples/api/browserAction/make_page_red/", + ], + "icon": null, + "id": "ede3c47b7757245be42ec33fd5ca63df4b490066", + "name": "A browser action with no icon that makes the page red", + "path": "examples/api/browserAction/make_page_red/", "protocols": [ "http://" - ], - "search_string": "A BROWSER ACTION WITH NO ICON THAT MAKES THE PAGE RED BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.EXECUTESCRIPT", + ], + "search_string": "A BROWSER ACTION WITH NO ICON THAT MAKES THE PAGE RED BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.EXECUTESCRIPT", "source_files": [ - "background.html", + "background.html", "manifest.json" - ], - "source_hash": "dfbb05ead54a0228a6d2f591ce1038d5c625249a", + ], + "source_hash": "dfbb05ead54a0228a6d2f591ce1038d5c625249a", "zip_path": "examples/api/browserAction/make_page_red.zip" - }, + }, { "api_calls": [ - "chrome.i18n.getAcceptLanguages", + "chrome.i18n.getAcceptLanguages", "chrome.i18n.getMessage" - ], - "description": "Returns accept languages of the browser", + ], + "description": "Returns accept languages of the browser", "features": [ - "browser_action", + "browser_action", "popup" - ], - "icon": null, - "id": "fbf0aa1a09a15ff8cc4fc7de4fd176d6c663d07a", - "name": "AcceptLanguage", - "path": "examples/api/i18n/getMessage/", - "protocols": [], - "search_string": "ACCEPTLANGUAGE RETURNS ACCEPT LANGUAGES OF THE BROWSER BROWSER_ACTION POPUP CHROME.I18N.GETACCEPTLANGUAGES CHROME.I18N.GETMESSAGE", + ], + "icon": null, + "id": "fbf0aa1a09a15ff8cc4fc7de4fd176d6c663d07a", + "name": "AcceptLanguage", + "path": "examples/api/i18n/getMessage/", + "protocols": [], + "search_string": "ACCEPTLANGUAGE RETURNS ACCEPT LANGUAGES OF THE BROWSER BROWSER_ACTION POPUP CHROME.I18N.GETACCEPTLANGUAGES CHROME.I18N.GETMESSAGE", "source_files": [ - "_locales/en_US/messages.json", - "_locales/es/messages.json", - "_locales/sr/messages.json", - "manifest.json", + "_locales/en_US/messages.json", + "_locales/es/messages.json", + "_locales/sr/messages.json", + "manifest.json", "popup.html" - ], - "source_hash": "67f203e2773eebf401d0aa0a9709d961e506d875", + ], + "source_hash": "67f203e2773eebf401d0aa0a9709d961e506d875", "zip_path": "examples/api/i18n/getMessage.zip" - }, + }, { "api_calls": [ - "chrome.pageAction.hide", - "chrome.pageAction.onClicked", - "chrome.pageAction.setIcon", - "chrome.pageAction.setTitle", - "chrome.pageAction.show", - "chrome.tabs.get", - "chrome.tabs.getSelected", + "chrome.pageAction.hide", + "chrome.pageAction.onClicked", + "chrome.pageAction.setIcon", + "chrome.pageAction.setTitle", + "chrome.pageAction.show", + "chrome.tabs.get", + "chrome.tabs.getSelected", "chrome.tabs.onSelectionChanged" - ], - "description": "This extension adds an animated browser action to the toolbar.", + ], + "description": "This extension adds an animated browser action to the toolbar.", "features": [ - "background_page", - "page_action", + "background_page", + "page_action", "tabs" - ], - "icon": null, - "id": "9a6e4ec46997fb92b324974afa08a3d007e2537f", - "name": "Animated Page Action", - "path": "examples/api/pageAction/set_icon/", - "protocols": [], - "search_string": "ANIMATED PAGE ACTION THIS EXTENSION ADDS AN ANIMATED BROWSER ACTION TO THE TOOLBAR. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.ONCLICKED CHROME.PAGEACTION.SETICON CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED", + ], + "icon": null, + "id": "9a6e4ec46997fb92b324974afa08a3d007e2537f", + "name": "Animated Page Action", + "path": "examples/api/pageAction/set_icon/", + "protocols": [], + "search_string": "ANIMATED PAGE ACTION THIS EXTENSION ADDS AN ANIMATED BROWSER ACTION TO THE TOOLBAR. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.ONCLICKED CHROME.PAGEACTION.SETICON CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED", "source_files": [ - "background.html", + "background.html", "manifest.json" - ], - "source_hash": "9d5e9f8fd525c6d02fe03e1843041f5b0f94f690", + ], + "source_hash": "9d5e9f8fd525c6d02fe03e1843041f5b0f94f690", "zip_path": "examples/api/pageAction/set_icon.zip" - }, + }, { "api_calls": [ - "chrome.extension.getURL", - "chrome.management.get", - "chrome.management.getAll", - "chrome.management.launchApp", + "chrome.extension.getURL", + "chrome.management.get", + "chrome.management.getAll", + "chrome.management.launchApp", "chrome.tabs.create" - ], - "description": "", + ], + "description": "", "features": [ - "browser_action", + "browser_action", "management" - ], - "icon": null, - "id": "a1f7cf79dd555b04fa8d603247a040e644996293", - "name": "App Launcher", - "path": "examples/extensions/app_launcher/", - "protocols": [], - "search_string": "APP LAUNCHER BROWSER_ACTION MANAGEMENT CHROME.EXTENSION.GETURL CHROME.MANAGEMENT.GET CHROME.MANAGEMENT.GETALL CHROME.MANAGEMENT.LAUNCHAPP CHROME.TABS.CREATE", + ], + "icon": null, + "id": "a1f7cf79dd555b04fa8d603247a040e644996293", + "name": "App Launcher", + "path": "examples/extensions/app_launcher/", + "protocols": [], + "search_string": "APP LAUNCHER BROWSER_ACTION MANAGEMENT CHROME.EXTENSION.GETURL CHROME.MANAGEMENT.GET CHROME.MANAGEMENT.GETALL CHROME.MANAGEMENT.LAUNCHAPP CHROME.TABS.CREATE", "source_files": [ - "manifest.json", - "popup.css", - "popup.html", + "manifest.json", + "popup.css", + "popup.html", "popup.js" - ], - "source_hash": "4cb1348cfca9c990117d52290f93eb5fc5081bc2", + ], + "source_hash": "4cb1348cfca9c990117d52290f93eb5fc5081bc2", "zip_path": "examples/extensions/app_launcher.zip" - }, + }, { - "api_calls": [], - "description": "", + "api_calls": [], + "description": "", "features": [ "chrome_url_overrides" - ], - "icon": null, - "id": "9747e3d6a3eab39bc7c17f11a80573c62d44c7e5", - "name": "Blank new tab page", - "path": "examples/api/override/blank_ntp/", - "protocols": [], - "search_string": "BLANK NEW TAB PAGE CHROME_URL_OVERRIDES", + ], + "icon": null, + "id": "9747e3d6a3eab39bc7c17f11a80573c62d44c7e5", + "name": "Blank new tab page", + "path": "examples/api/override/blank_ntp/", + "protocols": [], + "search_string": "BLANK NEW TAB PAGE CHROME_URL_OVERRIDES", "source_files": [ - "blank.html", + "blank.html", "manifest.json" - ], - "source_hash": "477acf6d15e3fa252e6307e156707538b61c86db", + ], + "source_hash": "477acf6d15e3fa252e6307e156707538b61c86db", "zip_path": "examples/api/override/blank_ntp.zip" - }, + }, { "api_calls": [ - "chrome.bookmarks.onCreated", - "chrome.bookmarks.onMoved", - "chrome.bookmarks.onRemoved", - "chrome.extension.getBackgroundPage", - "chrome.extension.onRequest", - "chrome.extension.sendRequest", - "chrome.tabs.get", - "chrome.tabs.onAttached", - "chrome.tabs.onCreated", - "chrome.tabs.onDetached", - "chrome.tabs.onMoved", - "chrome.tabs.onRemoved", - "chrome.tabs.onSelectionChanged", - "chrome.tabs.onUpdated", - "chrome.windows.onCreated", - "chrome.windows.onFocusChanged", + "chrome.bookmarks.onCreated", + "chrome.bookmarks.onMoved", + "chrome.bookmarks.onRemoved", + "chrome.extension.getBackgroundPage", + "chrome.extension.onRequest", + "chrome.extension.sendRequest", + "chrome.tabs.get", + "chrome.tabs.onAttached", + "chrome.tabs.onCreated", + "chrome.tabs.onDetached", + "chrome.tabs.onMoved", + "chrome.tabs.onRemoved", + "chrome.tabs.onSelectionChanged", + "chrome.tabs.onUpdated", + "chrome.windows.onCreated", + "chrome.windows.onFocusChanged", "chrome.windows.onRemoved" - ], - "description": "Enjoy a more magical and immersive experience when browsing the web using the power of sound.", + ], + "description": "Enjoy a more magical and immersive experience when browsing the web using the power of sound.", "features": [ - "background_page", - "bookmarks", - "options_page", + "background_page", + "bookmarks", + "options_page", "tabs" - ], - "icon": "icon.png", - "id": "903e7277139e1e6caec123d3319cab295d8d1b3a", - "name": "Chrome Sounds", - "path": "examples/extensions/fx/", + ], + "icon": "icon.png", + "id": "903e7277139e1e6caec123d3319cab295d8d1b3a", + "name": "Chrome Sounds", + "path": "examples/extensions/fx/", "protocols": [ - "http://", + "http://", "https://" - ], - "search_string": "CHROME SOUNDS ENJOY A MORE MAGICAL AND IMMERSIVE EXPERIENCE WHEN BROWSING THE WEB USING THE POWER OF SOUND. BACKGROUND_PAGE BOOKMARKS OPTIONS_PAGE TABS CHROME.BOOKMARKS.ONCREATED CHROME.BOOKMARKS.ONMOVED CHROME.BOOKMARKS.ONREMOVED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.TABS.GET CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED", + ], + "search_string": "CHROME SOUNDS ENJOY A MORE MAGICAL AND IMMERSIVE EXPERIENCE WHEN BROWSING THE WEB USING THE POWER OF SOUND. BACKGROUND_PAGE BOOKMARKS OPTIONS_PAGE TABS CHROME.BOOKMARKS.ONCREATED CHROME.BOOKMARKS.ONMOVED CHROME.BOOKMARKS.ONREMOVED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.TABS.GET CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED", "source_files": [ - "bg.html", - "bg.js", - "content.js", - "manifest.json", + "bg.html", + "bg.js", + "content.js", + "manifest.json", "options.html" - ], - "source_hash": "4155e4e6ba7d523ba7bc3b75da352c22e534c3c3", + ], + "source_hash": "4155e4e6ba7d523ba7bc3b75da352c22e534c3c3", "zip_path": "examples/extensions/fx.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.setBadgeBackgroundColor", - "chrome.browserAction.setBadgeText", - "chrome.browserAction.setTitle", + "chrome.browserAction.setBadgeBackgroundColor", + "chrome.browserAction.setBadgeText", + "chrome.browserAction.setTitle", "chrome.extension.getURL" - ], - "description": "Displays the status of the Chromium buildbot in the toolbar. Click to see more detailed status in a popup.", + ], + "description": "Displays the status of the Chromium buildbot in the toolbar. Click to see more detailed status in a popup.", "features": [ - "background_page", - "browser_action", - "notifications", - "options_page", + "background_page", + "browser_action", + "notifications", + "options_page", "popup" - ], - "icon": "icon.png", - "id": "0e790e035a4a00b6f1def5ef9a7d7be1bce95ab5", - "name": "Chromium Buildbot Monitor", - "path": "examples/extensions/buildbot/", + ], + "icon": "icon.png", + "id": "0e790e035a4a00b6f1def5ef9a7d7be1bce95ab5", + "name": "Chromium Buildbot Monitor", + "path": "examples/extensions/buildbot/", "protocols": [ - "http://", + "http://", "http://" - ], - "search_string": "CHROMIUM BUILDBOT MONITOR DISPLAYS THE STATUS OF THE CHROMIUM BUILDBOT IN THE TOOLBAR. CLICK TO SEE MORE DETAILED STATUS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION NOTIFICATIONS OPTIONS_PAGE POPUP CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETURL", + ], + "search_string": "CHROMIUM BUILDBOT MONITOR DISPLAYS THE STATUS OF THE CHROMIUM BUILDBOT IN THE TOOLBAR. CLICK TO SEE MORE DETAILED STATUS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION NOTIFICATIONS OPTIONS_PAGE POPUP CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETURL", "source_files": [ - "bg.html", - "manifest.json", - "options.html", + "bg.html", + "manifest.json", + "options.html", "popup.html" - ], - "source_hash": "ad985dc5b3e2b40826042be1d7b77c07fadfcc68", + ], + "source_hash": "ad985dc5b3e2b40826042be1d7b77c07fadfcc68", "zip_path": "examples/extensions/buildbot.zip" - }, + }, { "api_calls": [ - "chrome.omnibox.onInputCancelled", - "chrome.omnibox.onInputChanged", - "chrome.omnibox.onInputEntered", - "chrome.omnibox.onInputStarted", - "chrome.omnibox.setDefaultSuggestion", - "chrome.tabs.get", - "chrome.tabs.getSelected", + "chrome.omnibox.onInputCancelled", + "chrome.omnibox.onInputChanged", + "chrome.omnibox.onInputEntered", + "chrome.omnibox.onInputStarted", + "chrome.omnibox.setDefaultSuggestion", + "chrome.tabs.get", + "chrome.tabs.getSelected", "chrome.tabs.update" - ], - "description": "Add support to the omnibox to search the Chromium source code.", + ], + "description": "Add support to the omnibox to search the Chromium source code.", "features": [ - "background_page", + "background_page", "tabs" - ], - "icon": null, - "id": "ac31228200b41a87982e386cc90d3a6eee4ad885", - "name": "Chromium Search", - "path": "examples/extensions/chrome_search/", + ], + "icon": null, + "id": "ac31228200b41a87982e386cc90d3a6eee4ad885", + "name": "Chromium Search", + "path": "examples/extensions/chrome_search/", "protocols": [ "http://" - ], - "search_string": "CHROMIUM SEARCH ADD SUPPORT TO THE OMNIBOX TO SEARCH THE CHROMIUM SOURCE CODE. BACKGROUND_PAGE TABS CHROME.OMNIBOX.ONINPUTCANCELLED CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED CHROME.OMNIBOX.ONINPUTSTARTED CHROME.OMNIBOX.SETDEFAULTSUGGESTION CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE", + ], + "search_string": "CHROMIUM SEARCH ADD SUPPORT TO THE OMNIBOX TO SEARCH THE CHROMIUM SOURCE CODE. BACKGROUND_PAGE TABS CHROME.OMNIBOX.ONINPUTCANCELLED CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED CHROME.OMNIBOX.ONINPUTSTARTED CHROME.OMNIBOX.SETDEFAULTSUGGESTION CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE", "source_files": [ - "background.html", + "background.html", "manifest.json" - ], - "source_hash": "8f9a8a4bf21102b26fa626626374cb200b2bcac0", + ], + "source_hash": "8f9a8a4bf21102b26fa626626374cb200b2bcac0", "zip_path": "examples/extensions/chrome_search.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.setBadgeText", - "chrome.tabs.detectLanguage", - "chrome.tabs.get", - "chrome.tabs.getSelected", - "chrome.tabs.onSelectionChanged", + "chrome.browserAction.setBadgeText", + "chrome.tabs.detectLanguage", + "chrome.tabs.get", + "chrome.tabs.getSelected", + "chrome.tabs.onSelectionChanged", "chrome.tabs.onUpdated" - ], - "description": "Displays the language of a tab", + ], + "description": "Displays the language of a tab", "features": [ - "background_page", - "browser_action", + "background_page", + "browser_action", "tabs" - ], - "icon": null, - "id": "7d5d6cf195bc25480256618e360aa38c6e6fba82", - "name": "CLD", - "path": "examples/api/i18n/cld/", - "protocols": [], - "search_string": "CLD DISPLAYS THE LANGUAGE OF A TAB BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.DETECTLANGUAGE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED", + ], + "icon": null, + "id": "7d5d6cf195bc25480256618e360aa38c6e6fba82", + "name": "CLD", + "path": "examples/api/i18n/cld/", + "protocols": [], + "search_string": "CLD DISPLAYS THE LANGUAGE OF A TAB BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.DETECTLANGUAGE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED", "source_files": [ - "background.html", + "background.html", "manifest.json" - ], - "source_hash": "913694d89e0b081f1ea5ad6f07b60b0141e82394", + ], + "source_hash": "913694d89e0b081f1ea5ad6f07b60b0141e82394", "zip_path": "examples/api/i18n/cld.zip" - }, + }, { "api_calls": [ "chrome.contextMenus.create" - ], - "description": "Shows some of the features of the Context Menus API", + ], + "description": "Shows some of the features of the Context Menus API", "features": [ - "background_page", + "background_page", "contextMenus" - ], - "icon": null, - "id": "5d81304a17cf7ac2887484f730fbd2b01e51e166", - "name": "Context Menus Sample", - "path": "examples/api/contextMenus/basic/", - "protocols": [], - "search_string": "CONTEXT MENUS SAMPLE SHOWS SOME OF THE FEATURES OF THE CONTEXT MENUS API BACKGROUND_PAGE CONTEXTMENUS CHROME.CONTEXTMENUS.CREATE", + ], + "icon": null, + "id": "5d81304a17cf7ac2887484f730fbd2b01e51e166", + "name": "Context Menus Sample", + "path": "examples/api/contextMenus/basic/", + "protocols": [], + "search_string": "CONTEXT MENUS SAMPLE SHOWS SOME OF THE FEATURES OF THE CONTEXT MENUS API BACKGROUND_PAGE CONTEXTMENUS CHROME.CONTEXTMENUS.CREATE", "source_files": [ - "background.html", - "manifest.json", + "background.html", + "manifest.json", "sample.js" - ], - "source_hash": "0e35ce268b3b2cf3d9830e6411c85c5dfef2ffdf", + ], + "source_hash": "0e35ce268b3b2cf3d9830e6411c85c5dfef2ffdf", "zip_path": "examples/api/contextMenus/basic.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", - "chrome.cookies.get", - "chrome.cookies.getAll", - "chrome.cookies.onChanged", - "chrome.cookies.remove", - "chrome.extension.getURL", - "chrome.tabs.create", - "chrome.tabs.update", - "chrome.windows.get", + "chrome.browserAction.onClicked", + "chrome.cookies.get", + "chrome.cookies.getAll", + "chrome.cookies.onChanged", + "chrome.cookies.remove", + "chrome.extension.getURL", + "chrome.tabs.create", + "chrome.tabs.update", + "chrome.windows.get", "chrome.windows.getAll" - ], - "description": "Testing Cookie API", + ], + "description": "Testing Cookie API", "features": [ - "background_page", - "browser_action", - "cookies", + "background_page", + "browser_action", + "cookies", "tabs" - ], - "icon": "cookie.png", - "id": "4daa6becd0899a54776d9cf7f09613ed1a9f4d77", - "name": "Cookie API Test Extension", - "path": "examples/api/cookies/", + ], + "icon": "cookie.png", + "id": "4daa6becd0899a54776d9cf7f09613ed1a9f4d77", + "name": "Cookie API Test Extension", + "path": "examples/api/cookies/", "protocols": [ - "http://", + "http://", "https://" - ], - "search_string": "COOKIE API TEST EXTENSION TESTING COOKIE API BACKGROUND_PAGE BROWSER_ACTION COOKIES TABS CHROME.BROWSERACTION.ONCLICKED CHROME.COOKIES.GET CHROME.COOKIES.GETALL CHROME.COOKIES.ONCHANGED CHROME.COOKIES.REMOVE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL", + ], + "search_string": "COOKIE API TEST EXTENSION TESTING COOKIE API BACKGROUND_PAGE BROWSER_ACTION COOKIES TABS CHROME.BROWSERACTION.ONCLICKED CHROME.COOKIES.GET CHROME.COOKIES.GETALL CHROME.COOKIES.ONCHANGED CHROME.COOKIES.REMOVE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL", "source_files": [ - "background.html", - "manager.html", + "background.html", + "manager.html", "manifest.json" - ], - "source_hash": "d0741a5ff0ce9ac38a1be3e6abc46065d74cb498", + ], + "source_hash": "d0741a5ff0ce9ac38a1be3e6abc46065d74cb498", "zip_path": "examples/api/cookies.zip" - }, + }, { "api_calls": [ - "chrome.extension.onRequest", + "chrome.extension.onRequest", "chrome.extension.sendRequest" - ], - "description": "Demonstrates a method to make a cross-domain XMLHttpRequest fetch from a content script. This extension fetches the current trending topics from Twitter and inserts them in an overlay at the top of Google News. Visit http://news.google.com to test this extension.", + ], + "description": "Demonstrates a method to make a cross-domain XMLHttpRequest fetch from a content script. This extension fetches the current trending topics from Twitter and inserts them in an overlay at the top of Google News. Visit http://news.google.com to test this extension.", "features": [ "background_page" - ], - "icon": "sample-128.png", - "id": "6871d09f4a96bf9d4b6cc724d00e909cee0f3902", - "name": "Cross-domain XMLHttpRequest from a content script", - "path": "examples/howto/contentscript_xhr/", + ], + "icon": "sample-128.png", + "id": "6871d09f4a96bf9d4b6cc724d00e909cee0f3902", + "name": "Cross-domain XMLHttpRequest from a content script", + "path": "examples/howto/contentscript_xhr/", "protocols": [ "http://" - ], - "search_string": "CROSS-DOMAIN XMLHTTPREQUEST FROM A CONTENT SCRIPT DEMONSTRATES A METHOD TO MAKE A CROSS-DOMAIN XMLHTTPREQUEST FETCH FROM A CONTENT SCRIPT. THIS EXTENSION FETCHES THE CURRENT TRENDING TOPICS FROM TWITTER AND INSERTS THEM IN AN OVERLAY AT THE TOP OF GOOGLE NEWS. VISIT HTTP://NEWS.GOOGLE.COM TO TEST THIS EXTENSION. BACKGROUND_PAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST", + ], + "search_string": "CROSS-DOMAIN XMLHTTPREQUEST FROM A CONTENT SCRIPT DEMONSTRATES A METHOD TO MAKE A CROSS-DOMAIN XMLHTTPREQUEST FETCH FROM A CONTENT SCRIPT. THIS EXTENSION FETCHES THE CURRENT TRENDING TOPICS FROM TWITTER AND INSERTS THEM IN AN OVERLAY AT THE TOP OF GOOGLE NEWS. VISIT HTTP://NEWS.GOOGLE.COM TO TEST THIS EXTENSION. BACKGROUND_PAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST", "source_files": [ - "background.html", - "contentscript.js", + "background.html", + "contentscript.js", "manifest.json" - ], - "source_hash": "0fe56cea50dc18b7e5e31d47c383356a85d8b896", + ], + "source_hash": "0fe56cea50dc18b7e5e31d47c383356a85d8b896", "zip_path": "examples/howto/contentscript_xhr.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", - "chrome.extension.connect", - "chrome.extension.onConnect", - "chrome.tabs.create", - "chrome.tabs.executeScript", + "chrome.browserAction.onClicked", + "chrome.extension.connect", + "chrome.extension.onConnect", + "chrome.tabs.create", + "chrome.tabs.executeScript", "chrome.tabs.update" - ], - "description": "This extension adds an email button to the toolbar which allows you to email the page link using your default mail client or Gmail.", + ], + "description": "This extension adds an email button to the toolbar which allows you to email the page link using your default mail client or Gmail.", "features": [ - "background_page", - "browser_action", - "options_page", + "background_page", + "browser_action", + "options_page", "tabs" - ], - "icon": "mail_128x128.png", - "id": "028eb5364924344029bcbe1d527f132fc72b34e5", - "name": "Email this page (by Google)", - "path": "examples/extensions/email_this_page/", + ], + "icon": "mail_128x128.png", + "id": "028eb5364924344029bcbe1d527f132fc72b34e5", + "name": "Email this page (by Google)", + "path": "examples/extensions/email_this_page/", "protocols": [ - "http://", + "http://", "https://" - ], - "search_string": "EMAIL THIS PAGE (BY GOOGLE) THIS EXTENSION ADDS AN EMAIL BUTTON TO THE TOOLBAR WHICH ALLOWS YOU TO EMAIL THE PAGE LINK USING YOUR DEFAULT MAIL CLIENT OR GMAIL. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.CONNECT CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.UPDATE", + ], + "search_string": "EMAIL THIS PAGE (BY GOOGLE) THIS EXTENSION ADDS AN EMAIL BUTTON TO THE TOOLBAR WHICH ALLOWS YOU TO EMAIL THE PAGE LINK USING YOUR DEFAULT MAIL CLIENT OR GMAIL. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.CONNECT CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.UPDATE", "source_files": [ - "background.html", - "content_script.js", - "manifest.json", + "background.html", + "content_script.js", + "manifest.json", "options.html" - ], - "source_hash": "54b5469031ddcb2097f39dbaae1bcd81ae650154", + ], + "source_hash": "54b5469031ddcb2097f39dbaae1bcd81ae650154", "zip_path": "examples/extensions/email_this_page.zip" - }, + }, { - "api_calls": [], - "description": "A sample extension which uses Google Analytics to track usage.", + "api_calls": [ + "chrome.extension.isAllowedIncognitoAccess" + ], + "description": "Sample extension which demonstrates how to access a preference.", + "features": [ + "browser_action", + "contentSettings", + "experimental", + "popup" + ], + "icon": null, + "id": "0bf0930a32829dfb77170535ecb27adc2de3998a", + "name": "Enable/disable referrers API example extension", + "path": "examples/api/preferences/enableReferrer/", + "protocols": [], + "search_string": "ENABLE/DISABLE REFERRERS API EXAMPLE EXTENSION SAMPLE EXTENSION WHICH DEMONSTRATES HOW TO ACCESS A PREFERENCE. BROWSER_ACTION CONTENTSETTINGS EXPERIMENTAL POPUP CHROME.EXTENSION.ISALLOWEDINCOGNITOACCESS", + "source_files": [ + "manifest.json", + "popup.html" + ], + "source_hash": "df2a19be905672c2ced4e7108e05170175847d6b", + "zip_path": "examples/api/preferences/enableReferrer.zip" + }, + { + "api_calls": [], + "description": "A sample extension which uses Google Analytics to track usage.", "features": [ - "background_page", - "browser_action", + "background_page", + "browser_action", "popup" - ], - "icon": "analytics-extension-icon-128.png", - "id": "763a08e9b06595d785568a8d392b95a2f3700258", - "name": "Event Tracking with Google Analytics", - "path": "examples/tutorials/analytics/", - "protocols": [], - "search_string": "EVENT TRACKING WITH GOOGLE ANALYTICS A SAMPLE EXTENSION WHICH USES GOOGLE ANALYTICS TO TRACK USAGE. BACKGROUND_PAGE BROWSER_ACTION POPUP", + ], + "icon": "analytics-extension-icon-128.png", + "id": "763a08e9b06595d785568a8d392b95a2f3700258", + "name": "Event Tracking with Google Analytics", + "path": "examples/tutorials/analytics/", + "protocols": [], + "search_string": "EVENT TRACKING WITH GOOGLE ANALYTICS A SAMPLE EXTENSION WHICH USES GOOGLE ANALYTICS TO TRACK USAGE. BACKGROUND_PAGE BROWSER_ACTION POPUP", "source_files": [ - "analytics.js", - "background.html", - "manifest.json", + "analytics.js", + "background.html", + "manifest.json", "popup.html" - ], - "source_hash": "b02f040a3eb56f8a0e780549954f69172d62dcd3", + ], + "source_hash": "b02f040a3eb56f8a0e780549954f69172d62dcd3", "zip_path": "examples/tutorials/analytics.zip" - }, + }, { "api_calls": [ - "chrome.omnibox.onInputChanged", - "chrome.omnibox.onInputEntered", - "chrome.tabs.create", - "chrome.tabs.get", - "chrome.tabs.onRemoved", + "chrome.omnibox.onInputChanged", + "chrome.omnibox.onInputEntered", + "chrome.tabs.create", + "chrome.tabs.get", + "chrome.tabs.onRemoved", "chrome.tabs.update" - ], - "description": "Search the Chrome Extensions documentation. To use, type 'crdoc' plus a search term into the Omnibox.", + ], + "description": "Search the Chrome Extensions documentation. To use, type 'crdoc' plus a search term into the Omnibox.", "features": [ - "background_page", + "background_page", "tabs" - ], - "icon": "icon-128.png", - "id": "e3df888a89e35bdeb9c8bc8d03be5e1851b97c68", - "name": "Extension Docs Search", - "path": "examples/api/omnibox/extension-docs/", + ], + "icon": "icon-128.png", + "id": "e3df888a89e35bdeb9c8bc8d03be5e1851b97c68", + "name": "Extension Docs Search", + "path": "examples/api/omnibox/extension-docs/", "protocols": [ "http://" - ], - "search_string": "EXTENSION DOCS SEARCH SEARCH THE CHROME EXTENSIONS DOCUMENTATION. TO USE, TYPE CRDOC PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE TABS CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.ONREMOVED CHROME.TABS.UPDATE", + ], + "search_string": "EXTENSION DOCS SEARCH SEARCH THE CHROME EXTENSIONS DOCUMENTATION. TO USE, TYPE CRDOC PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE TABS CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.ONREMOVED CHROME.TABS.UPDATE", "source_files": [ - "background.html", + "background.html", "manifest.json" - ], - "source_hash": "0aa8ec9a2c091e227661ca5010d08f6823e643a7", + ], + "source_hash": "0aa8ec9a2c091e227661ca5010d08f6823e643a7", "zip_path": "examples/api/omnibox/extension-docs.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", - "chrome.browserAction.setBadgeBackgroundColor", - "chrome.browserAction.setBadgeText", - "chrome.browserAction.setIcon", - "chrome.browserAction.setTitle", - "chrome.extension.getBackgroundPage", - "chrome.extension.onRequest", - "chrome.extension.sendRequest", - "chrome.i18n.getMessage", - "chrome.tabs.create", - "chrome.tabs.get", - "chrome.tabs.getAllInWindow", - "chrome.tabs.onUpdated", + "chrome.browserAction.onClicked", + "chrome.browserAction.setBadgeBackgroundColor", + "chrome.browserAction.setBadgeText", + "chrome.browserAction.setIcon", + "chrome.browserAction.setTitle", + "chrome.extension.getBackgroundPage", + "chrome.extension.onRequest", + "chrome.extension.sendRequest", + "chrome.i18n.getMessage", + "chrome.tabs.create", + "chrome.tabs.get", + "chrome.tabs.getAllInWindow", + "chrome.tabs.onUpdated", "chrome.tabs.update" - ], - "description": "Quickly see the time until your next meeting from any of your calendars. Click on the button to be taken to your calendar.", + ], + "description": "Quickly see the time until your next meeting from any of your calendars. Click on the button to be taken to your calendar.", "features": [ - "background_page", - "browser_action", - "options_page", + "background_page", + "browser_action", + "options_page", "tabs" - ], - "icon": "images/icon-128.gif", - "id": "8b0dd31216235941bdd8eb33fda915ef5cf79a82", - "name": "Google Calendar Checker (by Google)", - "path": "examples/extensions/calendar/", + ], + "icon": "images/icon-128.gif", + "id": "8b0dd31216235941bdd8eb33fda915ef5cf79a82", + "name": "Google Calendar Checker (by Google)", + "path": "examples/extensions/calendar/", "protocols": [ - "http://", + "http://", "https://" - ], - "search_string": "GOOGLE CALENDAR CHECKER (BY GOOGLE) QUICKLY SEE THE TIME UNTIL YOUR NEXT MEETING FROM ANY OF YOUR CALENDARS. CLICK ON THE BUTTON TO BE TAKEN TO YOUR CALENDAR. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE", + ], + "search_string": "GOOGLE CALENDAR CHECKER (BY GOOGLE) QUICKLY SEE THE TIME UNTIL YOUR NEXT MEETING FROM ANY OF YOUR CALENDARS. CLICK ON THE BUTTON TO BE TAKEN TO YOUR CALENDAR. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE", "source_files": [ - "_locales/en/messages.json", - "javascript/background.js", - "javascript/options.js", - "javascript/util.js", - "manifest.json", - "views/background.html", + "_locales/en/messages.json", + "javascript/background.js", + "javascript/options.js", + "javascript/util.js", + "manifest.json", + "views/background.html", "views/options.html" - ], - "source_hash": "3d9782a3e8315bbbca36c63297db2c8926df4521", + ], + "source_hash": "3d9782a3e8315bbbca36c63297db2c8926df4521", "zip_path": "examples/extensions/calendar.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.setBadgeText", - "chrome.extension.getBackgroundPage", - "chrome.extension.getURL", - "chrome.tabs.create", - "chrome.tabs.get", - "chrome.tabs.getSelected", - "chrome.tabs.onUpdated", + "chrome.browserAction.setBadgeText", + "chrome.extension.getBackgroundPage", + "chrome.extension.getURL", + "chrome.tabs.create", + "chrome.tabs.get", + "chrome.tabs.getSelected", + "chrome.tabs.onUpdated", "chrome.tabs.remove" - ], - "description": "Demonstrates how to use OAuth to connect the Google Documents List Data API.", + ], + "description": "Demonstrates how to use OAuth to connect the Google Documents List Data API.", "features": [ - "background_page", - "browser_action", - "options_page", - "popup", + "background_page", + "browser_action", + "options_page", + "popup", "tabs" - ], - "icon": "img/docs_spreadsheets-128.gif", - "id": "4e35caa9742fb82dbd628892d23a781614f6eff6", - "name": "Google Document List Viewer", - "path": "examples/extensions/gdocs/", + ], + "icon": "img/docs_spreadsheets-128.gif", + "id": "4e35caa9742fb82dbd628892d23a781614f6eff6", + "name": "Google Document List Viewer", + "path": "examples/extensions/gdocs/", "protocols": [ - "https://", - "https://", - "https://", + "https://", + "https://", + "https://", "https://" - ], - "search_string": "GOOGLE DOCUMENT LIST VIEWER DEMONSTRATES HOW TO USE OAUTH TO CONNECT THE GOOGLE DOCUMENTS LIST DATA API. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE", + ], + "search_string": "GOOGLE DOCUMENT LIST VIEWER DEMONSTRATES HOW TO USE OAUTH TO CONNECT THE GOOGLE DOCUMENTS LIST DATA API. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE", "source_files": [ - "background.html", - "chrome_ex_oauth.html", - "chrome_ex_oauth.js", - "chrome_ex_oauthsimple.js", - "js/jquery-1.4.1.min.js", - "manifest.json", - "options.html", + "background.html", + "chrome_ex_oauth.html", + "chrome_ex_oauth.js", + "chrome_ex_oauthsimple.js", + "js/jquery-1.4.1.min.js", + "manifest.json", + "options.html", "popup.html" - ], - "source_hash": "284e44d603a62348dd1b7e98e593f3f4b877b124", + ], + "source_hash": "284e44d603a62348dd1b7e98e593f3f4b877b124", "zip_path": "examples/extensions/gdocs.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", - "chrome.browserAction.setBadgeBackgroundColor", - "chrome.browserAction.setBadgeText", - "chrome.browserAction.setIcon", - "chrome.extension.getBackgroundPage", - "chrome.i18n.getMessage", - "chrome.tabs.create", - "chrome.tabs.get", - "chrome.tabs.getAllInWindow", - "chrome.tabs.onUpdated", + "chrome.browserAction.onClicked", + "chrome.browserAction.setBadgeBackgroundColor", + "chrome.browserAction.setBadgeText", + "chrome.browserAction.setIcon", + "chrome.extension.getBackgroundPage", + "chrome.i18n.getMessage", + "chrome.tabs.create", + "chrome.tabs.get", + "chrome.tabs.getAllInWindow", + "chrome.tabs.onUpdated", "chrome.tabs.update" - ], - "description": "Displays the number of unread messages in your Google Mail inbox. You can also click the button to open your inbox.", + ], + "description": "Displays the number of unread messages in your Google Mail inbox. You can also click the button to open your inbox.", "features": [ - "background_page", - "browser_action", - "options_page", + "background_page", + "browser_action", + "options_page", "tabs" - ], - "icon": "icon_128.png", - "id": "bb57f7a0132cbeb36ad7e7bb0ab75c21704234ca", - "name": "Google Mail Checker", - "path": "examples/extensions/gmail/", + ], + "icon": "icon_128.png", + "id": "bb57f7a0132cbeb36ad7e7bb0ab75c21704234ca", + "name": "Google Mail Checker", + "path": "examples/extensions/gmail/", "protocols": [ - "http://", + "http://", "https://" - ], - "search_string": "GOOGLE MAIL CHECKER DISPLAYS THE NUMBER OF UNREAD MESSAGES IN YOUR GOOGLE MAIL INBOX. YOU CAN ALSO CLICK THE BUTTON TO OPEN YOUR INBOX. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE", + ], + "search_string": "GOOGLE MAIL CHECKER DISPLAYS THE NUMBER OF UNREAD MESSAGES IN YOUR GOOGLE MAIL INBOX. YOU CAN ALSO CLICK THE BUTTON TO OPEN YOUR INBOX. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE", "source_files": [ - "_locales/ar/messages.json", - "_locales/bg/messages.json", - "_locales/ca/messages.json", - "_locales/cs/messages.json", - "_locales/da/messages.json", - "_locales/de/messages.json", - "_locales/el/messages.json", - "_locales/en/messages.json", - "_locales/en_GB/messages.json", - "_locales/es/messages.json", - "_locales/es_419/messages.json", - "_locales/et/messages.json", - "_locales/fi/messages.json", - "_locales/fil/messages.json", - "_locales/fr/messages.json", - "_locales/he/messages.json", - "_locales/hi/messages.json", - "_locales/hr/messages.json", - "_locales/hu/messages.json", - "_locales/id/messages.json", - "_locales/it/messages.json", - "_locales/ja/messages.json", - "_locales/ko/messages.json", - "_locales/lt/messages.json", - "_locales/lv/messages.json", - "_locales/nb/messages.json", - "_locales/nl/messages.json", - "_locales/pl/messages.json", - "_locales/pt_BR/messages.json", - "_locales/pt_PT/messages.json", - "_locales/ro/messages.json", - "_locales/ru/messages.json", - "_locales/sk/messages.json", - "_locales/sl/messages.json", - "_locales/sr/messages.json", - "_locales/sv/messages.json", - "_locales/th/messages.json", - "_locales/tr/messages.json", - "_locales/uk/messages.json", - "_locales/vi/messages.json", - "_locales/zh_CN/messages.json", - "_locales/zh_TW/messages.json", - "background.html", - "manifest.json", + "_locales/ar/messages.json", + "_locales/bg/messages.json", + "_locales/ca/messages.json", + "_locales/cs/messages.json", + "_locales/da/messages.json", + "_locales/de/messages.json", + "_locales/el/messages.json", + "_locales/en/messages.json", + "_locales/en_GB/messages.json", + "_locales/es/messages.json", + "_locales/es_419/messages.json", + "_locales/et/messages.json", + "_locales/fi/messages.json", + "_locales/fil/messages.json", + "_locales/fr/messages.json", + "_locales/he/messages.json", + "_locales/hi/messages.json", + "_locales/hr/messages.json", + "_locales/hu/messages.json", + "_locales/id/messages.json", + "_locales/it/messages.json", + "_locales/ja/messages.json", + "_locales/ko/messages.json", + "_locales/lt/messages.json", + "_locales/lv/messages.json", + "_locales/nb/messages.json", + "_locales/nl/messages.json", + "_locales/pl/messages.json", + "_locales/pt_BR/messages.json", + "_locales/pt_PT/messages.json", + "_locales/ro/messages.json", + "_locales/ru/messages.json", + "_locales/sk/messages.json", + "_locales/sl/messages.json", + "_locales/sr/messages.json", + "_locales/sv/messages.json", + "_locales/th/messages.json", + "_locales/tr/messages.json", + "_locales/uk/messages.json", + "_locales/vi/messages.json", + "_locales/zh_CN/messages.json", + "_locales/zh_TW/messages.json", + "background.html", + "manifest.json", "options.html" - ], - "source_hash": "030b77992ed5bbbbc18f1b717bc330b965b26aaf", + ], + "source_hash": "030b77992ed5bbbbc18f1b717bc330b965b26aaf", "zip_path": "examples/extensions/gmail.zip" - }, + }, { "api_calls": [ - "chrome.extension.getBackgroundPage", - "chrome.extension.getURL", - "chrome.tabs.create", - "chrome.tabs.get", - "chrome.tabs.getSelected", - "chrome.tabs.onUpdated", + "chrome.extension.getBackgroundPage", + "chrome.extension.getURL", + "chrome.tabs.create", + "chrome.tabs.get", + "chrome.tabs.getSelected", + "chrome.tabs.onUpdated", "chrome.tabs.remove" - ], - "description": "Find out when you have new waves and preview them fast.", + ], + "description": "Find out when you have new waves and preview them fast.", "features": [ - "background_page", - "browser_action", - "options_page", - "popup", + "background_page", + "browser_action", + "options_page", + "popup", "tabs" - ], - "icon": "128.png", - "id": "1682e05ea9a1bde985123b04f6f8ac50a8a64033", - "name": "Google Wave Notifier", - "path": "examples/extensions/wave/", + ], + "icon": "128.png", + "id": "1682e05ea9a1bde985123b04f6f8ac50a8a64033", + "name": "Google Wave Notifier", + "path": "examples/extensions/wave/", "protocols": [ - "https://", + "https://", "http://" - ], - "search_string": "GOOGLE WAVE NOTIFIER FIND OUT WHEN YOU HAVE NEW WAVES AND PREVIEW THEM FAST. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE", + ], + "search_string": "GOOGLE WAVE NOTIFIER FIND OUT WHEN YOU HAVE NEW WAVES AND PREVIEW THEM FAST. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE", "source_files": [ - "background.html", - "chrome_ex_oauth.html", - "chrome_ex_oauth.js", - "chrome_ex_oauthsimple.js", - "manifest.json", - "options.html", - "popup.html", + "background.html", + "chrome_ex_oauth.html", + "chrome_ex_oauth.js", + "chrome_ex_oauthsimple.js", + "manifest.json", + "options.html", + "popup.html", "prettyload.js" - ], - "source_hash": "9f360bf8772e8a23862d854ea088e0bec867ba02", + ], + "source_hash": "9f360bf8772e8a23862d854ea088e0bec867ba02", "zip_path": "examples/extensions/wave.zip" - }, + }, { - "api_calls": [], - "description": "The first extension that I made.", + "api_calls": [], + "description": "The first extension that I made.", "features": [ - "browser_action", + "browser_action", "popup" - ], - "icon": null, - "id": "14b9651fda4e57b2a5914ba73a779812201b750a", - "name": "Hello World", - "path": "examples/tutorials/getstarted/", + ], + "icon": null, + "id": "14b9651fda4e57b2a5914ba73a779812201b750a", + "name": "Hello World", + "path": "examples/tutorials/getstarted/", "protocols": [ "http://" - ], - "search_string": "HELLO WORLD THE FIRST EXTENSION THAT I MADE. BROWSER_ACTION POPUP", + ], + "search_string": "HELLO WORLD THE FIRST EXTENSION THAT I MADE. BROWSER_ACTION POPUP", "source_files": [ - "manifest.json", + "manifest.json", "popup.html" - ], - "source_hash": "1a3139dcb7f3e3499023703643e7056c61235123", + ], + "source_hash": "1a3139dcb7f3e3499023703643e7056c61235123", "zip_path": "examples/tutorials/getstarted.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", - "chrome.extension.getBackgroundPage", - "chrome.idle.onStateChanged", + "chrome.browserAction.onClicked", + "chrome.extension.getBackgroundPage", + "chrome.idle.onStateChanged", "chrome.idle.queryState" - ], - "description": "Demonstrates the Idle API", + ], + "description": "Demonstrates the Idle API", "features": [ - "background_page", - "browser_action", + "background_page", + "browser_action", "idle" - ], - "icon": "sample-128.png", - "id": "2020d72f2577f53caf8e94e3dbac0fb849ceaa4d", - "name": "Idle - Simple Example", - "path": "examples/api/idle/idle_simple/", - "protocols": [], - "search_string": "IDLE - SIMPLE EXAMPLE DEMONSTRATES THE IDLE API BACKGROUND_PAGE BROWSER_ACTION IDLE CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.IDLE.ONSTATECHANGED CHROME.IDLE.QUERYSTATE", + ], + "icon": "sample-128.png", + "id": "2020d72f2577f53caf8e94e3dbac0fb849ceaa4d", + "name": "Idle - Simple Example", + "path": "examples/api/idle/idle_simple/", + "protocols": [], + "search_string": "IDLE - SIMPLE EXAMPLE DEMONSTRATES THE IDLE API BACKGROUND_PAGE BROWSER_ACTION IDLE CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.IDLE.ONSTATECHANGED CHROME.IDLE.QUERYSTATE", "source_files": [ - "background.html", - "history.html", + "background.html", + "history.html", "manifest.json" - ], - "source_hash": "1378042fee96e115d7b8003588eca369b43f772b", + ], + "source_hash": "1378042fee96e115d7b8003588eca369b43f772b", "zip_path": "examples/api/idle/idle_simple.zip" - }, + }, { - "api_calls": [], - "description": "", + "api_calls": [], + "description": "", "features": [ "chrome_url_overrides" - ], - "icon": null, - "id": "0ea1588bd07b20338fc21f725de1542a5fdf9726", - "name": "iGoogle new tab page", - "path": "examples/api/override/override_igoogle/", - "protocols": [], - "search_string": "IGOOGLE NEW TAB PAGE CHROME_URL_OVERRIDES", + ], + "icon": null, + "id": "0ea1588bd07b20338fc21f725de1542a5fdf9726", + "name": "iGoogle new tab page", + "path": "examples/api/override/override_igoogle/", + "protocols": [], + "search_string": "IGOOGLE NEW TAB PAGE CHROME_URL_OVERRIDES", "source_files": [ - "manifest.json", + "manifest.json", "redirect.html" - ], - "source_hash": "8ee76608adbf87c3260f9905e6cb1c8a45bd8e0c", + ], + "source_hash": "8ee76608adbf87c3260f9905e6cb1c8a45bd8e0c", "zip_path": "examples/api/override/override_igoogle.zip" - }, + }, { "api_calls": [ - "chrome.contextMenus.create", - "chrome.tabs.get", - "chrome.tabs.getCurrent", - "chrome.windows.create", + "chrome.contextMenus.create", + "chrome.tabs.get", + "chrome.tabs.getCurrent", + "chrome.windows.create", "chrome.windows.update" - ], - "description": "Get image info for images, including EXIF data", + ], + "description": "Get image info for images, including EXIF data", "features": [ - "background_page", - "contextMenus", + "background_page", + "contextMenus", "tabs" - ], - "icon": "imageinfo-128.png", - "id": "646325c25f572a1d15edc73d057f821d847a4fbe", - "name": "Imageinfo", - "path": "examples/extensions/imageinfo/", + ], + "icon": "imageinfo-128.png", + "id": "646325c25f572a1d15edc73d057f821d847a4fbe", + "name": "Imageinfo", + "path": "examples/extensions/imageinfo/", "protocols": [ - "http://", + "http://", "https://" - ], - "search_string": "IMAGEINFO GET IMAGE INFO FOR IMAGES, INCLUDING EXIF DATA BACKGROUND_PAGE CONTEXTMENUS TABS CHROME.CONTEXTMENUS.CREATE CHROME.TABS.GET CHROME.TABS.GETCURRENT CHROME.WINDOWS.CREATE CHROME.WINDOWS.UPDATE", + ], + "search_string": "IMAGEINFO GET IMAGE INFO FOR IMAGES, INCLUDING EXIF DATA BACKGROUND_PAGE CONTEXTMENUS TABS CHROME.CONTEXTMENUS.CREATE CHROME.TABS.GET CHROME.TABS.GETCURRENT CHROME.WINDOWS.CREATE CHROME.WINDOWS.UPDATE", "source_files": [ - "background.html", - "imageinfo/binaryajax.js", - "imageinfo/exif.js", - "imageinfo/imageinfo.js", - "info.html", + "background.html", + "imageinfo/binaryajax.js", + "imageinfo/exif.js", + "imageinfo/imageinfo.js", + "info.html", "manifest.json" - ], - "source_hash": "c746d9114348f4b414c1ec05e988e2807feb963a", + ], + "source_hash": "c746d9114348f4b414c1ec05e988e2807feb963a", "zip_path": "examples/extensions/imageinfo.zip" - }, + }, { "api_calls": [ - "chrome.extension.getBackgroundPage", - "chrome.extension.onRequest", - "chrome.pageAction.hide", - "chrome.pageAction.setTitle", - "chrome.pageAction.show", - "chrome.tabs.get", - "chrome.tabs.getSelected", - "chrome.tabs.onSelectionChanged", - "chrome.tabs.onUpdated", + "chrome.extension.onRequest", + "chrome.extension.sendRequest", + "chrome.tabs.get", + "chrome.tabs.getSelected", + "chrome.tabs.update" + ], + "description": "Creates a keyboard shortcut (C + Shift + P) to toggle the pinned state of the currently selected tab", + "features": [ + "background_page", + "tabs" + ], + "icon": null, + "id": "65b2fe595b7ac38dddd89cca50f5efd0017b0bd4", + "name": "Keyboard Pin", + "path": "examples/api/tabs/pin/", + "protocols": [], + "search_string": "KEYBOARD PIN CREATES A KEYBOARD SHORTCUT (C + SHIFT + P) TO TOGGLE THE PINNED STATE OF THE CURRENTLY SELECTED TAB BACKGROUND_PAGE TABS CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE", + "source_files": [ + "background.html", + "inject.js", + "manifest.json" + ], + "source_hash": "e7e86d83a781a8c575641cc11136bd42e15d0f67", + "zip_path": "examples/api/tabs/pin.zip" + }, + { + "api_calls": [ + "chrome.extension.getBackgroundPage", + "chrome.extension.onRequest", + "chrome.pageAction.hide", + "chrome.pageAction.setTitle", + "chrome.pageAction.show", + "chrome.tabs.get", + "chrome.tabs.getSelected", + "chrome.tabs.onSelectionChanged", + "chrome.tabs.onUpdated", "chrome.tabs.sendRequest" - ], - "description": "Finds addresses in the web page you're on and pops up a map window.", + ], + "description": "Finds addresses in the web page you're on and pops up a map window.", "features": [ - "background_page", - "page_action", - "popup", + "background_page", + "page_action", + "popup", "tabs" - ], - "icon": "icon.png", - "id": "ec97ec20ca2f095d081e39f1565fc12af09ef067", - "name": "Mappy", - "path": "examples/extensions/mappy/", + ], + "icon": "icon.png", + "id": "ec97ec20ca2f095d081e39f1565fc12af09ef067", + "name": "Mappy", + "path": "examples/extensions/mappy/", "protocols": [ "http://" - ], - "search_string": "MAPPY FINDS ADDRESSES IN THE WEB PAGE YOURE ON AND POPS UP A MAP WINDOW. BACKGROUND_PAGE PAGE_ACTION POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.SENDREQUEST", + ], + "search_string": "MAPPY FINDS ADDRESSES IN THE WEB PAGE YOURE ON AND POPS UP A MAP WINDOW. BACKGROUND_PAGE PAGE_ACTION POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.SENDREQUEST", "source_files": [ - "background.html", - "manifest.json", - "mappy_content_script.js", + "background.html", + "manifest.json", + "mappy_content_script.js", "popup.html" - ], - "source_hash": "81cf2d3975d7df8b58e5226c5b2b6df026446511", + ], + "source_hash": "81cf2d3975d7df8b58e5226c5b2b6df026446511", "zip_path": "examples/extensions/mappy.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", - "chrome.tabs.get", - "chrome.tabs.getAllInWindow", - "chrome.tabs.move", - "chrome.windows.get", - "chrome.windows.getAll", + "chrome.browserAction.onClicked", + "chrome.tabs.get", + "chrome.tabs.getAllInWindow", + "chrome.tabs.move", + "chrome.windows.get", + "chrome.windows.getAll", "chrome.windows.getCurrent" - ], - "description": "Merges all of the browser's windows into the current window", + ], + "description": "Merges all of the browser's windows into the current window", "features": [ - "background_page", - "browser_action", + "background_page", + "browser_action", "tabs" - ], - "icon": "merge_windows_128.png", - "id": "b2f5f8a790e16f091a7e4e0a39b2d0a6d32e3a6d", - "name": "Merge Windows", - "path": "examples/api/windows/merge_windows/", - "protocols": [], - "search_string": "MERGE WINDOWS MERGES ALL OF THE BROWSERS WINDOWS INTO THE CURRENT WINDOW BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.MOVE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT", + ], + "icon": "merge_windows_128.png", + "id": "b2f5f8a790e16f091a7e4e0a39b2d0a6d32e3a6d", + "name": "Merge Windows", + "path": "examples/api/windows/merge_windows/", + "protocols": [], + "search_string": "MERGE WINDOWS MERGES ALL OF THE BROWSERS WINDOWS INTO THE CURRENT WINDOW BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.MOVE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT", "source_files": [ - "background.html", + "background.html", "manifest.json" - ], - "source_hash": "4b5fe52788e0bef2f3871b36105eb53cc760c454", + ], + "source_hash": "4b5fe52788e0bef2f3871b36105eb53cc760c454", "zip_path": "examples/api/windows/merge_windows.zip" - }, + }, { "api_calls": [ - "chrome.extension.onConnect", - "chrome.extension.onRequest", - "chrome.tabs.connect", - "chrome.tabs.get", - "chrome.tabs.getSelected", + "chrome.extension.onConnect", + "chrome.extension.onRequest", + "chrome.tabs.connect", + "chrome.tabs.get", + "chrome.tabs.getSelected", "chrome.tabs.sendRequest" - ], - "description": "Times how long it takes to send a message to a content script and back.", + ], + "description": "Times how long it takes to send a message to a content script and back.", "features": [ - "browser_action", - "popup", + "browser_action", + "popup", "tabs" - ], - "icon": null, - "id": "51a83d2ba3a32e3ff1bdb624d4e18ccec4c4038e", - "name": "Message Timer", - "path": "examples/api/messaging/timer/", - "protocols": [], - "search_string": "MESSAGE TIMER TIMES HOW LONG IT TAKES TO SEND A MESSAGE TO A CONTENT SCRIPT AND BACK. BROWSER_ACTION POPUP TABS CHROME.EXTENSION.ONCONNECT CHROME.EXTENSION.ONREQUEST CHROME.TABS.CONNECT CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.SENDREQUEST", + ], + "icon": null, + "id": "51a83d2ba3a32e3ff1bdb624d4e18ccec4c4038e", + "name": "Message Timer", + "path": "examples/api/messaging/timer/", + "protocols": [], + "search_string": "MESSAGE TIMER TIMES HOW LONG IT TAKES TO SEND A MESSAGE TO A CONTENT SCRIPT AND BACK. BROWSER_ACTION POPUP TABS CHROME.EXTENSION.ONCONNECT CHROME.EXTENSION.ONREQUEST CHROME.TABS.CONNECT CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.SENDREQUEST", "source_files": [ - "manifest.json", - "page.js", + "manifest.json", + "page.js", "popup.html" - ], - "source_hash": "927cf398a95a665beb64f56a4bfb791b98a8ee96", + ], + "source_hash": "927cf398a95a665beb64f56a4bfb791b98a8ee96", "zip_path": "examples/api/messaging/timer.zip" - }, + }, { "api_calls": [ - "chrome.bookmarks.create", - "chrome.bookmarks.get", - "chrome.bookmarks.getTree", - "chrome.bookmarks.remove", - "chrome.bookmarks.update", + "chrome.bookmarks.create", + "chrome.bookmarks.get", + "chrome.bookmarks.getTree", + "chrome.bookmarks.remove", + "chrome.bookmarks.update", "chrome.tabs.create" - ], - "description": "A browser action with a popup dump of all bookmarks, including search, add, edit and delete.", + ], + "description": "A browser action with a popup dump of all bookmarks, including search, add, edit and delete.", "features": [ - "bookmarks", - "browser_action", - "popup", + "bookmarks", + "browser_action", + "popup", "tabs" - ], - "icon": null, - "id": "4f6785ec4f937add6728615682dd37c9a42d9548", - "name": "My Bookmarks", - "path": "examples/api/bookmarks/basic/", - "protocols": [], - "search_string": "MY BOOKMARKS A BROWSER ACTION WITH A POPUP DUMP OF ALL BOOKMARKS, INCLUDING SEARCH, ADD, EDIT AND DELETE. BOOKMARKS BROWSER_ACTION POPUP TABS CHROME.BOOKMARKS.CREATE CHROME.BOOKMARKS.GET CHROME.BOOKMARKS.GETTREE CHROME.BOOKMARKS.REMOVE CHROME.BOOKMARKS.UPDATE CHROME.TABS.CREATE", + ], + "icon": null, + "id": "4f6785ec4f937add6728615682dd37c9a42d9548", + "name": "My Bookmarks", + "path": "examples/api/bookmarks/basic/", + "protocols": [], + "search_string": "MY BOOKMARKS A BROWSER ACTION WITH A POPUP DUMP OF ALL BOOKMARKS, INCLUDING SEARCH, ADD, EDIT AND DELETE. BOOKMARKS BROWSER_ACTION POPUP TABS CHROME.BOOKMARKS.CREATE CHROME.BOOKMARKS.GET CHROME.BOOKMARKS.GETTREE CHROME.BOOKMARKS.REMOVE CHROME.BOOKMARKS.UPDATE CHROME.TABS.CREATE", "source_files": [ - "manifest.json", + "manifest.json", "popup.html" - ], - "source_hash": "5fd4c8e159a36d2462e9691db12d77a27684d6b7", + ], + "source_hash": "5fd4c8e159a36d2462e9691db12d77a27684d6b7", "zip_path": "examples/api/bookmarks/basic.zip" - }, + }, { "api_calls": [ "chrome.tabs.create" - ], - "description": "Displays the first 5 items from the 'Google News - top news' RSS feed in a popup.", + ], + "description": "Displays the first 5 items from the 'Google News - top news' RSS feed in a popup.", "features": [ - "browser_action", - "popup", + "browser_action", + "popup", "tabs" - ], - "icon": "news_icon.png", - "id": "597015d3bcce3da693b02314afd607bec4f55291", - "name": "News Reader", - "path": "examples/extensions/news_a11y/", + ], + "icon": "news_icon.png", + "id": "597015d3bcce3da693b02314afd607bec4f55291", + "name": "News Reader", + "path": "examples/extensions/news_a11y/", "protocols": [ "http://" - ], - "search_string": "NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE", + ], + "search_string": "NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE", "source_files": [ - "feed.html", + "feed.html", "manifest.json" - ], - "source_hash": "af7474bf0d3ef1a407f27ae0900167a1408ead35", + ], + "source_hash": "af7474bf0d3ef1a407f27ae0900167a1408ead35", "zip_path": "examples/extensions/news_a11y.zip" - }, + }, { "api_calls": [ - "chrome.i18n.getMessage", + "chrome.i18n.getMessage", "chrome.tabs.create" - ], - "description": "Displays the first 5 items from the 'Google News - top news' RSS feed in a popup.", + ], + "description": "Displays the first 5 items from the 'Google News - top news' RSS feed in a popup.", "features": [ - "browser_action", - "popup", + "browser_action", + "popup", "tabs" - ], - "icon": "news_icon.png", - "id": "6444e5c8ae112a6a433909c5e770669cd16e2e5f", - "name": "News Reader", - "path": "examples/extensions/news_i18n/", + ], + "icon": "news_icon.png", + "id": "6444e5c8ae112a6a433909c5e770669cd16e2e5f", + "name": "News Reader", + "path": "examples/extensions/news_i18n/", "protocols": [ - "http://", + "http://", "http://" - ], - "search_string": "NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE", + ], + "search_string": "NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE", "source_files": [ - "_locales/en/messages.json", - "_locales/es/messages.json", - "_locales/sr/messages.json", - "feed.html", + "_locales/en/messages.json", + "_locales/es/messages.json", + "_locales/sr/messages.json", + "feed.html", "manifest.json" - ], - "source_hash": "381268f1183beaeba8d6596e736dc2c00a55fd21", + ], + "source_hash": "381268f1183beaeba8d6596e736dc2c00a55fd21", "zip_path": "examples/extensions/news_i18n.zip" - }, + }, { "api_calls": [ - "chrome.extension.getURL", - "chrome.i18n.getMessage", + "chrome.extension.getURL", + "chrome.i18n.getMessage", "chrome.tabs.create" - ], - "description": "Displays the latest stories from Google News in a popup.", + ], + "description": "Displays the latest stories from Google News in a popup.", "features": [ - "background_page", - "browser_action", - "options_page", - "popup", + "background_page", + "browser_action", + "options_page", + "popup", "tabs" - ], - "icon": "images/news_icon.png", - "id": "3aea027164cb9b732ba4a8c51cb93708891726ef", - "name": "News Reader (by Google)", - "path": "examples/extensions/news/", + ], + "icon": "images/news_icon.png", + "id": "3aea027164cb9b732ba4a8c51cb93708891726ef", + "name": "News Reader (by Google)", + "path": "examples/extensions/news/", "protocols": [ "http://" - ], - "search_string": "NEWS READER (BY GOOGLE) DISPLAYS THE LATEST STORIES FROM GOOGLE NEWS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETURL CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE", + ], + "search_string": "NEWS READER (BY GOOGLE) DISPLAYS THE LATEST STORIES FROM GOOGLE NEWS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETURL CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE", "source_files": [ - "_locales/en/messages.json", - "css/feed.css", - "css/options.css", - "javascript/feed.js", - "javascript/options.js", - "javascript/util.js", - "manifest.json", - "views/background.html", - "views/feed.html", + "_locales/en/messages.json", + "css/feed.css", + "css/options.css", + "javascript/feed.js", + "javascript/options.js", + "javascript/util.js", + "manifest.json", + "views/background.html", + "views/feed.html", "views/options.html" - ], - "source_hash": "cc21920e101dd4d4c535f4842e3f0ab4be285166", + ], + "source_hash": "cc21920e101dd4d4c535f4842e3f0ab4be285166", "zip_path": "examples/extensions/news.zip" - }, + }, { "api_calls": [ "chrome.tabs.create" - ], - "description": "Shows off desktop notifications, which are \"toast\" windows that pop up on the desktop.", + ], + "description": "Shows off desktop notifications, which are \"toast\" windows that pop up on the desktop.", "features": [ - "background_page", - "notifications", - "options_page", + "background_page", + "notifications", + "options_page", "tabs" - ], - "icon": "128.png", - "id": "f799e26ceef2367cf836f24bcb47df4398b0df58", - "name": "Notification Demo", - "path": "examples/api/notifications/", - "protocols": [], - "search_string": "NOTIFICATION DEMO SHOWS OFF DESKTOP NOTIFICATIONS, WHICH ARE TOAST WINDOWS THAT POP UP ON THE DESKTOP. BACKGROUND_PAGE NOTIFICATIONS OPTIONS_PAGE TABS CHROME.TABS.CREATE", + ], + "icon": "128.png", + "id": "f799e26ceef2367cf836f24bcb47df4398b0df58", + "name": "Notification Demo", + "path": "examples/api/notifications/", + "protocols": [], + "search_string": "NOTIFICATION DEMO SHOWS OFF DESKTOP NOTIFICATIONS, WHICH ARE TOAST WINDOWS THAT POP UP ON THE DESKTOP. BACKGROUND_PAGE NOTIFICATIONS OPTIONS_PAGE TABS CHROME.TABS.CREATE", "source_files": [ - "background.html", - "error.html", - "manifest.json", + "background.html", + "error.html", + "manifest.json", "options.html" - ], - "source_hash": "bc2985ef75d717779cb6e1e523a3e063067c3494", + ], + "source_hash": "bc2985ef75d717779cb6e1e523a3e063067c3494", "zip_path": "examples/api/notifications.zip" - }, + }, { "api_calls": [ - "chrome.omnibox.onInputChanged", + "chrome.omnibox.onInputChanged", "chrome.omnibox.onInputEntered" - ], - "description": "To use, type 'omnix' plus a search term into the Omnibox.", + ], + "description": "To use, type 'omnix' plus a search term into the Omnibox.", "features": [ "background_page" - ], - "icon": null, - "id": "e787b322bddbc6289bb31b7d7550b1bf6456a80b", - "name": "Omnibox Example", - "path": "examples/api/omnibox/simple-example/", - "protocols": [], - "search_string": "OMNIBOX EXAMPLE TO USE, TYPE OMNIX PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED", + ], + "icon": null, + "id": "e787b322bddbc6289bb31b7d7550b1bf6456a80b", + "name": "Omnibox Example", + "path": "examples/api/omnibox/simple-example/", + "protocols": [], + "search_string": "OMNIBOX EXAMPLE TO USE, TYPE OMNIX PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE CHROME.OMNIBOX.ONINPUTCHANGED CHROME.OMNIBOX.ONINPUTENTERED", "source_files": [ - "background.html", + "background.html", "manifest.json" - ], - "source_hash": "21f142aad0637086ec08923a11ce3dee70e42bc6", + ], + "source_hash": "21f142aad0637086ec08923a11ce3dee70e42bc6", "zip_path": "examples/api/omnibox/simple-example.zip" - }, + }, { "api_calls": [ - "chrome.extension.onRequest", - "chrome.extension.sendRequest", + "chrome.extension.onRequest", + "chrome.extension.sendRequest", "chrome.pageAction.show" - ], - "description": "Shows a page action for HTML pages containing the word 'sandwich'", + ], + "description": "Shows a page action for HTML pages containing the word 'sandwich'", "features": [ - "background_page", + "background_page", "page_action" - ], - "icon": "sandwich-128.png", - "id": "8d0a50b57c26bb498be592e871001ffed91541b4", - "name": "Page action by content", - "path": "examples/api/pageAction/pageaction_by_content/", - "protocols": [], - "search_string": "PAGE ACTION BY CONTENT SHOWS A PAGE ACTION FOR HTML PAGES CONTAINING THE WORD SANDWICH BACKGROUND_PAGE PAGE_ACTION CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.PAGEACTION.SHOW", + ], + "icon": "sandwich-128.png", + "id": "8d0a50b57c26bb498be592e871001ffed91541b4", + "name": "Page action by content", + "path": "examples/api/pageAction/pageaction_by_content/", + "protocols": [], + "search_string": "PAGE ACTION BY CONTENT SHOWS A PAGE ACTION FOR HTML PAGES CONTAINING THE WORD SANDWICH BACKGROUND_PAGE PAGE_ACTION CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.PAGEACTION.SHOW", "source_files": [ - "background.html", - "contentscript.js", + "background.html", + "contentscript.js", "manifest.json" - ], - "source_hash": "0f4b881b1bc2e2fd6098fd219ca061b72a9654b7", + ], + "source_hash": "0f4b881b1bc2e2fd6098fd219ca061b72a9654b7", "zip_path": "examples/api/pageAction/pageaction_by_content.zip" - }, + }, { "api_calls": [ - "chrome.pageAction.show", + "chrome.pageAction.show", "chrome.tabs.onUpdated" - ], - "description": "Shows a page action for urls which have the letter 'g' in them.", + ], + "description": "Shows a page action for urls which have the letter 'g' in them.", "features": [ - "background_page", - "page_action", + "background_page", + "page_action", "tabs" - ], - "icon": "icon-128.png", - "id": "80b86ccc6e8520660fa591caa565826f0ed1b12c", - "name": "Page action by URL", - "path": "examples/api/pageAction/pageaction_by_url/", - "protocols": [], - "search_string": "PAGE ACTION BY URL SHOWS A PAGE ACTION FOR URLS WHICH HAVE THE LETTER G IN THEM. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.SHOW CHROME.TABS.ONUPDATED", + ], + "icon": "icon-128.png", + "id": "80b86ccc6e8520660fa591caa565826f0ed1b12c", + "name": "Page action by URL", + "path": "examples/api/pageAction/pageaction_by_url/", + "protocols": [], + "search_string": "PAGE ACTION BY URL SHOWS A PAGE ACTION FOR URLS WHICH HAVE THE LETTER G IN THEM. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.SHOW CHROME.TABS.ONUPDATED", "source_files": [ - "background.html", + "background.html", "manifest.json" - ], - "source_hash": "732ef0951e1d6ff4afedb884b0e63cb342bb1499", + ], + "source_hash": "732ef0951e1d6ff4afedb884b0e63cb342bb1499", "zip_path": "examples/api/pageAction/pageaction_by_url.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", - "chrome.browserAction.setBadgeBackgroundColor", - "chrome.browserAction.setBadgeText", - "chrome.browserAction.setTitle", - "chrome.extension.connect", - "chrome.extension.getBackgroundPage", - "chrome.extension.getExtensionTabs", - "chrome.extension.getURL", - "chrome.extension.onConnect", - "chrome.tabs.create", - "chrome.tabs.executeScript", - "chrome.tabs.get", - "chrome.tabs.getAllInWindow", - "chrome.tabs.getSelected", - "chrome.tabs.remove", - "chrome.tabs.update", - "chrome.windows.get", + "chrome.browserAction.onClicked", + "chrome.browserAction.setBadgeBackgroundColor", + "chrome.browserAction.setBadgeText", + "chrome.browserAction.setTitle", + "chrome.extension.connect", + "chrome.extension.getBackgroundPage", + "chrome.extension.getExtensionTabs", + "chrome.extension.getURL", + "chrome.extension.onConnect", + "chrome.tabs.create", + "chrome.tabs.executeScript", + "chrome.tabs.get", + "chrome.tabs.getAllInWindow", + "chrome.tabs.getSelected", + "chrome.tabs.remove", + "chrome.tabs.update", + "chrome.windows.get", "chrome.windows.getCurrent" - ], - "description": "Chromium Page Benchmarker.", + ], + "description": "Chromium Page Benchmarker.", "features": [ - "background_page", - "browser_action", - "options_page", + "background_page", + "browser_action", + "options_page", "tabs" - ], - "icon": null, - "id": "d74c3c18a1c1dd18b035149105a306f837c8823e", - "name": "Page Benchmarker", - "path": "examples/extensions/benchmark/", + ], + "icon": null, + "id": "d74c3c18a1c1dd18b035149105a306f837c8823e", + "name": "Page Benchmarker", + "path": "examples/extensions/benchmark/", "protocols": [ - "https://", + "https://", "http://" - ], - "search_string": "PAGE BENCHMARKER CHROMIUM PAGE BENCHMARKER. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.CONNECT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETEXTENSIONTABS CHROME.EXTENSION.GETURL CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETCURRENT", + ], + "search_string": "PAGE BENCHMARKER CHROMIUM PAGE BENCHMARKER. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.CONNECT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETEXTENSIONTABS CHROME.EXTENSION.GETURL CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETCURRENT", "source_files": [ - "background.html", - "jquery/jquery-1.4.2.min.js", - "jquery/jquery-ui-1.8.4.custom.min.js", - "jquery/jquery.client.js", - "jquery/jquery.flot.dashes.js", - "jquery/jquery.flot.js", - "jquery/jquery.flot.min.js", - "jquery/jquery.flot.navigate.js", - "jquery/jquery.flot.valuelabels.js", - "jst/jsevalcontext.js", - "jst/jstemplate.js", - "jst/jstemplate_test.js", - "jst/util.js", - "manifest.json", - "options.html", - "script.js", - "util/sorttable.js", + "background.html", + "jquery/jquery-1.4.2.min.js", + "jquery/jquery-ui-1.8.4.custom.min.js", + "jquery/jquery.client.js", + "jquery/jquery.flot.dashes.js", + "jquery/jquery.flot.js", + "jquery/jquery.flot.min.js", + "jquery/jquery.flot.navigate.js", + "jquery/jquery.flot.valuelabels.js", + "jst/jsevalcontext.js", + "jst/jstemplate.js", + "jst/jstemplate_test.js", + "jst/util.js", + "manifest.json", + "options.html", + "script.js", + "util/sorttable.js", "util/table2CSV.js" - ], - "source_hash": "7e592dbd3446353f7d98d1760f7cd773035aaaad", + ], + "source_hash": "7e592dbd3446353f7d98d1760f7cd773035aaaad", "zip_path": "examples/extensions/benchmark.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", + "chrome.browserAction.onClicked", "chrome.tabs.update" - ], - "description": "Adds a print button to the browser.", + ], + "description": "Adds a print button to the browser.", "features": [ - "background_page", - "browser_action", + "background_page", + "browser_action", "tabs" - ], - "icon": null, - "id": "e6ae17ab4ccfd7e059c8c01f25760ca5d894c7fd", - "name": "Print this page", - "path": "examples/api/browserAction/print/", + ], + "icon": null, + "id": "e6ae17ab4ccfd7e059c8c01f25760ca5d894c7fd", + "name": "Print this page", + "path": "examples/api/browserAction/print/", "protocols": [ - "http://", + "http://", "https://" - ], - "search_string": "PRINT THIS PAGE ADDS A PRINT BUTTON TO THE BROWSER. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.UPDATE", + ], + "search_string": "PRINT THIS PAGE ADDS A PRINT BUTTON TO THE BROWSER. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.UPDATE", "source_files": [ - "background.html", + "background.html", "manifest.json" - ], - "source_hash": "be980117222f6b041bb012c5a0793040cef747b6", + ], + "source_hash": "be980117222f6b041bb012c5a0793040cef747b6", "zip_path": "examples/api/browserAction/print.zip" - }, + }, { "api_calls": [ "chrome.experimental.processes.onUpdated" - ], - "description": "Adds a browser action that monitors resource usage of all browser processes.", + ], + "description": "Adds a browser action that monitors resource usage of all browser processes.", "features": [ - "browser_action", - "experimental", - "popup", + "browser_action", + "experimental", + "popup", "tabs" - ], - "icon": null, - "id": "beff6ecd9677dea0a7c648c5042165b48bb66f09", - "name": "Process Monitor", - "path": "examples/api/processes/process_monitor/", - "protocols": [], - "search_string": "PROCESS MONITOR ADDS A BROWSER ACTION THAT MONITORS RESOURCE USAGE OF ALL BROWSER PROCESSES. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.ONUPDATED", + ], + "icon": null, + "id": "beff6ecd9677dea0a7c648c5042165b48bb66f09", + "name": "Process Monitor", + "path": "examples/api/processes/process_monitor/", + "protocols": [], + "search_string": "PROCESS MONITOR ADDS A BROWSER ACTION THAT MONITORS RESOURCE USAGE OF ALL BROWSER PROCESSES. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.ONUPDATED", "source_files": [ - "manifest.json", + "manifest.json", "popup.html" - ], - "source_hash": "a4d002a65d5ec54ef4495f8b5552a260119df739", + ], + "source_hash": "a4d002a65d5ec54ef4495f8b5552a260119df739", "zip_path": "examples/api/processes/process_monitor.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", - "chrome.browserAction.setIcon", - "chrome.extension.getBackgroundPage", - "chrome.extension.getURL", - "chrome.tabs.create", - "chrome.tabs.get", - "chrome.tabs.getSelected", - "chrome.tabs.onUpdated", + "chrome.browserAction.setBadgeBackgroundColor", + "chrome.browserAction.setBadgeText", + "chrome.browserAction.setTitle", + "chrome.experimental.proxy.onProxyError", + "chrome.extension.isAllowedIncognitoAccess", + "chrome.extension.onRequest", + "chrome.extension.sendRequest", + "chrome.i18n.getMessage" + ], + "description": "Set Chrome-specific proxies; a demonstration of Chrome's Proxy API", + "features": [ + "background_page", + "browser_action", + "experimental", + "popup", + "proxy" + ], + "icon": "icon128.png", + "id": "3e8e226d87e431296bb110b4f6eb7eec2ca7a826", + "name": "Proxy Settings", + "path": "examples/extensions/proxy_configuration/", + "protocols": [], + "search_string": "PROXY SETTINGS SET CHROME-SPECIFIC PROXIES; A DEMONSTRATION OF CHROMES PROXY API BACKGROUND_PAGE BROWSER_ACTION EXPERIMENTAL POPUP PROXY CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXPERIMENTAL.PROXY.ONPROXYERROR CHROME.EXTENSION.ISALLOWEDINCOGNITOACCESS CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.I18N.GETMESSAGE", + "source_files": [ + "_locales/en/messages.json", + "background.html", + "manifest.json", + "popup.html", + "proxy_error_handler.js", + "proxy_form_controller.js", + "test/jsunittest.js", + "test/proxy_form_controller_test.html", + "test/proxy_form_controller_test.js", + "test/unittest.css" + ], + "source_hash": "8f6768d78649cc4e69e595e7e43b3f06431c84e0", + "zip_path": "examples/extensions/proxy_configuration.zip" + }, + { + "api_calls": [ + "chrome.browserAction.onClicked", + "chrome.browserAction.setIcon", + "chrome.extension.getBackgroundPage", + "chrome.extension.getURL", + "chrome.tabs.create", + "chrome.tabs.get", + "chrome.tabs.getSelected", + "chrome.tabs.onUpdated", "chrome.tabs.remove" - ], - "description": "Uses OAuth to connect to Google's contacts service and display a list of your contacts.", + ], + "description": "Uses OAuth to connect to Google's contacts service and display a list of your contacts.", "features": [ - "background_page", - "browser_action", + "background_page", + "browser_action", "tabs" - ], - "icon": "img/icon-128.png", - "id": "56a8d2ac24ca7bba78fd88ad57f43fc13c784497", - "name": "Sample - OAuth Contacts", - "path": "examples/extensions/oauth_contacts/", + ], + "icon": "img/icon-128.png", + "id": "56a8d2ac24ca7bba78fd88ad57f43fc13c784497", + "name": "Sample - OAuth Contacts", + "path": "examples/extensions/oauth_contacts/", "protocols": [ - "http://", - "https://", - "https://", + "http://", + "https://", + "https://", "https://" - ], - "search_string": "SAMPLE - OAUTH CONTACTS USES OAUTH TO CONNECT TO GOOGLES CONTACTS SERVICE AND DISPLAY A LIST OF YOUR CONTACTS. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE", + ], + "search_string": "SAMPLE - OAUTH CONTACTS USES OAUTH TO CONNECT TO GOOGLES CONTACTS SERVICE AND DISPLAY A LIST OF YOUR CONTACTS. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE", "source_files": [ - "background.html", - "chrome_ex_oauth.html", - "chrome_ex_oauth.js", - "chrome_ex_oauthsimple.js", - "contacts.html", + "background.html", + "chrome_ex_oauth.html", + "chrome_ex_oauth.js", + "chrome_ex_oauthsimple.js", + "contacts.html", "manifest.json" - ], - "source_hash": "e9afbd588b1593c9d3e9b9612ac242c781871f34", + ], + "source_hash": "e9afbd588b1593c9d3e9b9612ac242c781871f34", "zip_path": "examples/extensions/oauth_contacts.zip" - }, + }, { "api_calls": [ - "chrome.experimental.infobars.show", - "chrome.extension.onRequest", + "chrome.experimental.infobars.show", + "chrome.extension.onRequest", "chrome.extension.sendRequest" - ], - "description": "Shows an infobar on pages which contain the word 'sandwich'", + ], + "description": "Shows an infobar on pages which contain the word 'sandwich'", "features": [ - "background_page", + "background_page", "experimental" - ], - "icon": "sandwich-128.png", - "id": "38f6e1e17756ede38b1364c7114a738ca717dcbb", - "name": "SandwichBar", - "path": "examples/api/infobars/sandwichbar/", - "protocols": [], - "search_string": "SANDWICHBAR SHOWS AN INFOBAR ON PAGES WHICH CONTAIN THE WORD SANDWICH BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.INFOBARS.SHOW CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST", + ], + "icon": "sandwich-128.png", + "id": "38f6e1e17756ede38b1364c7114a738ca717dcbb", + "name": "SandwichBar", + "path": "examples/api/infobars/sandwichbar/", + "protocols": [], + "search_string": "SANDWICHBAR SHOWS AN INFOBAR ON PAGES WHICH CONTAIN THE WORD SANDWICH BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.INFOBARS.SHOW CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST", "source_files": [ - "background.html", - "contentscript.js", - "infobar.html", + "background.html", + "contentscript.js", + "infobar.html", "manifest.json" - ], - "source_hash": "890d698634e5228ef7da8ffca3008f843b9a7cab", + ], + "source_hash": "890d698634e5228ef7da8ffca3008f843b9a7cab", "zip_path": "examples/api/infobars/sandwichbar.zip" - }, + }, { "api_calls": [ - "chrome.experimental.processes.getProcessIdForTab", - "chrome.tabs.get", - "chrome.tabs.getSelected", - "chrome.tabs.update", - "chrome.windows.get", - "chrome.windows.getAll", - "chrome.windows.getCurrent", + "chrome.experimental.processes.getProcessIdForTab", + "chrome.tabs.get", + "chrome.tabs.getSelected", + "chrome.tabs.update", + "chrome.windows.get", + "chrome.windows.getAll", + "chrome.windows.getCurrent", "chrome.windows.update" - ], - "description": "Adds a browser action showing which tabs share the current tab's process.", + ], + "description": "Adds a browser action showing which tabs share the current tab's process.", "features": [ - "browser_action", - "experimental", - "popup", + "browser_action", + "experimental", + "popup", "tabs" - ], - "icon": null, - "id": "fc89b35755483af30b66cd72cefa34a43a3e8312", - "name": "Show Tabs in Process", - "path": "examples/api/processes/show_tabs/", - "protocols": [], - "search_string": "SHOW TABS IN PROCESS ADDS A BROWSER ACTION SHOWING WHICH TABS SHARE THE CURRENT TABS PROCESS. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.GETPROCESSIDFORTAB CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.UPDATE", + ], + "icon": null, + "id": "fc89b35755483af30b66cd72cefa34a43a3e8312", + "name": "Show Tabs in Process", + "path": "examples/api/processes/show_tabs/", + "protocols": [], + "search_string": "SHOW TABS IN PROCESS ADDS A BROWSER ACTION SHOWING WHICH TABS SHARE THE CURRENT TABS PROCESS. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.GETPROCESSIDFORTAB CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.UPDATE", "source_files": [ - "manifest.json", + "manifest.json", "popup.html" - ], - "source_hash": "c9818c3c4c2e4fae0a7cc29588514e050356fd52", + ], + "source_hash": "c9818c3c4c2e4fae0a7cc29588514e050356fd52", "zip_path": "examples/api/processes/show_tabs.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", - "chrome.extension.getURL", - "chrome.tabs.create", - "chrome.tabs.get", - "chrome.tabs.getAllInWindow", - "chrome.tabs.getSelected", - "chrome.tabs.move", - "chrome.tabs.onAttached", - "chrome.tabs.onCreated", - "chrome.tabs.onDetached", - "chrome.tabs.onMoved", - "chrome.tabs.onRemoved", - "chrome.tabs.onSelectionChanged", - "chrome.tabs.onUpdated", - "chrome.tabs.remove", - "chrome.tabs.update", - "chrome.windows.create", - "chrome.windows.get", - "chrome.windows.getAll", - "chrome.windows.getCurrent", - "chrome.windows.getLastFocused", - "chrome.windows.onCreated", - "chrome.windows.onFocusChanged", - "chrome.windows.onRemoved", - "chrome.windows.remove", + "chrome.browserAction.onClicked", + "chrome.extension.getURL", + "chrome.tabs.create", + "chrome.tabs.get", + "chrome.tabs.getAllInWindow", + "chrome.tabs.getSelected", + "chrome.tabs.move", + "chrome.tabs.onAttached", + "chrome.tabs.onCreated", + "chrome.tabs.onDetached", + "chrome.tabs.onMoved", + "chrome.tabs.onRemoved", + "chrome.tabs.onSelectionChanged", + "chrome.tabs.onUpdated", + "chrome.tabs.remove", + "chrome.tabs.update", + "chrome.windows.create", + "chrome.windows.get", + "chrome.windows.getAll", + "chrome.windows.getCurrent", + "chrome.windows.getLastFocused", + "chrome.windows.onCreated", + "chrome.windows.onFocusChanged", + "chrome.windows.onRemoved", + "chrome.windows.remove", "chrome.windows.update" - ], - "description": "Utility for working with the extension tabs api", + ], + "description": "Utility for working with the extension tabs api", "features": [ - "background_page", - "browser_action", + "background_page", + "browser_action", "tabs" - ], - "icon": null, - "id": "230463f2d5c3d4d0ca13c230e1f00f2aae0a8a64", - "name": "Tab Inspector", - "path": "examples/api/tabs/inspector/", - "protocols": [], - "search_string": "TAB INSPECTOR UTILITY FOR WORKING WITH THE EXTENSION TABS API BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.MOVE CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.CREATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.GETLASTFOCUSED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED CHROME.WINDOWS.REMOVE CHROME.WINDOWS.UPDATE", + ], + "icon": null, + "id": "230463f2d5c3d4d0ca13c230e1f00f2aae0a8a64", + "name": "Tab Inspector", + "path": "examples/api/tabs/inspector/", + "protocols": [], + "search_string": "TAB INSPECTOR UTILITY FOR WORKING WITH THE EXTENSION TABS API BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.MOVE CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.CREATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.GETLASTFOCUSED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED CHROME.WINDOWS.REMOVE CHROME.WINDOWS.UPDATE", "source_files": [ - "background.html", - "jstemplate_compiled.js", - "manifest.json", + "background.html", + "jstemplate_compiled.js", + "manifest.json", "tabs_api.html" - ], - "source_hash": "3076b39a4302d8e86f456e6d7367129187cce0c0", + ], + "source_hash": "3076b39a4302d8e86f456e6d7367129187cce0c0", "zip_path": "examples/api/tabs/inspector.zip" - }, + }, { "api_calls": [ - "chrome.browserAction.onClicked", - "chrome.extension.getURL", - "chrome.extension.getViews", - "chrome.tabs.captureVisibleTab", - "chrome.tabs.create", + "chrome.browserAction.onClicked", + "chrome.extension.getURL", + "chrome.extension.getViews", + "chrome.tabs.captureVisibleTab", + "chrome.tabs.create", "chrome.tabs.onUpdated" - ], - "description": "Demonstrate screenshot functionality in the chrome.tabs api.", + ], + "description": "Demonstrate screenshot functionality in the chrome.tabs api.", "features": [ - "background_page", - "browser_action", + "background_page", + "browser_action", "tabs" - ], - "icon": null, - "id": "e1697cacebad05218798bf3e8a0f724517f0e8c3", - "name": "Test Screenshot Extension", - "path": "examples/api/tabs/screenshot/", - "protocols": [], - "search_string": "TEST SCREENSHOT EXTENSION DEMONSTRATE SCREENSHOT FUNCTIONALITY IN THE CHROME.TABS API. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.EXTENSION.GETVIEWS CHROME.TABS.CAPTUREVISIBLETAB CHROME.TABS.CREATE CHROME.TABS.ONUPDATED", + ], + "icon": null, + "id": "e1697cacebad05218798bf3e8a0f724517f0e8c3", + "name": "Test Screenshot Extension", + "path": "examples/api/tabs/screenshot/", + "protocols": [], + "search_string": "TEST SCREENSHOT EXTENSION DEMONSTRATE SCREENSHOT FUNCTIONALITY IN THE CHROME.TABS API. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.EXTENSION.GETVIEWS CHROME.TABS.CAPTUREVISIBLETAB CHROME.TABS.CREATE CHROME.TABS.ONUPDATED", "source_files": [ - "background.html", - "manifest.json", - "screenshot.html", + "background.html", + "manifest.json", + "screenshot.html", "screenshot.js" - ], - "source_hash": "6be9b92850e86ce311cc12a2cf0cda3b47ab5d58", + ], + "source_hash": "6be9b92850e86ce311cc12a2cf0cda3b47ab5d58", "zip_path": "examples/api/tabs/screenshot.zip" - }, + }, { "api_calls": [ - "chrome.history.getVisits", - "chrome.history.search", + "chrome.history.getVisits", + "chrome.history.search", "chrome.tabs.create" - ], - "description": "Reads your history, and shows the top ten pages you go to by typing the URL.", + ], + "description": "Reads your history, and shows the top ten pages you go to by typing the URL.", "features": [ - "browser_action", - "history", + "browser_action", + "history", "tabs" - ], - "icon": null, - "id": "b3de91ab04b7d7a2670ca7ee9d740eb42cead0b6", - "name": "Typed URL History", - "path": "examples/api/history/showHistory/", - "protocols": [], - "search_string": "TYPED URL HISTORY READS YOUR HISTORY, AND SHOWS THE TOP TEN PAGES YOU GO TO BY TYPING THE URL. BROWSER_ACTION HISTORY TABS CHROME.HISTORY.GETVISITS CHROME.HISTORY.SEARCH CHROME.TABS.CREATE", + ], + "icon": null, + "id": "b3de91ab04b7d7a2670ca7ee9d740eb42cead0b6", + "name": "Typed URL History", + "path": "examples/api/history/showHistory/", + "protocols": [], + "search_string": "TYPED URL HISTORY READS YOUR HISTORY, AND SHOWS THE TOP TEN PAGES YOU GO TO BY TYPING THE URL. BROWSER_ACTION HISTORY TABS CHROME.HISTORY.GETVISITS CHROME.HISTORY.SEARCH CHROME.TABS.CREATE", "source_files": [ - "manifest.json", - "typedUrls.html", + "manifest.json", + "typedUrls.html", "typedUrls.js" - ], - "source_hash": "72d5c3586feefc692c63039ce8bdb5f9d366c0e2", + ], + "source_hash": "72d5c3586feefc692c63039ce8bdb5f9d366c0e2", "zip_path": "examples/api/history/showHistory.zip" + }, + { + "api_calls": [ + "chrome.experimental.webNavigation.onBeforeNavigate", + "chrome.experimental.webNavigation.onBeforeRetarget", + "chrome.experimental.webNavigation.onCommitted", + "chrome.experimental.webNavigation.onCompleted", + "chrome.experimental.webNavigation.onErrorOccurred", + "chrome.extension.onRequest", + "chrome.extension.sendRequest", + "chrome.i18n.getMessage" + ], + "description": "Demonstration of the WebNavigation extension API.", + "features": [ + "background_page", + "browser_action", + "experimental", + "extension", + "popup", + "webNavigation" + ], + "icon": null, + "id": "a3c674f7eb93621c590562ad3fa04de4f03f53a0", + "name": "WebNavigation Tech Demo", + "path": "examples/api/webNavigation/basic/", + "protocols": [], + "search_string": "WEBNAVIGATION TECH DEMO DEMONSTRATION OF THE WEBNAVIGATION EXTENSION API. BACKGROUND_PAGE BROWSER_ACTION EXPERIMENTAL EXTENSION POPUP WEBNAVIGATION CHROME.EXPERIMENTAL.WEBNAVIGATION.ONBEFORENAVIGATE CHROME.EXPERIMENTAL.WEBNAVIGATION.ONBEFORERETARGET CHROME.EXPERIMENTAL.WEBNAVIGATION.ONCOMMITTED CHROME.EXPERIMENTAL.WEBNAVIGATION.ONCOMPLETED CHROME.EXPERIMENTAL.WEBNAVIGATION.ONERROROCCURRED CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.I18N.GETMESSAGE", + "source_files": [ + "_locales/en/messages.json", + "background.html", + "manifest.json", + "navigation_collector.js", + "popup.html" + ], + "source_hash": "efa16032a02c474f9c9bafb90a964fd57c667ea2", + "zip_path": "examples/api/webNavigation/basic.zip" + }, + { + "api_calls": [ + "chrome.extension.isAllowedFileSchemeAccess", + "chrome.extension.isAllowedIncognitoAccess" + ], + "description": "Demonstrates the `extension.isAllowed???Access` APIs", + "features": [ + "browser_action" + ], + "icon": "sample-128.png", + "id": "0fa4abe6545f0316fea7cfa07c53aa48c4f3d018", + "name": "`extension.isAllowed???Access` Example", + "path": "examples/api/extension/isAllowedAccess/", + "protocols": [ + "file://" + ], + "search_string": "`EXTENSION.ISALLOWED???ACCESS` EXAMPLE DEMONSTRATES THE `EXTENSION.ISALLOWED???ACCESS` APIS BROWSER_ACTION CHROME.EXTENSION.ISALLOWEDFILESCHEMEACCESS CHROME.EXTENSION.ISALLOWEDINCOGNITOACCESS", + "source_files": [ + "manifest.json", + "popup.html", + "sample.css" + ], + "source_hash": "c8bfc2b60f25c24dc3c5aaf75dd635c9419df66d", + "zip_path": "examples/api/extension/isAllowedAccess.zip" } ] }
\ No newline at end of file diff --git a/chrome/common/extensions/docs/static/api_index.html b/chrome/common/extensions/docs/static/api_index.html index 3273f08..60b98e2 100644 --- a/chrome/common/extensions/docs/static/api_index.html +++ b/chrome/common/extensions/docs/static/api_index.html @@ -20,21 +20,24 @@ Here are the supported chrome.* APIs: <h2 id="experimental">Experimental APIs</h2> <p> -Google Chrome also has some +Google Chrome also has <a href="experimental.html">experimental APIs</a>, -which give you access to -functionality such as process information and history. +some of which will become supported APIs +in future releases of Chrome. </p> <h2 id="conventions">API conventions</h2> <p> Unless the doc says otherwise, -methods in the chrome.* APIs are asynchronous: +methods in the chrome.* APIs are <b>asynchronous</b>: they return immediately, without waiting for the operation to finish. If you need to know the outcome of an operation, then you pass a callback function into the method. -For more information, see the video -<a href="http://www.youtube.com/watch?v=bmxr75CV36A&feature=PlayList&p=CA101D6A85FE9D4B&index=2">Extension API Design</a>. +For more information, watch this video: +</p> + +<p> +<iframe title="YouTube video player" width="640" height="390" src="http://www.youtube.com/embed/bmxr75CV36A?rel=0" frameborder="0" allowfullscreen></iframe> </p> diff --git a/chrome/common/extensions/docs/static/apps.html b/chrome/common/extensions/docs/static/apps.html index 2cf3944..394dfae 100644 --- a/chrome/common/extensions/docs/static/apps.html +++ b/chrome/common/extensions/docs/static/apps.html @@ -53,12 +53,14 @@ instead of a hosted app if any of the following are true: </ul> <p> -The following articles can help you understand +To learn more about the differences between web apps and websites, -extensions and packaged apps, and packaged apps and hosted apps: +extensions and packaged apps, and packaged apps and hosted apps, +read these: </p> <ul> + <li> <a href="http://code.google.com/chrome/webstore/docs/choosing.html">Choosing an App Type</a> </li> <li> <a href="http://code.google.com/chrome/apps/articles/thinking_in_web_apps.html">Thinking in Web Apps</a> </li> <li> <a href="http://code.google.com/chrome/webstore/articles/apps_vs_extensions.html">Extensions, Packaged Apps, and Hosted Apps in the Chrome Web Store</a> </li> </ul> @@ -88,11 +90,7 @@ Here is a typical manifest for a packaged app: "icons": { "16": "icon_16.png", "128": "icon_128.png" - }, - "permissions": [ - "unlimitedStorage", - "notifications" - ] + } } </pre> diff --git a/chrome/common/extensions/docs/static/bookmarks.html b/chrome/common/extensions/docs/static/bookmarks.html index 04a4376..8e9ecd5 100644 --- a/chrome/common/extensions/docs/static/bookmarks.html +++ b/chrome/common/extensions/docs/static/bookmarks.html @@ -30,8 +30,8 @@ For example:</p> <p> Bookmarks are organized in a tree, where each node in the tree -is either a bookmark or a group -(a folder that can contain nodes). +is either a bookmark or a folder +(sometimes called a <em>group</em>). Each node in the tree is represented by a <a href="#type-BookmarkTreeNode"><code>BookmarkTreeNode</code></a> object. @@ -49,6 +49,9 @@ See <a href="#type-BookmarkTreeNode"><code>BookmarkTreeNode</code></a> for information about the properties a node can have. </p> +<p class="note"><b>Note:</b> You cannot use this API to add or remove entries +in the root folder. You also cannot rename, move, or remove the special +"Bookmarks Bar" and "Other Bookmarks" folders.</p> <h2 id="overview-examples">Examples</h2> diff --git a/chrome/common/extensions/docs/static/content_scripts.html b/chrome/common/extensions/docs/static/content_scripts.html index 9cbf0c3..d5d210a 100644 --- a/chrome/common/extensions/docs/static/content_scripts.html +++ b/chrome/common/extensions/docs/static/content_scripts.html @@ -434,7 +434,7 @@ The first video describes content scripts and isolated worlds. </p> <p> -<object width="560" height="340"><param name="movie" value="http://www.youtube.com/v/laLudeUmXHM&hl=en_US&fs=1&"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/laLudeUmXHM&hl=en_US&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"></embed></object> +<iframe title="YouTube video player" width="640" height="390" src="http://www.youtube.com/embed/laLudeUmXHM?rel=0" frameborder="0" allowfullscreen></iframe> </p> <p> @@ -444,5 +444,5 @@ sending a request to its parent extension. </p> <p> -<object width="560" height="340"><param name="movie" value="http://www.youtube.com/v/B4M_a7xejYI&hl=en_US&fs=1&"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/B4M_a7xejYI&hl=en_US&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"></embed></object> +<iframe title="YouTube video player" width="640" height="390" src="http://www.youtube.com/embed/B4M_a7xejYI?rel=0" frameborder="0" allowfullscreen></iframe> </p> diff --git a/chrome/common/extensions/docs/static/docs.html b/chrome/common/extensions/docs/static/docs.html index 5dafa68..df0b761 100644 --- a/chrome/common/extensions/docs/static/docs.html +++ b/chrome/common/extensions/docs/static/docs.html @@ -5,8 +5,7 @@ This documentation tells you how to write extensions and packaged apps for the <a href="http://www.google.com/chrome">Google Chrome browser</a>. Because extensions came first, -the APIs and these docs say <em>extension</em> everywhere, -even though almost everything applies to packaged apps, as well. +the APIs and these docs say <em>extension</em> everywhere. </p> <p class="caution"> diff --git a/chrome/common/extensions/docs/static/experimental.debugger.html b/chrome/common/extensions/docs/static/experimental.debugger.html new file mode 100644 index 0000000..1794647 --- /dev/null +++ b/chrome/common/extensions/docs/static/experimental.debugger.html @@ -0,0 +1,24 @@ +<!-- BEGIN AUTHORED CONTENT --> + + +<h2>Notes</h2> + +<p> +Debugger API exposes Google Chrome debugging interface to the extensions. +Debugging messages that are being sent and received are all JSON objects +with the structure defined by the Developer Tools / Web Inspector Protocol. +We are currently drafting this protocol, it is by no means ready and stable, +but we can already expose the way to attach to a given tab and instrument it. +Although not finalized, this API is feature rich. There is a proof of concept +implementation of the Chrome Developer Tools front-end running as an extension +using the debugger API. +</p> + +<p> +Note that attaching to the page by means of the debugger API and using embedded +Chrome Developer Tools with it are mutually exclusive. If extension is attached +to the page and user invokes Developer Tools, debugging session is being +terminated. Extension can then re-establish it via attaching to a tab later. +</p> + +<!-- END AUTHORED CONTENT --> diff --git a/chrome/common/extensions/docs/static/experimental.webInspector.audits.html b/chrome/common/extensions/docs/static/experimental.webInspector.audits.html new file mode 100644 index 0000000..edd46f1 --- /dev/null +++ b/chrome/common/extensions/docs/static/experimental.webInspector.audits.html @@ -0,0 +1,65 @@ +<!-- BEGIN AUTHORED CONTENT --> +<p id="classSummary"> +Use the <code>experimental.webInspector.audits</code> module to add new audit +categories and rules to WebInspector's Audit panel. +</p><p> +See <a href="experimental.webInspector.html">WebInspector API summary</a> for +general introduction to using WebInspector API</a>. +</p> + +<h2>Notes</h2> + +<p> +Each audit category is represented by a line on <em>Select audits to run</em> +screen in the Audits panel. The following example adds a category named +<em>Readability</em>:</p> +<pre> +var category = webInspector.audits.addCategory("Readability", 2); +</pre> +<img src="images/devtools-audits-category.png" + style="margin-left: 20px" + width="489" height="342" + alt="Extension audit category on the launch screen of Audits panel" /> +<p> +If the category's checkbox is checked, the <code>onAuditStarted</code> event of +that category will be fired when user clicks the <em>Run</em> button. +</p> +<p>The event handler in your extension receives <code>AuditResults</code> +as an argument and should add one or more results using <code>addResult()</code> +method. This may be done asynchronously, i.e. after the handler returns. The +run of the category is considered to be complete once the extension adds the +number of results declared when adding the category with +<code>experimental.webInspector.audits.addCategory()</code> or +calls AuditResult's <code>done()</code> method. +</p> +<p>The results may include additional details visualized as an expandable +tree by the Audits panel. You may build the details tree using +<code>createResult()</code> and <code>addChild()</code> methods. The child node +may include specially formatted fragments created by +<code>auditResults.snippet()</code> or <code>auditResults.url()</code>. +</p> +The following example adds a handler for onAuditStarted event that creates two +audit results and populates one of them with the additional details: + +<pre> +category.onAuditStarted.addListener(function(results) { + var details = results.createResult("Details..."); + var styles = details.addChild("2 styles with small font"); + var elements = details.addChild("3 elements with small font"); + + results.addResult("Font Size (5)", + "5 elements use font size below 10pt", + results.Severity.Severe, + details); + results.addResult("Contrast", + "Text should stand out from background", + results.Severity.Info); +}); +</pre> +<p>The audit result tree produced by the snippet above will look like this: +</p> +<img src="images/devtools-audits-results.png" + style="margin-left: 20px" + width="330" height="169" + alt="Audit results example" /> +<!-- END AUTHORED CONTENT --> diff --git a/chrome/common/extensions/docs/static/experimental.webInspector.html b/chrome/common/extensions/docs/static/experimental.webInspector.html new file mode 100644 index 0000000..cd34927 --- /dev/null +++ b/chrome/common/extensions/docs/static/experimental.webInspector.html @@ -0,0 +1,77 @@ +<div id="pageData-name" class="pageData">experimental.webInspector.* APIs</div> + +<p> +The following API modules provide support for extending +Chrome Development Tools (aka WebInspector): +</p> + +<a name="api-list"></a> +<ul> + <li jsselect="webInspectorAPIs();"> + <a jsvalues=".href: $this + '.html'" jscontent="$this"></a></li> +</ul> + +<p class="warning"> +<b>Caution:</b> +Don't depend on these experimental APIs. They might disappear, and they +<em>will</em> change. +Also, the Chrome Developer Dashboard doesn't allow you to +upload extensions that use experimental APIs. +</p> + +<h2 id="using">How to use WebInspector APIs</h2> + +<ol> + <li> + WebInspector APIs are currently experimental, so please start with + <a href="experimental.html">the steps for using experimental extension + APIs</a>. + </li> + <li> + Specify the "devtools_page" field in your extension's manifest and make + sure you have "experimental" permission: +<pre> +{ + "name": ... + "version": "1.0", + "minimum_chrome_version": "10.0", + <b>"devtools_page": "devtools.html"</b>, + "permissions": [ <b>"experimental"</b> ... ], + ... +} +</pre> + </li> + <li> + An instance of the devtools_page specified in your extension's manifest + will be created for every Developer Tools window opened. The page may add + other extension pages as panels and sidebars to the Developer Tools window + using <a href="experimental.webInspector.panels" + >experimental.webInspector.panels</a> + API. + </li> + <li>The APIs available to extension pages within the Developer Tools + window include all <a href="#api-list">experimental.webInspector modules + listed above</a> and <a href="extension.html">chrome.extension</a> API. + Other extension APIs are not available to the Developer Tools pages, but + you may invoke them by sending a request to the background page of your + extension, similarly to how it's done in the + <a href="overview.html#contentScripts">content scripts</a>. + <li>Please note that, unlike other Chrome Extension APIs, the WebInspector + APIs lack "chrome" prefix. This is because the APIs, as the WebInspector + itself, are a part of WebKit and may eventually appear in other browsers. + </li> + <li> + <a href="http://groups.google.com/group/google-chrome-developer-tools/topics" + >Give us feedback!</a> + Your comments and suggestions help us improve the APIs and decide which + ones should move from experimental to supported. + </li> +</ol> + +<h2 id="other">More information</h2> + +<p> +For information on the standard APIs that extensions can use, see +<a href="api_index.html">chrome.* APIs</a> and +<a href="api_other.html">Other APIs</a>. +</p> diff --git a/chrome/common/extensions/docs/static/experimental.webInspector.panels.html b/chrome/common/extensions/docs/static/experimental.webInspector.panels.html new file mode 100644 index 0000000..9c719eb --- /dev/null +++ b/chrome/common/extensions/docs/static/experimental.webInspector.panels.html @@ -0,0 +1,50 @@ +<!-- BEGIN AUTHORED CONTENT --> +<p id="classSummary"> +Use the <code>experimental.webInspector.panels</code> module to integrate your +extension into Developer Tools window UI: create your own panels, access +existing panels and add sidebars. +</p><p> +See <a href="experimental.webInspector.html">WebInspector API summary</a> for +general introduction to using WebInspector API</a>. +</p> + +<h2>Notes</h2> + +<p> +Each extension panel and sidebar is displayed as a separate HTML page. All +extension pages displayed in the Developer Tools window have access to all +modules in <code>experimental.webInspector</code> API, as well as to +<a href="extension.html">chrome.extension</a> API. Other extension APIs are not +available to the pages within Developer Tools window, but you may invoke them +by sending a request to the background page of your extension, similarly to how +it's done in the <a href="overview.html#contentScripts">content scripts</a>. +</p> + +<h2 id="overview-examples">Examples</h2> +<p>The following code adds a panel contained in <code>Panel.html</code>, +represented by <code>FontPicker.png</code> on the Developer Tools toolbar +and labeled as <em>Font Picker</em>:</p> + +<pre> +webInspector.panels.create("Font Picker", "FontPicker.png", "Panel.html"); +</pre> +<p>The following code adds a sidebar pane contained in +<code>Sidebar.html</code> and titled <em>Font Properties</em> to the Elements +panel, then sets its height to <code>8ex</code>: +<pre> +webInspector.panels.elements.createSidebarPane("Font Properties", "Sidebar.html", + function(sidebar) { + sidebar.setHeight("8ex"); + } +})); +</pre> +<p> +This screenshot demonstrates the effect the above examples would have on +Developer Tools window: + +<img src="images/devtools-panels.png" + style="margin-left: 20px" + width="686" height="289" + alt="Extension icon panel on DevTools toolbar" /> +</p> +<!-- END AUTHORED CONTENT --> diff --git a/chrome/common/extensions/docs/static/experimental.webInspector.resources.html b/chrome/common/extensions/docs/static/experimental.webInspector.resources.html new file mode 100644 index 0000000..1d88718 --- /dev/null +++ b/chrome/common/extensions/docs/static/experimental.webInspector.resources.html @@ -0,0 +1,44 @@ +<!-- BEGIN AUTHORED CONTENT --> +<p id="classSummary"> +Use the <code>experimental.webInspector.resources</code> module to retrieve the +information about network resources displayed by DevTools' Network panel. +</p><p> +See <a href="experimental.webInspector.html">WebInspector API summary</a> for +general introduction to using WebInspector API</a>. +</p> + +<h2>Notes</h2> + +<p> +Network resource information is represented in HTTP Archive format +(<em>HAR</em>). The description of HAR is outside of scope of this document, +please refer to +<a href= +"http://groups.google.com/group/http-archive-specification/web/har-1-2-spec"> +HAR v1.2 Specification</a>. +</p><p> +In terms of HAR, the <code>webInspector.resources.getHAR()</code> method +returns entire <em>HAR log</em>, while +<code>webInspector.resources.onFinish</code> event provides <em>HAR entry</em> +as an argument to the event callback. +</p> +<p>Note that resource content is not provided as part of HAR for efficieny +reasons. You may call resource's <code>getContent()</code> method to retrieve +content. +<p>Some resources may be missing in the array of entries returned by <code> +getHAR()</code> in case WebInspector was opened after the page was loaded — +reload the page to get all resources. In general, the list of resources returned +by <code>getHAR()</code> should match that displayed by the Network panel. +<h2 id="overview-examples">Examples</h2> + +<p>The following code logs URLs of all images larger than 40KB as they are +loaded:</p> + +<pre> +experimental.webInspector.resources.onFinished.addListener(function(resource) { + if (resource.response.bodySize > 40*1024) + webInspector.log("Large image: " + resource.request.url); +}); +</pre> + +<!-- END AUTHORED CONTENT --> diff --git a/chrome/common/extensions/docs/static/experimental.webNavigation.html b/chrome/common/extensions/docs/static/experimental.webNavigation.html new file mode 100644 index 0000000..3f214ad --- /dev/null +++ b/chrome/common/extensions/docs/static/experimental.webNavigation.html @@ -0,0 +1,23 @@ +<div id="pageData-name" class="pageData">WebNavigation API</div> + +<!-- BEGIN AUTHORED CONTENT --> +<p id="classSummary"> +Use the <code>chrome.experimental.webNavigation</code> module to recieve +notifications about the status of navigations requests in-flight. This +module is still very much experimental. For information on how to use +experimental APIs, see the <a href="experimental.html">chrome.experimental.* +APIs</a> page. +</p> + +<h2>A note about timestamps</h2> +<p> +It's important to note that some technical oddities in the OS's handling +of distinct Chrome processes can cause the clock to be skewed between the +browser itself and extension processes. That means that WebNavigation's events' +<code>timeStamp</code> property is only guaranteed to be <i>internally</i> +consistent. Comparing one event to another event will give you the correct +offset between them, but comparing them to the current time inside the +extension (via <code>(new Date()).getTime()</code>, for instance) might give +unexpected results. +</p> +<!-- END AUTHORED CONTENT --> diff --git a/chrome/common/extensions/docs/static/experimental.webRequest.html b/chrome/common/extensions/docs/static/experimental.webRequest.html new file mode 100644 index 0000000..43cb6b1 --- /dev/null +++ b/chrome/common/extensions/docs/static/experimental.webRequest.html @@ -0,0 +1,22 @@ +<div id="pageData-name" class="pageData">WebRequest API</div> + +<!-- BEGIN AUTHORED CONTENT --> +<p id="classSummary"> +Use the <code>chrome.experimental.webRequest</code> module to intercept, block, +or modify requests in-flight. This module is still very much experimental. For +information on how to use experimental APIs, see the +<a href="experimental.html">chrome.experimental.* APIs</a> page. +</p> + +<h2>A note about timestamps</h2> +<p> +It's important to note that some technical oddities in the OS's handling +of distinct Chrome processes can cause the clock to be skewed between the +browser itself and extension processes. That means that WebRequest's events' +<code>timeStamp</code> property is only guaranteed to be <i>internally</i> +consistent. Comparing one event to another event will give you the correct +offset between them, but comparing them to the current time inside the +extension (via <code>(new Date()).getTime()</code>, for instance) might give +unexpected results. +</p> +<!-- END AUTHORED CONTENT --> diff --git a/chrome/common/extensions/docs/static/i18n.html b/chrome/common/extensions/docs/static/i18n.html index abe5c9e..0635545 100644 --- a/chrome/common/extensions/docs/static/i18n.html +++ b/chrome/common/extensions/docs/static/i18n.html @@ -293,8 +293,8 @@ padding-right: 1.5em; <h2 id="overview-locales">Locales</h2> <p> -Extensions can use all the locales that Google Chrome supports, -plus a few (such as <code>en</code>) +You can choose from many locales, +including some (such as <code>en</code>) that let a single translation support multiple variations of a language (such as <code>en_GB</code> and <code>en_US</code>). </p> @@ -303,12 +303,8 @@ that let a single translation support multiple variations of a language <h3 id="locales-supported">Supported locales</h3> <p> -Your extension can use any of the following locales: -</p> - -<p> -<code>am ar bg bn ca cs da de el en en_GB en_US es es_419 et fi fil fr gu he hi hr hu id it ja kn ko lt -lv ml mr nb nl or pl pt pt_BR pt_PT ro ru sk sl sr sv sw ta te th tr uk vi zh zh_CN zh_TW</code> +Extensions can use any of the +<a href="http://code.google.com/chrome/webstore/docs/i18n.html#localeTable">locales that the Chrome Web Store supports</a>. </p> diff --git a/chrome/common/extensions/docs/static/index.html b/chrome/common/extensions/docs/static/index.html index 079e37f..b72a91f 100644 --- a/chrome/common/extensions/docs/static/index.html +++ b/chrome/common/extensions/docs/static/index.html @@ -110,7 +110,7 @@ For more information, see the </p> <p> -<object width="300" height="250"><param name="movie" value="http://www.youtube.com/p/38DF05697DE372B1&hl=en_US&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/p/38DF05697DE372B1&hl=en_US&fs=1" type="application/x-shockwave-flash" width="300" height="250" allowscriptaccess="always" allowfullscreen="true"></embed></object> +<iframe title="YouTube video player" width="300" height="199" src="http://www.youtube.com/embed/wRDPTnY3yO8?rel=0" frameborder="0" allowfullscreen></iframe> </p> </td> </tr> diff --git a/chrome/common/extensions/docs/static/override.html b/chrome/common/extensions/docs/static/override.html index 83bbd5d..93f1c85 100644 --- a/chrome/common/extensions/docs/static/override.html +++ b/chrome/common/extensions/docs/static/override.html @@ -1,3 +1,6 @@ +<div id="pageData-name" class="pageData">Override Pages</div> +<div id="pageData-showTOC" class="pageData">true</div> + <style> #pics { margin:2em 1em 1.5em; @@ -18,9 +21,6 @@ } </style> -<div id="pageData-title" class="pageData">Override Pages</div> -<div id="pageData-showTOC" class="pageData">true</div> - <p> Override pages are a way to substitute an HTML file from your extension for a page that Google Chrome normally provides. @@ -60,19 +60,23 @@ An extension can replace any one of the following pages: <p class="note"> <b>Note:</b> A single extension can override -only one page. +<b>only one page</b>. +For example, an extension can't override both +the Bookmark Manager and History pages. </p> -<p class="note"> -<b>Note:</b> -If you want to override the page in incognito windows as well, make sure to -specify "spanning" mode for the <a href="manifest.html#incognito">incognito</a> -manifest property. -</p> - -<p class="note"> -<b>Note:</b> -You cannot override the New Tab page in incognito windows. +<p> +Incognito windows are treated specially. +New Tab pages cannot be overridden in incognito windows. +Other override pages work in incognito windows +as long as the +<a href="manifest.html#incognito">incognito</a> +manifest property is set to "spanning" +(which is the default value for extensions +but not for packaged apps). +See <a href="overview.html#incognito">Saving data and incognito mode</a> +in the Overview for more details on how you should treat +incognito windows. </p> <p> @@ -178,12 +182,6 @@ For an effective override page, follow these guidelines: <h2 id="examples"> Examples </h2> <p> -You can find simple examples of defining override pages in the -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/override/">examples/api/override</a> -directory. -For other examples and for help in viewing the source code, see -<a href="samples.html">Samples</a>. +See the +<a href="samples.html#chrome_url_overrides">override samples</a>. </p> - - -<!-- [PENDING: Maybe have a gallery of NTPs?] --> diff --git a/chrome/common/extensions/docs/static/overview.html b/chrome/common/extensions/docs/static/overview.html index 98ab568..dabc217 100644 --- a/chrome/common/extensions/docs/static/overview.html +++ b/chrome/common/extensions/docs/static/overview.html @@ -53,19 +53,19 @@ depending on the page. <table class="columns"> <tr> - <td> - <img src="images/index/gmail.png" - width="150" height="79" + <td width="33%"> + <img src="images/overview/browser-action.png" + width="147" height="100" alt="screenshot" /> </td> - <td> - <img src="images/index/news.png" - width="150" height="79" + <td width="33%"> + <img src="images/overview/page-action.png" + width="147" height="100" alt="screenshot" /> </td> <td> - <img src="images/index/rss.png" - width="150" height="79" + <img src="images/overview/browser-action-with-popup.png" + width="147" height="100" alt="screenshot" /> </td> </tr> @@ -77,18 +77,18 @@ depending on the page. (icon in the toolbar). </td> <td> - This <a href="samples.html#news">news reader extension</a> - features a browser action that, - when clicked, - shows a <em>popup</em>. - </td> - <td> This <a href="samples.html#mappy">map extension</a> uses a <em>page action</em> (icon in the address bar) and <em>content script</em> (code injected into a web page). </td> + <td> + This <a href="samples.html#news">news extension</a> + features a browser action that, + when clicked, + shows a <em>popup</em>. + </td> </tr> </table> @@ -113,7 +113,7 @@ For example, the following packaged app displays a Flash file within an HTML page. </p> -<img src="images/index/flashapp.png" +<img src="images/overview/flash-app.png" width="372" height="300" alt="screenshot" /> @@ -223,40 +223,91 @@ For details, see <h2 id="arch">Architecture</h2> <p> -Most extensions have a -<a href="background_pages.html">background page</a>, +Many extensions have a <em>background page</em>, an invisible page that holds the main logic of the extension. +An extension can also contain other pages +that present the extension's UI. +If an extension needs to interact with web pages that the user loads +(as opposed to pages that are included in the extension), +then the extension must use a content script. </p> -<img src="images/arch-1.gif" - width="232" height="168" - alt="Two windows and a box representing a background page (background.html). One window has a yellow icon; the other has both a yellow icon and a blue icon. The yellow icons are connected to the background page." /> + +<h3 id="background_page">The background page</h3> <p> -The preceding figure shows a browser +The following figure shows a browser that has at least two extensions installed: a browser action (yellow icon) and a page action (blue icon). -The browser action's background page, -which is defined by an HTML file -(<code>background.html</code>), -has JavaScript code that controls +Both the browser action and the page action +have background pages defined by HTML files. +This figure shows the browser action's background page, +which is defined by <code>background.html</code> +and has JavaScript code that controls the behavior of the browser action in both windows. </p> +<img src="images/overview/arch-1.gif" + width="232" height="168" + alt="Two windows and a box representing a background page (background.html). One window has a yellow icon; the other has both a yellow icon and a blue icon. The yellow icons are connected to the background page." /> + +<p> +Although background pages can be useful, +don't use one if you don't need it. +Background pages are always open, +so when a user installs many extensions that have background pages, +Chrome's performance can suffer. +</p> + +<!-- PENDING: Perhaps show a picture of many background page processes. + This could build on a figure that shows the process architecture, + and perhaps the differences between packaged apps and extensions. --> + +<p> +Here are some examples of extensions that usually +<b>do not need</b> a background page: +</p> + +<ul> + <li> An extension with a browser action that + presents its UI solely through a popup + (and perhaps an options page). + </li> + <li> + An extension that provides an <em>override page</em>—a + page that replaces a standard Chrome page. + </li> + <li> + An extension with a content script + that doesn't use cross-origin XMLHttpRequests or localStorage, + and that doesn't need to use + <a href="api_index.html">extension APIs</a>. + </li> + <li> + An extension that has no UI except for an options page. + </li> +</ul> + +<p> +See <a href="background_pages.html">Background Pages</a> +for more details. +</p> -<h3 id="pages">Pages</h3> +<h3 id="pages">UI pages</h3> <p> -The background page isn't the only HTML page -that an extension can have. +Extensions can contain ordinary HTML pages that display the extension's UI. For example, a browser action can have a popup, which is implemented by an HTML file. -Extensions can also -use <a href="tabs.html#Method-create"><code>chrome.tabs.create()</code></a> +Any extension can have an options page, +which lets users customize how the extension works. +Another type of special page is the override page. +And finally, you can +use <a href="tabs.html#method-create"><code>chrome.tabs.create()</code></a> or <code>window.open()</code> -to display HTML files that are in the extension. +to display any other HTML files that are in the extension. </p> <p> @@ -265,25 +316,32 @@ have complete access to each other's DOMs, and they can invoke functions on each other. </p> +<!-- PENDING: Change the following example and figure +to use something that's not a popup? +(It might lead people to think that popups need background pages.) --> + <p> The following figure shows the architecture of a browser action's popup. The popup's contents are a web page defined by an HTML file (<code>popup.html</code>). +This extension also happens to have a background page +(<code>background.html</code>). The popup doesn't need to duplicate code that's in the background page -(<code>background.html</code>) because the popup can invoke functions on the background page. </p> -<img src="images/arch-2.gif" +<img src="images/overview/arch-2.gif" width="256" height="168" alt="A browser window containing a browser action that's displaying a popup. The popup's HTML file (popup.html) can communicate with the extension's background page (background.html)." /> <p> -See the <a href="browserAction.html">Browser Actions</a> page and -the <a href="#pageComm">Communication between pages</a> section +See <a href="browserAction.html">Browser Actions</a>, +<a href="options.html">Options</a>, +<a href="override.html">Override Pages</a>, +and the <a href="#pageComm">Communication between pages</a> section for more details. </p> @@ -320,7 +378,7 @@ the DOM for the displayed web page. It cannot, however, modify the DOM of its parent extension's background page. </p> -<img src="images/arch-3.gif" +<img src="images/overview/arch-3.gif" width="238" height="169" alt="A browser window with a browser action (controlled by background.html) and a content script (controlled by contentscript.js)." /> @@ -334,7 +392,7 @@ Or a background page might send a message asking a content script to change the appearance of its browser page. </p> -<img src="images/arch-cs.gif" +<img src="images/overview/arch-cs.gif" width="238" height="194" alt="Like the previous figure, but showing more of the parent extension's files, as well as a communication path between the content script and the parent extension." /> diff --git a/chrome/common/extensions/docs/static/permission_warnings.html b/chrome/common/extensions/docs/static/permission_warnings.html index b70280f..9c02e08 100644 --- a/chrome/common/extensions/docs/static/permission_warnings.html +++ b/chrome/common/extensions/docs/static/permission_warnings.html @@ -3,7 +3,7 @@ <!-- NOTE: When this doc is updated, the online help should also be updated: -http://www.google.com/support/chrome/bin/answer.py?hl=en&answer=186213 +http://www.google.com/support/chrome_webstore/bin/answer.py?hl=en&answer=186213 We should periodically look at http://src.chromium.org/viewvc/chrome/trunk/src/chrome/app/generated_resources.grd?view=markup @@ -211,17 +211,24 @@ that trigger them. <!-- HasEffectiveAccessToAllHosts() --> Any of the following: <ul> - <li> "proxy" permission (experimental) </li> + <li> "proxy" permission </li> + <li> "debugger" permission </li> <li> A match pattern in the "permissions" field that matches all hosts </li> <li> A "content_scripts" field with a "matches" entry that matches all hosts </li> + <li> "devtools_page" (experimental) </li> </ul> </td> <td> <p> The "proxy" permission is required by the - <a href="http://code.google.com/chrome/extensions/dev/experimental.proxy.html">experimental proxy</a> module. + <a href="experimental.proxy.html">experimental proxy</a> module. + </p> + + <p> + The "debugger" permission is required by the + <a href="experimental.debugger.html">experimental debugger</a> module. </p> <p> diff --git a/chrome/common/extensions/docs/static/themes.html b/chrome/common/extensions/docs/static/themes.html index 651a74d..8be5c52 100644 --- a/chrome/common/extensions/docs/static/themes.html +++ b/chrome/common/extensions/docs/static/themes.html @@ -66,7 +66,7 @@ file for a theme: Colors are in RGB format. To find the strings you can use within the "colors" field, look for kColor* strings in -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/browser_theme_provider.cc"><code>browser_theme_provider.cc</code></a>. +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/theme_service.cc"><code>theme_service.cc</code></a>. </p> <h3 id="images">images</h3> @@ -75,7 +75,7 @@ look for kColor* strings in Image resources use paths relative to the root of the extension. You can override any of the images that are specified by <code>kThemeableImages</code> in -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/browser_theme_provider.cc"><code>browser_theme_provider.cc</code></a>. +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/theme_service.cc"><code>theme_service.cc</code></a>. Just remove the "IDR_" and convert the remaining characters to lowercase. For example, <code>IDR_THEME_NTP_BACKGROUND</code> @@ -92,7 +92,7 @@ properties such as background alignment, background repeat, and an alternate logo. To see the properties and the values they can have, see -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/browser_theme_provider.cc"><code>browser_theme_provider.cc</code></a>. +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/theme_service.cc"><code>theme_service.cc</code></a>. <!-- [PENDING: We should flesh this out.] --> </p> @@ -106,7 +106,7 @@ because images don't work across platforms and are brittle in the case of adding new buttons. To find the strings you can use within the "tints" field, look for kTint* strings in -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/browser_theme_provider.cc"><code>browser_theme_provider.cc</code></a>. +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/theme_service.cc"><code>theme_service.cc</code></a>. </p> <p> diff --git a/chrome/common/extensions/docs/static/whats_new.html b/chrome/common/extensions/docs/static/whats_new.html index 438896e..437e9cd 100644 --- a/chrome/common/extensions/docs/static/whats_new.html +++ b/chrome/common/extensions/docs/static/whats_new.html @@ -7,6 +7,7 @@ made in recent releases. </p> <ul> + <li> <a href="#11">Google Chrome 11</a> </li> <li> <a href="#10">Google Chrome 10</a> </li> <li> <a href="#9">Google Chrome 9</a> </li> <li> <a href="#8">Google Chrome 8</a> </li> @@ -15,6 +16,42 @@ made in recent releases. </ul> +<h2 id="11"> Google Chrome 11 </h2> + +<h4> Changes to existing APIs </h4> + <ul> + <li> + For security reasons, you can no longer call + <a href="tabs.html#method-captureVisibleTab">chrome.tab.captureVisibleTab()</a> + on just any tab. + Instead, you now must have + <a href="manifest.html#permissions">host permission</a> + for the URL displayed by that tab. + To get the previous behavior, + specify <code><all_urls></code> for the host permission. + </li> + </ul> + +<h4> Additions to existing APIs </h4> + <ul> + <li> + The management API's + <a href="management.html#type-ExtensionInfo">ExtensionInfo</a> object + now has a <code>homepageUrl</code> property. + </li> + <li> The management API now lets you get the icons of + disabled apps and extensions. + Also, you can now modify the regular icon's URL + to get its disabled equivalent. + See <a href="management.html#type-IconInfo">IconInfo</a> for details. + </li> + <li> The cookies API + <a href="cookies.html#method-set">set()</a> + and <a href="cookies.html#method-remove">remove()</a> methods + can now take callbacks. + </li> + </ul> + <h2 id="10"> Google Chrome 10 </h2> <h4> Manifest changes </h4> @@ -34,7 +71,7 @@ made in recent releases. <p class="note"> <b>Note:</b> This change was incorrectly attributed to Chrome 9 - in previous versions of this page. + in previous release notes. </li> </ul> @@ -86,10 +123,10 @@ No API or manifest changes worth noting. <ul> <li> All users can now install <a href="http://code.google.com/chrome/apps/index.html">apps</a>; - packaged apps can use extension APIs + packaged apps can use extension APIs. </li> <li> The <a href="management.html">management API</a> - lets you work with installed apps and extensions + lets you work with installed apps and extensions. </li> </ul> @@ -97,12 +134,12 @@ No API or manifest changes worth noting. <ul> <li> Introduced <a href="manifest.html#incognito">split incognito</a> mode as the default for installable web apps - (also available to extensions) + (also available to extensions). </li> <li> The <a href="tabs.html">tabs API</a> <code>create()</code> and <code>update()</code> methods no longer require the "tabs" permission, removing one common cause of - <a href="permission_warnings.html">scary dialogs</a> + <a href="permission_warnings.html">scary dialogs</a>. </li> </ul> @@ -124,42 +161,42 @@ No API or manifest changes worth noting. <h4> New APIs </h4> <ul> <li>The <a href="contextMenus.html">context menus API</a> allows you to - add context menus to pages or specific objects on a page </li> + add context menus to pages or specific objects on a page. </li> <li>The <a href="cookies.html">cookies API</a> allows you to manage the - browser's cookie system </li> + browser's cookie system. </li> <li>The <a href="idle.html">idle API</a> allows you to detect when the - machine's idle state changes </li> + machine's idle state changes. </li> </ul> <h4> New experimental APIs </h4> <ul> <li>The <a href="experimental.omnibox.html">omnibox API</a> allows you to - add functionality to the browser's address bar </li> + add functionality to the browser's address bar. </li> <li>The <a href="experimental.infobars.html">infobars API</a> allows you - to add a UI panel across the top of a tab </li> + to add a UI panel across the top of a tab. </li> </ul> <h4> Additions to existing APIs </h4> <ul> <li>The <a href="extension.html#method-getViews">chrome.extension.getViews()</a> - method can now return popup views </li> + method can now return popup views. </li> <li>A new <a href="windows.html#property-WINDOW_ID_NONE">WINDOW_ID_NONE</a> constant - identifies when focus shifts away from the browser </li> + identifies when focus shifts away from the browser. </li> <li>The new <a href="tabs.html#method-getCurrent">chrome.tabs.getCurrent()</a> method - returns the tab associated with the currently executing script </li> + returns the tab associated with the currently executing script. </li> </ul> <h4> Manifest changes </h4> <ul> <li>The <a href="manifest.html#geolocation">geolocation</a> permission - gives an extension access to the user's physical location </li> + gives an extension access to the user's physical location. </li> <li><a href="match_patterns.html">Match patterns</a> can now select all - schemes or all URLs </li> + schemes or all URLs. </li> <li>Access to file:/// URLs no longer triggers the "access to your machine" security warning, but now requires user opt-in from the extensions - management page </li> + management page. </li> </ul> diff --git a/chrome/common/extensions/docs/tabs.html b/chrome/common/extensions/docs/tabs.html index 1708562..86198b7 100644 --- a/chrome/common/extensions/docs/tabs.html +++ b/chrome/common/extensions/docs/tabs.html @@ -448,7 +448,7 @@ For other examples and for help in viewing the source code, see <div class="description"> <p class="todo" style="display: none; ">Undocumented.</p> - <p>Captures the visible area of the currently selected tab in the specified window.</p> + <p>Captures the visible area of the currently selected tab in the specified window. You must have <a href="manifest.html#permissions">host permission</a> for the URL displayed by the tab.</p> <!-- PARAMETERS --> <h4>Parameters</h4> @@ -553,9 +553,9 @@ For other examples and for help in viewing the source code, see Undocumented. </dd> <dd>Set parameters of image capture, such as the format of the resulting image.</dd> - <dd> + <dd style="display: none; "> This parameter was added in version - <b><span>5.0.372.0</span></b>. + <b><span></span></b>. You must omit this parameter in earlier versions, and you may omit it in any version. If you require this parameter, the manifest key diff --git a/chrome/common/extensions/docs/themes.html b/chrome/common/extensions/docs/themes.html index d2eafef..9bcbfec 100644 --- a/chrome/common/extensions/docs/themes.html +++ b/chrome/common/extensions/docs/themes.html @@ -389,7 +389,7 @@ file for a theme: Colors are in RGB format. To find the strings you can use within the "colors" field, look for kColor* strings in -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/browser_theme_provider.cc"><code>browser_theme_provider.cc</code></a>. +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/theme_service.cc"><code>theme_service.cc</code></a>. </p> <h3 id="images">images</h3> @@ -398,7 +398,7 @@ look for kColor* strings in Image resources use paths relative to the root of the extension. You can override any of the images that are specified by <code>kThemeableImages</code> in -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/browser_theme_provider.cc"><code>browser_theme_provider.cc</code></a>. +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/theme_service.cc"><code>theme_service.cc</code></a>. Just remove the "IDR_" and convert the remaining characters to lowercase. For example, <code>IDR_THEME_NTP_BACKGROUND</code> @@ -415,7 +415,7 @@ properties such as background alignment, background repeat, and an alternate logo. To see the properties and the values they can have, see -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/browser_theme_provider.cc"><code>browser_theme_provider.cc</code></a>. +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/theme_service.cc"><code>theme_service.cc</code></a>. <!-- [PENDING: We should flesh this out.] --> </p> @@ -429,7 +429,7 @@ because images don't work across platforms and are brittle in the case of adding new buttons. To find the strings you can use within the "tints" field, look for kTint* strings in -<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/browser_theme_provider.cc"><code>browser_theme_provider.cc</code></a>. +<a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/theme_service.cc"><code>theme_service.cc</code></a>. </p> <p> diff --git a/chrome/common/extensions/docs/whats_new.html b/chrome/common/extensions/docs/whats_new.html index ac4b0f6..6e45782 100644 --- a/chrome/common/extensions/docs/whats_new.html +++ b/chrome/common/extensions/docs/whats_new.html @@ -321,6 +321,7 @@ made in recent releases. </p> <ul> + <li> <a href="#11">Google Chrome 11</a> </li> <li> <a href="#10">Google Chrome 10</a> </li> <li> <a href="#9">Google Chrome 9</a> </li> <li> <a href="#8">Google Chrome 8</a> </li> @@ -329,6 +330,42 @@ made in recent releases. </ul> +<h2 id="11"> Google Chrome 11 </h2> + +<h4> Changes to existing APIs </h4> + <ul> + <li> + For security reasons, you can no longer call + <a href="tabs.html#method-captureVisibleTab">chrome.tab.captureVisibleTab()</a> + on just any tab. + Instead, you now must have + <a href="manifest.html#permissions">host permission</a> + for the URL displayed by that tab. + To get the previous behavior, + specify <code><all_urls></code> for the host permission. + </li> + </ul> + +<h4> Additions to existing APIs </h4> + <ul> + <li> + The management API's + <a href="management.html#type-ExtensionInfo">ExtensionInfo</a> object + now has a <code>homepageUrl</code> property. + </li> + <li> The management API now lets you get the icons of + disabled apps and extensions. + Also, you can now modify the regular icon's URL + to get its disabled equivalent. + See <a href="management.html#type-IconInfo">IconInfo</a> for details. + </li> + <li> The cookies API + <a href="cookies.html#method-set">set()</a> + and <a href="cookies.html#method-remove">remove()</a> methods + can now take callbacks. + </li> + </ul> + <h2 id="10"> Google Chrome 10 </h2> <h4> Manifest changes </h4> @@ -348,7 +385,7 @@ made in recent releases. <p class="note"> <b>Note:</b> This change was incorrectly attributed to Chrome 9 - in previous versions of this page. + in previous release notes. </p></li> </ul> @@ -400,10 +437,10 @@ No API or manifest changes worth noting. <ul> <li> All users can now install <a href="http://code.google.com/chrome/apps/index.html">apps</a>; - packaged apps can use extension APIs + packaged apps can use extension APIs. </li> <li> The <a href="management.html">management API</a> - lets you work with installed apps and extensions + lets you work with installed apps and extensions. </li> </ul> @@ -411,12 +448,12 @@ No API or manifest changes worth noting. <ul> <li> Introduced <a href="manifest.html#incognito">split incognito</a> mode as the default for installable web apps - (also available to extensions) + (also available to extensions). </li> <li> The <a href="tabs.html">tabs API</a> <code>create()</code> and <code>update()</code> methods no longer require the "tabs" permission, removing one common cause of - <a href="permission_warnings.html">scary dialogs</a> + <a href="permission_warnings.html">scary dialogs</a>. </li> </ul> @@ -438,40 +475,40 @@ No API or manifest changes worth noting. <h4> New APIs </h4> <ul> <li>The <a href="contextMenus.html">context menus API</a> allows you to - add context menus to pages or specific objects on a page </li> + add context menus to pages or specific objects on a page. </li> <li>The <a href="cookies.html">cookies API</a> allows you to manage the - browser's cookie system </li> + browser's cookie system. </li> <li>The <a href="idle.html">idle API</a> allows you to detect when the - machine's idle state changes </li> + machine's idle state changes. </li> </ul> <h4> New experimental APIs </h4> <ul> <li>The <a href="experimental.omnibox.html">omnibox API</a> allows you to - add functionality to the browser's address bar </li> + add functionality to the browser's address bar. </li> <li>The <a href="experimental.infobars.html">infobars API</a> allows you - to add a UI panel across the top of a tab </li> + to add a UI panel across the top of a tab. </li> </ul> <h4> Additions to existing APIs </h4> <ul> <li>The <a href="extension.html#method-getViews">chrome.extension.getViews()</a> - method can now return popup views </li> + method can now return popup views. </li> <li>A new <a href="windows.html#property-WINDOW_ID_NONE">WINDOW_ID_NONE</a> constant - identifies when focus shifts away from the browser </li> + identifies when focus shifts away from the browser. </li> <li>The new <a href="tabs.html#method-getCurrent">chrome.tabs.getCurrent()</a> method - returns the tab associated with the currently executing script </li> + returns the tab associated with the currently executing script. </li> </ul> <h4> Manifest changes </h4> <ul> <li>The <a href="manifest.html#geolocation">geolocation</a> permission - gives an extension access to the user's physical location </li> + gives an extension access to the user's physical location. </li> <li><a href="match_patterns.html">Match patterns</a> can now select all - schemes or all URLs </li> + schemes or all URLs. </li> <li>Access to file:/// URLs no longer triggers the "access to your machine" security warning, but now requires user opt-in from the extensions - management page </li> + management page. </li> </ul> </div> diff --git a/chrome/common/extensions/docs/windows.html b/chrome/common/extensions/docs/windows.html index a6cb357..ba96d58 100644 --- a/chrome/common/extensions/docs/windows.html +++ b/chrome/common/extensions/docs/windows.html @@ -986,6 +986,74 @@ For other examples and for help in viewing the source code, see </div><div> <div> <dt> + <var>focused</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>If true, opens an active window. If false, opens an inactive window.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> <var>incognito</var> <em> @@ -1071,7 +1139,7 @@ For other examples and for help in viewing the source code, see array of <span><span></span></span> </span> <span>string</span> - <span>["normal", "popup"]</span> + <span>["normal", "popup", "panel"]</span> </span> </span> ) @@ -1082,7 +1150,7 @@ For other examples and for help in viewing the source code, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Specifies what type of browser window to create.</dd> + <dd>Specifies what type of browser window to create. The 'panel' type requires the 'experimental' permission.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2969,7 +3037,7 @@ For other examples and for help in viewing the source code, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>It true, brings the window to the front. If false, brings the next window in the z-order to the front.</dd> + <dd>If true, brings the window to the front. If false, brings the next window in the z-order to the front.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -4122,7 +4190,7 @@ For other examples and for help in viewing the source code, see array of <span><span></span></span> </span> <span>string</span> - <span>["normal", "popup", "app"]</span> + <span>["normal", "popup", "panel", "app"]</span> </span> </span> ) @@ -4133,7 +4201,7 @@ For other examples and for help in viewing the source code, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The type of browser window this is.</dd> + <dd>The type of browser window this is. The 'panel' type requires the 'experimental' permission.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index ce77fa3..bd28e42 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -13,15 +13,15 @@ #include "base/file_util.h" #include "base/i18n/rtl.h" #include "base/logging.h" -#include "base/sha2.h" -#include "base/singleton.h" +#include "base/memory/singleton.h" #include "base/stl_util-inl.h" -#include "base/third_party/nss/blapi.h" #include "base/string16.h" #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "base/version.h" +#include "crypto/sha2.h" +#include "crypto/third_party/nss/blapi.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_version_info.h" @@ -32,14 +32,17 @@ #include "chrome/common/extensions/extension_resource.h" #include "chrome/common/extensions/extension_sidebar_defaults.h" #include "chrome/common/extensions/extension_sidebar_utils.h" +#include "chrome/common/extensions/file_browser_handler.h" #include "chrome/common/extensions/user_script.h" #include "chrome/common/url_constants.h" #include "googleurl/src/url_util.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" +#include "grit/theme_resources.h" #include "net/base/registry_controlled_domain.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" #include "webkit/glue/image_decoder.h" namespace keys = extension_manifest_keys; @@ -95,6 +98,9 @@ bool IsBaseCrxKey(const std::string& key) { return false; } +// Constant used to represent an undefined l10n message id. +const int kUndefinedMessageId = -1; + // Names of API modules that do not require a permission. const char kBrowserActionModuleName[] = "browserAction"; const char kBrowserActionsModuleName[] = "browserActions"; @@ -139,7 +145,8 @@ class ExtensionConfig { return Singleton<ExtensionConfig>::get(); } - int GetPermissionMessageId(const std::string& permission) { + Extension::PermissionMessage::MessageId GetPermissionMessageId( + const std::string& permission) { return Extension::kPermissions[permission_map_[permission]].message_id; } @@ -168,6 +175,60 @@ class ExtensionConfig { // in the permissions field of the manifest. static const char kWindowPermission[] = "windows"; +// Rank extension locations in a way that allows +// Extension::GetHigherPriorityLocation() to compare locations. +// An extension installed from two locations will have the location +// with the higher rank, as returned by this function. The actual +// integer values may change, and should never be persisted. +int GetLocationRank(Extension::Location location) { + const int kInvalidRank = -1; + int rank = kInvalidRank; // Will CHECK that rank is not kInvalidRank. + + switch (location) { + // Component extensions can not be overriden by any other type. + case Extension::COMPONENT: + rank = 6; + break; + + // Policy controlled extensions may not be overridden by any type + // that is not part of chrome. + case Extension::EXTERNAL_POLICY_DOWNLOAD: + rank = 5; + break; + + // A developer-loaded extension should override any installed type + // that a user can disable. + case Extension::LOAD: + rank = 4; + break; + + // The relative priority of various external sources is not important, + // but having some order ensures deterministic behavior. + case Extension::EXTERNAL_REGISTRY: + rank = 3; + break; + + case Extension::EXTERNAL_PREF: + rank = 2; + break; + + case Extension::EXTERNAL_PREF_DOWNLOAD: + rank = 1; + break; + + // User installed extensions are overridden by any external type. + case Extension::INTERNAL: + rank = 0; + break; + + default: + NOTREACHED() << "Need to add new extension locaton " << location; + } + + CHECK(rank != kInvalidRank); + return rank; +} + } // namespace const FilePath::CharType Extension::kManifestFilename[] = @@ -205,7 +266,11 @@ const char Extension::kBookmarkPermission[] = "bookmarks"; const char Extension::kContextMenusPermission[] = "contextMenus"; const char Extension::kContentSettingsPermission[] = "contentSettings"; const char Extension::kCookiePermission[] = "cookies"; +const char Extension::kChromeosInfoPrivatePermissions[] = "chromeosInfoPrivate"; +const char Extension::kDebuggerPermission[] = "debugger"; const char Extension::kExperimentalPermission[] = "experimental"; +const char Extension::kFileBrowserHandlerPermission[] = "fileBrowserHandler"; +const char Extension::kFileBrowserPrivatePermission[] = "fileBrowserPrivate"; const char Extension::kGeolocationPermission[] = "geolocation"; const char Extension::kHistoryPermission[] = "history"; const char Extension::kIdlePermission[] = "idle"; @@ -220,21 +285,25 @@ const char Extension::kWebstorePrivatePermission[] = "webstorePrivate"; // See ExtensionsTest.PermissionMessages for an explanation of each // exception. const Extension::Permission Extension::kPermissions[] = { - { kBackgroundPermission, 0 }, - { kBookmarkPermission, IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS }, - { kContentSettingsPermission, 0 }, - { kContextMenusPermission, 0 }, - { kCookiePermission, 0 }, - { kExperimentalPermission, 0 }, - { kGeolocationPermission, IDS_EXTENSION_PROMPT_WARNING_GEOLOCATION }, - { kIdlePermission, 0 }, - { kHistoryPermission, IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY }, - { kManagementPermission, IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT }, - { kNotificationPermission, 0 }, - { kProxyPermission, 0 }, - { kTabPermission, IDS_EXTENSION_PROMPT_WARNING_TABS }, - { kUnlimitedStoragePermission, 0 }, - { kWebstorePrivatePermission, 0 }, + { kBackgroundPermission, PermissionMessage::ID_NONE }, + { kBookmarkPermission, PermissionMessage::ID_BOOKMARKS }, + { kChromeosInfoPrivatePermissions, PermissionMessage::ID_NONE }, + { kContentSettingsPermission, PermissionMessage::ID_NONE }, + { kContextMenusPermission, PermissionMessage::ID_NONE }, + { kCookiePermission, PermissionMessage::ID_NONE }, + { kDebuggerPermission, PermissionMessage::ID_DEBUGGER }, + { kExperimentalPermission, PermissionMessage::ID_NONE }, + { kFileBrowserHandlerPermission, PermissionMessage::ID_NONE }, + { kFileBrowserPrivatePermission, PermissionMessage::ID_NONE }, + { kGeolocationPermission, PermissionMessage::ID_GEOLOCATION }, + { kIdlePermission, PermissionMessage::ID_NONE }, + { kHistoryPermission, PermissionMessage::ID_BROWSING_HISTORY }, + { kManagementPermission, PermissionMessage::ID_MANAGEMENT }, + { kNotificationPermission, PermissionMessage::ID_NONE }, + { kProxyPermission, PermissionMessage::ID_NONE }, + { kTabPermission, PermissionMessage::ID_TABS }, + { kUnlimitedStoragePermission, PermissionMessage::ID_NONE }, + { kWebstorePrivatePermission, PermissionMessage::ID_NONE } }; const size_t Extension::kNumPermissions = arraysize(Extension::kPermissions); @@ -249,6 +318,14 @@ const char* const Extension::kHostedAppPermissionNames[] = { const size_t Extension::kNumHostedAppPermissions = arraysize(Extension::kHostedAppPermissionNames); +const char* const Extension::kComponentPrivatePermissionNames[] = { + Extension::kFileBrowserPrivatePermission, + Extension::kWebstorePrivatePermission, + Extension::kChromeosInfoPrivatePermissions, +}; +const size_t Extension::kNumComponentPrivatePermissions = + arraysize(Extension::kComponentPrivatePermissionNames); + // We purposefully don't put this into kPermissionNames. const char Extension::kOldUnlimitedStoragePermission[] = "unlimited_storage"; @@ -256,8 +333,84 @@ const int Extension::kValidWebExtentSchemes = URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; const int Extension::kValidHostPermissionSchemes = - (UserScript::kValidUserScriptSchemes | - URLPattern::SCHEME_CHROMEUI) & ~URLPattern::SCHEME_FILE; + UserScript::kValidUserScriptSchemes | URLPattern::SCHEME_CHROMEUI; + +// +// PermissionMessage +// + +// static +Extension::PermissionMessage Extension::PermissionMessage::CreateFromMessageId( + Extension::PermissionMessage::MessageId message_id) { + DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN); + if (message_id <= ID_NONE) + return PermissionMessage(message_id, string16()); + + string16 message = l10n_util::GetStringUTF16(kMessageIds[message_id]); + return PermissionMessage(message_id, message); +} + +// static +Extension::PermissionMessage Extension::PermissionMessage::CreateFromHostList( + const std::vector<std::string> hosts) { + CHECK(hosts.size() > 0); + + MessageId message_id; + string16 message; + switch (hosts.size()) { + case 1: + message_id = ID_HOSTS_1; + message = l10n_util::GetStringFUTF16(kMessageIds[message_id], + UTF8ToUTF16(hosts[0])); + break; + case 2: + message_id = ID_HOSTS_2; + message = l10n_util::GetStringFUTF16(kMessageIds[message_id], + UTF8ToUTF16(hosts[0]), + UTF8ToUTF16(hosts[1])); + break; + case 3: + message_id = ID_HOSTS_3; + message = l10n_util::GetStringFUTF16(kMessageIds[message_id], + UTF8ToUTF16(hosts[0]), + UTF8ToUTF16(hosts[1]), + UTF8ToUTF16(hosts[2])); + break; + default: + message_id = ID_HOSTS_4_OR_MORE; + message = l10n_util::GetStringFUTF16( + kMessageIds[message_id], + UTF8ToUTF16(hosts[0]), + UTF8ToUTF16(hosts[1]), + base::IntToString16(hosts.size() - 2)); + break; + } + + return PermissionMessage(message_id, message); +} + +Extension::PermissionMessage::PermissionMessage( + Extension::PermissionMessage::MessageId message_id, string16 message) + : message_id_(message_id), + message_(message) { +} + +const int Extension::PermissionMessage::kMessageIds[] = { + kUndefinedMessageId, // "unknown" + kUndefinedMessageId, // "none" + IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS, + IDS_EXTENSION_PROMPT_WARNING_GEOLOCATION, + IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY, + IDS_EXTENSION_PROMPT_WARNING_TABS, + IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT, + IDS_EXTENSION_PROMPT_WARNING_DEBUGGER, + IDS_EXTENSION_PROMPT_WARNING_1_HOST, + IDS_EXTENSION_PROMPT_WARNING_2_HOSTS, + IDS_EXTENSION_PROMPT_WARNING_3_HOSTS, + IDS_EXTENSION_PROMPT_WARNING_4_OR_MORE_HOSTS, + IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS, + IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS +}; // // Extension @@ -267,12 +420,11 @@ const int Extension::kValidHostPermissionSchemes = scoped_refptr<Extension> Extension::Create(const FilePath& path, Location location, const DictionaryValue& value, - bool require_key, - bool strict_error_checks, + int flags, std::string* error) { scoped_refptr<Extension> extension = new Extension(path, location); - if (!extension->InitFromValue(value, require_key, strict_error_checks, error)) + if (!extension->InitFromValue(value, flags, error)) return NULL; return extension; } @@ -294,35 +446,70 @@ GURL Extension::GalleryUpdateUrl(bool secure) { } // static -int Extension::GetPermissionMessageId(const std::string& permission) { +Extension::Location Extension::GetHigherPriorityLocation( + Extension::Location loc1, Extension::Location loc2) { + if (loc1 == loc2) + return loc1; + + int loc1_rank = GetLocationRank(loc1); + int loc2_rank = GetLocationRank(loc2); + + // If two different locations have the same rank, then we can not + // deterministicly choose a location. + CHECK(loc1_rank != loc2_rank); + + // Lowest rank has highest priority. + return (loc1_rank > loc2_rank ? loc1 : loc2 ); +} + +// static +Extension::PermissionMessage::MessageId Extension::GetPermissionMessageId( + const std::string& permission) { return ExtensionConfig::GetInstance()->GetPermissionMessageId(permission); } -std::vector<string16> Extension::GetPermissionMessages() const { - std::vector<string16> messages; +Extension::PermissionMessages Extension::GetPermissionMessages() const { + PermissionMessages messages; if (!plugins().empty()) { - messages.push_back( - l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS)); + messages.push_back(PermissionMessage::CreateFromMessageId( + PermissionMessage::ID_FULL_ACCESS)); return messages; } - string16 host_msg = GetHostPermissionMessage(); - if (!host_msg.empty()) - messages.push_back(host_msg); + if (HasEffectiveAccessToAllHosts()) { + messages.push_back(PermissionMessage::CreateFromMessageId( + PermissionMessage::ID_HOSTS_ALL)); + } else { + std::vector<std::string> hosts = GetDistinctHostsForDisplay( + GetEffectiveHostPermissions().patterns()); + if (!hosts.empty()) + messages.push_back(PermissionMessage::CreateFromHostList(hosts)); + } - std::set<string16> simple_msgs = GetSimplePermissionMessages(); + std::set<PermissionMessage> simple_msgs = GetSimplePermissionMessages(); messages.insert(messages.end(), simple_msgs.begin(), simple_msgs.end()); return messages; } -std::set<string16> Extension::GetSimplePermissionMessages() const { - std::set<string16> messages; +std::vector<string16> Extension::GetPermissionMessageStrings() const { + std::vector<string16> messages; + PermissionMessages permissions = GetPermissionMessages(); + for (PermissionMessages::const_iterator i = permissions.begin(); + i != permissions.end(); ++i) + messages.push_back(i->message()); + return messages; +} + +std::set<Extension::PermissionMessage> + Extension::GetSimplePermissionMessages() const { + std::set<PermissionMessage> messages; std::set<std::string>::const_iterator i; for (i = api_permissions().begin(); i != api_permissions().end(); ++i) { - int message_id = GetPermissionMessageId(*i); - if (message_id) - messages.insert(l10n_util::GetStringUTF16(message_id)); + PermissionMessage::MessageId message_id = GetPermissionMessageId(*i); + DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN); + if (message_id > PermissionMessage::ID_NONE) + messages.insert(PermissionMessage::CreateFromMessageId(message_id)); } return messages; } @@ -354,69 +541,67 @@ bool Extension::IsElevatedHostList( return !new_hosts_only.empty(); } +// Helper for GetDistinctHosts(): com > net > org > everything else. +static bool RcdBetterThan(const std::string& a, const std::string& b) { + if (a == b) + return false; + if (a == "com") + return true; + if (a == "net") + return b != "com"; + if (a == "org") + return b != "com" && b != "net"; + return false; +} + // static std::vector<std::string> Extension::GetDistinctHosts( const URLPatternList& host_patterns, bool include_rcd) { - // Vector because we later want to access these by index. - std::vector<std::string> distinct_hosts; - - std::set<std::string> rcd_set; + // Use a vector to preserve order (also faster than a map on small sets). + // Each item is a host split into two parts: host without RCDs and + // current best RCD. + typedef std::vector<std::pair<std::string, std::string> > HostVector; + HostVector hosts_best_rcd; for (size_t i = 0; i < host_patterns.size(); ++i) { - std::string candidate = host_patterns[i].host(); + std::string host = host_patterns[i].host(); // Add the subdomain wildcard back to the host, if necessary. if (host_patterns[i].match_subdomains()) - candidate = "*." + candidate; + host = "*." + host; - size_t registry = net::RegistryControlledDomainService::GetRegistryLength( - candidate, false); - if (registry && registry != std::string::npos) { - std::string no_rcd(candidate, 0, candidate.size() - registry); - if (rcd_set.count(no_rcd)) - continue; - rcd_set.insert(no_rcd); - if (!include_rcd) - candidate = no_rcd; + // If the host has an RCD, split it off so we can detect duplicates. + std::string rcd; + size_t reg_len = net::RegistryControlledDomainService::GetRegistryLength( + host, false); + if (reg_len && reg_len != std::string::npos) { + if (include_rcd) // else leave rcd empty + rcd = host.substr(host.size() - reg_len); + host = host.substr(0, host.size() - reg_len); } - if (std::find(distinct_hosts.begin(), distinct_hosts.end(), candidate) == - distinct_hosts.end()) { - distinct_hosts.push_back(candidate); + + // Check if we've already seen this host. + HostVector::iterator it = hosts_best_rcd.begin(); + for (; it != hosts_best_rcd.end(); ++it) { + if (it->first == host) + break; + } + // If this host was found, replace the RCD if this one is better. + if (it != hosts_best_rcd.end()) { + if (include_rcd && RcdBetterThan(rcd, it->second)) + it->second = rcd; + } else { // Previously unseen host, append it. + hosts_best_rcd.push_back(std::make_pair(host, rcd)); } } + // Build up the final vector by concatenating hosts and RCDs. + std::vector<std::string> distinct_hosts; + for (HostVector::iterator it = hosts_best_rcd.begin(); + it != hosts_best_rcd.end(); ++it) + distinct_hosts.push_back(it->first + it->second); return distinct_hosts; } -string16 Extension::GetHostPermissionMessage() const { - if (HasEffectiveAccessToAllHosts()) - return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS); - - std::vector<std::string> hosts = GetDistinctHostsForDisplay( - GetEffectiveHostPermissions().patterns()); - - if (hosts.size() == 1) { - return l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_1_HOST, - UTF8ToUTF16(hosts[0])); - } else if (hosts.size() == 2) { - return l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_2_HOSTS, - UTF8ToUTF16(hosts[0]), - UTF8ToUTF16(hosts[1])); - } else if (hosts.size() == 3) { - return l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_3_HOSTS, - UTF8ToUTF16(hosts[0]), - UTF8ToUTF16(hosts[1]), - UTF8ToUTF16(hosts[2])); - } else if (hosts.size() >= 4) { - return l10n_util::GetStringFUTF16( - IDS_EXTENSION_PROMPT_WARNING_4_OR_MORE_HOSTS, - UTF8ToUTF16(hosts[0]), - UTF8ToUTF16(hosts[1]), - base::IntToString16(hosts.size() - 2)); - } - - return string16(); -} - FilePath Extension::MaybeNormalizePath(const FilePath& path) { #if defined(OS_WIN) // Normalize any drive letter to upper-case. We do this for consistency with @@ -471,8 +656,11 @@ bool Extension::IdIsValid(const std::string& id) { // static std::string Extension::GenerateIdForPath(const FilePath& path) { FilePath new_path = Extension::MaybeNormalizePath(path); + std::string path_bytes = + std::string(reinterpret_cast<const char*>(new_path.value().data()), + new_path.value().size() * sizeof(FilePath::CharType)); std::string id; - if (!GenerateId(WideToUTF8(new_path.ToWStringHack()), &id)) + if (!GenerateId(path_bytes, &id)) return ""; return id; } @@ -503,11 +691,8 @@ GURL Extension::GetResourceURL(const GURL& extension_url, bool Extension::GenerateId(const std::string& input, std::string* output) { CHECK(output); - if (input.empty()) - return false; - uint8 hash[Extension::kIdSize]; - base::SHA256HashString(input, hash, sizeof(hash)); + crypto::SHA256HashString(input, hash, sizeof(hash)); *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash))); ConvertHexadecimalToIDAlphabet(output); @@ -518,9 +703,14 @@ bool Extension::GenerateId(const std::string& input, std::string* output) { // content_script list of the manifest. bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, int definition_index, - URLPattern::ParseOption parse_strictness, + int flags, std::string* error, UserScript* result) { + // When strict error checks are enabled, make URL pattern parsing strict. + URLPattern::ParseOption parse_strictness = + (flags & STRICT_ERROR_CHECKS ? URLPattern::PARSE_STRICT + : URLPattern::PARSE_LENIENT); + // run_at if (content_script->HasKey(keys::kRunAt)) { std::string run_location; @@ -593,6 +783,14 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, return false; } + if (pattern.MatchesScheme(chrome::kFileScheme) && + !CanExecuteScriptEverywhere()) { + wants_file_access_ = true; + if (!(flags & ALLOW_FILE_ACCESS)) + pattern.set_valid_schemes( + pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); + } + result->add_url_pattern(pattern); } @@ -822,6 +1020,91 @@ ExtensionAction* Extension::LoadExtensionActionHelper( return result.release(); } +Extension::FileBrowserHandlerList* Extension::LoadFileBrowserHandlers( + const ListValue* extension_actions, std::string* error) { + scoped_ptr<FileBrowserHandlerList> result( + new FileBrowserHandlerList()); + for (ListValue::const_iterator iter = extension_actions->begin(); + iter != extension_actions->end(); + ++iter) { + if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) { + *error = errors::kInvalidFileBrowserHandler; + return NULL; + } + scoped_ptr<FileBrowserHandler> action( + LoadFileBrowserHandler( + reinterpret_cast<DictionaryValue*>(*iter), error)); + if (!action.get()) + return NULL; // Failed to parse file browser action definition. + result->push_back(linked_ptr<FileBrowserHandler>(action.release())); + } + return result.release(); +} + +FileBrowserHandler* Extension::LoadFileBrowserHandler( + const DictionaryValue* file_browser_handler, std::string* error) { + scoped_ptr<FileBrowserHandler> result( + new FileBrowserHandler()); + result->set_extension_id(id()); + + std::string id; + // Read the file action |id| (mandatory). + if (!file_browser_handler->HasKey(keys::kPageActionId) || + !file_browser_handler->GetString(keys::kPageActionId, &id)) { + *error = errors::kInvalidPageActionId; + return NULL; + } + result->set_id(id); + + // Read the page action title from |default_title| (mandatory). + std::string title; + if (!file_browser_handler->HasKey(keys::kPageActionDefaultTitle) || + !file_browser_handler->GetString(keys::kPageActionDefaultTitle, &title)) { + *error = errors::kInvalidPageActionDefaultTitle; + return NULL; + } + result->set_title(title); + + // Initialize file filters (mandatory). + ListValue* list_value = NULL; + if (!file_browser_handler->HasKey(keys::kFileFilters) || + !file_browser_handler->GetList(keys::kFileFilters, &list_value) || + list_value->empty()) { + *error = errors::kInvalidFileFiltersList; + return NULL; + } + for (size_t i = 0; i < list_value->GetSize(); ++i) { + std::string filter; + if (!list_value->GetString(i, &filter)) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidFileFilterValue, base::IntToString(i)); + return NULL; + } + URLPattern pattern(URLPattern::SCHEME_FILESYSTEM); + if (URLPattern::PARSE_SUCCESS != pattern.Parse(filter, + URLPattern::PARSE_STRICT)) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidURLPatternError, filter); + return NULL; + } + result->AddPattern(pattern); + } + + std::string default_icon; + // Read the file browser action |default_icon| (optional). + if (file_browser_handler->HasKey(keys::kPageActionDefaultIcon)) { + if (!file_browser_handler->GetString( + keys::kPageActionDefaultIcon,&default_icon) || + default_icon.empty()) { + *error = errors::kInvalidPageActionIconPath; + return NULL; + } + result->set_icon_path(default_icon); + } + + return result.release(); +} + ExtensionSidebarDefaults* Extension::LoadExtensionSidebarDefaults( const DictionaryValue* extension_sidebar, std::string* error) { scoped_ptr<ExtensionSidebarDefaults> result(new ExtensionSidebarDefaults()); @@ -995,8 +1278,10 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest, return false; } - // Ensure the launch URL is a valid absolute URL. - if (!GURL(launch_url).is_valid()) { + // Ensure the launch URL is a valid absolute URL and web extent scheme. + GURL url(launch_url); + URLPattern pattern(kValidWebExtentSchemes); + if (!url.is_valid() || !pattern.SetScheme(url.scheme())) { *error = errors::kInvalidLaunchWebURL; return false; } @@ -1109,6 +1394,43 @@ bool Extension::LoadLaunchContainer(const DictionaryValue* manifest, return true; } +bool Extension::LoadAppIsolation(const DictionaryValue* manifest, + std::string* error) { + // Only parse app isolation features if this switch is present. + if (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalAppManifests)) + return true; + + Value* temp = NULL; + if (!manifest->Get(keys::kIsolation, &temp)) + return true; + + if (temp->GetType() != Value::TYPE_LIST) { + *error = errors::kInvalidIsolation; + return false; + } + + ListValue* isolation_list = static_cast<ListValue*>(temp); + for (size_t i = 0; i < isolation_list->GetSize(); ++i) { + std::string isolation_string; + if (!isolation_list->GetString(i, &isolation_string)) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidIsolationValue, + base::UintToString(i)); + return false; + } + + // Check for isolated storage. + if (isolation_string == values::kIsolatedStorage) { + is_storage_isolated_ = true; + } else { + LOG(WARNING) << "Did not recognize isolation type: " + << isolation_string; + } + } + return true; +} + bool Extension::EnsureNotHybridApp(const DictionaryValue* manifest, std::string* error) { if (web_extent().is_empty()) @@ -1119,8 +1441,10 @@ bool Extension::EnsureNotHybridApp(const DictionaryValue* manifest, if (!IsBaseCrxKey(*key) && *key != keys::kApp && *key != keys::kPermissions && - *key != keys::kOptionsPage) { - *error = errors::kHostedAppsCannotIncludeExtensionFeatures; + *key != keys::kOptionsPage && + *key != keys::kBackground) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kHostedAppsCannotIncludeExtensionFeatures, *key); return false; } } @@ -1134,14 +1458,18 @@ Extension::Extension(const FilePath& path, Location location) converted_from_user_script_(false), is_theme_(false), is_app_(false), + is_storage_isolated_(false), launch_container_(extension_misc::LAUNCH_TAB), launch_width_(0), - launch_height_(0) { - DCHECK(path.IsAbsolute()); + launch_height_(0), + wants_file_access_(false) { + DCHECK(path.empty() || path.IsAbsolute()); path_ = MaybeNormalizePath(path); } + Extension::~Extension() { } + ExtensionResource Extension::GetResource( const std::string& relative_path) const { #if defined(OS_POSIX) @@ -1190,8 +1518,7 @@ bool Extension::ParsePEMKeyBytes(const std::string& input, return base::Base64Decode(working, output); } -bool Extension::ProducePEM(const std::string& input, - std::string* output) { +bool Extension::ProducePEM(const std::string& input, std::string* output) { CHECK(output); if (input.length() == 0) return false; @@ -1199,7 +1526,7 @@ bool Extension::ProducePEM(const std::string& input, return base::Base64Encode(input, output); } -bool Extension::FormatPEMForFileOutput(const std::string input, +bool Extension::FormatPEMForFileOutput(const std::string& input, std::string* output, bool is_public) { CHECK(output); @@ -1266,7 +1593,8 @@ bool Extension::IsPrivilegeIncrease(const bool granted_full_access, size_t new_api_count = 0; for (std::set<std::string>::iterator i = new_apis_only.begin(); i != new_apis_only.end(); ++i) { - if (GetPermissionMessageId(*i)) + DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN); + if (GetPermissionMessageId(*i) > PermissionMessage::ID_NONE) new_api_count++; } @@ -1320,17 +1648,28 @@ void Extension::DecodeIconFromPath(const FilePath& icon_path, result->swap(decoded); } +// static +const SkBitmap& Extension::GetDefaultIcon(bool is_app) { + if (is_app) { + return *ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_APP_DEFAULT_ICON); + } else { + return *ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_EXTENSION_DEFAULT_ICON); + } +} + GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { return GURL(std::string(chrome::kExtensionScheme) + chrome::kStandardSchemeSeparator + extension_id + "/"); } -bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, - bool strict_error_checks, std::string* error) { +bool Extension::InitFromValue(const DictionaryValue& source, int flags, + std::string* error) { // When strict error checks are enabled, make URL pattern parsing strict. URLPattern::ParseOption parse_strictness = - (strict_error_checks ? URLPattern::PARSE_STRICT - : URLPattern::PARSE_LENIENT); + (flags & STRICT_ERROR_CHECKS ? URLPattern::PARSE_STRICT + : URLPattern::PARSE_LENIENT); if (source.HasKey(keys::kPublicKey)) { std::string public_key_bytes; @@ -1342,7 +1681,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kInvalidKey; return false; } - } else if (require_key) { + } else if (flags & REQUIRE_KEY) { *error = errors::kInvalidKey; return false; } else { @@ -1510,14 +1849,14 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } - DictionaryValue* theme_value; + DictionaryValue* theme_value = NULL; if (!source.GetDictionary(keys::kTheme, &theme_value)) { *error = errors::kInvalidTheme; return false; } is_theme_ = true; - DictionaryValue* images_value; + DictionaryValue* images_value = NULL; if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) { // Validate that the images are all strings for (DictionaryValue::key_iterator iter = images_value->begin_keys(); @@ -1531,15 +1870,15 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, theme_images_.reset(images_value->DeepCopy()); } - DictionaryValue* colors_value; + DictionaryValue* colors_value = NULL; if (theme_value->GetDictionary(keys::kThemeColors, &colors_value)) { // Validate that the colors are RGB or RGBA lists for (DictionaryValue::key_iterator iter = colors_value->begin_keys(); iter != colors_value->end_keys(); ++iter) { - ListValue* color_list; - double alpha; - int alpha_int; - int color; + ListValue* color_list = NULL; + double alpha = 0.0; + int alpha_int = 0; + int color = 0; // The color must be a list if (!colors_value->GetListWithoutPathExpansion(*iter, &color_list) || // And either 3 items (RGB) or 4 (RGBA) @@ -1559,14 +1898,14 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, theme_colors_.reset(colors_value->DeepCopy()); } - DictionaryValue* tints_value; + DictionaryValue* tints_value = NULL; if (theme_value->GetDictionary(keys::kThemeTints, &tints_value)) { // Validate that the tints are all reals. for (DictionaryValue::key_iterator iter = tints_value->begin_keys(); iter != tints_value->end_keys(); ++iter) { - ListValue* tint_list; - double v; - int vi; + ListValue* tint_list = NULL; + double v = 0.0; + int vi = 0; if (!tints_value->GetListWithoutPathExpansion(*iter, &tint_list) || tint_list->GetSize() != 3 || !(tint_list->GetDouble(0, &v) || tint_list->GetInteger(0, &vi)) || @@ -1579,7 +1918,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, theme_tints_.reset(tints_value->DeepCopy()); } - DictionaryValue* display_properties_value; + DictionaryValue* display_properties_value = NULL; if (theme_value->GetDictionary(keys::kThemeDisplayProperties, &display_properties_value)) { theme_display_properties_.reset( @@ -1591,21 +1930,14 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // Initialize plugins (optional). if (source.HasKey(keys::kPlugins)) { - ListValue* list_value; + ListValue* list_value = NULL; if (!source.GetList(keys::kPlugins, &list_value)) { *error = errors::kInvalidPlugins; return false; } -#if defined(OS_CHROMEOS) - if (list_value->GetSize() > 0) { - *error = errors::kIllegalPlugins; - return false; - } -#endif - for (size_t i = 0; i < list_value->GetSize(); ++i) { - DictionaryValue* plugin_value; + DictionaryValue* plugin_value = NULL; std::string path_str; bool is_public = false; @@ -1630,20 +1962,54 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, } } + // We don't allow extension plugins to run on Chrome OS. We still + // parse the manifest entry so that error messages are consistently + // displayed across platforms. +#if !defined(OS_CHROMEOS) plugins_.push_back(PluginInfo()); plugins_.back().path = path().AppendASCII(path_str); plugins_.back().is_public = is_public; +#endif } } - // Initialize background url (optional). - if (source.HasKey(keys::kBackground)) { - std::string background_str; - if (!source.GetString(keys::kBackground, &background_str)) { - *error = errors::kInvalidBackground; + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalExtensionApis) && + source.HasKey(keys::kNaClModules)) { + ListValue* list_value = NULL; + if (!source.GetList(keys::kNaClModules, &list_value)) { + *error = errors::kInvalidNaClModules; return false; } - background_url_ = GetResourceURL(background_str); + + for (size_t i = 0; i < list_value->GetSize(); ++i) { + DictionaryValue* module_value = NULL; + std::string path_str; + std::string mime_type; + + if (!list_value->GetDictionary(i, &module_value)) { + *error = errors::kInvalidNaClModules; + return false; + } + + // Get nacl_modules[i].path. + if (!module_value->GetString(keys::kNaClModulesPath, &path_str)) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidNaClModulesPath, base::IntToString(i)); + return false; + } + + // Get nacl_modules[i].mime_type. + if (!module_value->GetString(keys::kNaClModulesMIMEType, &mime_type)) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidNaClModulesMIMEType, base::IntToString(i)); + return false; + } + + nacl_modules_.push_back(NaClModuleInfo()); + nacl_modules_.back().url = GetResourceURL(path_str); + nacl_modules_.back().mime_type = mime_type; + } } // Initialize toolstrips. This is deprecated for public use. @@ -1652,7 +2018,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, if (CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableExperimentalExtensionApis) && source.HasKey(keys::kToolstrips)) { - ListValue* list_value; + ListValue* list_value = NULL; if (!source.GetList(keys::kToolstrips, &list_value)) { *error = errors::kInvalidToolstrips; return false; @@ -1660,7 +2026,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, for (size_t i = 0; i < list_value->GetSize(); ++i) { GURL toolstrip; - DictionaryValue* toolstrip_value; + DictionaryValue* toolstrip_value = NULL; std::string toolstrip_path; if (list_value->GetString(i, &toolstrip_path)) { // Support a simple URL value for backwards compatibility. @@ -1691,7 +2057,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, } for (size_t i = 0; i < list_value->GetSize(); ++i) { - DictionaryValue* content_script; + DictionaryValue* content_script = NULL; if (!list_value->GetDictionary(i, &content_script)) { *error = ExtensionErrorUtils::FormatErrorMessage( errors::kInvalidContentScript, base::IntToString(i)); @@ -1699,8 +2065,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, } UserScript script; - if (!LoadUserScriptHelper(content_script, i, parse_strictness, error, - &script)) + if (!LoadUserScriptHelper(content_script, i, flags, error, &script)) return false; // Failed to parse script context definition. script.set_extension_id(id()); if (converted_from_user_script_) { @@ -1715,7 +2080,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, DictionaryValue* page_action_value = NULL; if (source.HasKey(keys::kPageActions)) { - ListValue* list_value; + ListValue* list_value = NULL; if (!source.GetList(keys::kPageActions, &list_value)) { *error = errors::kInvalidPageActionsList; return false; @@ -1752,7 +2117,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // Initialize browser action (optional). if (source.HasKey(keys::kBrowserAction)) { - DictionaryValue* browser_action_value; + DictionaryValue* browser_action_value = NULL; if (!source.GetDictionary(keys::kBrowserAction, &browser_action_value)) { *error = errors::kInvalidBrowserAction; return false; @@ -1764,6 +2129,21 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; // Failed to parse browser action definition. } + // Initialize file browser actions (optional). + if (source.HasKey(keys::kFileBrowserHandlers)) { + ListValue* file_browser_handlers_value = NULL; + if (!source.GetList(keys::kFileBrowserHandlers, + &file_browser_handlers_value)) { + *error = errors::kInvalidFileBrowserHandler; + return false; + } + + file_browser_handlers_.reset( + LoadFileBrowserHandlers(file_browser_handlers_value, error)); + if (!file_browser_handlers_.get()) + return false; // Failed to parse file browser actions definition. + } + // Load App settings. if (!LoadIsApp(manifest_value_.get(), error) || !LoadExtent(manifest_value_.get(), keys::kWebURLs, @@ -1772,7 +2152,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, parse_strictness, error) || !EnsureNotHybridApp(manifest_value_.get(), error) || !LoadLaunchURL(manifest_value_.get(), error) || - !LoadLaunchContainer(manifest_value_.get(), error)) { + !LoadLaunchContainer(manifest_value_.get(), error) || + !LoadAppIsolation(manifest_value_.get(), error)) { return false; } @@ -1794,7 +2175,6 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } options_url_ = options_url; - } else { GURL absolute(options_str); if (absolute.is_valid()) { @@ -1826,11 +2206,15 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } - // Only COMPONENT extensions can use the webstorePrivate APIs. + // Only COMPONENT extensions can use private APIs. // TODO(asargent) - We want a more general purpose mechanism for this, // and better error messages. (http://crbug.com/54013) - if (permission_str == kWebstorePrivatePermission && - location_ != Extension::COMPONENT) { + if (!IsComponentOnlyPermission(permission_str) +#ifndef NDEBUG + && !CommandLine::ForCurrentProcess()->HasSwitch( + switches::kExposePrivateExtensionApi) +#endif + ) { continue; } @@ -1878,6 +2262,14 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // to match all paths. pattern.SetPath("/*"); + if (pattern.MatchesScheme(chrome::kFileScheme) && + !CanExecuteScriptEverywhere()) { + wants_file_access_ = true; + if (!(flags & ALLOW_FILE_ACCESS)) + pattern.set_valid_schemes( + pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); + } + host_permissions_.push_back(pattern); } @@ -1893,6 +2285,41 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, } } + // Initialize background url (optional). + if (source.HasKey(keys::kBackground)) { + std::string background_str; + if (!source.GetString(keys::kBackground, &background_str)) { + *error = errors::kInvalidBackground; + return false; + } + + if (is_hosted_app()) { + // Make sure "background" permission is set. + if (api_permissions_.find(kBackgroundPermission) == + api_permissions_.end()) { + *error = errors::kBackgroundPermissionNeeded; + return false; + } + // Hosted apps require an absolute URL. + GURL bg_page(background_str); + if (!bg_page.is_valid()) { + *error = errors::kInvalidBackgroundInHostedApp; + return false; + } + + if (!(bg_page.SchemeIs("https") || + (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAllowHTTPBackgroundPage) && + bg_page.SchemeIs("http")))) { + *error = errors::kInvalidBackgroundInHostedApp; + return false; + } + background_url_ = bg_page; + } else { + background_url_ = GetResourceURL(background_str); + } + } + if (source.HasKey(keys::kDefaultLocale)) { if (!source.GetString(keys::kDefaultLocale, &default_locale_) || !l10n_util::IsValidLocaleSyntax(default_locale_)) { @@ -1903,7 +2330,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // Chrome URL overrides (optional) if (source.HasKey(keys::kChromeURLOverrides)) { - DictionaryValue* overrides; + DictionaryValue* overrides = NULL; if (!source.GetDictionary(keys::kChromeURLOverrides, &overrides)) { *error = errors::kInvalidChromeURLOverrides; return false; @@ -1963,7 +2390,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // Initialize sidebar action (optional). if (source.HasKey(keys::kSidebar)) { - DictionaryValue* sidebar_value; + DictionaryValue* sidebar_value = NULL; if (!source.GetDictionary(keys::kSidebar, &sidebar_value)) { *error = errors::kInvalidSidebar; return false; @@ -1979,21 +2406,21 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // Initialize text-to-speech voices (optional). if (source.HasKey(keys::kTts)) { - DictionaryValue* tts_dict; + DictionaryValue* tts_dict = NULL; if (!source.GetDictionary(keys::kTts, &tts_dict)) { *error = errors::kInvalidTts; return false; } if (tts_dict->HasKey(keys::kTtsVoices)) { - ListValue* tts_voices; + ListValue* tts_voices = NULL; if (!tts_dict->GetList(keys::kTtsVoices, &tts_voices)) { *error = errors::kInvalidTtsVoices; return false; } for (size_t i = 0; i < tts_voices->GetSize(); i++) { - DictionaryValue* one_tts_voice; + DictionaryValue* one_tts_voice = NULL; if (!tts_voices->GetDictionary(i, &one_tts_voice)) { *error = errors::kInvalidTtsVoices; return false; @@ -2237,7 +2664,7 @@ bool Extension::CanSpecifyHostPermission(const URLPattern& pattern) const { pattern.MatchesScheme(chrome::kChromeUIScheme)) { // Only allow access to chrome://favicon to regular extensions. Component // extensions can have access to all of chrome://*. - return (pattern.host() == chrome::kChromeUIFavIconHost || + return (pattern.host() == chrome::kChromeUIFaviconHost || CanExecuteScriptEverywhere()); } @@ -2282,6 +2709,13 @@ bool Extension::HasApiPermission( bool Extension::HasHostPermission(const GURL& url) const { for (URLPatternList::const_iterator host = host_permissions().begin(); host != host_permissions().end(); ++host) { + // Non-component extensions can only access chrome://favicon and no other + // chrome:// scheme urls. + if (url.SchemeIs(chrome::kChromeUIScheme) && + url.host() != chrome::kChromeUIFaviconHost && + location() != Extension::COMPONENT) + return false; + if (host->MatchesUrl(url)) return true; } @@ -2289,6 +2723,16 @@ bool Extension::HasHostPermission(const GURL& url) const { } void Extension::InitEffectiveHostPermissions() { + // Some APIs effectively grant access to every site. New ones should be + // added here. (I'm looking at you, network API) + if (HasApiPermission(api_permissions_, kProxyPermission) || + !devtools_url_.is_empty()) { + URLPattern all_urls(URLPattern::SCHEME_ALL); + all_urls.set_match_all_urls(true); + effective_host_permissions_.AddPattern(all_urls); + return; + } + for (URLPatternList::const_iterator host = host_permissions().begin(); host != host_permissions().end(); ++host) effective_host_permissions_.AddPattern(*host); @@ -2303,6 +2747,19 @@ void Extension::InitEffectiveHostPermissions() { } } +bool Extension::IsComponentOnlyPermission + (const std::string& permission) const { + if (location() == Extension::COMPONENT) + return true; + + // Non-component extensions are not allowed to access private apis. + for (size_t i = 0; i < Extension::kNumComponentPrivatePermissions; ++i) { + if (permission == Extension::kComponentPrivatePermissionNames[i]) + return false; + } + return true; +} + bool Extension::HasMultipleUISurfaces() const { int num_surfaces = 0; @@ -2319,7 +2776,7 @@ bool Extension::HasMultipleUISurfaces() const { } bool Extension::CanExecuteScriptOnPage(const GURL& page_url, - UserScript* script, + const UserScript* script, std::string* error) const { // The gallery is special-cased as a restricted URL for scripting to prevent // access to special JS bindings we expose to the gallery (and avoid things @@ -2335,6 +2792,10 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url, return false; } + if (page_url.SchemeIs(chrome::kChromeUIScheme) && + !CanExecuteScriptEverywhere()) + return false; + // If a script is specified, use its matches. if (script) return script->MatchesUrl(page_url); @@ -2358,15 +2819,11 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url, bool Extension::HasEffectiveAccessToAllHosts( const ExtensionExtent& effective_host_permissions, const std::set<std::string>& api_permissions) { - // Some APIs effectively grant access to every site. New ones should be - // added here. (I'm looking at you, network API) - if (HasApiPermission(api_permissions, kProxyPermission)) - return true; - const URLPatternList patterns = effective_host_permissions.patterns(); for (URLPatternList::const_iterator host = patterns.begin(); host != patterns.end(); ++host) { - if (host->match_subdomains() && host->host().empty()) + if (host->match_all_urls() || + (host->match_subdomains() && host->host().empty())) return true; } @@ -2400,7 +2857,12 @@ bool Extension::IsAPIPermission(const std::string& str) const { } bool Extension::CanExecuteScriptEverywhere() const { - if (location() == Extension::COMPONENT) + if (location() == Extension::COMPONENT +#ifndef NDEBUG + || CommandLine::ForCurrentProcess()->HasSwitch( + switches::kExposePrivateExtensionApi) +#endif + ) return true; ScriptingWhitelist* whitelist = diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 49850aa..d08f17e 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -13,8 +13,9 @@ #include "base/file_path.h" #include "base/gtest_prod_util.h" -#include "base/scoped_ptr.h" -#include "base/ref_counted.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_extent.h" #include "chrome/common/extensions/extension_icon_set.h" @@ -27,6 +28,8 @@ class DictionaryValue; class ExtensionAction; class ExtensionResource; class ExtensionSidebarDefaults; +class FileBrowserHandler; +class ListValue; class SkBitmap; class Version; @@ -35,6 +38,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { public: typedef std::map<const std::string, GURL> URLOverrideMap; typedef std::vector<std::string> ScriptingWhitelist; + typedef std::vector<linked_ptr<FileBrowserHandler> > FileBrowserHandlerList; // What an extension was loaded from. // NOTE: These values are stored as integers in the preferences and used @@ -61,8 +65,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> { enum State { DISABLED = 0, ENABLED, - KILLBIT, // Don't install/upgrade (applies to external extensions only). - + // An external extension that the user uninstalled. We should not reinstall + // such extensions on startup. + EXTERNAL_EXTENSION_UNINSTALLED, NUM_STATES }; @@ -101,53 +106,154 @@ class Extension : public base::RefCountedThreadSafe<Extension> { bool is_public; // False if only this extension can load this plugin. }; + // An NaCl module included in the extension. + struct NaClModuleInfo { + GURL url; + std::string mime_type; + }; + struct TtsVoice { std::string voice_name; std::string locale; std::string gender; }; + // When prompting the user to install or approve permissions, we display + // messages describing the effects of the permissions and not the permissions + // themselves. Each PermissionMessage represents one of the messages that is + // shown to the user. + class PermissionMessage { + public: + // Do not reorder or add new enumerations in this list. If you need to add a + // new enum, add it just prior to ID_ENUM_BOUNDARY and enter its l10n + // message in kMessageIds. + enum MessageId { + ID_UNKNOWN, + ID_NONE, + ID_BOOKMARKS, + ID_GEOLOCATION, + ID_BROWSING_HISTORY, + ID_TABS, + ID_MANAGEMENT, + ID_DEBUGGER, + ID_HOSTS_1, + ID_HOSTS_2, + ID_HOSTS_3, + ID_HOSTS_4_OR_MORE, + ID_HOSTS_ALL, + ID_FULL_ACCESS, + ID_ENUM_BOUNDARY + }; + + // Creates a permission message with the given |message_id| and initializes + // its message to the appropriate value. + static PermissionMessage CreateFromMessageId(MessageId message_id); + + // Creates the corresponding permission message for a list of hosts. This + // method exists because the hosts are presented as one message that depends + // on what and how many hosts there are. + static PermissionMessage CreateFromHostList( + const std::vector<std::string> hosts); + + // Gets the id of the permission message, which can be used in UMA + // histograms. + MessageId message_id() const { return message_id_; } + + // Gets a localized message describing this permission. Please note that + // the message will be empty for message types TYPE_NONE and TYPE_UNKNOWN. + const string16& message() const { return message_; } + + // Comparator to work with std::set. + bool operator<(const PermissionMessage& that) const { + return message_id_ < that.message_id_; + } + + private: + PermissionMessage(MessageId message_id, string16 message_); + + // The index of the id in the array is its enum value. The first two values + // are non-existent message ids to act as placeholders for "unknown" and + // "none". + // Note: Do not change the order of the items in this list since they + // are used in a histogram. The order must match the MessageId order. + static const int kMessageIds[]; + + MessageId message_id_; + string16 message_; + }; + + typedef std::vector<PermissionMessage> PermissionMessages; + // A permission is defined by its |name| (what is used in the manifest), // and the |message_id| that's used by install/update UI. struct Permission { const char* const name; - const int message_id; + const PermissionMessage::MessageId message_id; + }; + + enum InitFromValueFlags { + NO_FLAGS = 0, + + // Usually, the id of an extension is generated by the "key" property of + // its manifest, but if |REQUIRE_KEY| is not set, a temporary ID will be + // generated based on the path. + REQUIRE_KEY = 1 << 0, + + // |STRICT_ERROR_CHECKS| enables extra error checking, such as + // checks that URL patterns do not contain ports. This error + // checking may find an error that a previous version of + // Chrome did not flag. To avoid errors in installed extensions + // when Chrome is upgraded, strict error checking is only enabled + // when loading extensions as a developer would (such as loading + // an unpacked extension), or when loading an extension that is + // tied to a specific version of Chrome (such as a component + // extension). Most callers will set the |STRICT_ERROR_CHECKS| bit when + // Extension::ShouldDoStrictErrorChecking(location) returns true. + STRICT_ERROR_CHECKS = 1 << 1, + + // |ALLOW_FILE_ACCESS| indicates that the user is allowing this extension + // to have file access. If it's not present, then permissions and content + // scripts that match file:/// URLs will be filtered out. + ALLOW_FILE_ACCESS = 1 << 2, }; - // |strict_error_checks| enables extra error checking, such as - // checks that URL patterns do not contain ports. This error - // checking may find an error that a previous version of - // chrome did not flag. To avoid errors in installed extensions - // when chrome is upgraded, strict error checking is only enabled - // when loading extensions as a developer would (such as loading - // an unpacked extension), or when loading an extension that is - // tied to a specific version of chrome (such as a component - // extension). Most callers will set |strict_error_checks| to - // Extension::ShouldDoStrictErrorChecking(location). static scoped_refptr<Extension> Create(const FilePath& path, Location location, const DictionaryValue& value, - bool require_key, - bool strict_error_checks, + int flags, std::string* error); // Return the update url used by gallery/webstore extensions. static GURL GalleryUpdateUrl(bool secure); - // The install message id for |permission|. Returns 0 if none exists. - static int GetPermissionMessageId(const std::string& permission); + // Given two install sources, return the one which should take priority + // over the other. If an extension is installed from two sources A and B, + // its install source should be set to GetHigherPriorityLocation(A, B). + static Location GetHigherPriorityLocation(Location loc1, Location loc2); + + // Get's the install message id for |permission|. Returns + // MessageId::TYPE_NONE if none exists. + static PermissionMessage::MessageId GetPermissionMessageId( + const std::string& permission); // Returns the full list of permission messages that this extension // should display at install time. - std::vector<string16> GetPermissionMessages() const; + PermissionMessages GetPermissionMessages() const; + + // Returns the full list of permission messages that this extension + // should display at install time. The messages are returned as strings + // for convenience. + std::vector<string16> GetPermissionMessageStrings() const; // Returns the distinct hosts that should be displayed in the install UI // for the URL patterns |list|. This discards some of the detail that is // present in the manifest to make it as easy as possible to process by // users. In particular we disregard the scheme and path components of // URLPatterns and de-dupe the result, which includes filtering out common - // hosts with differing RCDs. (NOTE: when de-duping hosts with common RCDs, - // the first pattern is returned and the rest discarded) + // hosts with differing RCDs (aka Registry Controlled Domains, most of which + // are Top Level Domains but also include exceptions like co.uk). + // NOTE: when de-duping hosts the preferred RCD will be returned, given this + // order of preference: .com, .net, .org, first in list. static std::vector<std::string> GetDistinctHostsForDisplay( const URLPatternList& list); @@ -174,7 +280,11 @@ class Extension : public base::RefCountedThreadSafe<Extension> { static const char kContentSettingsPermission[]; static const char kContextMenusPermission[]; static const char kCookiePermission[]; + static const char kChromeosInfoPrivatePermissions[]; + static const char kDebuggerPermission[]; static const char kExperimentalPermission[]; + static const char kFileBrowserHandlerPermission[]; + static const char kFileBrowserPrivatePermission[]; static const char kGeolocationPermission[]; static const char kHistoryPermission[]; static const char kIdlePermission[]; @@ -189,6 +299,8 @@ class Extension : public base::RefCountedThreadSafe<Extension> { static const size_t kNumPermissions; static const char* const kHostedAppPermissionNames[]; static const size_t kNumHostedAppPermissions; + static const char* const kComponentPrivatePermissionNames[]; + static const size_t kNumComponentPrivatePermissions; // The old name for the unlimited storage permission, which is deprecated but // still accepted as meaning the same thing as kUnlimitedStoragePermission. @@ -227,6 +339,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { static bool IdIsValid(const std::string& id); // Generate an ID for an extension in the given path. + // Used while developing extensions, before they have a key. static std::string GenerateIdForPath(const FilePath& file_name); // Returns true if the specified file is an extension. @@ -247,6 +360,14 @@ class Extension : public base::RefCountedThreadSafe<Extension> { IsExternalLocation(location); } + // Whether extensions with |location| can be uninstalled or not. Policy + // controlled extensions are silently auto-installed and updated, and cannot + // be disabled by the user. The same applies for internal components. + static inline bool UserMayDisable(Location location) { + return location != Extension::EXTERNAL_POLICY_DOWNLOAD && + location != Extension::COMPONENT; + } + // Whether extensions with |location| should be loaded with strict // error checking. Strict error checks may flag errors older versions // of chrome did not detect. To avoid breaking installed extensions, @@ -258,6 +379,12 @@ class Extension : public base::RefCountedThreadSafe<Extension> { location == Extension::COMPONENT; } + // Unpacked extensions start off with file access since they are a developer + // feature. + static inline bool ShouldAlwaysAllowFileAccess(Location location) { + return location == Extension::LOAD; + } + // See Type definition above. Type GetType() const; @@ -294,8 +421,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // Expects base64 encoded |input| and formats into |output| including // the appropriate header & footer. - static bool FormatPEMForFileOutput(const std::string input, - std::string* output, bool is_public); + static bool FormatPEMForFileOutput(const std::string& input, + std::string* output, + bool is_public); // Determine whether |new_extension| has increased privileges compared to // its previously granted permissions, specified by |granted_apis|, @@ -321,6 +449,10 @@ class Extension : public base::RefCountedThreadSafe<Extension> { Icons icon_size, scoped_ptr<SkBitmap>* result); + // Returns the default extension/app icon (for extensions or apps that don't + // have one). + static const SkBitmap& GetDefaultIcon(bool is_app); + // Returns the base extension url for a given |extension_id|. static GURL GetBaseURLFromExtensionId(const std::string& extension_id); @@ -414,7 +546,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // This method is also aware of certain special pages that extensions are // usually not allowed to run script on. bool CanExecuteScriptOnPage(const GURL& page_url, - UserScript* script, + const UserScript* script, std::string* error) const; // Returns true if this extension is a COMPONENT extension, or if it is @@ -454,7 +586,13 @@ class Extension : public base::RefCountedThreadSafe<Extension> { ExtensionSidebarDefaults* sidebar_defaults() const { return sidebar_defaults_.get(); } + const FileBrowserHandlerList* file_browser_handlers() const { + return file_browser_handlers_.get(); + } const std::vector<PluginInfo>& plugins() const { return plugins_; } + const std::vector<NaClModuleInfo>& nacl_modules() const { + return nacl_modules_; + } const GURL& background_url() const { return background_url_; } const GURL& options_url() const { return options_url_; } const GURL& devtools_url() const { return devtools_url_; } @@ -476,10 +614,13 @@ class Extension : public base::RefCountedThreadSafe<Extension> { bool incognito_split_mode() const { return incognito_split_mode_; } const std::vector<TtsVoice>& tts_voices() const { return tts_voices_; } + bool wants_file_access() const { return wants_file_access_; } + // App-related. bool is_app() const { return is_app_; } bool is_hosted_app() const { return is_app() && !web_extent().is_empty(); } bool is_packaged_app() const { return is_app() && web_extent().is_empty(); } + bool is_storage_isolated() const { return is_app() && is_storage_isolated_; } const ExtensionExtent& web_extent() const { return extent_; } const std::string& launch_local_path() const { return launch_local_path_; } const std::string& launch_web_url() const { return launch_web_url_; } @@ -531,11 +672,8 @@ class Extension : public base::RefCountedThreadSafe<Extension> { ~Extension(); // Initialize the extension from a parsed manifest. - // Usually, the id of an extension is generated by the "key" property of - // its manifest, but if |require_key| is |false|, a temporary ID will be - // generated based on the path. - bool InitFromValue(const DictionaryValue& value, bool require_key, - bool strict_error_checks, std::string* error); + bool InitFromValue(const DictionaryValue& value, int flags, + std::string* error); // Helper function for implementing HasCachedImage/GetCachedImage. A return // value of NULL means there is no matching image cached (we allow caching an @@ -547,7 +685,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // dictionary in the content_script list of the manifest. bool LoadUserScriptHelper(const DictionaryValue* content_script, int definition_index, - URLPattern::ParseOption parse_strictness, + int flags, std::string* error, UserScript* result); @@ -571,6 +709,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { std::string* error); bool LoadLaunchContainer(const DictionaryValue* manifest, std::string* error); bool LoadLaunchURL(const DictionaryValue* manifest, std::string* error); + bool LoadAppIsolation(const DictionaryValue* manifest, std::string* error); bool EnsureNotHybridApp(const DictionaryValue* manifest, std::string* error); // Helper method to load an ExtensionAction from the page_action or @@ -578,6 +717,13 @@ class Extension : public base::RefCountedThreadSafe<Extension> { ExtensionAction* LoadExtensionActionHelper( const DictionaryValue* extension_action, std::string* error); + // Helper method to load an FileBrowserHandlerList from the manifest. + FileBrowserHandlerList* LoadFileBrowserHandlers( + const ListValue* extension_actions, std::string* error); + // Helper method to load an FileBrowserHandler from manifest. + FileBrowserHandler* LoadFileBrowserHandler( + const DictionaryValue* file_browser_handlers, std::string* error); + // Helper method to load an ExtensionSidebarDefaults from the sidebar manifest // entry. ExtensionSidebarDefaults* LoadExtensionSidebarDefaults( @@ -599,15 +745,15 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // kPermissions). bool IsAPIPermission(const std::string& permission) const; + // Returns true if this is a component, or we are not attempting to access a + // component-private permission. + bool IsComponentOnlyPermission(const std::string& permission) const; + // The set of unique API install messages that the extension has. // NOTE: This only includes messages related to permissions declared in the // "permissions" key in the manifest. Permissions implied from other features // of the manifest, like plugins and content scripts are not included. - std::set<string16> GetSimplePermissionMessages() const; - - // The permission message displayed related to the host permissions for - // this extension. - string16 GetHostPermissionMessage() const; + std::set<PermissionMessage> GetSimplePermissionMessages() const; // Cached images for this extension. This should only be touched on the UI // thread. @@ -676,12 +822,18 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // The extension's browser action, if any. scoped_ptr<ExtensionAction> browser_action_; + // The extension's file browser actions, if any. + scoped_ptr<FileBrowserHandlerList> file_browser_handlers_; + // The extension's sidebar, if any. scoped_ptr<ExtensionSidebarDefaults> sidebar_defaults_; // Optional list of NPAPI plugins and associated properties. std::vector<PluginInfo> plugins_; + // Optional list of NaCl modules and associated properties. + std::vector<NaClModuleInfo> nacl_modules_; + // Optional URL to a master page of which a single instance should be always // loaded in the background. GURL background_url_; @@ -733,6 +885,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // Whether this extension uses app features. bool is_app_; + // Whether this extension requests isolated storage. + bool is_storage_isolated_; + // The local path inside the extension to use with the launcher. std::string launch_local_path_; @@ -758,6 +913,11 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // List of text-to-speech voices that this extension provides, if any. std::vector<TtsVoice> tts_voices_; + // Whether the extension has host permissions or user script patterns that + // imply access to file:/// scheme URLs (the user may not have actually + // granted it that access). + bool wants_file_access_; + FRIEND_TEST_ALL_PREFIXES(ExtensionServiceTest, UpdateExtensionPreservesLocation); FRIEND_TEST_ALL_PREFIXES(ExtensionTest, LoadPageActionHelper); diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index 96fd349..fc5f4ef 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -19,10 +19,13 @@ const char* kDefaultLocale = "default_locale"; const char* kDescription = "description"; const char* kDevToolsPage = "devtools_page"; const char* kExcludeGlobs = "exclude_globs"; +const char* kFileFilters = "file_filters"; +const char* kFileBrowserHandlers = "file_browser_handlers"; const char* kHomepageURL = "homepage_url"; const char* kIcons = "icons"; const char* kIncognito = "incognito"; const char* kIncludeGlobs = "include_globs"; +const char* kIsolation = "app.isolation"; const char* kJs = "js"; const char* kLaunch = "app.launch"; const char* kLaunchContainer = "app.launch.container"; @@ -33,6 +36,9 @@ const char* kLaunchWidth = "app.launch.width"; const char* kMatches = "matches"; const char* kMinimumChromeVersion = "minimum_chrome_version"; const char* kName = "name"; +const char* kNaClModules = "nacl_modules"; +const char* kNaClModulesMIMEType = "mime_type"; +const char* kNaClModulesPath = "path"; const char* kOmnibox = "omnibox"; const char* kOmniboxKeyword = "omnibox.keyword"; const char* kOptionsPage = "options_page"; @@ -80,6 +86,7 @@ const char* kWebURLs = "app.urls"; namespace extension_manifest_values { const char* kIncognitoSplit = "split"; const char* kIncognitoSpanning = "spanning"; +const char* kIsolatedStorage = "storage"; const char* kRunAtDocumentStart = "document_start"; const char* kRunAtDocumentEnd = "document_end"; const char* kRunAtDocumentIdle = "document_idle"; @@ -97,6 +104,9 @@ const char* kLaunchContainerWindow = "window"; namespace extension_manifest_errors { const char* kAppsNotEnabled = "Apps are not enabled."; +const char* kBackgroundPermissionNeeded = + "Hosted apps that use 'background_page' must have the 'background' " + "permission."; const char* kCannotAccessPage = "Cannot access contents of url \"*\". " "Extension manifest must request permission to access this host."; @@ -106,6 +116,8 @@ const char* kCannotClaimAllURLsInExtent = "Cannot claim all URLs in an extent."; const char* kCannotScriptGallery = "The extensions gallery cannot be scripted."; +const char* kCannotUninstallManagedExtension = + "Attempted uninstallation of an extension that is not user-manageable."; const char* kChromeVersionTooLow = "This extension requires * version * or greater."; const char* kDisabledByPolicy = @@ -117,12 +129,18 @@ const char* kExpectString = "Expect string value."; const char* kExperimentalFlagRequired = "Loading extensions with 'experimental' permission requires" " --enable-experimental-extension-apis command line flag."; +const char *kExperimentalFeature = + "This feature requires 'experimental' permissions and" + " --enable-experimental-extension-apis command line flag."; const char* kHostedAppsCannotIncludeExtensionFeatures = - "Hosted apps cannot use extension features."; + "Hosted apps cannot use the extension feature '*'."; const char* kInvalidAllFrames = "Invalid value for 'content_scripts[*].all_frames'."; const char* kInvalidBackground = "Invalid value for 'background_page'."; +const char* kInvalidBackgroundInHostedApp = + "Invalid value for 'background_page'. Hosted apps must specify an " + "absolute HTTPS URL for the background page."; const char* kInvalidBrowserAction = "Invalid value for 'browser_action'."; const char* kInvalidChromeURLOverrides = @@ -141,6 +159,12 @@ const char* kInvalidDescription = "Invalid value for 'description'."; const char* kInvalidDevToolsPage = "Invalid value for 'devtools_page'."; +const char* kInvalidFileBrowserHandler = + "Invalid value for 'file_browser_handers'."; +const char* kInvalidFileFiltersList = + "Invalid value for 'file_filters'."; +const char* kInvalidFileFilterValue = + "Invalid value for 'file_filters[*]'."; const char* kInvalidGlob = "Invalid value for 'content_scripts[*].*[*]'."; const char* kInvalidGlobList = @@ -153,6 +177,10 @@ const char* kInvalidIcons = "Invalid value for 'icons'."; const char* kInvalidIncognitoBehavior = "Invalid value for 'incognito'."; +const char* kInvalidIsolation = + "Invalid value for 'app.isolation'."; +const char* kInvalidIsolationValue = + "Invalid value for 'app.isolation[*]'."; const char* kInvalidJs = "Invalid value for 'content_scripts[*].js[*]'."; const char* kInvalidJsList = @@ -186,6 +214,12 @@ const char* kInvalidMinimumChromeVersion = "Invalid value for 'minimum_chrome_version'."; const char* kInvalidName = "Required value 'name' is missing or invalid."; +const char* kInvalidNaClModules = + "Invalid value for 'nacl_modules'."; +const char* kInvalidNaClModulesPath = + "Invalid value for 'nacl_modules[*].path'."; +const char* kInvalidNaClModulesMIMEType = + "Invalid value for 'nacl_modules[*].mime_type'."; const char* kInvalidOmniboxKeyword = "Invalid value for 'omnibox.keyword'."; const char* kInvalidOptionsPage = @@ -270,6 +304,8 @@ const char* kInvalidTtsVoicesVoiceName = "Invalid value for 'tts.voices[*].voiceName'."; const char* kInvalidUpdateURL = "Invalid value for update url: '[*]'."; +const char* kInvalidURLPatternError = + "Invalid url pattern '*'"; const char* kInvalidVersion = "Required value 'version' is missing or invalid. It must be between 1-4 " "dot-separated integers each between 0 and 65536."; diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index bad2a5b..6b703f3 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -24,10 +24,13 @@ namespace extension_manifest_keys { extern const char* kDescription; extern const char* kDevToolsPage; extern const char* kExcludeGlobs; + extern const char* kFileFilters; + extern const char* kFileBrowserHandlers; extern const char* kHomepageURL; extern const char* kIcons; extern const char* kIncognito; extern const char* kIncludeGlobs; + extern const char* kIsolation; extern const char* kJs; extern const char* kLaunch; extern const char* kLaunchContainer; @@ -37,6 +40,9 @@ namespace extension_manifest_keys { extern const char* kLaunchWidth; extern const char* kMatches; extern const char* kMinimumChromeVersion; + extern const char* kNaClModules; + extern const char* kNaClModulesMIMEType; + extern const char* kNaClModulesPath; extern const char* kName; extern const char* kOmnibox; extern const char* kOmniboxKeyword; @@ -86,6 +92,7 @@ namespace extension_manifest_keys { namespace extension_manifest_values { extern const char* kIncognitoSplit; extern const char* kIncognitoSpanning; + extern const char* kIsolatedStorage; extern const char* kLaunchContainerPanel; extern const char* kLaunchContainerTab; extern const char* kLaunchContainerWindow; @@ -99,18 +106,22 @@ namespace extension_manifest_values { // Error messages returned from Extension::InitFromValue(). namespace extension_manifest_errors { extern const char* kAppsNotEnabled; + extern const char* kBackgroundPermissionNeeded; extern const char* kCannotAccessPage; extern const char* kCannotClaimAllHostsInExtent; extern const char* kCannotClaimAllURLsInExtent; extern const char* kCannotScriptGallery; + extern const char* kCannotUninstallManagedExtension; extern const char* kChromeVersionTooLow; extern const char* kDevToolsExperimental; extern const char* kDisabledByPolicy; extern const char* kExperimentalFlagRequired; + extern const char* kExperimentalFeature; extern const char* kExpectString; extern const char* kHostedAppsCannotIncludeExtensionFeatures; extern const char* kInvalidAllFrames; extern const char* kInvalidBackground; + extern const char* kInvalidBackgroundInHostedApp; extern const char* kInvalidBrowserAction; extern const char* kInvalidBrowseURL; extern const char* kInvalidBrowseURLs; @@ -122,12 +133,17 @@ namespace extension_manifest_errors { extern const char* kInvalidDefaultLocale; extern const char* kInvalidDescription; extern const char* kInvalidDevToolsPage; + extern const char* kInvalidFileBrowserHandler; + extern const char* kInvalidFileFiltersList; + extern const char* kInvalidFileFilterValue; extern const char* kInvalidGlob; extern const char* kInvalidGlobList; extern const char* kInvalidHomepageURL; extern const char* kInvalidIconPath; extern const char* kInvalidIcons; extern const char* kInvalidIncognitoBehavior; + extern const char* kInvalidIsolation; + extern const char* kInvalidIsolationValue; extern const char* kInvalidJs; extern const char* kInvalidJsList; extern const char* kInvalidKey; @@ -143,6 +159,9 @@ namespace extension_manifest_errors { extern const char* kInvalidMatchCount; extern const char* kInvalidMatches; extern const char* kInvalidMinimumChromeVersion; + extern const char* kInvalidNaClModules; + extern const char* kInvalidNaClModulesMIMEType; + extern const char* kInvalidNaClModulesPath; extern const char* kInvalidName; extern const char* kInvalidOmniboxKeyword; extern const char* kInvalidOptionsPage; @@ -185,6 +204,7 @@ namespace extension_manifest_errors { extern const char* kInvalidTtsVoicesLocale; extern const char* kInvalidTtsVoicesVoiceName; extern const char* kInvalidUpdateURL; + extern const char* kInvalidURLPatternError; extern const char* kInvalidVersion; extern const char* kInvalidWebURL; extern const char* kInvalidWebURLs; @@ -329,6 +349,11 @@ namespace extension_misc { // App launched after the user re-enabled it on the NTP. APP_LAUNCH_NTP_APP_RE_ENABLE, + // URL launched using the --app cmd line option, but the URL does not + // correspond to an installed app. These launches are left over from a + // feature that let you make desktop shortcuts from the file menu. + APP_LAUNCH_CMD_LINE_APP_LEGACY, + APP_LAUNCH_BUCKET_BOUNDARY, APP_LAUNCH_BUCKET_INVALID }; diff --git a/chrome/common/extensions/extension_file_util.cc b/chrome/common/extensions/extension_file_util.cc index 6e9a4eb..d378d85 100644 --- a/chrome/common/extensions/extension_file_util.cc +++ b/chrome/common/extensions/extension_file_util.cc @@ -9,9 +9,9 @@ #include "base/file_util.h" #include "base/logging.h" +#include "base/memory/scoped_temp_dir.h" #include "base/metrics/histogram.h" #include "base/path_service.h" -#include "base/scoped_temp_dir.h" #include "base/threading/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "chrome/common/chrome_paths.h" @@ -21,7 +21,7 @@ #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_resource.h" #include "chrome/common/extensions/extension_sidebar_defaults.h" -#include "chrome/common/json_value_serializer.h" +#include "content/common/json_value_serializer.h" #include "grit/generated_resources.h" #include "net/base/escape.h" #include "net/base/file_stream.h" @@ -88,8 +88,7 @@ void UninstallExtension(const FilePath& extensions_dir, scoped_refptr<Extension> LoadExtension(const FilePath& extension_path, Extension::Location location, - bool require_key, - bool strict_error_checks, + int flags, std::string* error) { FilePath manifest_path = extension_path.Append(Extension::kManifestFilename); @@ -128,8 +127,7 @@ scoped_refptr<Extension> LoadExtension(const FilePath& extension_path, extension_path, location, *manifest, - require_key, - strict_error_checks, + flags, error)); if (!extension.get()) return NULL; @@ -247,8 +245,10 @@ bool ValidateExtension(Extension* extension, std::string* error) { } } - // Validate background page location. - if (!extension->background_url().is_empty()) { + // Validate background page location, except for hosted apps, which should use + // an external URL. Background page for hosted apps are verified when the + // extension is created (in Extension::InitFromValue) + if (!extension->background_url().is_empty() && !extension->is_hosted_app()) { FilePath page_path = ExtensionURLToRelativeFilePath( extension->background_url()); const FilePath path = extension->GetResource(page_path).GetFilePath(); diff --git a/chrome/common/extensions/extension_file_util.h b/chrome/common/extensions/extension_file_util.h index 066ee80..114549f 100644 --- a/chrome/common/extensions/extension_file_util.h +++ b/chrome/common/extensions/extension_file_util.h @@ -39,8 +39,7 @@ void UninstallExtension(const FilePath& extensions_dir, // on failure, with a description of the error in |error|. scoped_refptr<Extension> LoadExtension(const FilePath& extension_root, Extension::Location location, - bool require_key, - bool strict_error_checks, + int flags, std::string* error); // Returns true if the given extension object is valid and consistent. diff --git a/chrome/common/extensions/extension_file_util_unittest.cc b/chrome/common/extensions/extension_file_util_unittest.cc index d3cabff..5a6f782 100644 --- a/chrome/common/extensions/extension_file_util_unittest.cc +++ b/chrome/common/extensions/extension_file_util_unittest.cc @@ -1,12 +1,12 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. #include "chrome/common/extensions/extension_file_util.h" #include "base/file_util.h" +#include "base/memory/scoped_temp_dir.h" #include "base/path_service.h" -#include "base/scoped_temp_dir.h" #include "base/utf_string_conversions.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/extensions/extension.h" @@ -79,7 +79,7 @@ TEST(ExtensionFileUtil, LoadExtensionWithValidLocales) { std::string error; scoped_refptr<Extension> extension(extension_file_util::LoadExtension( - install_dir, Extension::LOAD, false, true, &error)); + install_dir, Extension::LOAD, Extension::STRICT_ERROR_CHECKS, &error)); ASSERT_TRUE(extension != NULL); EXPECT_EQ("The first extension that I made.", extension->description()); } @@ -95,7 +95,7 @@ TEST(ExtensionFileUtil, LoadExtensionWithoutLocalesFolder) { std::string error; scoped_refptr<Extension> extension(extension_file_util::LoadExtension( - install_dir, Extension::LOAD, false, true, &error)); + install_dir, Extension::LOAD, Extension::STRICT_ERROR_CHECKS, &error)); ASSERT_FALSE(extension == NULL); EXPECT_TRUE(error.empty()); } @@ -153,7 +153,7 @@ TEST(ExtensionFileUtil, LoadExtensionGivesHelpfullErrorOnMissingManifest) { std::string error; scoped_refptr<Extension> extension(extension_file_util::LoadExtension( - install_dir, Extension::LOAD, false, true, &error)); + install_dir, Extension::LOAD, Extension::STRICT_ERROR_CHECKS, &error)); ASSERT_TRUE(extension == NULL); ASSERT_FALSE(error.empty()); ASSERT_STREQ("Manifest file is missing or unreadable.", error.c_str()); @@ -170,7 +170,7 @@ TEST(ExtensionFileUtil, LoadExtensionGivesHelpfullErrorOnBadManifest) { std::string error; scoped_refptr<Extension> extension(extension_file_util::LoadExtension( - install_dir, Extension::LOAD, false, true, &error)); + install_dir, Extension::LOAD, Extension::STRICT_ERROR_CHECKS, &error)); ASSERT_TRUE(extension == NULL); ASSERT_FALSE(error.empty()); ASSERT_STREQ("Manifest is not valid JSON. " @@ -186,7 +186,7 @@ TEST(ExtensionFileUtil, FailLoadingNonUTF8Scripts) { std::string error; scoped_refptr<Extension> extension(extension_file_util::LoadExtension( - install_dir, Extension::LOAD, false, true, &error)); + install_dir, Extension::LOAD, Extension::STRICT_ERROR_CHECKS, &error)); ASSERT_TRUE(extension == NULL); ASSERT_STREQ("Could not load file 'bad_encoding.js' for content script. " "It isn't UTF-8 encoded.", error.c_str()); diff --git a/chrome/common/extensions/extension_l10n_util.cc b/chrome/common/extensions/extension_l10n_util.cc index 0874046..14b9178 100644 --- a/chrome/common/extensions/extension_l10n_util.cc +++ b/chrome/common/extensions/extension_l10n_util.cc @@ -1,24 +1,25 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. #include "chrome/common/extensions/extension_l10n_util.h" +#include <algorithm> #include <set> #include <string> #include <vector> #include "base/file_util.h" -#include "base/linked_ptr.h" #include "base/logging.h" +#include "base/memory/linked_ptr.h" #include "base/string_util.h" #include "base/values.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_file_util.h" #include "chrome/common/extensions/extension_message_bundle.h" -#include "chrome/common/json_value_serializer.h" #include "chrome/common/url_constants.h" +#include "content/common/json_value_serializer.h" #include "ui/base/l10n/l10n_util.h" #include "unicode/uloc.h" @@ -111,6 +112,20 @@ bool LocalizeManifest(const ExtensionMessageBundle& messages, if (!LocalizeManifestValue(keys::kOmniboxKeyword, messages, manifest, error)) return false; + ListValue* file_handlers = NULL; + if (manifest->GetList(keys::kFileBrowserHandlers, &file_handlers)) { + key.assign(keys::kFileBrowserHandlers); + for (size_t i = 0; i < file_handlers->GetSize(); i++) { + DictionaryValue* handler = NULL; + if (!file_handlers->GetDictionary(i, &handler)) { + *error = errors::kInvalidFileBrowserHandler; + return false; + } + if (!LocalizeManifestValue(keys::kPageActionDefaultTitle, messages, + handler, error)) + return false; + } + } // Add current locale key to the manifest, so we can overwrite prefs // with new manifest when chrome locale changes. manifest->SetString(keys::kCurrentLocale, CurrentLocaleOrDefault()); diff --git a/chrome/common/extensions/extension_l10n_util_unittest.cc b/chrome/common/extensions/extension_l10n_util_unittest.cc index 5a318bc..211cc9c 100644 --- a/chrome/common/extensions/extension_l10n_util_unittest.cc +++ b/chrome/common/extensions/extension_l10n_util_unittest.cc @@ -1,13 +1,13 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. #include "base/file_path.h" #include "base/file_util.h" -#include "base/linked_ptr.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_temp_dir.h" #include "base/path_service.h" -#include "base/scoped_ptr.h" -#include "base/scoped_temp_dir.h" #include "base/values.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/extensions/extension.h" @@ -234,6 +234,10 @@ ExtensionMessageBundle* CreateManifestBundle() { omnibox_keyword_tree->SetString("message", "omnibox keyword"); catalog->Set("omnibox_keyword", omnibox_keyword_tree); + DictionaryValue* file_handler_title_tree = new DictionaryValue(); + file_handler_title_tree->SetString("message", "file handler title"); + catalog->Set("file_handler_title", file_handler_title_tree); + std::vector<linked_ptr<DictionaryValue> > catalogs; catalogs.push_back(catalog); @@ -365,6 +369,36 @@ TEST(ExtensionL10nUtil, LocalizeManifestWithNameDescriptionOmniboxMsgs) { EXPECT_TRUE(error.empty()); } +TEST(ExtensionL10nUtil, LocalizeManifestWithNameDescriptionFileHandlerTitle) { + DictionaryValue manifest; + manifest.SetString(keys::kName, "__MSG_name__"); + manifest.SetString(keys::kDescription, "__MSG_description__"); + ListValue* handlers = new ListValue(); + manifest.Set(keys::kFileBrowserHandlers, handlers); + DictionaryValue* handler = new DictionaryValue(); + handlers->Append(handler); + handler->SetString(keys::kPageActionDefaultTitle, + "__MSG_file_handler_title__"); + + std::string error; + scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle()); + + EXPECT_TRUE( + extension_l10n_util::LocalizeManifest(*messages, &manifest, &error)); + + std::string result; + ASSERT_TRUE(manifest.GetString(keys::kName, &result)); + EXPECT_EQ("name", result); + + ASSERT_TRUE(manifest.GetString(keys::kDescription, &result)); + EXPECT_EQ("description", result); + + ASSERT_TRUE(handler->GetString(keys::kPageActionDefaultTitle, &result)); + EXPECT_EQ("file handler title", result); + + EXPECT_TRUE(error.empty()); +} + // Try with NULL manifest. TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithNullManifest) { ExtensionInfo info(NULL, "", FilePath(), Extension::LOAD); diff --git a/chrome/common/extensions/extension_localization_peer.cc b/chrome/common/extensions/extension_localization_peer.cc index f76ba38..dfb4f1d 100644 --- a/chrome/common/extensions/extension_localization_peer.cc +++ b/chrome/common/extensions/extension_localization_peer.cc @@ -4,10 +4,10 @@ #include "chrome/common/extensions/extension_localization_peer.h" -#include "base/scoped_ptr.h" +#include "base/memory/scoped_ptr.h" #include "base/string_util.h" #include "chrome/common/extensions/extension_message_bundle.h" -#include "chrome/common/render_messages.h" +#include "chrome/common/extensions/extension_messages.h" #include "chrome/common/url_constants.h" #include "grit/generated_resources.h" #include "net/base/net_errors.h" @@ -59,8 +59,10 @@ void ExtensionLocalizationPeer::OnReceivedResponse( response_info_ = info; } -void ExtensionLocalizationPeer::OnReceivedData(const char* data, int len) { - data_.append(data, len); +void ExtensionLocalizationPeer::OnReceivedData(const char* data, + int data_length, + int encoded_data_length) { + data_.append(data, data_length); } void ExtensionLocalizationPeer::OnCompletedRequest( @@ -85,7 +87,8 @@ void ExtensionLocalizationPeer::OnCompletedRequest( original_peer_->OnReceivedResponse(response_info_); if (!data_.empty()) original_peer_->OnReceivedData(data_.data(), - static_cast<int>(data_.size())); + static_cast<int>(data_.size()), + -1); original_peer_->OnCompletedRequest(status, security_info, completion_time); } @@ -100,7 +103,7 @@ void ExtensionLocalizationPeer::ReplaceMessages() { L10nMessagesMap* l10n_messages = GetL10nMessagesMap(extension_id); if (!l10n_messages) { L10nMessagesMap messages; - message_sender_->Send(new ViewHostMsg_GetExtensionMessageBundle( + message_sender_->Send(new ExtensionHostMsg_GetMessageBundle( extension_id, &messages)); // Save messages we got, so we don't have to ask again. diff --git a/chrome/common/extensions/extension_localization_peer.h b/chrome/common/extensions/extension_localization_peer.h index c633933..0a33bc9 100644 --- a/chrome/common/extensions/extension_localization_peer.h +++ b/chrome/common/extensions/extension_localization_peer.h @@ -39,7 +39,9 @@ class ExtensionLocalizationPeer virtual void OnReceivedResponse( const webkit_glue::ResourceResponseInfo& info); virtual void OnDownloadedData(int len) {} - virtual void OnReceivedData(const char* data, int len); + virtual void OnReceivedData(const char* data, + int data_length, + int encoded_data_length); virtual void OnCompletedRequest(const net::URLRequestStatus& status, const std::string& security_info, const base::Time& completion_time); @@ -63,7 +65,7 @@ class ExtensionLocalizationPeer // We just pass though the response info. This holds the copy of the original. webkit_glue::ResourceResponseInfo response_info_; - // Sends ViewHostMsg_GetExtensionMessageBundle message to the browser to fetch + // Sends ExtensionHostMsg_GetMessageBundle message to the browser to fetch // message catalog. IPC::Message::Sender* message_sender_; diff --git a/chrome/common/extensions/extension_localization_peer_unittest.cc b/chrome/common/extensions/extension_localization_peer_unittest.cc index a0205d5..df847ce 100644 --- a/chrome/common/extensions/extension_localization_peer_unittest.cc +++ b/chrome/common/extensions/extension_localization_peer_unittest.cc @@ -5,7 +5,7 @@ #include <map> #include <string> -#include "base/scoped_ptr.h" +#include "base/memory/scoped_ptr.h" #include "chrome/common/extensions/extension_message_bundle.h" #include "chrome/common/extensions/extension_localization_peer.h" #include "ipc/ipc_message.h" @@ -66,7 +66,9 @@ class MockResourceLoaderBridgePeer MOCK_METHOD1(OnReceivedResponse, void( const webkit_glue::ResourceResponseInfo& info)); MOCK_METHOD1(OnDownloadedData, void(int len)); - MOCK_METHOD2(OnReceivedData, void(const char* data, int len)); + MOCK_METHOD3(OnReceivedData, void(const char* data, + int data_length, + int encoded_data_length)); MOCK_METHOD3(OnCompletedRequest, void( const net::URLRequestStatus& status, const std::string& security_info, @@ -124,11 +126,11 @@ TEST_F(ExtensionLocalizationPeerTest, OnReceivedData) { EXPECT_TRUE(GetData(filter_peer_.get()).empty()); const std::string data_chunk("12345"); - filter_peer_->OnReceivedData(data_chunk.c_str(), data_chunk.length()); + filter_peer_->OnReceivedData(data_chunk.c_str(), data_chunk.length(), -1); EXPECT_EQ(data_chunk, GetData(filter_peer_.get())); - filter_peer_->OnReceivedData(data_chunk.c_str(), data_chunk.length()); + filter_peer_->OnReceivedData(data_chunk.c_str(), data_chunk.length(), -1); EXPECT_EQ(data_chunk + data_chunk, GetData(filter_peer_.get())); } @@ -151,7 +153,7 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestEmptyData) { // It will self-delete once it exits OnCompletedRequest. ExtensionLocalizationPeer* filter_peer = filter_peer_.release(); - EXPECT_CALL(*original_peer_, OnReceivedData(_, _)).Times(0); + EXPECT_CALL(*original_peer_, OnReceivedData(_, _, _)).Times(0); EXPECT_CALL(*sender_, Send(_)).Times(0); EXPECT_CALL(*original_peer_, OnReceivedResponse(_)); @@ -173,7 +175,7 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestNoCatalogs) { std::string data = GetData(filter_peer); EXPECT_CALL(*original_peer_, - OnReceivedData(StrEq(data.data()), data.length())).Times(2); + OnReceivedData(StrEq(data.data()), data.length(), -1)).Times(2); EXPECT_CALL(*original_peer_, OnReceivedResponse(_)).Times(2); EXPECT_CALL(*original_peer_, OnCompletedRequest( @@ -211,7 +213,7 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestWithCatalogs) { // __MSG_text__ gets replaced with "new text". std::string data("some new text"); EXPECT_CALL(*original_peer_, - OnReceivedData(StrEq(data.data()), data.length())); + OnReceivedData(StrEq(data.data()), data.length(), -1)); EXPECT_CALL(*original_peer_, OnReceivedResponse(_)); EXPECT_CALL(*original_peer_, OnCompletedRequest( @@ -241,7 +243,7 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestReplaceMessagesFails) { // __MSG_missing_message__ is missing, so message stays the same. EXPECT_CALL(*original_peer_, - OnReceivedData(StrEq(message.data()), message.length())); + OnReceivedData(StrEq(message.data()), message.length(), -1)); EXPECT_CALL(*original_peer_, OnReceivedResponse(_)); EXPECT_CALL(*original_peer_, OnCompletedRequest( diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc index 63172a9..641cd66 100644 --- a/chrome/common/extensions/extension_manifests_unittest.cc +++ b/chrome/common/extensions/extension_manifests_unittest.cc @@ -1,12 +1,12 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. #include "base/command_line.h" #include "base/file_path.h" #include "base/file_util.h" +#include "base/memory/scoped_ptr.h" #include "base/path_service.h" -#include "base/scoped_ptr.h" #include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" @@ -16,7 +16,9 @@ #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_error_utils.h" #include "chrome/common/extensions/extension_sidebar_defaults.h" -#include "chrome/common/json_value_serializer.h" +#include "chrome/common/extensions/file_browser_handler.h" +#include "chrome/common/extensions/url_pattern.h" +#include "content/common/json_value_serializer.h" #include "testing/gtest/include/gtest/gtest.h" namespace errors = extension_manifest_errors; @@ -48,8 +50,10 @@ class ExtensionManifestTest : public testing::Test { FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); path = path.AppendASCII("extensions").AppendASCII("manifest_tests"); - return Extension::Create(path.DirName(), location, *value, false, - strict_error_checks, error); + int flags = Extension::NO_FLAGS; + if (strict_error_checks) + flags |= Extension::STRICT_ERROR_CHECKS; + return Extension::Create(path.DirName(), location, *value, flags, error); } scoped_refptr<Extension> LoadExtension(const std::string& name, @@ -252,7 +256,11 @@ TEST_F(ExtensionManifestTest, AppLaunchURL) { errors::kInvalidLaunchLocalPath); LoadAndExpectError("launch_path_invalid_value.json", errors::kInvalidLaunchLocalPath); - LoadAndExpectError("launch_url_invalid_type.json", + LoadAndExpectError("launch_url_invalid_type_1.json", + errors::kInvalidLaunchWebURL); + LoadAndExpectError("launch_url_invalid_type_2.json", + errors::kInvalidLaunchWebURL); + LoadAndExpectError("launch_url_invalid_type_3.json", errors::kInvalidLaunchWebURL); scoped_refptr<Extension> extension; @@ -362,6 +370,8 @@ TEST_F(ExtensionManifestTest, DevToolsExtensions) { extension = LoadAndExpectSuccess("devtools_extension.json"); EXPECT_EQ(extension->url().spec() + "devtools.html", extension->devtools_url().spec()); + EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts()); + *CommandLine::ForCurrentProcess() = old_command_line; } @@ -401,9 +411,11 @@ TEST_F(ExtensionManifestTest, Sidebar) { TEST_F(ExtensionManifestTest, DisallowHybridApps) { LoadAndExpectError("disallow_hybrid_1.json", - errors::kHostedAppsCannotIncludeExtensionFeatures); + ExtensionErrorUtils::FormatErrorMessage( + errors::kHostedAppsCannotIncludeExtensionFeatures, + keys::kBrowserAction)); LoadAndExpectError("disallow_hybrid_2.json", - errors::kHostedAppsCannotIncludeExtensionFeatures); + errors::kBackgroundPermissionNeeded); } TEST_F(ExtensionManifestTest, OptionsPageInApps) { @@ -547,3 +559,53 @@ TEST_F(ExtensionManifestTest, ForbidPortsInPermissions) { // to flag this case. LoadStrictAndExpectSuccess("forbid_ports_in_permissions.json"); } + +TEST_F(ExtensionManifestTest, IsolatedApps) { + // Requires --enable-experimental-app-manifests + scoped_refptr<Extension> extension( + LoadAndExpectSuccess("isolated_app_valid.json")); + EXPECT_FALSE(extension->is_storage_isolated()); + + CommandLine old_command_line = *CommandLine::ForCurrentProcess(); + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalAppManifests); + scoped_refptr<Extension> extension2( + LoadAndExpectSuccess("isolated_app_valid.json")); + EXPECT_TRUE(extension2->is_storage_isolated()); + *CommandLine::ForCurrentProcess() = old_command_line; +} + + +TEST_F(ExtensionManifestTest, FileBrowserHandlers) { + LoadAndExpectError("filebrowser_invalid_actions_1.json", + errors::kInvalidFileBrowserHandler); + LoadAndExpectError("filebrowser_invalid_actions_2.json", + errors::kInvalidFileBrowserHandler); + LoadAndExpectError("filebrowser_invalid_action_id.json", + errors::kInvalidPageActionId); + LoadAndExpectError("filebrowser_invalid_action_title.json", + errors::kInvalidPageActionDefaultTitle); + LoadAndExpectError("filebrowser_invalid_action_id.json", + errors::kInvalidPageActionId); + LoadAndExpectError("filebrowser_invalid_file_filters_1.json", + errors::kInvalidFileFiltersList); + LoadAndExpectError("filebrowser_invalid_file_filters_2.json", + ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidFileFilterValue, base::IntToString(0))); + LoadAndExpectError("filebrowser_invalid_file_filters_url.json", + ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidURLPatternError, + "http:*.html")); + + scoped_refptr<Extension> extension( + LoadAndExpectSuccess("filebrowser_valid.json")); + ASSERT_TRUE(extension->file_browser_handlers() != NULL); + ASSERT_EQ(extension->file_browser_handlers()->size(), 1U); + const FileBrowserHandler* action = + extension->file_browser_handlers()->at(0).get(); + EXPECT_EQ(action->title(), "Default title"); + EXPECT_EQ(action->icon_path(), "icon.png"); + const FileBrowserHandler::PatternList& patterns = action->file_url_patterns(); + ASSERT_EQ(patterns.size(), 1U); + ASSERT_TRUE(action->MatchesURL( + GURL("filesystem:chrome-extension://foo/local/test.txt"))); +} diff --git a/chrome/common/extensions/extension_message_bundle.cc b/chrome/common/extensions/extension_message_bundle.cc index e6e00af..26e9c97 100644 --- a/chrome/common/extensions/extension_message_bundle.cc +++ b/chrome/common/extensions/extension_message_bundle.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -10,8 +10,8 @@ #include "base/hash_tables.h" #include "base/i18n/rtl.h" #include "base/lazy_instance.h" -#include "base/linked_ptr.h" -#include "base/scoped_ptr.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_ptr.h" #include "base/stl_util-inl.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" @@ -336,7 +336,7 @@ ExtensionToL10nMessagesMap* GetExtensionToL10nMessagesMap() { return &g_extension_to_messages_map.Get().messages_map; } -L10nMessagesMap* GetL10nMessagesMap(const std::string extension_id) { +L10nMessagesMap* GetL10nMessagesMap(const std::string& extension_id) { ExtensionToL10nMessagesMap::iterator it = g_extension_to_messages_map.Get().messages_map.find(extension_id); if (it != g_extension_to_messages_map.Get().messages_map.end()) diff --git a/chrome/common/extensions/extension_message_bundle.h b/chrome/common/extensions/extension_message_bundle.h index df3bf35..8f3078e 100644 --- a/chrome/common/extensions/extension_message_bundle.h +++ b/chrome/common/extensions/extension_message_bundle.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -10,7 +10,7 @@ #include <string> #include <vector> -#include "base/linked_ptr.h" +#include "base/memory/linked_ptr.h" class DictionaryValue; @@ -159,6 +159,6 @@ typedef std::map<std::string, L10nMessagesMap > ExtensionToL10nMessagesMap; ExtensionToL10nMessagesMap* GetExtensionToL10nMessagesMap(); // Returns message map that matches given extension_id, or NULL. -L10nMessagesMap* GetL10nMessagesMap(const std::string extension_id); +L10nMessagesMap* GetL10nMessagesMap(const std::string& extension_id); #endif // CHROME_COMMON_EXTENSIONS_EXTENSION_MESSAGE_BUNDLE_H_ diff --git a/chrome/common/extensions/extension_message_bundle_unittest.cc b/chrome/common/extensions/extension_message_bundle_unittest.cc index dd87406..082308e 100644 --- a/chrome/common/extensions/extension_message_bundle_unittest.cc +++ b/chrome/common/extensions/extension_message_bundle_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -8,8 +8,8 @@ #include <vector> #include "base/i18n/rtl.h" -#include "base/linked_ptr.h" -#include "base/scoped_ptr.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_ptr.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" diff --git a/chrome/common/extensions/extension_messages.cc b/chrome/common/extensions/extension_messages.cc new file mode 100644 index 0000000..88c9492 --- /dev/null +++ b/chrome/common/extensions/extension_messages.cc @@ -0,0 +1,149 @@ +// Copyright (c) 2011 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. + +#include "chrome/common/extensions/extension_messages.h" + +#include "chrome/common/extensions/extension_constants.h" +#include "content/common/common_param_traits.h" + +ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params() + : location(Extension::INVALID) { +} + +ExtensionMsg_Loaded_Params::~ExtensionMsg_Loaded_Params() { +} + +ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params( + const ExtensionMsg_Loaded_Params& other) + : manifest(other.manifest->DeepCopy()), + location(other.location), + path(other.path), + id(other.id) { +} + +ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params( + const Extension* extension) + : manifest(new DictionaryValue()), + location(extension->location()), + path(extension->path()), + id(extension->id()) { + // As we need more bits of extension data in the renderer, add more keys to + // this list. + const char* kRendererExtensionKeys[] = { + extension_manifest_keys::kPublicKey, + extension_manifest_keys::kName, + extension_manifest_keys::kVersion, + extension_manifest_keys::kIcons, + extension_manifest_keys::kPermissions, + extension_manifest_keys::kApp + }; + + // Copy only the data we need. + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRendererExtensionKeys); ++i) { + Value* temp = NULL; + if (extension->manifest_value()->Get(kRendererExtensionKeys[i], &temp)) + manifest->Set(kRendererExtensionKeys[i], temp->DeepCopy()); + } +} + +scoped_refptr<Extension> + ExtensionMsg_Loaded_Params::ConvertToExtension() const { + std::string error; + + scoped_refptr<Extension> extension( + Extension::Create(path, location, *manifest, Extension::NO_FLAGS, + &error)); + if (!extension.get()) + LOG(ERROR) << "Error deserializing extension: " << error; + + return extension; +} + +namespace IPC { + +template <> +struct ParamTraits<Extension::Location> { + typedef Extension::Location param_type; + static void Write(Message* m, const param_type& p) { + int val = static_cast<int>(p); + WriteParam(m, val); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int val = 0; + if (!ReadParam(m, iter, &val) || + val < Extension::INVALID || + val >= Extension::NUM_LOCATIONS) + return false; + *p = static_cast<param_type>(val); + return true; + } + static void Log(const param_type& p, std::string* l) { + ParamTraits<int>::Log(static_cast<int>(p), l); + } +}; + +void ParamTraits<URLPattern>::Write(Message* m, const param_type& p) { + WriteParam(m, p.valid_schemes()); + WriteParam(m, p.GetAsString()); +} + +bool ParamTraits<URLPattern>::Read(const Message* m, void** iter, + param_type* p) { + int valid_schemes; + std::string spec; + if (!ReadParam(m, iter, &valid_schemes) || + !ReadParam(m, iter, &spec)) + return false; + + p->set_valid_schemes(valid_schemes); + return URLPattern::PARSE_SUCCESS == p->Parse(spec, URLPattern::PARSE_LENIENT); +} + +void ParamTraits<URLPattern>::Log(const param_type& p, std::string* l) { + LogParam(p.GetAsString(), l); +} + +void ParamTraits<ExtensionExtent>::Write(Message* m, const param_type& p) { + WriteParam(m, p.patterns()); +} + +bool ParamTraits<ExtensionExtent>::Read(const Message* m, void** iter, + param_type* p) { + std::vector<URLPattern> patterns; + bool success = + ReadParam(m, iter, &patterns); + if (!success) + return false; + + for (size_t i = 0; i < patterns.size(); ++i) + p->AddPattern(patterns[i]); + return true; +} + +void ParamTraits<ExtensionExtent>::Log(const param_type& p, std::string* l) { + LogParam(p.patterns(), l); +} + +void ParamTraits<ExtensionMsg_Loaded_Params>::Write(Message* m, + const param_type& p) { + WriteParam(m, p.location); + WriteParam(m, p.path); + WriteParam(m, *(p.manifest)); +} + +bool ParamTraits<ExtensionMsg_Loaded_Params>::Read(const Message* m, + void** iter, + param_type* p) { + p->manifest.reset(new DictionaryValue()); + return ReadParam(m, iter, &p->location) && + ReadParam(m, iter, &p->path) && + ReadParam(m, iter, p->manifest.get()); +} + +void ParamTraits<ExtensionMsg_Loaded_Params>::Log(const param_type& p, + std::string* l) { + l->append(p.id); +} + +} // namespace IPC diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h new file mode 100644 index 0000000..69575e9 --- /dev/null +++ b/chrome/common/extensions/extension_messages.h @@ -0,0 +1,285 @@ +// Copyright (c) 2011 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. + +// IPC messages for extensions. +// Multiply-included message file, hence no include guard. + +#include "base/shared_memory.h" +#include "base/values.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_extent.h" +#include "chrome/common/extensions/url_pattern.h" +#include "chrome/common/web_apps.h" +#include "ipc/ipc_message_macros.h" + +#define IPC_MESSAGE_START ExtensionMsgStart + +// Parameters structure for ExtensionHostMsg_Request. +IPC_STRUCT_BEGIN(ExtensionHostMsg_DomMessage_Params) + // Message name. + IPC_STRUCT_MEMBER(std::string, name) + + // List of message arguments. + IPC_STRUCT_MEMBER(ListValue, arguments) + + // URL of the frame request was sent from. + IPC_STRUCT_MEMBER(GURL, source_url) + + // Unique request id to match requests and responses. + IPC_STRUCT_MEMBER(int, request_id) + + // True if request has a callback specified. + IPC_STRUCT_MEMBER(bool, has_callback) + + // True if request is executed in response to an explicit user gesture. + IPC_STRUCT_MEMBER(bool, user_gesture) +IPC_STRUCT_END() + +// Allows an extension to execute code in a tab. +IPC_STRUCT_BEGIN(ExtensionMsg_ExecuteCode_Params) + // The extension API request id, for responding. + IPC_STRUCT_MEMBER(int, request_id) + + // The ID of the requesting extension. To know which isolated world to + // execute the code inside of. + IPC_STRUCT_MEMBER(std::string, extension_id) + + // Whether the code is JavaScript or CSS. + IPC_STRUCT_MEMBER(bool, is_javascript) + + // String of code to execute. + IPC_STRUCT_MEMBER(std::string, code) + + // Whether to inject into all frames, or only the root frame. + IPC_STRUCT_MEMBER(bool, all_frames) + + // Whether to execute code in the main world (as opposed to an isolated + // world). + IPC_STRUCT_MEMBER(bool, in_main_world) +IPC_STRUCT_END() + +IPC_STRUCT_TRAITS_BEGIN(WebApplicationInfo::IconInfo) + IPC_STRUCT_TRAITS_MEMBER(url) + IPC_STRUCT_TRAITS_MEMBER(width) + IPC_STRUCT_TRAITS_MEMBER(height) + IPC_STRUCT_TRAITS_MEMBER(data) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(WebApplicationInfo) + IPC_STRUCT_TRAITS_MEMBER(title) + IPC_STRUCT_TRAITS_MEMBER(description) + IPC_STRUCT_TRAITS_MEMBER(app_url) + IPC_STRUCT_TRAITS_MEMBER(icons) + IPC_STRUCT_TRAITS_MEMBER(permissions) + IPC_STRUCT_TRAITS_MEMBER(launch_container) +IPC_STRUCT_TRAITS_END() + +// Singly-included section for custom IPC traits. +#ifndef CHROME_COMMON_EXTENSIONS_EXTENSION_MESSAGES_H_ +#define CHROME_COMMON_EXTENSIONS_EXTENSION_MESSAGES_H_ + +// IPC_MESSAGE macros choke on extra , in the std::map, when expanding. We need +// to typedef it to avoid that. +// Substitution map for l10n messages. +typedef std::map<std::string, std::string> SubstitutionMap; + +struct ExtensionMsg_Loaded_Params { + ExtensionMsg_Loaded_Params(); + ~ExtensionMsg_Loaded_Params(); + explicit ExtensionMsg_Loaded_Params(const Extension* extension); + + // A copy constructor is needed because this structure can end up getting + // copied inside the IPC machinery on gcc <= 4.2. + ExtensionMsg_Loaded_Params(const ExtensionMsg_Loaded_Params& other); + + // Creates a new extension from the data in this object. + scoped_refptr<Extension> ConvertToExtension() const; + + // The subset of the extension manifest data we send to renderers. + scoped_ptr<DictionaryValue> manifest; + + // The location the extension was installed from. + Extension::Location location; + + // The path the extension was loaded from. This is used in the renderer only + // to generate the extension ID for extensions that are loaded unpacked. + FilePath path; + + // We keep this separate so that it can be used in logging. + std::string id; +}; + +namespace IPC { + +template <> +struct ParamTraits<URLPattern> { + typedef URLPattern param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* p); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<ExtensionExtent> { + typedef ExtensionExtent param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* p); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<ExtensionMsg_Loaded_Params> { + typedef ExtensionMsg_Loaded_Params param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* p); + static void Log(const param_type& p, std::string* l); +}; + +} // namespace IPC + +#endif // CHROME_COMMON_EXTENSIONS_EXTENSION_MESSAGES_H_ + +// Messages sent from the browser to the renderer. + +// The browser sends this message in response to all extension api calls. +IPC_MESSAGE_ROUTED4(ExtensionMsg_Response, + int /* request_id */, + bool /* success */, + std::string /* response */, + std::string /* error */) + +// This message is optionally routed. If used as a control message, it +// will call a javascript function in every registered context in the +// target process. If routed, it will be restricted to the contexts that +// are part of the target RenderView. +// If |extension_id| is non-empty, the function will be invoked only in +// contexts owned by the extension. |args| is a list of primitive Value types +// that are passed to the function. +IPC_MESSAGE_ROUTED4(ExtensionMsg_MessageInvoke, + std::string /* extension_id */, + std::string /* function_name */, + ListValue /* args */, + GURL /* event URL */) + +// Tell the renderer process all known extension function names. +IPC_MESSAGE_CONTROL1(ExtensionMsg_SetFunctionNames, + std::vector<std::string>) + +// TODO(aa): SetAPIPermissions, SetHostPermissions, and possibly +// UpdatePageActions should be replaced with just sending additional data in +// ExtensionLoaded. See: crbug.com/70516. + +// Tell the renderer process which permissions the given extension has. See +// Extension::Permissions for which elements correspond to which permissions. +IPC_MESSAGE_CONTROL2(ExtensionMsg_SetAPIPermissions, + std::string /* extension_id */, + std::set<std::string> /* permissions */) + +// Tell the renderer process which host permissions the given extension has. +IPC_MESSAGE_CONTROL2(ExtensionMsg_SetHostPermissions, + GURL /* source extension's origin */, + /* URLPatterns the extension can access */ + std::vector<URLPattern>) + +// Tell the renderer process all known page action ids for a particular +// extension. +IPC_MESSAGE_CONTROL2(ExtensionMsg_UpdatePageActions, + std::string /* extension_id */, + std::vector<std::string> /* page_action_ids */) + +// Notifies the renderer that an extension was loaded in the browser. +IPC_MESSAGE_CONTROL1(ExtensionMsg_Loaded, + ExtensionMsg_Loaded_Params) + +// Notifies the renderer that an extension was unloaded in the browser. +IPC_MESSAGE_CONTROL1(ExtensionMsg_Unloaded, + std::string) + +// Updates the scripting whitelist for extensions in the render process. This is +// only used for testing. +IPC_MESSAGE_CONTROL1(ExtensionMsg_SetScriptingWhitelist, + Extension::ScriptingWhitelist /* extenison ids */) + +// Notification that renderer should run some JavaScript code. +IPC_MESSAGE_ROUTED1(ExtensionMsg_ExecuteCode, + ExtensionMsg_ExecuteCode_Params) + +// Notification that the user scripts have been updated. It has one +// SharedMemoryHandle argument consisting of the pickled script data. This +// handle is valid in the context of the renderer. +IPC_MESSAGE_CONTROL1(ExtensionMsg_UpdateUserScripts, + base::SharedMemoryHandle) + +// Requests application info for the page. The renderer responds back with +// ExtensionHostMsg_DidGetApplicationInfo. +IPC_MESSAGE_ROUTED1(ExtensionMsg_GetApplicationInfo, + int32 /*page_id*/) + +// Messages sent from the renderer to the browser. + +// A renderer sends this message when an extension process starts an API +// request. The browser will always respond with a ExtensionMsg_Response. +IPC_MESSAGE_ROUTED1(ExtensionHostMsg_Request, + ExtensionHostMsg_DomMessage_Params) + +// Notify the browser that the given extension added a listener to an event. +IPC_MESSAGE_CONTROL2(ExtensionHostMsg_AddListener, + std::string /* extension_id */, + std::string /* name */) + +// Notify the browser that the given extension removed a listener from an +// event. +IPC_MESSAGE_CONTROL2(ExtensionHostMsg_RemoveListener, + std::string /* extension_id */, + std::string /* name */) + +// Open a channel to all listening contexts owned by the extension with +// the given ID. This always returns a valid port ID which can be used for +// sending messages. If an error occurred, the opener will be notified +// asynchronously. +IPC_SYNC_MESSAGE_CONTROL4_1(ExtensionHostMsg_OpenChannelToExtension, + int /* routing_id */, + std::string /* source_extension_id */, + std::string /* target_extension_id */, + std::string /* channel_name */, + int /* port_id */) + +// Get a port handle to the given tab. The handle can be used for sending +// messages to the extension. +IPC_SYNC_MESSAGE_CONTROL4_1(ExtensionHostMsg_OpenChannelToTab, + int /* routing_id */, + int /* tab_id */, + std::string /* extension_id */, + std::string /* channel_name */, + int /* port_id */) + +// Send a message to an extension process. The handle is the value returned +// by ViewHostMsg_OpenChannelTo*. +IPC_MESSAGE_ROUTED2(ExtensionHostMsg_PostMessage, + int /* port_id */, + std::string /* message */) + +// Send a message to an extension process. The handle is the value returned +// by ViewHostMsg_OpenChannelTo*. +IPC_MESSAGE_CONTROL1(ExtensionHostMsg_CloseChannel, + int /* port_id */) + +// Used to get the extension message bundle. +IPC_SYNC_MESSAGE_CONTROL1_1(ExtensionHostMsg_GetMessageBundle, + std::string /* extension id */, + SubstitutionMap /* message bundle */) + +// Send from the renderer to the browser to return the script running result. +IPC_MESSAGE_ROUTED3(ExtensionHostMsg_ExecuteCodeFinished, + int /* request id */, + bool /* whether the script ran successfully */, + std::string /* error message */) + +IPC_MESSAGE_ROUTED2(ExtensionHostMsg_DidGetApplicationInfo, + int32 /* page_id */, + WebApplicationInfo) + +// Sent by the renderer to implement chrome.app.installApplication(). +IPC_MESSAGE_ROUTED1(ExtensionHostMsg_InstallApplication, + WebApplicationInfo) diff --git a/chrome/common/extensions/extension_resource.cc b/chrome/common/extensions/extension_resource.cc index cc88382..1b344c9 100644 --- a/chrome/common/extensions/extension_resource.cc +++ b/chrome/common/extensions/extension_resource.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -65,15 +65,17 @@ FilePath ExtensionResource::GetFilePath( // Unit-testing helpers. FilePath::StringType ExtensionResource::NormalizeSeperators( - FilePath::StringType path) const { + const FilePath::StringType& path) const { #if defined(FILE_PATH_USES_WIN_SEPARATORS) - FilePath::StringType ret_val; - for (size_t i = 0; i < path.length(); i++) { - if (FilePath::IsSeparator(path[i])) - path[i] = FilePath::kSeparators[0]; + FilePath::StringType win_path = path; + for (size_t i = 0; i < win_path.length(); i++) { + if (FilePath::IsSeparator(win_path[i])) + win_path[i] = FilePath::kSeparators[0]; } -#endif // FILE_PATH_USES_WIN_SEPARATORS + return win_path; +#else return path; +#endif // FILE_PATH_USES_WIN_SEPARATORS } bool ExtensionResource::ComparePathWithDefault(const FilePath& path) const { diff --git a/chrome/common/extensions/extension_resource.h b/chrome/common/extensions/extension_resource.h index 6fe3773..4a7d8cf 100644 --- a/chrome/common/extensions/extension_resource.h +++ b/chrome/common/extensions/extension_resource.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -44,7 +44,8 @@ class ExtensionResource { bool empty() { return extension_root().empty(); } // Unit test helpers. - FilePath::StringType NormalizeSeperators(FilePath::StringType path) const; + FilePath::StringType NormalizeSeperators( + const FilePath::StringType& path) const; bool ComparePathWithDefault(const FilePath& path) const; private: diff --git a/chrome/common/extensions/extension_resource_unittest.cc b/chrome/common/extensions/extension_resource_unittest.cc index 9f47666..e22f288 100644 --- a/chrome/common/extensions/extension_resource_unittest.cc +++ b/chrome/common/extensions/extension_resource_unittest.cc @@ -1,12 +1,12 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. #include <algorithm> #include "base/file_util.h" +#include "base/memory/scoped_temp_dir.h" #include "base/path_service.h" -#include "base/scoped_temp_dir.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_l10n_util.h" diff --git a/chrome/common/extensions/extension_set.h b/chrome/common/extensions/extension_set.h index 9d00d1d..dbd16ad 100644 --- a/chrome/common/extensions/extension_set.h +++ b/chrome/common/extensions/extension_set.h @@ -10,7 +10,7 @@ #include <vector> #include "base/gtest_prod_util.h" -#include "base/ref_counted.h" +#include "base/memory/ref_counted.h" #include "chrome/common/extensions/extension.h" #include "googleurl/src/gurl.h" diff --git a/chrome/common/extensions/extension_set_unittest.cc b/chrome/common/extensions/extension_set_unittest.cc index a68e1d7..e51d571 100644 --- a/chrome/common/extensions/extension_set_unittest.cc +++ b/chrome/common/extensions/extension_set_unittest.cc @@ -4,8 +4,8 @@ #include "base/file_path.h" #include "base/logging.h" -#include "base/ref_counted.h" -#include "base/scoped_ptr.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" #include "base/values.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_set.h" @@ -36,12 +36,10 @@ scoped_refptr<Extension> CreateTestExtension(const std::string& name, urls->Append(Value::CreateStringValue(extent)); } - const bool kRequireKey = false; - const bool kStrictErrorChecks = true; std::string error; scoped_refptr<Extension> extension( - Extension::Create(path, Extension::INTERNAL, manifest, kRequireKey, - kStrictErrorChecks, &error)); + Extension::Create(path, Extension::INTERNAL, manifest, + Extension::STRICT_ERROR_CHECKS, &error)); EXPECT_TRUE(extension.get()) << error; return extension; } diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index ab3c3a2..d493672 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -21,8 +21,8 @@ #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_error_utils.h" #include "chrome/common/extensions/extension_resource.h" -#include "chrome/common/json_value_serializer.h" #include "chrome/common/url_constants.h" +#include "content/common/json_value_serializer.h" #include "googleurl/src/gurl.h" #include "net/base/mime_sniffer.h" #include "skia/ext/image_operations.h" @@ -67,6 +67,45 @@ TEST(ExtensionTest, LocationValuesTest) { ASSERT_EQ(3, Extension::EXTERNAL_REGISTRY); ASSERT_EQ(4, Extension::LOAD); ASSERT_EQ(5, Extension::COMPONENT); + ASSERT_EQ(6, Extension::EXTERNAL_PREF_DOWNLOAD); + ASSERT_EQ(7, Extension::EXTERNAL_POLICY_DOWNLOAD); +} + +TEST(ExtensionTest, LocationPriorityTest) { + for (int i = 0; i < Extension::NUM_LOCATIONS; i++) { + Extension::Location loc = static_cast<Extension::Location>(i); + + // INVALID is not a valid location. + if (loc == Extension::INVALID) + continue; + + // Comparing a location that has no rank will hit a CHECK. Do a + // compare with every valid location, to be sure each one is covered. + + // Check that no install source can override a componenet extension. + ASSERT_EQ(Extension::COMPONENT, + Extension::GetHigherPriorityLocation(Extension::COMPONENT, loc)); + ASSERT_EQ(Extension::COMPONENT, + Extension::GetHigherPriorityLocation(loc, Extension::COMPONENT)); + + // Check that any source can override a user install. This might change + // in the future, in which case this test should be updated. + ASSERT_EQ(loc, + Extension::GetHigherPriorityLocation(Extension::INTERNAL, loc)); + ASSERT_EQ(loc, + Extension::GetHigherPriorityLocation(loc, Extension::INTERNAL)); + } + + // Check a few interesting cases that we know can happen: + ASSERT_EQ(Extension::EXTERNAL_POLICY_DOWNLOAD, + Extension::GetHigherPriorityLocation( + Extension::EXTERNAL_POLICY_DOWNLOAD, + Extension::EXTERNAL_PREF)); + + ASSERT_EQ(Extension::EXTERNAL_PREF, + Extension::GetHigherPriorityLocation( + Extension::INTERNAL, + Extension::EXTERNAL_PREF)); } @@ -105,7 +144,8 @@ TEST(ExtensionTest, InitFromValueInvalid) { EXPECT_EQ("", error); EXPECT_EQ(0, error_code); ASSERT_TRUE(valid_value.get()); - ASSERT_TRUE(extension.InitFromValue(*valid_value, true, false, &error)); + ASSERT_TRUE(extension.InitFromValue(*valid_value, Extension::REQUIRE_KEY, + &error)); ASSERT_EQ("", error); EXPECT_EQ("en_US", extension.default_locale()); @@ -114,33 +154,39 @@ TEST(ExtensionTest, InitFromValueInvalid) { // Test missing and invalid versions input_value.reset(valid_value->DeepCopy()); input_value->Remove(keys::kVersion, NULL); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_EQ(errors::kInvalidVersion, error); input_value->SetInteger(keys::kVersion, 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_EQ(errors::kInvalidVersion, error); // Test missing and invalid names. input_value.reset(valid_value->DeepCopy()); input_value->Remove(keys::kName, NULL); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_EQ(errors::kInvalidName, error); input_value->SetInteger(keys::kName, 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_EQ(errors::kInvalidName, error); // Test invalid description input_value.reset(valid_value->DeepCopy()); input_value->SetInteger(keys::kDescription, 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_EQ(errors::kInvalidDescription, error); // Test invalid icons input_value.reset(valid_value->DeepCopy()); input_value->SetInteger(keys::kIcons, 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_EQ(errors::kInvalidIcons, error); // Test invalid icon paths @@ -149,13 +195,15 @@ TEST(ExtensionTest, InitFromValueInvalid) { input_value->GetDictionary(keys::kIcons, &icons); ASSERT_FALSE(NULL == icons); icons->SetInteger(base::IntToString(128), 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidIconPath)); // Test invalid user scripts list input_value.reset(valid_value->DeepCopy()); input_value->SetInteger(keys::kContentScripts, 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_EQ(errors::kInvalidContentScriptsList, error); // Test invalid user script item @@ -164,7 +212,8 @@ TEST(ExtensionTest, InitFromValueInvalid) { input_value->GetList(keys::kContentScripts, &content_scripts); ASSERT_FALSE(NULL == content_scripts); content_scripts->Set(0, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidContentScript)); // Test missing and invalid matches array @@ -173,25 +222,30 @@ TEST(ExtensionTest, InitFromValueInvalid) { DictionaryValue* user_script = NULL; content_scripts->GetDictionary(0, &user_script); user_script->Remove(keys::kMatches, NULL); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatches)); user_script->Set(keys::kMatches, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatches)); ListValue* matches = new ListValue; user_script->Set(keys::kMatches, matches); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatchCount)); // Test invalid match element matches->Set(0, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatch)); matches->Set(0, Value::CreateStringValue("chrome://*/*")); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatch)); // Test missing and invalid files array @@ -200,45 +254,53 @@ TEST(ExtensionTest, InitFromValueInvalid) { content_scripts->GetDictionary(0, &user_script); user_script->Remove(keys::kJs, NULL); user_script->Remove(keys::kCss, NULL); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kMissingFile)); user_script->Set(keys::kJs, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidJsList)); user_script->Set(keys::kCss, new ListValue); user_script->Set(keys::kJs, new ListValue); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kMissingFile)); user_script->Remove(keys::kCss, NULL); ListValue* files = new ListValue; user_script->Set(keys::kJs, files); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kMissingFile)); // Test invalid file element files->Set(0, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidJs)); user_script->Remove(keys::kJs, NULL); // Test the css element user_script->Set(keys::kCss, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidCssList)); // Test invalid file element ListValue* css_files = new ListValue; user_script->Set(keys::kCss, css_files); css_files->Set(0, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidCss)); // Test missing and invalid permissions array input_value.reset(valid_value->DeepCopy()); - EXPECT_TRUE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_TRUE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); ListValue* permissions = NULL; input_value->GetList(keys::kPermissions, &permissions); @@ -246,22 +308,26 @@ TEST(ExtensionTest, InitFromValueInvalid) { permissions = new ListValue; input_value->Set(keys::kPermissions, permissions); - EXPECT_TRUE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_TRUE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); input_value->Set(keys::kPermissions, Value::CreateIntegerValue(9)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidPermissions)); input_value.reset(valid_value->DeepCopy()); input_value->GetList(keys::kPermissions, &permissions); permissions->Set(0, Value::CreateIntegerValue(24)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidPermission)); // We allow unknown API permissions, so this will be valid until we better // distinguish between API and host permissions. permissions->Set(0, Value::CreateStringValue("www.google.com")); - EXPECT_TRUE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_TRUE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); // Multiple page actions are not allowed. input_value.reset(valid_value->DeepCopy()); @@ -272,36 +338,42 @@ TEST(ExtensionTest, InitFromValueInvalid) { action_list->Append(action->DeepCopy()); action_list->Append(action); input_value->Set(keys::kPageActions, action_list); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_STREQ(errors::kInvalidPageActionsListSize, error.c_str()); // Test invalid options page url. input_value.reset(valid_value->DeepCopy()); input_value->Set(keys::kOptionsPage, Value::CreateNullValue()); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidOptionsPage)); // Test invalid/empty default locale. input_value.reset(valid_value->DeepCopy()); input_value->Set(keys::kDefaultLocale, Value::CreateIntegerValue(5)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidDefaultLocale)); input_value->Set(keys::kDefaultLocale, Value::CreateStringValue("")); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidDefaultLocale)); // Test invalid minimum_chrome_version. input_value.reset(valid_value->DeepCopy()); input_value->Set(keys::kMinimumChromeVersion, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMinimumChromeVersion)); #if !defined(OS_MACOSX) // TODO(aa): The version isn't stamped into the unit test binary on mac. input_value->Set(keys::kMinimumChromeVersion, Value::CreateStringValue("88.8")); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, Extension::REQUIRE_KEY, + &error)); EXPECT_TRUE(MatchPattern(error, errors::kChromeVersionTooLow)); #endif } @@ -322,7 +394,8 @@ TEST(ExtensionTest, InitFromValueValid) { input_value.SetString(keys::kVersion, "1.0.0.0"); input_value.SetString(keys::kName, "my extension"); - EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS, + &error)); EXPECT_EQ("", error); EXPECT_TRUE(Extension::IdIsValid(extension.id())); EXPECT_EQ("1.0.0.0", extension.VersionString()); @@ -337,12 +410,14 @@ TEST(ExtensionTest, InitFromValueValid) { // We allow unknown API permissions, so this will be valid until we better // distinguish between API and host permissions. - EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS, + &error)); input_value.Remove(keys::kPermissions, NULL); // Test with an options page. input_value.SetString(keys::kOptionsPage, "options.html"); - EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS, + &error)); EXPECT_EQ("", error); EXPECT_EQ("chrome-extension", extension.options_url().scheme()); EXPECT_EQ("/options.html", extension.options_url().path()); @@ -351,14 +426,16 @@ TEST(ExtensionTest, InitFromValueValid) { // from being loaded. ListValue* empty_list = new ListValue; input_value.Set(keys::kPageActions, empty_list); - EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS, + &error)); EXPECT_EQ("", error); #if !defined(OS_MACOSX) // TODO(aa): The version isn't stamped into the unit test binary on mac. // Test with a minimum_chrome_version. input_value.SetString(keys::kMinimumChromeVersion, "1.0"); - EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS, + &error)); EXPECT_EQ("", error); // The minimum chrome version is not stored in the Extension object. #endif @@ -388,7 +465,8 @@ TEST(ExtensionTest, InitFromValueValidNameInRTL) { // No strong RTL characters in name. std::wstring name(L"Dictionary (by Google)"); input_value.SetString(keys::kName, WideToUTF16Hack(name)); - EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS, + &error)); EXPECT_EQ("", error); std::wstring localized_name(name); base::i18n::AdjustStringForLocaleDirection(&localized_name); @@ -397,7 +475,8 @@ TEST(ExtensionTest, InitFromValueValidNameInRTL) { // Strong RTL characters in name. name = L"Dictionary (\x05D1\x05D2"L" Google)"; input_value.SetString(keys::kName, WideToUTF16Hack(name)); - EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, Extension::NO_FLAGS, + &error)); EXPECT_EQ("", error); localized_name = name; base::i18n::AdjustStringForLocaleDirection(&localized_name); @@ -420,8 +499,8 @@ TEST(ExtensionTest, GetResourceURLAndPath) { DictionaryValue input_value; input_value.SetString(keys::kVersion, "1.0.0.0"); input_value.SetString(keys::kName, "my extension"); - scoped_refptr<Extension> extension(Extension::Create( - path, Extension::INVALID, input_value, false, true, NULL)); + scoped_refptr<Extension> extension(Extension::Create(path, + Extension::INVALID, input_value, Extension::STRICT_ERROR_CHECKS, NULL)); EXPECT_TRUE(extension.get()); EXPECT_EQ(extension->url().spec() + "bar/baz.js", @@ -680,7 +759,8 @@ TEST(ExtensionTest, UpdateUrls) { input_value.SetString(keys::kUpdateURL, url.spec()); scoped_refptr<Extension> extension(Extension::Create( - path, Extension::INVALID, input_value, false, true, &error)); + path, Extension::INVALID, input_value, Extension::STRICT_ERROR_CHECKS, + &error)); EXPECT_TRUE(extension.get()) << error; } @@ -704,7 +784,8 @@ TEST(ExtensionTest, UpdateUrls) { input_value.SetString(keys::kUpdateURL, invalid[i]); scoped_refptr<Extension> extension(Extension::Create( - path, Extension::INVALID, input_value, false, true, &error)); + path, Extension::INVALID, input_value, Extension::STRICT_ERROR_CHECKS, + &error)); EXPECT_FALSE(extension.get()); EXPECT_TRUE(MatchPattern(error, errors::kInvalidUpdateURL)); } @@ -735,7 +816,8 @@ TEST(ExtensionTest, MimeTypeSniffing) { } static scoped_refptr<Extension> LoadManifest(const std::string& dir, - const std::string& test_file) { + const std::string& test_file, + int extra_flags) { FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); path = path.AppendASCII("extensions") @@ -752,11 +834,17 @@ static scoped_refptr<Extension> LoadManifest(const std::string& dir, scoped_refptr<Extension> extension = Extension::Create( path.DirName(), Extension::INVALID, - *static_cast<DictionaryValue*>(result.get()), false, true, &error); + *static_cast<DictionaryValue*>(result.get()), + Extension::STRICT_ERROR_CHECKS | extra_flags, &error); EXPECT_TRUE(extension) << error; return extension; } +static scoped_refptr<Extension> LoadManifest(const std::string& dir, + const std::string& test_file) { + return LoadManifest(dir, test_file, Extension::NO_FLAGS); +} + TEST(ExtensionTest, EffectiveHostPermissions) { scoped_refptr<Extension> extension; ExtensionExtent hosts; @@ -933,20 +1021,31 @@ TEST(ExtensionTest, PermissionMessages) { // The proxy permission is warned as part of host permission checks. skip.insert(Extension::kProxyPermission); + // This permission requires explicit user action (context menu handler) + // so we won't prompt for it for now. + skip.insert(Extension::kFileBrowserHandlerPermission); + // If you've turned on the experimental command-line flag, we don't need // to warn you further. skip.insert(Extension::kExperimentalPermission); - // This is only usable by component extensions. + // These are only usable by component extensions. skip.insert(Extension::kWebstorePrivatePermission); + skip.insert(Extension::kFileBrowserPrivatePermission); + skip.insert(Extension::kChromeosInfoPrivatePermissions); + + const Extension::PermissionMessage::MessageId ID_NONE = + Extension::PermissionMessage::ID_NONE; for (size_t i = 0; i < Extension::kNumPermissions; ++i) { - int message_id = Extension::kPermissions[i].message_id; - std::string name = Extension::kPermissions[i].name; - if (skip.count(name)) - EXPECT_EQ(0, message_id) << "unexpected message_id for " << name; - else - EXPECT_NE(0, message_id) << "missing message_id for " << name; + Extension::Permission permission = Extension::kPermissions[i]; + if (skip.count(permission.name)) { + EXPECT_EQ(ID_NONE, permission.message_id) + << "unexpected message_id for " << permission.name; + } else { + EXPECT_NE(ID_NONE, permission.message_id) + << "missing message_id for " << permission.name; + } } } @@ -973,7 +1072,8 @@ TEST(ExtensionTest, ImageCaching) { values.SetString(keys::kName, "test"); values.SetString(keys::kVersion, "0.1"); scoped_refptr<Extension> extension(Extension::Create( - path, Extension::INVALID, values, false, true, &errors)); + path, Extension::INVALID, values, Extension::STRICT_ERROR_CHECKS, + &errors)); ASSERT_TRUE(extension.get()); // Create an ExtensionResource pointing at an icon. @@ -1057,7 +1157,8 @@ TEST(ExtensionTest, OldUnlimitedStoragePermission) { // is present. std::string errors; scoped_refptr<Extension> extension(Extension::Create( - extension_path, Extension::INVALID, dictionary, false, true, &errors)); + extension_path, Extension::INVALID, dictionary, + Extension::STRICT_ERROR_CHECKS, &errors)); EXPECT_TRUE(extension.get()); EXPECT_TRUE(extension->HasApiPermission( Extension::kUnlimitedStoragePermission)); @@ -1105,6 +1206,243 @@ TEST(ExtensionTest, ApiPermissions) { } } +TEST(ExtensionTest, GetHostPermissionMessages_ManyHosts) { + scoped_refptr<Extension> extension; + extension = LoadManifest("permissions", "many-hosts.json"); + std::vector<string16> warnings = extension->GetPermissionMessageStrings(); + ASSERT_EQ(1u, warnings.size()); + EXPECT_EQ("Your data on www.google.com and encrypted.google.com", + UTF16ToUTF8(warnings[0])); +} + +TEST(ExtensionTest, GetPermissionMessages_Plugins) { + scoped_refptr<Extension> extension; + extension = LoadManifest("permissions", "plugins.json"); + std::vector<string16> warnings = extension->GetPermissionMessageStrings(); + // We don't parse the plugins key on Chrome OS, so it should not ask for any + // permissions. +#if defined(OS_CHROMEOS) + ASSERT_EQ(0u, warnings.size()); +#else + ASSERT_EQ(1u, warnings.size()); + EXPECT_EQ("All data on your computer and the websites you visit", + UTF16ToUTF8(warnings[0])); +#endif +} + +TEST(ExtensionTest, WantsFileAccess) { + scoped_refptr<Extension> extension; + GURL file_url("file:///etc/passwd"); + + // <all_urls> permission + extension = LoadManifest("permissions", "permissions_all_urls.json"); + EXPECT_TRUE(extension->wants_file_access()); + EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + extension = LoadManifest( + "permissions", "permissions_all_urls.json", Extension::ALLOW_FILE_ACCESS); + EXPECT_TRUE(extension->wants_file_access()); + EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + + // file:///* permission + extension = LoadManifest("permissions", "permissions_file_scheme.json"); + EXPECT_TRUE(extension->wants_file_access()); + EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + extension = LoadManifest("permissions", "permissions_file_scheme.json", + Extension::ALLOW_FILE_ACCESS); + EXPECT_TRUE(extension->wants_file_access()); + EXPECT_TRUE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + + // http://* permission + extension = LoadManifest("permissions", "permissions_http_scheme.json"); + EXPECT_FALSE(extension->wants_file_access()); + EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + extension = LoadManifest("permissions", "permissions_http_scheme.json", + Extension::ALLOW_FILE_ACCESS); + EXPECT_FALSE(extension->wants_file_access()); + EXPECT_FALSE(extension->CanExecuteScriptOnPage(file_url, NULL, NULL)); + + // <all_urls> content script match + extension = LoadManifest("permissions", "content_script_all_urls.json"); + EXPECT_TRUE(extension->wants_file_access()); + EXPECT_FALSE(extension->CanExecuteScriptOnPage( + file_url, &extension->content_scripts()[0], NULL)); + extension = LoadManifest("permissions", "content_script_all_urls.json", + Extension::ALLOW_FILE_ACCESS); + EXPECT_TRUE(extension->wants_file_access()); + EXPECT_TRUE(extension->CanExecuteScriptOnPage( + file_url, &extension->content_scripts()[0], NULL)); + + // file:///* content script match + extension = LoadManifest("permissions", "content_script_file_scheme.json"); + EXPECT_TRUE(extension->wants_file_access()); + EXPECT_FALSE(extension->CanExecuteScriptOnPage( + file_url, &extension->content_scripts()[0], NULL)); + extension = LoadManifest("permissions", "content_script_file_scheme.json", + Extension::ALLOW_FILE_ACCESS); + EXPECT_TRUE(extension->wants_file_access()); + EXPECT_TRUE(extension->CanExecuteScriptOnPage( + file_url, &extension->content_scripts()[0], NULL)); + + // http://* content script match + extension = LoadManifest("permissions", "content_script_http_scheme.json"); + EXPECT_FALSE(extension->wants_file_access()); + EXPECT_FALSE(extension->CanExecuteScriptOnPage( + file_url, &extension->content_scripts()[0], NULL)); + extension = LoadManifest("permissions", "content_script_http_scheme.json", + Extension::ALLOW_FILE_ACCESS); + EXPECT_FALSE(extension->wants_file_access()); + EXPECT_FALSE(extension->CanExecuteScriptOnPage( + file_url, &extension->content_scripts()[0], NULL)); +} + +// Base class for testing the CanExecuteScriptOnPage and CanCaptureVisiblePage +// methods of Extension for extensions with various permissions. +class ExtensionScriptAndCaptureVisibleTest : public testing::Test { + public: + ExtensionScriptAndCaptureVisibleTest() { + PathService::Get(chrome::DIR_TEST_DATA, &dirpath_); + } + + scoped_refptr<Extension> MakeExtension(const std::string& permissions, + Extension::Location location) { + // Replace single-quotes with double-quotes in permissions, since JSON + // mandates double-quotes. + std::string munged_permissions = permissions; + ReplaceSubstringsAfterOffset(&munged_permissions, 0, "'", "\""); + + DictionaryValue dictionary; + dictionary.SetString(keys::kName, "permission test"); + dictionary.SetString(keys::kVersion, "1"); + std::string error; + JSONStringValueSerializer serializer(munged_permissions); + scoped_ptr<Value> permission_value(serializer.Deserialize(NULL, &error)); + EXPECT_EQ("", error); + if (!permission_value.get()) + return NULL; + EXPECT_TRUE(permission_value->IsType(Value::TYPE_LIST)); + dictionary.Set(keys::kPermissions, permission_value.release()); + + FilePath dirpath; + PathService::Get(chrome::DIR_TEST_DATA, &dirpath); + dirpath = dirpath.AppendASCII("extensions").AppendASCII("permissions"); + + scoped_refptr<Extension> extension = Extension::Create( + dirpath, + location, + dictionary, + Extension::STRICT_ERROR_CHECKS, + &error); + if (!extension) + VLOG(1) << error; + return extension; + } + + bool Allowed(const Extension* extension, const GURL& url) { + return (extension->CanExecuteScriptOnPage(url, NULL, NULL) && + extension->CanCaptureVisiblePage(url, NULL)); + } + + bool CaptureOnly(const Extension* extension, const GURL& url) { + return !extension->CanExecuteScriptOnPage(url, NULL, NULL) && + extension->CanCaptureVisiblePage(url, NULL); + } + + bool Blocked(const Extension* extension, const GURL& url) { + return !(extension->CanExecuteScriptOnPage(url, NULL, NULL) || + extension->CanCaptureVisiblePage(url, NULL)); + } + + protected: + FilePath dirpath_; +}; + +TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) { + scoped_refptr<Extension> extension; + // URLs that are "safe" to provide scripting and capture visible tab access + // to if the permissions allow it. + GURL http_url("http://www.google.com"); + GURL https_url("https://www.google.com"); + GURL file_url("file:///foo/bar"); + + // We should allow host permission but not scripting permission for favicon + // urls. + GURL favicon_url("chrome://favicon/http://www.google.com"); + + std::string dummy_id = + Extension::GenerateIdForPath(FilePath(FILE_PATH_LITERAL("whatever"))); + + // URLs that regular extensions should never get access to. + GURL extension_url("chrome-extension://" + dummy_id); + GURL settings_url("chrome://settings"); + GURL about_url("about:flags"); + + // Test <all_urls> for regular extensions. + extension = MakeExtension("['tabs','<all_urls>']", Extension::INTERNAL); + EXPECT_TRUE(Allowed(extension, http_url)); + EXPECT_TRUE(Allowed(extension, https_url)); + EXPECT_TRUE(Blocked(extension, file_url)); + EXPECT_TRUE(Blocked(extension, settings_url)); + EXPECT_TRUE(CaptureOnly(extension, favicon_url)); + EXPECT_TRUE(Blocked(extension, about_url)); + EXPECT_TRUE(Blocked(extension, extension_url)); + + EXPECT_FALSE(extension->HasHostPermission(settings_url)); + EXPECT_FALSE(extension->HasHostPermission(about_url)); + EXPECT_TRUE(extension->HasHostPermission(favicon_url)); + + // Test * for scheme, which implies just the http/https schemes. + extension = MakeExtension("['tabs','*://*/']", Extension::INTERNAL); + EXPECT_TRUE(Allowed(extension, http_url)); + EXPECT_TRUE(Allowed(extension, https_url)); + EXPECT_TRUE(Blocked(extension, settings_url)); + EXPECT_TRUE(Blocked(extension, about_url)); + EXPECT_TRUE(Blocked(extension, file_url)); + EXPECT_TRUE(Blocked(extension, favicon_url)); + extension = MakeExtension("['tabs','*://settings/*']", Extension::INTERNAL); + EXPECT_TRUE(Blocked(extension, settings_url)); + + // Having chrome://*/ should not work for regular extensions. Note that + // for favicon access, we require the explicit pattern chrome://favicon/*. + extension = MakeExtension("['tabs','chrome://*/']", + Extension::INTERNAL); + EXPECT_TRUE(extension == NULL); + + // Having chrome://favicon/* should not give you chrome://* + extension = MakeExtension("['tabs','chrome://favicon/*']", + Extension::INTERNAL); + EXPECT_TRUE(Blocked(extension, settings_url)); + EXPECT_TRUE(CaptureOnly(extension, favicon_url)); + EXPECT_TRUE(Blocked(extension, about_url)); + EXPECT_TRUE(extension->HasHostPermission(favicon_url)); + + // Having http://favicon should not give you chrome://favicon + extension = MakeExtension("['tabs', 'http://favicon/']", Extension::INTERNAL); + EXPECT_TRUE(Blocked(extension, settings_url)); + EXPECT_TRUE(Blocked(extension, favicon_url)); + + // Component extensions with <all_urls> should get everything. + extension = MakeExtension("['tabs','<all_urls>']", Extension::COMPONENT); + EXPECT_TRUE(Allowed(extension, http_url)); + EXPECT_TRUE(Allowed(extension, https_url)); + EXPECT_TRUE(Allowed(extension, settings_url)); + EXPECT_TRUE(Allowed(extension, about_url)); + EXPECT_TRUE(Allowed(extension, favicon_url)); + EXPECT_TRUE(extension->HasHostPermission(favicon_url)); + + // Component extensions should only get access to what they ask for. + extension = MakeExtension("['tabs', 'http://www.google.com/']", + Extension::COMPONENT); + EXPECT_TRUE(Allowed(extension, http_url)); + EXPECT_TRUE(Blocked(extension, https_url)); + EXPECT_TRUE(Blocked(extension, file_url)); + EXPECT_TRUE(Blocked(extension, settings_url)); + EXPECT_TRUE(Blocked(extension, favicon_url)); + EXPECT_TRUE(Blocked(extension, about_url)); + EXPECT_TRUE(Blocked(extension, extension_url)); + EXPECT_FALSE(extension->HasHostPermission(settings_url)); +} + + TEST(ExtensionTest, GetDistinctHostsForDisplay) { std::vector<std::string> expected; expected.push_back("www.foo.com"); @@ -1194,6 +1532,9 @@ TEST(ExtensionTest, GetDistinctHostsForDisplay) { // This is an unknown RCD, which shouldn't be uniqued out. actual.push_back( URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path")); + // But it should only occur once. + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path")); expected.push_back("www.foo.xyzzy"); @@ -1214,6 +1555,88 @@ TEST(ExtensionTest, GetDistinctHostsForDisplay) { } } +TEST(ExtensionTest, GetDistinctHostsForDisplay_ComIsBestRcd) { + URLPatternList actual; + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path")); + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path")); + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path")); + + std::vector<std::string> expected; + expected.push_back("www.foo.com"); + + CompareLists(expected, + Extension::GetDistinctHostsForDisplay(actual)); +} + +TEST(ExtensionTest, GetDistinctHostsForDisplay_NetIs2ndBestRcd) { + URLPatternList actual; + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path")); + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path")); + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); + // No http://www.foo.com/path + + std::vector<std::string> expected; + expected.push_back("www.foo.net"); + + CompareLists(expected, + Extension::GetDistinctHostsForDisplay(actual)); +} + +TEST(ExtensionTest, GetDistinctHostsForDisplay_OrgIs3rdBestRcd) { + URLPatternList actual; + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path")); + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); + // No http://www.foo.net/path + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); + // No http://www.foo.com/path + + std::vector<std::string> expected; + expected.push_back("www.foo.org"); + + CompareLists(expected, + Extension::GetDistinctHostsForDisplay(actual)); +} + +TEST(ExtensionTest, GetDistinctHostsForDisplay_FirstInListIs4thBestRcd) { + URLPatternList actual; + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); + // No http://www.foo.org/path + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); + // No http://www.foo.net/path + actual.push_back( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); + // No http://www.foo.com/path + + std::vector<std::string> expected; + expected.push_back("www.foo.ca"); + + CompareLists(expected, + Extension::GetDistinctHostsForDisplay(actual)); +} + TEST(ExtensionTest, IsElevatedHostList) { URLPatternList list1; URLPatternList list2; @@ -1273,7 +1696,7 @@ TEST(ExtensionTest, IsElevatedHostList) { TEST(ExtensionTest, GenerateId) { std::string result; - EXPECT_FALSE(Extension::GenerateId("", &result)); + EXPECT_TRUE(Extension::GenerateId("", &result)); EXPECT_TRUE(Extension::GenerateId("test", &result)); EXPECT_EQ(result, "jpignaibiiemhngfjkcpokkamffknabf"); diff --git a/chrome/common/extensions/extension_unpacker.cc b/chrome/common/extensions/extension_unpacker.cc index fee4b54..4c5f30f 100644 --- a/chrome/common/extensions/extension_unpacker.cc +++ b/chrome/common/extensions/extension_unpacker.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -7,21 +7,21 @@ #include <set> #include "base/file_util.h" -#include "base/scoped_handle.h" -#include "base/scoped_temp_dir.h" +#include "base/memory/scoped_handle.h" +#include "base/memory/scoped_temp_dir.h" #include "base/string_util.h" #include "base/threading/thread.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "net/base/file_stream.h" -#include "chrome/common/common_param_traits.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_file_util.h" #include "chrome/common/extensions/extension_l10n_util.h" -#include "chrome/common/json_value_serializer.h" #include "chrome/common/url_constants.h" #include "chrome/common/zip.h" +#include "content/common/common_param_traits.h" +#include "content/common/json_value_serializer.h" #include "ipc/ipc_message_utils.h" #include "third_party/skia/include/core/SkBitmap.h" #include "webkit/glue/image_decoder.h" @@ -180,8 +180,7 @@ bool ExtensionUnpacker::Run() { temp_install_dir_, Extension::INVALID, *parsed_manifest_, - false, // Do not require a key - false, // Do not enable strict error checks + Extension::NO_FLAGS, &error)); if (!extension.get()) { SetError(error); diff --git a/chrome/common/extensions/extension_unpacker.h b/chrome/common/extensions/extension_unpacker.h index 6883597..0282718 100644 --- a/chrome/common/extensions/extension_unpacker.h +++ b/chrome/common/extensions/extension_unpacker.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -10,7 +10,7 @@ #include <vector> #include "base/file_path.h" -#include "base/scoped_ptr.h" +#include "base/memory/scoped_ptr.h" #include "base/tuple.h" class DictionaryValue; diff --git a/chrome/common/extensions/extension_unpacker_unittest.cc b/chrome/common/extensions/extension_unpacker_unittest.cc index 930c271..80ed7a5 100644 --- a/chrome/common/extensions/extension_unpacker_unittest.cc +++ b/chrome/common/extensions/extension_unpacker_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -6,6 +6,7 @@ #include "base/path_service.h" #include "base/string_util.h" #include "base/values.h" +#include "base/memory/scoped_temp_dir.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_unpacker.h" @@ -27,12 +28,9 @@ public: // Try bots won't let us write into DIR_TEST_DATA, so we have to create // a temp folder to play in. - ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &install_dir_)); - install_dir_ = install_dir_.AppendASCII("extension_unpacker_test"); - file_util::Delete(install_dir_, true); - file_util::CreateDirectory(install_dir_); + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - FilePath crx_path = install_dir_.AppendASCII(crx_name); + FilePath crx_path = temp_dir_.path().AppendASCII(crx_name); ASSERT_TRUE(file_util::CopyFile(original_path, crx_path)) << "Original path " << original_path.value() << ", Crx path " << crx_path.value(); @@ -40,13 +38,8 @@ public: unpacker_.reset(new ExtensionUnpacker(crx_path)); } - virtual void TearDown() { - ASSERT_TRUE(file_util::Delete(install_dir_, true)) << - install_dir_.value(); - } - protected: - FilePath install_dir_; + ScopedTempDir temp_dir_; scoped_ptr<ExtensionUnpacker> unpacker_; }; diff --git a/chrome/common/extensions/file_browser_handler.cc b/chrome/common/extensions/file_browser_handler.cc new file mode 100644 index 0000000..ea270b4 --- /dev/null +++ b/chrome/common/extensions/file_browser_handler.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2011 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. + +#include "chrome/common/extensions/file_browser_handler.h" + +#include "chrome/common/extensions/url_pattern.h" +#include "googleurl/src/gurl.h" + +FileBrowserHandler::FileBrowserHandler() { +} + +FileBrowserHandler::~FileBrowserHandler() { +} + +void FileBrowserHandler::AddPattern(const URLPattern& pattern) { + patterns_.push_back(pattern); +} + +void FileBrowserHandler::ClearPatterns() { + patterns_.clear(); +} + +bool FileBrowserHandler::MatchesURL(const GURL& url) const { + for (PatternList::const_iterator pattern = patterns_.begin(); + pattern != patterns_.end(); ++pattern) { + if (pattern->MatchesUrl(url)) + return true; + } + return false; +} + diff --git a/chrome/common/extensions/file_browser_handler.h b/chrome/common/extensions/file_browser_handler.h new file mode 100644 index 0000000..420be17 --- /dev/null +++ b/chrome/common/extensions/file_browser_handler.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011 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. + +#ifndef CHROME_COMMON_EXTENSIONS_FILE_BROWSER_HANDLER_H_ +#define CHROME_COMMON_EXTENSIONS_FILE_BROWSER_HANDLER_H_ +#pragma once + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "chrome/common/extensions/url_pattern.h" +#include "googleurl/src/gurl.h" + +class URLPattern; + +// FileBrowserHandler encapsulates the state of a file browser action. +class FileBrowserHandler { + public: + typedef std::vector<URLPattern> PatternList; + + FileBrowserHandler(); + ~FileBrowserHandler(); + + // extension id + std::string extension_id() const { return extension_id_; } + void set_extension_id(const std::string& extension_id) { + extension_id_ = extension_id; + } + + // action id + const std::string& id() const { return id_; } + void set_id(const std::string& id) { id_ = id; } + + // default title + const std::string& title() const { return title_; } + void set_title(const std::string& title) { title_ = title; } + + // File schema URL patterns. + const PatternList& file_url_patterns() const { return patterns_; } + void AddPattern(const URLPattern& pattern); + bool MatchesURL(const GURL& url) const; + void ClearPatterns(); + + // Action icon path. + const std::string icon_path() const { return default_icon_path_; } + void set_icon_path(const std::string& path) { + default_icon_path_ = path; + } + + private: + // The id for the extension this action belongs to (as defined in the + // extension manifest). + std::string extension_id_; + std::string title_; + std::string default_icon_path_; + // The id for the FileBrowserHandler, for example: "PdfFileAction". + std::string id_; + // A list of file filters. + PatternList patterns_; +}; + +#endif // CHROME_COMMON_EXTENSIONS_FILE_BROWSER_HANDLER_H_ diff --git a/chrome/common/extensions/update_manifest.cc b/chrome/common/extensions/update_manifest.cc index 8970aa5..713898f 100644 --- a/chrome/common/extensions/update_manifest.cc +++ b/chrome/common/extensions/update_manifest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -6,7 +6,7 @@ #include <algorithm> -#include "base/scoped_ptr.h" +#include "base/memory/scoped_ptr.h" #include "base/stl_util-inl.h" #include "base/string_util.h" #include "base/string_number_conversions.h" diff --git a/chrome/common/extensions/update_manifest_unittest.cc b/chrome/common/extensions/update_manifest_unittest.cc index 0de8df0..9af11df 100644 --- a/chrome/common/extensions/update_manifest_unittest.cc +++ b/chrome/common/extensions/update_manifest_unittest.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. -#include "base/scoped_vector.h" +#include "base/memory/scoped_vector.h" #include "chrome/common/extensions/update_manifest.h" #include "testing/gtest/include/gtest/gtest.h" #include "libxml/globals.h" diff --git a/chrome/common/extensions/url_pattern.cc b/chrome/common/extensions/url_pattern.cc index 5eb0beb..986487a 100644 --- a/chrome/common/extensions/url_pattern.cc +++ b/chrome/common/extensions/url_pattern.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -24,6 +24,7 @@ const char* kValidSchemes[] = { chrome::kFileScheme, chrome::kFtpScheme, chrome::kChromeUIScheme, + chrome::kFileSystemScheme, }; const int kValidSchemeMasks[] = { @@ -32,6 +33,7 @@ const int kValidSchemeMasks[] = { URLPattern::SCHEME_FILE, URLPattern::SCHEME_FTP, URLPattern::SCHEME_CHROMEUI, + URLPattern::SCHEME_FILESYSTEM, }; COMPILE_ASSERT(arraysize(kValidSchemes) == arraysize(kValidSchemeMasks), @@ -230,10 +232,10 @@ bool URLPattern::MatchesUrl(const GURL &test) const { } bool URLPattern::MatchesScheme(const std::string& test) const { - if (scheme_ == "*") - return IsValidScheme(test); + if (!IsValidScheme(test)) + return false; - return test == scheme_; + return scheme_ == "*" || test == scheme_; } bool URLPattern::MatchesHost(const std::string& host) const { @@ -333,7 +335,7 @@ bool URLPattern::OverlapsWith(const URLPattern& other) const { std::vector<URLPattern> URLPattern::ConvertToExplicitSchemes() const { std::vector<URLPattern> result; - if (scheme_ != "*" && !match_all_urls_) { + if (scheme_ != "*" && !match_all_urls_ && IsValidScheme(scheme_)) { result.push_back(*this); return result; } diff --git a/chrome/common/extensions/url_pattern.h b/chrome/common/extensions/url_pattern.h index edd92d4..cf89382 100644 --- a/chrome/common/extensions/url_pattern.h +++ b/chrome/common/extensions/url_pattern.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. #ifndef CHROME_COMMON_EXTENSIONS_URL_PATTERN_H_ @@ -77,12 +77,13 @@ class URLPattern { public: // A collection of scheme bitmasks for use with valid_schemes. enum SchemeMasks { - SCHEME_NONE = 0, - SCHEME_HTTP = 1 << 0, - SCHEME_HTTPS = 1 << 1, - SCHEME_FILE = 1 << 2, - SCHEME_FTP = 1 << 3, - SCHEME_CHROMEUI = 1 << 4, + SCHEME_NONE = 0, + SCHEME_HTTP = 1 << 0, + SCHEME_HTTPS = 1 << 1, + SCHEME_FILE = 1 << 2, + SCHEME_FTP = 1 << 3, + SCHEME_CHROMEUI = 1 << 4, + SCHEME_FILESYSTEM = 1 << 5, // SCHEME_ALL will match every scheme, including chrome://, chrome- // extension://, about:, etc. Because this has lots of security // implications, third-party extensions should never be able to get access diff --git a/chrome/common/extensions/url_pattern_unittest.cc b/chrome/common/extensions/url_pattern_unittest.cc index cc9c605..ef1406b 100644 --- a/chrome/common/extensions/url_pattern_unittest.cc +++ b/chrome/common/extensions/url_pattern_unittest.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. -#include "base/scoped_ptr.h" +#include "base/memory/scoped_ptr.h" #include "chrome/common/extensions/url_pattern.h" #include "testing/gtest/include/gtest/gtest.h" #include "googleurl/src/gurl.h" diff --git a/chrome/common/extensions/user_script.cc b/chrome/common/extensions/user_script.cc index a73b65b..6439338 100644 --- a/chrome/common/extensions/user_script.cc +++ b/chrome/common/extensions/user_script.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -61,8 +61,7 @@ UserScript::File::~File() {} UserScript::UserScript() : run_location_(DOCUMENT_IDLE), emulate_greasemonkey_(false), - match_all_frames_(false), incognito_enabled_(false), - allow_file_access_(false) { + match_all_frames_(false), incognito_enabled_(false) { } UserScript::~UserScript() { @@ -72,8 +71,6 @@ void UserScript::add_url_pattern(const URLPattern& pattern) { url_patterns_.push_back(pattern); } -void UserScript::clear_url_patterns() { url_patterns_.clear(); } - bool UserScript::MatchesUrl(const GURL& url) const { if (!url_patterns_.empty()) { if (!UrlMatchesPatterns(&url_patterns_, url)) @@ -113,7 +110,6 @@ void UserScript::Pickle(::Pickle* pickle) const { pickle->WriteBool(emulate_greasemonkey()); pickle->WriteBool(match_all_frames()); pickle->WriteBool(is_incognito_enabled()); - pickle->WriteBool(allow_file_access()); // Write globs. std::vector<std::string>::const_iterator glob; @@ -160,7 +156,6 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) { CHECK(pickle.ReadBool(iter, &emulate_greasemonkey_)); CHECK(pickle.ReadBool(iter, &match_all_frames_)); CHECK(pickle.ReadBool(iter, &incognito_enabled_)); - CHECK(pickle.ReadBool(iter, &allow_file_access_)); // Read globs. size_t num_globs = 0; @@ -191,8 +186,18 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) { std::string pattern_str; URLPattern pattern(valid_schemes); CHECK(pickle.ReadString(iter, &pattern_str)); + + // We remove the file scheme if it's not actually allowed (see Extension:: + // LoadUserScriptHelper), but we need it temporarily while loading the + // pattern so that it's valid. + bool had_file_scheme = (valid_schemes & URLPattern::SCHEME_FILE) != 0; + if (!had_file_scheme) + pattern.set_valid_schemes(valid_schemes | URLPattern::SCHEME_FILE); CHECK(URLPattern::PARSE_SUCCESS == pattern.Parse(pattern_str, URLPattern::PARSE_LENIENT)); + if (!had_file_scheme) + pattern.set_valid_schemes(valid_schemes); + url_patterns_.push_back(pattern); } diff --git a/chrome/common/extensions/user_script.h b/chrome/common/extensions/user_script.h index 3be2f11..3d91de1 100644 --- a/chrome/common/extensions/user_script.h +++ b/chrome/common/extensions/user_script.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -152,7 +152,6 @@ class UserScript { // against. const PatternList& url_patterns() const { return url_patterns_; } void add_url_pattern(const URLPattern& pattern); - void clear_url_patterns(); // List of js scripts for this user script FileList& js_scripts() { return js_scripts_; } @@ -168,9 +167,6 @@ class UserScript { bool is_incognito_enabled() const { return incognito_enabled_; } void set_incognito_enabled(bool enabled) { incognito_enabled_ = enabled; } - bool allow_file_access() const { return allow_file_access_; } - void set_allow_file_access(bool allowed) { allow_file_access_ = allowed; } - bool is_standalone() const { return extension_id_.empty(); } // Returns true if the script should be applied to the specified URL, false @@ -233,9 +229,6 @@ class UserScript { // True if the script should be injected into an incognito tab. bool incognito_enabled_; - - // True if the user agreed to allow this script access to file URLs. - bool allow_file_access_; }; typedef std::vector<UserScript> UserScriptList; |