diff options
author | jstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-27 14:36:49 +0000 |
---|---|---|
committer | jstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-27 14:36:49 +0000 |
commit | 38383b194bc471ebba4de124cbf391b00114923a (patch) | |
tree | f7a027d2ea9f88f28216a29d0b9a0368e03ae502 /chrome/common/extensions | |
parent | 4d48dccadf757b39157537331a3becd13a5f7d9f (diff) | |
download | chromium_src-38383b194bc471ebba4de124cbf391b00114923a.zip chromium_src-38383b194bc471ebba4de124cbf391b00114923a.tar.gz chromium_src-38383b194bc471ebba4de124cbf391b00114923a.tar.bz2 |
Add an experimental permissions API for extensions.
The permissions API lets extensions specify optional permissions in their manifest that they can request at run-time. It currently supports API permissions through a white-list. Host permissions will come later.
BUG=48119, 70466, 84507
TEST=*Extension*
Review URL: http://codereview.chromium.org/7432006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@94288 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/extensions')
17 files changed, 2861 insertions, 293 deletions
diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index f3f8559..3e032aa 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -1060,6 +1060,143 @@ ] }, { + "namespace": "experimental.permissions", + "types": [ + { + "id": "Permissions", + "type": "object", + "properties": { + "permissions": { + "type": "array", + "items": {"type": "string"}, + "optional": true, + "description": "List of named permissions (does not include hosts or origins)." + } + } + } + ], + "events": [ + { + "name": "onAdded", + "type": "function", + "unprivileged": true, + "description": "Fired when the extension acquires new permissions.", + "parameters": [ + { + "$ref": "Permissions", + "name": "permissions", + "description": "The newly acquired permissions." + } + ] + }, + { + "name": "onRemoved", + "type": "function", + "unprivileged": true, + "description": "Fired when access to permissions has been removed from the extension.", + "parameters": [ + { + "$ref": "Permissions", + "name": "permissions", + "description": "The permissions that have been removed." + } + ] + } + ], + "functions": [ + { + "name": "getAll", + "type": "function", + "unprivileged": true, + "description": "Gets the extension's current set of permissions.", + "parameters": [ + { + "name": "callback", + "type": "function", + "parameters": [ + { + "name": "permissions", + "$ref": "Permissions", + "description": "The extension's active permissions." + } + ] + } + ] + }, + { + "name": "contains", + "type": "function", + "unprivileged": true, + "description": "Checks if the extension has the specified permissions.", + "parameters": [ + { + "name": "permissions", + "$ref": "Permissions" + }, + { + "name": "callback", + "type": "function", + "parameters": [ + { + "name": "result", + "type": "boolean", + "description": "True if the extension has the specified permissions." + } + ] + } + ] + }, + { + "name": "request", + "type": "function", + "unprivileged": true, + "description": "Requests access to the specified permissions. These permissions must be defined in the optional_permissions field of the manifest.", + "parameters": [ + { + "name": "permissions", + "$ref": "Permissions" + }, + { + "name": "callback", + "type": "function", + "optional": true, + "parameters": [ + { + "name": "granted", + "type": "boolean", + "description": "True if the user granted the specified permissions." + } + ] + } + ] + }, + { + "name": "remove", + "type": "function", + "unprivileged": true, + "description": "Removes access to the specified permissions.", + "parameters": [ + { + "name": "permissions", + "$ref": "Permissions" + }, + { + "name": "callback", + "type": "function", + "optional": true, + "parameters": [ + { + "name": "removed", + "type": "boolean", + "description": "True if the permissions were removed." + } + ] + } + ] + } + ] + }, + { "namespace": "tabs", "types": [ { diff --git a/chrome/common/extensions/docs/experimental.html b/chrome/common/extensions/docs/experimental.html index 49aa374..8c62aec 100644 --- a/chrome/common/extensions/docs/experimental.html +++ b/chrome/common/extensions/docs/experimental.html @@ -328,6 +328,7 @@ on the following experimental APIs: <a href="experimental.devtools.panels.html">experimental.devtools.panels</a></li><li> <a href="experimental.devtools.resources.html">experimental.devtools.resources</a></li><li> <a href="experimental.infobars.html">experimental.infobars</a></li><li> + <a href="experimental.permissions.html">experimental.permissions</a></li><li> <a href="experimental.processes.html">experimental.processes</a></li><li> <a href="experimental.sidebar.html">experimental.sidebar</a></li><li> <a href="experimental.webNavigation.html">experimental.webNavigation</a></li><li> diff --git a/chrome/common/extensions/docs/experimental.permissions.html b/chrome/common/extensions/docs/experimental.permissions.html new file mode 100644 index 0000000..b167ee9 --- /dev/null +++ b/chrome/common/extensions/docs/experimental.permissions.html @@ -0,0 +1,1784 @@ +<!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.permissions - 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.permissions</h1> + </div> + <!-- TABLE OF CONTENTS --> + <div id="toc"> + <h2>Contents</h2> + <ol> + <li style="display: none; "> + <a>h2Name</a> + <ol> + <li> + <a>h3Name</a> + </li> + </ol> + </li> + <li> + <a href="#apiReference">API reference: chrome.experimental.permissions</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-contains">contains</a> + </li><li> + <a href="#method-getAll">getAll</a> + </li><li> + <a href="#method-remove">remove</a> + </li><li> + <a href="#method-request">request</a> + </li> + </ol> + </li> + <li> + <a href="#global-events">Events</a> + <ol> + <li> + <a href="#event-onAdded">onAdded</a> + </li><li> + <a href="#event-onRemoved">onRemoved</a> + </li> + </ol> + </li> + <li> + <a href="#types">Types</a> + <ol> + <li> + <a href="#type-Permissions">Permissions</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"></div> + + <!-- API PAGE --> + <div class="apiPage"> + <a name="apiReference"></a> + <h2>API reference: chrome.experimental.permissions</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-contains"></a> <!-- method-anchor --> + <h4>contains</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.experimental.permissions.contains</span>(<span class="null"><span style="display: none; ">, </span><span>Permissions</span> + <var><span>permissions</span></var></span><span class="null"><span>, </span><span>function</span> + <var><span>callback</span></var></span>)</div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Checks if the extension has the specified permissions.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>permissions</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.permissions.html#type-Permissions">Permissions</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"> + 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>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 result</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + <dt> + <var>result</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 the specified permissions.</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 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-getAll"></a> <!-- method-anchor --> + <h4>getAll</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.experimental.permissions.getAll</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>Gets the extension's current set of permissions.</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>Permissions permissions</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + <dt> + <var>permissions</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.permissions.html#type-Permissions">Permissions</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>The extension's active permissions.</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 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-remove"></a> <!-- method-anchor --> + <h4>remove</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.experimental.permissions.remove</span>(<span class="null"><span style="display: none; ">, </span><span>Permissions</span> + <var><span>permissions</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>Removes access to the specified permissions.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>permissions</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.permissions.html#type-Permissions">Permissions</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"> + 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>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"> + 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 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>boolean removed</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + <dt> + <var>removed</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 permissions were removed.</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 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-request"></a> <!-- method-anchor --> + <h4>request</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.experimental.permissions.request</span>(<span class="null"><span style="display: none; ">, </span><span>Permissions</span> + <var><span>permissions</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>Requests access to the specified permissions. These permissions must be defined in the optional_permissions field of the manifest.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>permissions</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.permissions.html#type-Permissions">Permissions</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"> + 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>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"> + 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 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>boolean granted</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + <dt> + <var>granted</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 user granted the specified permissions.</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 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-onAdded"></a> + <h4>onAdded</h4> + + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span class="subdued">chrome.experimental.permissions.</span><span>onAdded</span><span class="subdued">.addListener</span>(function(<span>Permissions permissions</span>) <span class="subdued">{...}</span><span></span>)); + </div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Fired when the extension acquires new permissions.</p> + + <!-- LISTENER PARAMETERS --> + <div> + <h4>Listener parameters</h4> + <dl> + <div> + <div> + <dt> + <var>permissions</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.permissions.html#type-Permissions">Permissions</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>The newly acquired permissions.</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> + + <!-- EXTRA PARAMETERS --> + <div style="display: none; "> + <h4>Extra parameters to addListener</h4> + <dl> + <div> + <div> + </div> + </div> + </dl> + </div> + + <!-- LISTENER RETURN VALUE --> + <h4 style="display: none; ">Listener returns</h4> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + + </div> <!-- /description --> + </div><div class="apiItem"> + <a name="event-onRemoved"></a> + <h4>onRemoved</h4> + + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span class="subdued">chrome.experimental.permissions.</span><span>onRemoved</span><span class="subdued">.addListener</span>(function(<span>Permissions permissions</span>) <span class="subdued">{...}</span><span></span>)); + </div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Fired when access to permissions has been removed from the extension.</p> + + <!-- LISTENER PARAMETERS --> + <div> + <h4>Listener parameters</h4> + <dl> + <div> + <div> + <dt> + <var>permissions</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.permissions.html#type-Permissions">Permissions</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>The permissions that have been removed.</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> + + <!-- EXTRA PARAMETERS --> + <div style="display: none; "> + <h4>Extra parameters to addListener</h4> + <dl> + <div> + <div> + </div> + </div> + </dl> + </div> + + <!-- LISTENER RETURN VALUE --> + <h4 style="display: none; ">Listener returns</h4> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + + </div> <!-- /description --> + </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 name="type-Permissions"></a> + <h4>Permissions</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"> + 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>permissions</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> + array of <span><span> + <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></span> + </span> + <span style="display: none; ">paramType</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>List of named permissions (does not include hosts or origins).</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 --> + + </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/samples.json b/chrome/common/extensions/docs/samples.json index 862059d..56dbf21 100644 --- a/chrome/common/extensions/docs/samples.json +++ b/chrome/common/extensions/docs/samples.json @@ -51,6 +51,12 @@ "chrome.experimental.devtools.resources.onFinished": "experimental.devtools.resources.html#event-onFinished", "chrome.experimental.devtools.resources.onNavigation": "experimental.devtools.resources.html#event-onNavigation", "chrome.experimental.infobars.show": "experimental.infobars.html#method-show", + "chrome.experimental.permissions.contains": "experimental.permissions.html#method-contains", + "chrome.experimental.permissions.getAll": "experimental.permissions.html#method-getAll", + "chrome.experimental.permissions.onAdded": "experimental.permissions.html#event-onAdded", + "chrome.experimental.permissions.onRemoved": "experimental.permissions.html#event-onRemoved", + "chrome.experimental.permissions.remove": "experimental.permissions.html#method-remove", + "chrome.experimental.permissions.request": "experimental.permissions.html#method-request", "chrome.experimental.processes.getProcessIdForTab": "experimental.processes.html#method-getProcessIdForTab", "chrome.experimental.processes.onUpdated": "experimental.processes.html#event-onUpdated", "chrome.experimental.sidebar.collapse": "experimental.sidebar.html#method-collapse", diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index 111ed76..5488349 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -286,20 +286,6 @@ Extension::Location Extension::GetHigherPriorityLocation( return (loc1_rank > loc2_rank ? loc1 : loc2 ); } -ExtensionPermissionMessages Extension::GetPermissionMessages() const { - if (IsTrustedId(id_)) - return ExtensionPermissionMessages(); - else - return permission_set_->GetPermissionMessages(); -} - -std::vector<string16> Extension::GetPermissionMessageStrings() const { - if (IsTrustedId(id_)) - return std::vector<string16>(); - else - return permission_set_->GetWarningMessages(); -} - void Extension::OverrideLaunchUrl(const GURL& override_url) { GURL new_url(override_url); if (!new_url.is_valid()) { @@ -1202,6 +1188,7 @@ bool Extension::EnsureNotHybridApp(const DictionaryValue* manifest, if (!IsBaseCrxKey(*key) && *key != keys::kApp && *key != keys::kPermissions && + *key != keys::kOptionalPermissions && *key != keys::kOptionsPage && *key != keys::kBackground) { *error = ExtensionErrorUtils::FormatErrorMessage( @@ -1385,13 +1372,16 @@ GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { bool Extension::InitFromValue(const DictionaryValue& source, int flags, std::string* error) { + base::AutoLock auto_lock(runtime_data_lock_); // When strict error checks are enabled, make URL pattern parsing strict. URLPattern::ParseOption parse_strictness = (flags & STRICT_ERROR_CHECKS ? URLPattern::ERROR_ON_PORTS : URLPattern::IGNORE_PORTS); // Initialize permissions with an empty, default permission set. - permission_set_.reset(new ExtensionPermissionSet()); + runtime_data_.SetActivePermissions(new ExtensionPermissionSet()); + optional_permission_set_ = new ExtensionPermissionSet(); + required_permission_set_ = new ExtensionPermissionSet(); if (source.HasKey(keys::kPublicKey)) { std::string public_key_bytes; @@ -1911,104 +1901,30 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, } } + // Initialize the permissions (optional). ExtensionAPIPermissionSet api_permissions; URLPatternSet host_permissions; + if (!ParsePermissions(&source, + keys::kPermissions, + flags, + error, + &api_permissions, + &host_permissions)) { + return false; + } - // Initialize the permissions (optional). - if (source.HasKey(keys::kPermissions)) { - ListValue* permissions = NULL; - if (!source.GetList(keys::kPermissions, &permissions)) { - *error = ExtensionErrorUtils::FormatErrorMessage( - errors::kInvalidPermissions, ""); - return false; - } - - for (size_t i = 0; i < permissions->GetSize(); ++i) { - std::string permission_str; - if (!permissions->GetString(i, &permission_str)) { - *error = ExtensionErrorUtils::FormatErrorMessage( - errors::kInvalidPermission, base::IntToString(i)); - return false; - } - - ExtensionAPIPermission* permission = - ExtensionPermissionsInfo::GetInstance()->GetByName(permission_str); - - // 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 (!IsComponentOnlyPermission(permission) -#ifndef NDEBUG - && !CommandLine::ForCurrentProcess()->HasSwitch( - switches::kExposePrivateExtensionApi) -#endif - ) { - continue; - } - - if (web_extent().is_empty() || location() == Extension::COMPONENT) { - // Check if it's a module permission. If so, enable that permission. - if (permission != NULL) { - // Only allow the experimental API permission if the command line - // flag is present, or if the extension is a component of Chrome. - if (IsDisallowedExperimentalPermission(permission->id()) && - location() != Extension::COMPONENT) { - *error = errors::kExperimentalFlagRequired; - return false; - } - api_permissions.insert(permission->id()); - continue; - } - } else { - // Hosted apps only get access to a subset of the valid permissions. - if (permission != NULL && permission->is_hosted_app()) { - if (IsDisallowedExperimentalPermission(permission->id())) { - *error = errors::kExperimentalFlagRequired; - return false; - } - api_permissions.insert(permission->id()); - continue; - } - } - - // Check if it's a host pattern permission. - URLPattern pattern = URLPattern(CanExecuteScriptEverywhere() ? - URLPattern::SCHEME_ALL : kValidHostPermissionSchemes); - - URLPattern::ParseResult parse_result = pattern.Parse(permission_str, - parse_strictness); - if (parse_result == URLPattern::PARSE_SUCCESS) { - if (!CanSpecifyHostPermission(pattern)) { - *error = ExtensionErrorUtils::FormatErrorMessage( - errors::kInvalidPermissionScheme, base::IntToString(i)); - return false; - } - - // The path component is not used for host permissions, so we force it - // to match all paths. - pattern.SetPath("/*"); - - if (pattern.MatchesScheme(chrome::kFileScheme) && - !CanExecuteScriptEverywhere()) { - wants_file_access_ = true; - if (!(flags & ALLOW_FILE_ACCESS)) - pattern.SetValidSchemes( - pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); - } - - host_permissions.AddPattern(pattern); - } - - // If it's not a host permission, then it's probably an unknown API - // permission. Do not throw an error so extensions can retain - // backwards compatability (http://crbug.com/42742). - // TODO(jstritar): We can improve error messages by adding better - // validation of API permissions here. - // TODO(skerner): Consider showing the reason |permission_str| is not - // a valid URL pattern if it is almost valid. For example, if it has - // a valid scheme, and failed to parse because it has a port, show an - // error. - } + // Initialize the optional permissions (optional). + ExtensionAPIPermissionSet optional_api_permissions; + URLPatternSet optional_host_permissions; + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalExtensionApis) && + !ParsePermissions(&source, + keys::kOptionalPermissions, + flags, + error, + &optional_api_permissions, + &optional_host_permissions)) { + return false; } // Initialize background url (optional). @@ -2394,8 +2310,12 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, return false; } - permission_set_.reset( - new ExtensionPermissionSet(this, api_permissions, host_permissions)); + runtime_data_.SetActivePermissions(new ExtensionPermissionSet( + this, api_permissions, host_permissions)); + required_permission_set_ = new ExtensionPermissionSet( + this, api_permissions, host_permissions); + optional_permission_set_ = new ExtensionPermissionSet( + optional_api_permissions, optional_host_permissions, URLPatternSet()); // Although |source| is passed in as a const, it's still possible to modify // it. This is dangerous since the utility process re-uses |source| after @@ -2573,6 +2493,118 @@ GURL Extension::GetIconURL(int size, return GetResourceURL(path); } +bool Extension::ParsePermissions(const DictionaryValue* source, + const char* key, + int flags, + std::string* error, + ExtensionAPIPermissionSet* api_permissions, + URLPatternSet* host_permissions) { + if (source->HasKey(key)) { + // When strict error checks are enabled, make URL pattern parsing strict. + URLPattern::ParseOption parse_strictness = + (flags & STRICT_ERROR_CHECKS ? URLPattern::ERROR_ON_PORTS + : URLPattern::IGNORE_PORTS); + ListValue* permissions = NULL; + if (!source->GetList(key, &permissions)) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidPermissions, ""); + return false; + } + + for (size_t i = 0; i < permissions->GetSize(); ++i) { + std::string permission_str; + if (!permissions->GetString(i, &permission_str)) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidPermission, base::IntToString(i)); + return false; + } + + ExtensionAPIPermission* permission = + ExtensionPermissionsInfo::GetInstance()->GetByName(permission_str); + + // 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 (!IsComponentOnlyPermission(permission) +#ifndef NDEBUG + && !CommandLine::ForCurrentProcess()->HasSwitch( + switches::kExposePrivateExtensionApi) +#endif + ) { + continue; + } + + if (web_extent().is_empty() || location() == Extension::COMPONENT) { + // Check if it's a module permission. If so, enable that permission. + if (permission != NULL) { + // Only allow the experimental API permission if the command line + // flag is present, or if the extension is a component of Chrome. + if (IsDisallowedExperimentalPermission(permission->id()) && + location() != Extension::COMPONENT) { + *error = errors::kExperimentalFlagRequired; + return false; + } + api_permissions->insert(permission->id()); + continue; + } + } else { + // Hosted apps only get access to a subset of the valid permissions. + if (permission != NULL && permission->is_hosted_app()) { + if (IsDisallowedExperimentalPermission(permission->id())) { + *error = errors::kExperimentalFlagRequired; + return false; + } + api_permissions->insert(permission->id()); + continue; + } + } + + // Check if it's a host pattern permission. + URLPattern pattern = URLPattern(CanExecuteScriptEverywhere() ? + URLPattern::SCHEME_ALL : kValidHostPermissionSchemes); + + URLPattern::ParseResult parse_result = pattern.Parse(permission_str, + parse_strictness); + if (parse_result == URLPattern::PARSE_SUCCESS) { + if (!CanSpecifyHostPermission(pattern)) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidPermissionScheme, base::IntToString(i)); + return false; + } + + // The path component is not used for host permissions, so we force it + // to match all paths. + pattern.SetPath("/*"); + + if (pattern.MatchesScheme(chrome::kFileScheme) && + !CanExecuteScriptEverywhere()) { + wants_file_access_ = true; + if (!(flags & ALLOW_FILE_ACCESS)) + pattern.SetValidSchemes( + pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); + } + + host_permissions->AddPattern(pattern); + } + + // If it's not a host permission, then it's probably an unknown API + // permission. Do not throw an error so extensions can retain + // backwards compatability (http://crbug.com/42742). + // TODO(jstritar): We can improve error messages by adding better + // validation of API permissions here. + // TODO(skerner): Consider showing the reason |permission_str| is not + // a valid URL pattern if it is almost valid. For example, if it has + // a valid scheme, and failed to parse because it has a port, show an + // error. + } + } + return true; +} + +bool Extension::CanSilentlyIncreasePermissions() const { + return location() != INTERNAL; +} + bool Extension::CanSpecifyHostPermission(const URLPattern& pattern) const { if (!pattern.match_all_urls() && pattern.MatchesScheme(chrome::kChromeUIScheme)) { @@ -2588,24 +2620,68 @@ bool Extension::CanSpecifyHostPermission(const URLPattern& pattern) const { bool Extension::HasAPIPermission( ExtensionAPIPermission::ID permission) const { - return permission_set()->HasAPIPermission(permission); + base::AutoLock auto_lock(runtime_data_lock_); + return runtime_data_.GetActivePermissions()->HasAPIPermission(permission); } bool Extension::HasAPIPermission( const std::string& function_name) const { - return permission_set()->HasAccessToFunction(function_name); + base::AutoLock auto_lock(runtime_data_lock_); + return runtime_data_.GetActivePermissions()-> + HasAccessToFunction(function_name); } const URLPatternSet& Extension::GetEffectiveHostPermissions() const { - return permission_set()->effective_hosts(); + base::AutoLock auto_lock(runtime_data_lock_); + return runtime_data_.GetActivePermissions()->effective_hosts(); } bool Extension::HasHostPermission(const GURL& url) const { + base::AutoLock auto_lock(runtime_data_lock_); if (url.SchemeIs(chrome::kChromeUIScheme) && url.host() != chrome::kChromeUIFaviconHost && location() != Extension::COMPONENT) return false; - return permission_set()->HasExplicitAccessToOrigin(url); + return runtime_data_.GetActivePermissions()-> + HasExplicitAccessToOrigin(url); +} + +bool Extension::HasEffectiveAccessToAllHosts() const { + base::AutoLock auto_lock(runtime_data_lock_); + return runtime_data_.GetActivePermissions()->HasEffectiveAccessToAllHosts(); +} + +bool Extension::HasFullPermissions() const { + base::AutoLock auto_lock(runtime_data_lock_); + return runtime_data_.GetActivePermissions()->HasEffectiveFullAccess(); +} + +ExtensionPermissionMessages Extension::GetPermissionMessages() const { + base::AutoLock auto_lock(runtime_data_lock_); + if (IsTrustedId(id_)) + return ExtensionPermissionMessages(); + else + return runtime_data_.GetActivePermissions()->GetPermissionMessages(); +} + +std::vector<string16> Extension::GetPermissionMessageStrings() const { + base::AutoLock auto_lock(runtime_data_lock_); + if (IsTrustedId(id_)) + return std::vector<string16>(); + else + return runtime_data_.GetActivePermissions()->GetWarningMessages(); +} + +void Extension::SetActivePermissions( + const ExtensionPermissionSet* permissions) const { + base::AutoLock auto_lock(runtime_data_lock_); + runtime_data_.SetActivePermissions(permissions); +} + +scoped_refptr<const ExtensionPermissionSet> + Extension::GetActivePermissions() const { + base::AutoLock auto_lock(runtime_data_lock_); + return runtime_data_.GetActivePermissions(); } bool Extension::IsComponentOnlyPermission( @@ -2637,6 +2713,7 @@ bool Extension::HasMultipleUISurfaces() const { bool Extension::CanExecuteScriptOnPage(const GURL& page_url, const UserScript* script, std::string* error) const { + base::AutoLock auto_lock(runtime_data_lock_); // 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 // like extensions removing the "report abuse" link). @@ -2661,7 +2738,8 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url, // Otherwise, see if this extension has permission to execute script // programmatically on pages. - if (permission_set()->HasExplicitAccessToOrigin(page_url)) + if (runtime_data_.GetActivePermissions()->HasExplicitAccessToOrigin( + page_url)) return true; if (error) { @@ -2672,14 +2750,6 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url, return false; } -bool Extension::HasEffectiveAccessToAllHosts() const { - return permission_set_->HasEffectiveAccessToAllHosts(); -} - -bool Extension::HasFullPermissions() const { - return permission_set_->HasEffectiveFullAccess(); -} - bool Extension::ShowConfigureContextMenus() const { // Don't show context menu for component extensions. We might want to show // options for component extension button but now there is no component @@ -2767,11 +2837,26 @@ ExtensionInfo::ExtensionInfo(const DictionaryValue* manifest, ExtensionInfo::~ExtensionInfo() {} +Extension::RuntimeData::RuntimeData() {} +Extension::RuntimeData::RuntimeData(const ExtensionPermissionSet* active) + : active_permissions_(active) {} +Extension::RuntimeData::~RuntimeData() {} + +scoped_refptr<const ExtensionPermissionSet> + Extension::RuntimeData::GetActivePermissions() const { + return active_permissions_; +} + +void Extension::RuntimeData::SetActivePermissions( + const ExtensionPermissionSet* active) { + active_permissions_ = active; +} + UninstalledExtensionInfo::UninstalledExtensionInfo( const Extension& extension) : extension_id(extension.id()), extension_api_permissions( - extension.permission_set()->GetAPIsAsStrings()), + extension.GetActivePermissions()->GetAPIsAsStrings()), extension_type(extension.GetType()), update_url(extension.update_url()) {} @@ -2784,3 +2869,11 @@ UnloadedExtensionInfo::UnloadedExtensionInfo( : reason(reason), already_disabled(false), extension(extension) {} + +UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( + const Extension* extension, + const ExtensionPermissionSet* permissions, + Reason reason) + : reason(reason), + extension(extension), + permissions(permissions) {} diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index a2ee0e7..e46cba1 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -16,6 +16,7 @@ #include "base/memory/linked_ptr.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/synchronization/lock.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_icon_set.h" #include "chrome/common/extensions/extension_permission_set.h" @@ -209,15 +210,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // its install source should be set to GetHigherPriorityLocation(A, B). static Location GetHigherPriorityLocation(Location loc1, Location loc2); - // Returns the full list of permission messages that this extension - // should display at install time. - ExtensionPermissionMessages 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; - // Icon sizes used by the extension system. static const int kIconSizes[]; @@ -375,11 +367,25 @@ class Extension : public base::RefCountedThreadSafe<Extension> { static void SetScriptingWhitelist(const ScriptingWhitelist& whitelist); static const ScriptingWhitelist* GetScriptingWhitelist(); + // Parses the host and api permissions from the specified permission |key| + // in the manifest |source|. + bool ParsePermissions(const base::DictionaryValue* source, + const char* key, + int flags, + std::string* error, + ExtensionAPIPermissionSet* api_permissions, + URLPatternSet* host_permissions); + bool HasAPIPermission(ExtensionAPIPermission::ID permission) const; bool HasAPIPermission(const std::string& function_name) const; const URLPatternSet& GetEffectiveHostPermissions() const; + // Returns true if the extension can silently increase its permission level. + // Extensions that can silently increase permissions are installed through + // mechanisms that are implicitly trusted. + bool CanSilentlyIncreasePermissions() const; + // Whether or not the extension is allowed permission for a URL pattern from // the manifest. http, https, and chrome://favicon/ is allowed for all // extensions, while component extensions are allowed access to @@ -400,6 +406,21 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // having an NPAPI plugin). bool HasFullPermissions() const; + // Returns the full list of permission messages that this extension + // should display at install time. + ExtensionPermissionMessages 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; + + // Sets the active |permissions|. + void SetActivePermissions(const ExtensionPermissionSet* permissions) const; + + // Gets the extension's active permission set. + scoped_refptr<const ExtensionPermissionSet> GetActivePermissions() const; + // Whether context menu should be shown for page and browser actions. bool ShowConfigureContextMenus() const; @@ -494,8 +515,11 @@ class Extension : public base::RefCountedThreadSafe<Extension> { const GURL& options_url() const { return options_url_; } const GURL& devtools_url() const { return devtools_url_; } const std::vector<GURL>& toolstrips() const { return toolstrips_; } - const ExtensionPermissionSet* permission_set() const { - return permission_set_.get(); + const ExtensionPermissionSet* optional_permission_set() const { + return optional_permission_set_.get(); + } + const ExtensionPermissionSet* required_permission_set() const { + return required_permission_set_.get(); } const GURL& update_url() const { return update_url_; } const ExtensionIconSet& icons() const { return icons_; } @@ -551,6 +575,20 @@ class Extension : public base::RefCountedThreadSafe<Extension> { typedef std::pair<FilePath, std::string> ImageCacheKey; typedef std::map<ImageCacheKey, SkBitmap> ImageCache; + class RuntimeData { + public: + RuntimeData(); + explicit RuntimeData(const ExtensionPermissionSet* active); + ~RuntimeData(); + + void SetActivePermissions(const ExtensionPermissionSet* active); + scoped_refptr<const ExtensionPermissionSet> GetActivePermissions() const; + + private: + friend class base::RefCountedThreadSafe<RuntimeData>; + scoped_refptr<const ExtensionPermissionSet> active_permissions_; + }; + // Normalize the path for use by the extension. On Windows, this will make // sure the drive letter is uppercase. static FilePath MaybeNormalizePath(const FilePath& path); @@ -673,8 +711,15 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // Defines the set of URLs in the extension's web content. URLPatternSet extent_; - // The set of permissions that the extension effectively has access to. - scoped_ptr<ExtensionPermissionSet> permission_set_; + // The extension runtime data. + mutable base::Lock runtime_data_lock_; + mutable RuntimeData runtime_data_; + + // The set of permissions the extension can request at runtime. + scoped_refptr<const ExtensionPermissionSet> optional_permission_set_; + + // The extension's required / default set of permissions. + scoped_refptr<const ExtensionPermissionSet> required_permission_set_; // The icons for the extension. ExtensionIconSet icons_; @@ -870,4 +915,27 @@ struct UnloadedExtensionInfo { UnloadedExtensionInfo(const Extension* extension, Reason reason); }; +// The details sent for EXTENSION_PERMISSIONS_UPDATED notifications. +struct UpdatedExtensionPermissionsInfo { + enum Reason { + ADDED, // The permissions were added to the extension. + REMOVED, // The permissions were removed from the extension. + }; + + Reason reason; + + // The extension who's permissions have changed. + const Extension* extension; + + // The permissions that have changed. For Reason::ADDED, this would contain + // only the permissions that have added, and for Reason::REMOVED, this would + // only contain the removed permissions. + const ExtensionPermissionSet* permissions; + + UpdatedExtensionPermissionsInfo( + const Extension* extension, + const ExtensionPermissionSet* permissions, + Reason reason); +}; + #endif // CHROME_COMMON_EXTENSIONS_EXTENSION_H_ diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index df532ad..a0eb53f 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -50,6 +50,7 @@ const char* kNaClModulesMIMEType = "mime_type"; const char* kNaClModulesPath = "path"; const char* kOmnibox = "omnibox"; const char* kOmniboxKeyword = "omnibox.keyword"; +const char* kOptionalPermissions = "optional_permissions"; const char* kOptionsPage = "options_page"; const char* kPageAction = "page_action"; const char* kPageActionDefaultIcon = "default_icon"; diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index fe2c7bf..5c980e0f 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -55,6 +55,7 @@ namespace extension_manifest_keys { extern const char* kName; extern const char* kOmnibox; extern const char* kOmniboxKeyword; + extern const char* kOptionalPermissions; extern const char* kOptionsPage; extern const char* kPageAction; extern const char* kPageActionDefaultIcon; diff --git a/chrome/common/extensions/extension_messages.cc b/chrome/common/extensions/extension_messages.cc index b388234..fe88896 100644 --- a/chrome/common/extensions/extension_messages.cc +++ b/chrome/common/extensions/extension_messages.cc @@ -24,10 +24,14 @@ ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params( } ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params( - const Extension* extension) + const Extension* extension, + const ExtensionPermissionSet* active) : manifest(new DictionaryValue()), location(extension->location()), path(extension->path()), + apis(active->apis()), + explicit_hosts(active->explicit_hosts()), + scriptable_hosts(active->scriptable_hosts()), id(extension->id()), creation_flags(extension->creation_flags()) { // As we need more bits of extension data in the renderer, add more keys to @@ -65,6 +69,11 @@ scoped_refptr<Extension> return extension; } +const ExtensionPermissionSet* + ExtensionMsg_Loaded_Params::GetActivePermissions() const { + return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts); +} + namespace IPC { template <> @@ -101,8 +110,15 @@ bool ParamTraits<URLPattern>::Read(const Message* m, void** iter, !ReadParam(m, iter, &spec)) return false; + // TODO(jstritar): We don't want the URLPattern to fail parsing when the + // scheme is invalid. Instead, the pattern should parse but it should not + // match the invalid patterns. We get around this by setting the valid + // schemes after parsing the pattern. Update these method calls once we can + // ignore scheme validation with URLPattern parse options. crbug.com/90544 + p->SetValidSchemes(URLPattern::SCHEME_ALL); + URLPattern::ParseResult result = p->Parse(spec, URLPattern::IGNORE_PORTS); p->SetValidSchemes(valid_schemes); - return URLPattern::PARSE_SUCCESS == p->Parse(spec, URLPattern::IGNORE_PORTS); + return URLPattern::PARSE_SUCCESS == result; } void ParamTraits<URLPattern>::Log(const param_type& p, std::string* l) { @@ -116,9 +132,7 @@ void ParamTraits<URLPatternSet>::Write(Message* m, const param_type& p) { bool ParamTraits<URLPatternSet>::Read(const Message* m, void** iter, param_type* p) { std::set<URLPattern> patterns; - bool success = - ReadParam(m, iter, &patterns); - if (!success) + if (!ReadParam(m, iter, &patterns)) return false; for (std::set<URLPattern>::iterator i = patterns.begin(); @@ -131,12 +145,35 @@ void ParamTraits<URLPatternSet>::Log(const param_type& p, std::string* l) { LogParam(p.patterns(), l); } +void ParamTraits<ExtensionAPIPermission::ID>::Write( + Message* m, const param_type& p) { + WriteParam(m, static_cast<int>(p)); +} + +bool ParamTraits<ExtensionAPIPermission::ID>::Read( + const Message* m, void** iter, param_type* p) { + int api_id = -2; + if (!ReadParam(m, iter, &api_id)) + return false; + + *p = static_cast<ExtensionAPIPermission::ID>(api_id); + return true; +} + +void ParamTraits<ExtensionAPIPermission::ID>::Log( + const param_type& p, std::string* l) { + LogParam(static_cast<int>(p), 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)); WriteParam(m, p.creation_flags); + WriteParam(m, p.apis); + WriteParam(m, p.explicit_hosts); + WriteParam(m, p.scriptable_hosts); } bool ParamTraits<ExtensionMsg_Loaded_Params>::Read(const Message* m, @@ -146,7 +183,10 @@ bool ParamTraits<ExtensionMsg_Loaded_Params>::Read(const Message* m, return ReadParam(m, iter, &p->location) && ReadParam(m, iter, &p->path) && ReadParam(m, iter, p->manifest.get()) && - ReadParam(m, iter, &p->creation_flags); + ReadParam(m, iter, &p->creation_flags) && + ReadParam(m, iter, &p->apis) && + ReadParam(m, iter, &p->explicit_hosts) && + ReadParam(m, iter, &p->scriptable_hosts); } void ParamTraits<ExtensionMsg_Loaded_Params>::Log(const param_type& p, diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h index 53abba6..6d41b9c 100644 --- a/chrome/common/extensions/extension_messages.h +++ b/chrome/common/extensions/extension_messages.h @@ -8,6 +8,7 @@ #include "base/shared_memory.h" #include "base/values.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_permission_set.h" #include "chrome/common/extensions/url_pattern.h" #include "chrome/common/extensions/url_pattern_set.h" #include "chrome/common/web_apps.h" @@ -90,7 +91,9 @@ 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); + explicit ExtensionMsg_Loaded_Params( + const Extension* extension, + const ExtensionPermissionSet* active_permissions); // A copy constructor is needed because this structure can end up getting // copied inside the IPC machinery on gcc <= 4.2. @@ -99,6 +102,9 @@ struct ExtensionMsg_Loaded_Params { // Creates a new extension from the data in this object. scoped_refptr<Extension> ConvertToExtension() const; + // Passes ownership to the caller. + const ExtensionPermissionSet* GetActivePermissions() const; + // The subset of the extension manifest data we send to renderers. scoped_ptr<DictionaryValue> manifest; @@ -109,6 +115,11 @@ struct ExtensionMsg_Loaded_Params { // to generate the extension ID for extensions that are loaded unpacked. FilePath path; + // The extension's current active permissions. + ExtensionAPIPermissionSet apis; + URLPatternSet explicit_hosts; + URLPatternSet scriptable_hosts; + // We keep this separate so that it can be used in logging. std::string id; @@ -135,6 +146,14 @@ struct ParamTraits<URLPatternSet> { }; template <> +struct ParamTraits<ExtensionAPIPermission::ID> { + typedef ExtensionAPIPermission::ID 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); @@ -214,6 +233,13 @@ IPC_MESSAGE_ROUTED1(ExtensionMsg_GetApplicationInfo, IPC_MESSAGE_ROUTED1(ExtensionMsg_UpdateBrowserWindowId, int /* id of browser window */) +// Tell the renderer to update an extension's permission set. +IPC_MESSAGE_CONTROL4(ExtensionMsg_UpdatePermissions, + std::string /* extension_id*/, + ExtensionAPIPermissionSet, + URLPatternSet, + URLPatternSet) + // Tell the renderer which type this view is. IPC_MESSAGE_ROUTED1(ExtensionMsg_NotifyRenderViewType, ViewType::Type /* view_type */) diff --git a/chrome/common/extensions/extension_permission_set.cc b/chrome/common/extensions/extension_permission_set.cc index 0fbf2cd..462e022 100644 --- a/chrome/common/extensions/extension_permission_set.cc +++ b/chrome/common/extensions/extension_permission_set.cc @@ -247,6 +247,9 @@ ExtensionPermissionsInfo::ExtensionPermissionsInfo() RegisterHostedAppPermission( ExtensionAPIPermission::kUnlimitedStorage, "unlimitedStorage", 0, ExtensionPermissionMessage::kNone); + RegisterHostedAppPermission( + ExtensionAPIPermission::kPermissions, "permissions", 0, + ExtensionPermissionMessage::kNone); // Hosted app and private permissions. RegisterPermission( @@ -433,12 +436,62 @@ ExtensionPermissionSet::~ExtensionPermissionSet() { } // static +ExtensionPermissionSet* ExtensionPermissionSet::CreateDifference( + const ExtensionPermissionSet* set1, + const ExtensionPermissionSet* set2) { + scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet(); + const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1; + const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2; + + ExtensionAPIPermissionSet apis; + std::set_difference(set1_safe->apis().begin(), set1_safe->apis().end(), + set2_safe->apis().begin(), set2_safe->apis().end(), + std::insert_iterator<ExtensionAPIPermissionSet>( + apis, apis.begin())); + + URLPatternSet explicit_hosts; + URLPatternSet::CreateDifference(set1_safe->explicit_hosts(), + set2_safe->explicit_hosts(), + &explicit_hosts); + + URLPatternSet scriptable_hosts; + URLPatternSet::CreateDifference(set1_safe->scriptable_hosts(), + set2_safe->scriptable_hosts(), + &scriptable_hosts); + return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts); +} + +// static +ExtensionPermissionSet* ExtensionPermissionSet::CreateIntersection( + const ExtensionPermissionSet* set1, + const ExtensionPermissionSet* set2) { + scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet(); + const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1; + const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2; + + ExtensionAPIPermissionSet apis; + std::set_intersection(set1_safe->apis().begin(), set1_safe->apis().end(), + set2_safe->apis().begin(), set2_safe->apis().end(), + std::insert_iterator<ExtensionAPIPermissionSet>( + apis, apis.begin())); + URLPatternSet explicit_hosts; + URLPatternSet::CreateIntersection(set1_safe->explicit_hosts(), + set2_safe->explicit_hosts(), + &explicit_hosts); + + URLPatternSet scriptable_hosts; + URLPatternSet::CreateIntersection(set1_safe->scriptable_hosts(), + set2_safe->scriptable_hosts(), + &scriptable_hosts); + return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts); +} +// static ExtensionPermissionSet* ExtensionPermissionSet::CreateUnion( const ExtensionPermissionSet* set1, const ExtensionPermissionSet* set2) { - ExtensionPermissionSet empty; - const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? &empty : set1; - const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? &empty : set2; + scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet(); + const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1; + const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2; ExtensionAPIPermissionSet apis; std::set_union(set1_safe->apis().begin(), set1_safe->apis().end(), @@ -447,10 +500,11 @@ ExtensionPermissionSet* ExtensionPermissionSet::CreateUnion( apis, apis.begin())); URLPatternSet explicit_hosts; - URLPatternSet scriptable_hosts; URLPatternSet::CreateUnion(set1_safe->explicit_hosts(), set2_safe->explicit_hosts(), &explicit_hosts); + + URLPatternSet scriptable_hosts; URLPatternSet::CreateUnion(set1_safe->scriptable_hosts(), set2_safe->scriptable_hosts(), &scriptable_hosts); @@ -458,6 +512,31 @@ ExtensionPermissionSet* ExtensionPermissionSet::CreateUnion( return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts); } +bool ExtensionPermissionSet::operator==( + const ExtensionPermissionSet& rhs) const { + return apis_ == rhs.apis_ && + scriptable_hosts_ == rhs.scriptable_hosts_ && + explicit_hosts_ == rhs.explicit_hosts_; +} + +bool ExtensionPermissionSet::Contains(const ExtensionPermissionSet& set) const { + // Every set includes the empty set. + if (set.IsEmpty()) + return true; + + if (!std::includes(apis_.begin(), apis_.end(), + set.apis().begin(), set.apis().end())) + return false; + + if (!explicit_hosts().Contains(set.explicit_hosts())) + return false; + + if (!scriptable_hosts().Contains(set.scriptable_hosts())) + return false; + + return true; +} + std::set<std::string> ExtensionPermissionSet::GetAPIsAsStrings() const { ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); std::set<std::string> apis_str; diff --git a/chrome/common/extensions/extension_permission_set.h b/chrome/common/extensions/extension_permission_set.h index c9a67e9..3d4d353 100644 --- a/chrome/common/extensions/extension_permission_set.h +++ b/chrome/common/extensions/extension_permission_set.h @@ -13,6 +13,7 @@ #include "base/gtest_prod_util.h" #include "base/memory/singleton.h" +#include "base/memory/ref_counted.h" #include "base/scoped_ptr.h" #include "base/string16.h" #include "chrome/common/extensions/url_pattern_set.h" @@ -121,6 +122,7 @@ class ExtensionAPIPermission { kWebstorePrivate, kDevtools, kPlugin, + kPermissions, kEnumBoundary }; @@ -266,7 +268,8 @@ class ExtensionPermissionsInfo { // The ExtensionPermissionSet is an immutable class that encapsulates an // extension's permissions. The class exposes set operations for combining and // manipulating the permissions. -class ExtensionPermissionSet { +class ExtensionPermissionSet + : public base::RefCountedThreadSafe<ExtensionPermissionSet> { public: // Creates an empty permission set (e.g. default permissions). ExtensionPermissionSet(); @@ -286,11 +289,26 @@ class ExtensionPermissionSet { ~ExtensionPermissionSet(); + // Creates a new permission set equal to |set1| - |set2|, passing ownership of + // the new set to the caller. + static ExtensionPermissionSet* CreateDifference( + const ExtensionPermissionSet* set1, const ExtensionPermissionSet* set2); + + // Creates a new permission set equal to the intersection of |set1| and + // |set2|, passing ownership of the new set to the caller. + static ExtensionPermissionSet* CreateIntersection( + const ExtensionPermissionSet* set1, const ExtensionPermissionSet* set2); + // Creates a new permission set equal to the union of |set1| and |set2|. // Passes ownership of the new set to the caller. static ExtensionPermissionSet* CreateUnion( const ExtensionPermissionSet* set1, const ExtensionPermissionSet* set2); + bool operator==(const ExtensionPermissionSet& rhs) const; + + // Returns true if |set| is a subset of this. + bool Contains(const ExtensionPermissionSet& set) const; + // Gets the API permissions in this set as a set of strings. std::set<std::string> GetAPIsAsStrings() const; @@ -353,6 +371,8 @@ class ExtensionPermissionSet { FRIEND_TEST_ALL_PREFIXES(ExtensionPermissionSetTest, HasLessHostPrivilegesThan); + friend class base::RefCountedThreadSafe<ExtensionPermissionSet>; + static std::set<std::string> GetDistinctHosts( const URLPatternSet& host_patterns, bool include_rcd); @@ -380,9 +400,11 @@ class ExtensionPermissionSet { ExtensionAPIPermissionSet apis_; // The list of hosts that can be accessed directly from the extension. + // TODO(jstritar): Rename to "hosts_"? URLPatternSet explicit_hosts_; // The list of hosts that can be scripted by content scripts. + // TODO(jstritar): Rename to "user_script_hosts_"? URLPatternSet scriptable_hosts_; // The list of hosts this effectively grants access to. diff --git a/chrome/common/extensions/extension_permission_set_unittest.cc b/chrome/common/extensions/extension_permission_set_unittest.cc index b1a08a3..7bba33a 100644 --- a/chrome/common/extensions/extension_permission_set_unittest.cc +++ b/chrome/common/extensions/extension_permission_set_unittest.cc @@ -152,6 +152,7 @@ TEST(ExtensionAPIPermissionTest, HostedAppPermissions) { hosted_perms.insert(ExtensionAPIPermission::kNotification); hosted_perms.insert(ExtensionAPIPermission::kUnlimitedStorage); hosted_perms.insert(ExtensionAPIPermission::kWebstorePrivate); + hosted_perms.insert(ExtensionAPIPermission::kPermissions); ExtensionAPIPermissionSet perms = info->GetAll(); size_t count = 0; @@ -161,8 +162,8 @@ TEST(ExtensionAPIPermissionTest, HostedAppPermissions) { EXPECT_EQ(hosted_perms.count(*i) > 0, info->GetByID(*i)->is_hosted_app()); } - EXPECT_EQ(10u, count); - EXPECT_EQ(10u, info->get_hosted_app_permission_count()); + EXPECT_EQ(hosted_perms.size(), count); + EXPECT_EQ(hosted_perms.size(), info->get_hosted_app_permission_count()); } TEST(ExtensionAPIPermissionTest, ComponentOnlyPermissions) { @@ -188,17 +189,17 @@ TEST(ExtensionAPIPermissionTest, ComponentOnlyPermissions) { TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) { scoped_refptr<Extension> extension; - const ExtensionPermissionSet* permissions = NULL; + scoped_refptr<const ExtensionPermissionSet> permissions; extension = LoadManifest("effective_host_permissions", "empty.json"); - permissions = extension->permission_set(); + permissions = extension->GetActivePermissions(); EXPECT_EQ(0u, extension->GetEffectiveHostPermissions().patterns().size()); EXPECT_FALSE(permissions->HasEffectiveAccessToURL( GURL("http://www.google.com"))); EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts()); extension = LoadManifest("effective_host_permissions", "one_host.json"); - permissions = extension->permission_set(); + permissions = extension->GetActivePermissions(); EXPECT_TRUE(permissions->HasEffectiveAccessToURL( GURL("http://www.google.com"))); EXPECT_FALSE(permissions->HasEffectiveAccessToURL( @@ -207,14 +208,14 @@ TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) { extension = LoadManifest("effective_host_permissions", "one_host_wildcard.json"); - permissions = extension->permission_set(); + permissions = extension->GetActivePermissions(); EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com"))); EXPECT_TRUE(permissions->HasEffectiveAccessToURL( GURL("http://foo.google.com"))); EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts()); extension = LoadManifest("effective_host_permissions", "two_hosts.json"); - permissions = extension->permission_set(); + permissions = extension->GetActivePermissions(); EXPECT_TRUE(permissions->HasEffectiveAccessToURL( GURL("http://www.google.com"))); EXPECT_TRUE(permissions->HasEffectiveAccessToURL( @@ -223,14 +224,14 @@ TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) { extension = LoadManifest("effective_host_permissions", "https_not_considered.json"); - permissions = extension->permission_set(); + permissions = extension->GetActivePermissions(); EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com"))); EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("https://google.com"))); EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts()); extension = LoadManifest("effective_host_permissions", "two_content_scripts.json"); - permissions = extension->permission_set(); + permissions = extension->GetActivePermissions(); EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com"))); EXPECT_TRUE(permissions->HasEffectiveAccessToURL( GURL("http://www.reddit.com"))); @@ -239,7 +240,7 @@ TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) { EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts()); extension = LoadManifest("effective_host_permissions", "all_hosts.json"); - permissions = extension->permission_set(); + permissions = extension->GetActivePermissions(); EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://test/"))); EXPECT_FALSE(permissions->HasEffectiveAccessToURL(GURL("https://test/"))); EXPECT_TRUE( @@ -247,14 +248,14 @@ TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) { EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts()); extension = LoadManifest("effective_host_permissions", "all_hosts2.json"); - permissions = extension->permission_set(); + permissions = extension->GetActivePermissions(); EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://test/"))); EXPECT_TRUE( permissions->HasEffectiveAccessToURL(GURL("http://www.google.com"))); EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts()); extension = LoadManifest("effective_host_permissions", "all_hosts3.json"); - permissions = extension->permission_set(); + permissions = extension->GetActivePermissions(); EXPECT_FALSE(permissions->HasEffectiveAccessToURL(GURL("http://test/"))); EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("https://test/"))); EXPECT_TRUE( @@ -271,16 +272,17 @@ TEST(ExtensionPermissionSetTest, ExplicitAccessToOrigin) { // The explicit host paths should get set to /*. AddPattern(&explicit_hosts, "http://www.example.com/a/particular/path/*"); - ExtensionPermissionSet perm_set(apis, explicit_hosts, scriptable_hosts); - ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin( + scoped_refptr<ExtensionPermissionSet> perm_set = new ExtensionPermissionSet( + apis, explicit_hosts, scriptable_hosts); + ASSERT_TRUE(perm_set->HasExplicitAccessToOrigin( GURL("http://www.google.com/"))); - ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin( + ASSERT_TRUE(perm_set->HasExplicitAccessToOrigin( GURL("http://test.google.com/"))); - ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin( + ASSERT_TRUE(perm_set->HasExplicitAccessToOrigin( GURL("http://www.example.com"))); - ASSERT_TRUE(perm_set.HasEffectiveAccessToURL( + ASSERT_TRUE(perm_set->HasEffectiveAccessToURL( GURL("http://www.example.com"))); - ASSERT_FALSE(perm_set.HasExplicitAccessToOrigin( + ASSERT_FALSE(perm_set->HasExplicitAccessToOrigin( GURL("http://test.example.com"))); } @@ -299,9 +301,9 @@ TEST(ExtensionPermissionSetTest, CreateUnion) { URLPatternSet effective_hosts; - scoped_ptr<ExtensionPermissionSet> set1; - scoped_ptr<ExtensionPermissionSet> set2; - scoped_ptr<ExtensionPermissionSet> union_set; + scoped_refptr<ExtensionPermissionSet> set1; + scoped_refptr<ExtensionPermissionSet> set2; + scoped_refptr<ExtensionPermissionSet> union_set; // Union with an empty set. apis1.insert(ExtensionAPIPermission::kTab); @@ -313,11 +315,15 @@ TEST(ExtensionPermissionSetTest, CreateUnion) { AddPattern(&expected_explicit_hosts, "http://*.google.com/*"); AddPattern(&effective_hosts, "http://*.google.com/*"); - set1.reset(new ExtensionPermissionSet( - apis1, explicit_hosts1, scriptable_hosts1)); - set2.reset(new ExtensionPermissionSet( - apis2, explicit_hosts2, scriptable_hosts2)); - union_set.reset(ExtensionPermissionSet::CreateUnion(set1.get(), set2.get())); + set1 = new ExtensionPermissionSet(apis1, explicit_hosts1, scriptable_hosts1); + set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + union_set = ExtensionPermissionSet::CreateUnion(set1.get(), set2.get()); + EXPECT_TRUE(set1->Contains(*set2)); + EXPECT_TRUE(set1->Contains(*union_set)); + EXPECT_FALSE(set2->Contains(*set1)); + EXPECT_FALSE(set2->Contains(*union_set)); + EXPECT_TRUE(union_set->Contains(*set1)); + EXPECT_TRUE(union_set->Contains(*set2)); EXPECT_FALSE(union_set->HasEffectiveFullAccess()); EXPECT_EQ(expected_apis, union_set->apis()); @@ -343,9 +349,16 @@ TEST(ExtensionPermissionSetTest, CreateUnion) { effective_hosts.ClearPatterns(); AddPattern(&effective_hosts, "<all_urls>"); - set2.reset(new ExtensionPermissionSet( - apis2, explicit_hosts2, scriptable_hosts2)); - union_set.reset(ExtensionPermissionSet::CreateUnion(set1.get(), set2.get())); + set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + union_set = ExtensionPermissionSet::CreateUnion(set1.get(), set2.get()); + + EXPECT_FALSE(set1->Contains(*set2)); + EXPECT_FALSE(set1->Contains(*union_set)); + EXPECT_FALSE(set2->Contains(*set1)); + EXPECT_FALSE(set2->Contains(*union_set)); + EXPECT_TRUE(union_set->Contains(*set1)); + EXPECT_TRUE(union_set->Contains(*set2)); + EXPECT_TRUE(union_set->HasEffectiveFullAccess()); EXPECT_TRUE(union_set->HasEffectiveAccessToAllHosts()); EXPECT_EQ(expected_apis, union_set->apis()); @@ -354,6 +367,146 @@ TEST(ExtensionPermissionSetTest, CreateUnion) { EXPECT_EQ(effective_hosts, union_set->effective_hosts()); } +TEST(ExtensionPermissionSetTest, CreateIntersection) { + ExtensionAPIPermissionSet apis1; + ExtensionAPIPermissionSet apis2; + ExtensionAPIPermissionSet expected_apis; + + URLPatternSet explicit_hosts1; + URLPatternSet explicit_hosts2; + URLPatternSet expected_explicit_hosts; + + URLPatternSet scriptable_hosts1; + URLPatternSet scriptable_hosts2; + URLPatternSet expected_scriptable_hosts; + + URLPatternSet effective_hosts; + + scoped_refptr<ExtensionPermissionSet> set1; + scoped_refptr<ExtensionPermissionSet> set2; + scoped_refptr<ExtensionPermissionSet> new_set; + + // Intersection with an empty set. + apis1.insert(ExtensionAPIPermission::kTab); + apis1.insert(ExtensionAPIPermission::kBackground); + + AddPattern(&explicit_hosts1, "http://*.google.com/*"); + AddPattern(&scriptable_hosts1, "http://www.reddit.com/*"); + + set1 = new ExtensionPermissionSet(apis1, explicit_hosts1, scriptable_hosts1); + set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + new_set = ExtensionPermissionSet::CreateIntersection(set1.get(), set2.get()); + EXPECT_TRUE(set1->Contains(*new_set)); + EXPECT_TRUE(set2->Contains(*new_set)); + EXPECT_TRUE(set1->Contains(*set2)); + EXPECT_FALSE(set2->Contains(*set1)); + EXPECT_FALSE(new_set->Contains(*set1)); + EXPECT_TRUE(new_set->Contains(*set2)); + + EXPECT_TRUE(new_set->IsEmpty()); + EXPECT_FALSE(new_set->HasEffectiveFullAccess()); + EXPECT_EQ(expected_apis, new_set->apis()); + EXPECT_EQ(expected_explicit_hosts, new_set->explicit_hosts()); + EXPECT_EQ(expected_scriptable_hosts, new_set->scriptable_hosts()); + EXPECT_EQ(expected_explicit_hosts, new_set->effective_hosts()); + + // Now use a real second set. + apis2.insert(ExtensionAPIPermission::kTab); + apis2.insert(ExtensionAPIPermission::kProxy); + apis2.insert(ExtensionAPIPermission::kClipboardWrite); + apis2.insert(ExtensionAPIPermission::kPlugin); + expected_apis.insert(ExtensionAPIPermission::kTab); + + AddPattern(&explicit_hosts2, "http://*.example.com/*"); + AddPattern(&explicit_hosts2, "http://*.google.com/*"); + AddPattern(&scriptable_hosts2, "http://*.google.com/*"); + AddPattern(&expected_explicit_hosts, "http://*.google.com/*"); + + effective_hosts.ClearPatterns(); + AddPattern(&effective_hosts, "http://*.google.com/*"); + + set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + new_set = ExtensionPermissionSet::CreateIntersection(set1.get(), set2.get()); + + EXPECT_TRUE(set1->Contains(*new_set)); + EXPECT_TRUE(set2->Contains(*new_set)); + EXPECT_FALSE(set1->Contains(*set2)); + EXPECT_FALSE(set2->Contains(*set1)); + EXPECT_FALSE(new_set->Contains(*set1)); + EXPECT_FALSE(new_set->Contains(*set2)); + + EXPECT_FALSE(new_set->HasEffectiveFullAccess()); + EXPECT_FALSE(new_set->HasEffectiveAccessToAllHosts()); + EXPECT_EQ(expected_apis, new_set->apis()); + EXPECT_EQ(expected_explicit_hosts, new_set->explicit_hosts()); + EXPECT_EQ(expected_scriptable_hosts, new_set->scriptable_hosts()); + EXPECT_EQ(effective_hosts, new_set->effective_hosts()); +} + +TEST(ExtensionPermissionSetTest, CreateDifference) { + ExtensionAPIPermissionSet apis1; + ExtensionAPIPermissionSet apis2; + ExtensionAPIPermissionSet expected_apis; + + URLPatternSet explicit_hosts1; + URLPatternSet explicit_hosts2; + URLPatternSet expected_explicit_hosts; + + URLPatternSet scriptable_hosts1; + URLPatternSet scriptable_hosts2; + URLPatternSet expected_scriptable_hosts; + + URLPatternSet effective_hosts; + + scoped_refptr<ExtensionPermissionSet> set1; + scoped_refptr<ExtensionPermissionSet> set2; + scoped_refptr<ExtensionPermissionSet> new_set; + + // Difference with an empty set. + apis1.insert(ExtensionAPIPermission::kTab); + apis1.insert(ExtensionAPIPermission::kBackground); + + AddPattern(&explicit_hosts1, "http://*.google.com/*"); + AddPattern(&scriptable_hosts1, "http://www.reddit.com/*"); + + set1 = new ExtensionPermissionSet(apis1, explicit_hosts1, scriptable_hosts1); + set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + new_set = ExtensionPermissionSet::CreateDifference(set1.get(), set2.get()); + EXPECT_EQ(*set1, *new_set); + + // Now use a real second set. + apis2.insert(ExtensionAPIPermission::kTab); + apis2.insert(ExtensionAPIPermission::kProxy); + apis2.insert(ExtensionAPIPermission::kClipboardWrite); + apis2.insert(ExtensionAPIPermission::kPlugin); + expected_apis.insert(ExtensionAPIPermission::kBackground); + + AddPattern(&explicit_hosts2, "http://*.example.com/*"); + AddPattern(&explicit_hosts2, "http://*.google.com/*"); + AddPattern(&scriptable_hosts2, "http://*.google.com/*"); + AddPattern(&expected_scriptable_hosts, "http://www.reddit.com/*"); + + effective_hosts.ClearPatterns(); + AddPattern(&effective_hosts, "http://www.reddit.com/*"); + + set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + new_set = ExtensionPermissionSet::CreateDifference(set1.get(), set2.get()); + + EXPECT_TRUE(set1->Contains(*new_set)); + EXPECT_FALSE(set2->Contains(*new_set)); + + EXPECT_FALSE(new_set->HasEffectiveFullAccess()); + EXPECT_FALSE(new_set->HasEffectiveAccessToAllHosts()); + EXPECT_EQ(expected_apis, new_set->apis()); + EXPECT_EQ(expected_explicit_hosts, new_set->explicit_hosts()); + EXPECT_EQ(expected_scriptable_hosts, new_set->scriptable_hosts()); + EXPECT_EQ(effective_hosts, new_set->effective_hosts()); + + // |set3| = |set1| - |set2| --> |set3| intersect |set2| == empty_set + set1 = ExtensionPermissionSet::CreateIntersection(new_set.get(), set2.get()); + EXPECT_TRUE(set1->IsEmpty()); +} + TEST(ExtensionPermissionSetTest, HasLessPrivilegesThan) { const struct { const char* base_name; @@ -435,11 +588,13 @@ TEST(ExtensionPermissionSetTest, HasLessPrivilegesThan) { if (!new_extension.get()) continue; - const ExtensionPermissionSet* old_p = old_extension->permission_set(); - const ExtensionPermissionSet* new_p = new_extension->permission_set(); + scoped_refptr<const ExtensionPermissionSet> old_p( + old_extension->GetActivePermissions()); + scoped_refptr<const ExtensionPermissionSet> new_p( + new_extension->GetActivePermissions()); - EXPECT_EQ(kTests[i].expect_increase, old_p->HasLessPrivilegesThan(new_p)) - << kTests[i].base_name; + EXPECT_EQ(kTests[i].expect_increase, + old_p->HasLessPrivilegesThan(new_p)) << kTests[i].base_name; } } @@ -491,6 +646,10 @@ TEST(ExtensionPermissionSetTest, PermissionMessages) { // Warned as part of host permissions. skip.insert(ExtensionAPIPermission::kDevtools); + + // This will warn users later, when they request new permissions. + skip.insert(ExtensionAPIPermission::kPermissions); + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); ExtensionAPIPermissionSet permissions = info->GetAll(); for (ExtensionAPIPermissionSet::const_iterator i = permissions.begin(); @@ -538,10 +697,10 @@ TEST(ExtensionPermissionSetTest, DefaultFunctionAccess) { { "tabs.getSelected", false}, }; - ExtensionPermissionSet permissions; + scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet(); for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { EXPECT_EQ(kTests[i].expect_success, - permissions.HasAccessToFunction(kTests[i].permission_name)); + empty->HasAccessToFunction(kTests[i].permission_name)); } } @@ -550,7 +709,7 @@ TEST(ExtensionPermissionSetTest, GetWarningMessages_ManyHosts) { extension = LoadManifest("permissions", "many-hosts.json"); std::vector<string16> warnings = - extension->permission_set()->GetWarningMessages(); + extension->GetActivePermissions()->GetWarningMessages(); ASSERT_EQ(1u, warnings.size()); EXPECT_EQ("Your data on encrypted.google.com and www.google.com", UTF16ToUTF8(warnings[0])); @@ -558,11 +717,11 @@ TEST(ExtensionPermissionSetTest, GetWarningMessages_ManyHosts) { TEST(ExtensionPermissionSetTest, GetWarningMessages_Plugins) { scoped_refptr<Extension> extension; - scoped_ptr<ExtensionPermissionSet> permissions; + scoped_refptr<ExtensionPermissionSet> permissions; extension = LoadManifest("permissions", "plugins.json"); std::vector<string16> warnings = - extension->permission_set()->GetWarningMessages(); + extension->GetActivePermissions()->GetWarningMessages(); // We don't parse the plugins key on Chrome OS, so it should not ask for any // permissions. #if defined(OS_CHROMEOS) @@ -575,7 +734,7 @@ TEST(ExtensionPermissionSetTest, GetWarningMessages_Plugins) { } TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) { - scoped_ptr<ExtensionPermissionSet> perm_set; + scoped_refptr<ExtensionPermissionSet> perm_set; ExtensionAPIPermissionSet empty_perms; std::set<std::string> expected; expected.insert("www.foo.com"); @@ -594,8 +753,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) { URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/path")); explicit_hosts.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path")); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } @@ -607,8 +766,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) { URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path")); explicit_hosts.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path")); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } @@ -618,8 +777,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) { // Add a pattern that differs only by scheme. This should be filtered out. explicit_hosts.AddPattern( URLPattern(URLPattern::SCHEME_HTTPS, "https://www.bar.com/path")); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } @@ -629,8 +788,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) { // Add some dupes by path. explicit_hosts.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/pathypath")); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } @@ -646,8 +805,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) { expected.insert("monkey.www.bar.com"); expected.insert("bar.com"); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } @@ -677,8 +836,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) { expected.insert("www.foo.xyzzy"); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } @@ -690,8 +849,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) { expected.insert("*.google.com"); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } @@ -709,14 +868,14 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) { expected.insert("*.google.com"); expected.insert("*.example.com"); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } } TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_ComIsBestRcd) { - scoped_ptr<ExtensionPermissionSet> perm_set; + scoped_refptr<ExtensionPermissionSet> perm_set; ExtensionAPIPermissionSet empty_perms; URLPatternSet explicit_hosts; URLPatternSet scriptable_hosts; @@ -735,13 +894,13 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_ComIsBestRcd) { std::set<std::string> expected; expected.insert("www.foo.com"); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_NetIs2ndBestRcd) { - scoped_ptr<ExtensionPermissionSet> perm_set; + scoped_refptr<ExtensionPermissionSet> perm_set; ExtensionAPIPermissionSet empty_perms; URLPatternSet explicit_hosts; URLPatternSet scriptable_hosts; @@ -759,14 +918,14 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_NetIs2ndBestRcd) { std::set<std::string> expected; expected.insert("www.foo.net"); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_OrgIs3rdBestRcd) { - scoped_ptr<ExtensionPermissionSet> perm_set; + scoped_refptr<ExtensionPermissionSet> perm_set; ExtensionAPIPermissionSet empty_perms; URLPatternSet explicit_hosts; URLPatternSet scriptable_hosts; @@ -783,14 +942,14 @@ TEST(ExtensionPermissionSetTest, std::set<std::string> expected; expected.insert("www.foo.org"); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_FirstInListIs4thBestRcd) { - scoped_ptr<ExtensionPermissionSet> perm_set; + scoped_refptr<ExtensionPermissionSet> perm_set; ExtensionAPIPermissionSet empty_perms; URLPatternSet explicit_hosts; URLPatternSet scriptable_hosts; @@ -806,8 +965,8 @@ TEST(ExtensionPermissionSetTest, std::set<std::string> expected; expected.insert("www.foo.ca"); - perm_set.reset(new ExtensionPermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + perm_set = new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts); EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay()); } @@ -816,8 +975,8 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) { URLPatternSet elist2; URLPatternSet slist1; URLPatternSet slist2; - scoped_ptr<ExtensionPermissionSet> set1; - scoped_ptr<ExtensionPermissionSet> set2; + scoped_refptr<ExtensionPermissionSet> set1; + scoped_refptr<ExtensionPermissionSet> set2; ExtensionAPIPermissionSet empty_perms; elist1.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path")); @@ -830,8 +989,8 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) { elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path")); - set1.reset(new ExtensionPermissionSet(empty_perms, elist1, slist1)); - set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + set1 = new ExtensionPermissionSet(empty_perms, elist1, slist1); + set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2); EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get())); EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get())); @@ -840,7 +999,7 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) { elist2.ClearPatterns(); elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/*")); - set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2); EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get())); EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get())); @@ -848,7 +1007,7 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) { elist2.ClearPatterns(); elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/*")); - set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2); EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get())); EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get())); @@ -856,7 +1015,7 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) { elist2.ClearPatterns(); elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com.hk/*")); - set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2); EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get())); //TODO(jstritar): Does not match subdomains properly. http://crbug.com/65337 //EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get())); @@ -867,7 +1026,7 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) { URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path")); elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.example.org/path")); - set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2); EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get())); EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get())); @@ -875,7 +1034,7 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) { elist2.ClearPatterns(); elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://mail.google.com/*")); - set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2); EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get())); EXPECT_TRUE(set2->HasLessHostPrivilegesThan(set1.get())); } @@ -889,8 +1048,9 @@ TEST(ExtensionPermissionSetTest, GetAPIsAsStrings) { apis.insert(ExtensionAPIPermission::kNotification); apis.insert(ExtensionAPIPermission::kTab); - ExtensionPermissionSet perm_set(apis, empty_set, empty_set); - std::set<std::string> api_names = perm_set.GetAPIsAsStrings(); + scoped_refptr<ExtensionPermissionSet> perm_set = new ExtensionPermissionSet( + apis, empty_set, empty_set); + std::set<std::string> api_names = perm_set->GetAPIsAsStrings(); // The result is correct if it has the same number of elements // and we can convert it back to the id set. @@ -903,27 +1063,28 @@ TEST(ExtensionPermissionSetTest, IsEmpty) { ExtensionAPIPermissionSet empty_apis; URLPatternSet empty_extent; - ExtensionPermissionSet perm_set; - EXPECT_TRUE(perm_set.IsEmpty()); + scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet(); + EXPECT_TRUE(empty->IsEmpty()); + scoped_refptr<ExtensionPermissionSet> perm_set; - perm_set = ExtensionPermissionSet(empty_apis, empty_extent, empty_extent); - EXPECT_TRUE(perm_set.IsEmpty()); + perm_set = new ExtensionPermissionSet(empty_apis, empty_extent, empty_extent); + EXPECT_TRUE(perm_set->IsEmpty()); ExtensionAPIPermissionSet non_empty_apis; non_empty_apis.insert(ExtensionAPIPermission::kBackground); - perm_set = ExtensionPermissionSet( + perm_set = new ExtensionPermissionSet( non_empty_apis, empty_extent, empty_extent); - EXPECT_FALSE(perm_set.IsEmpty()); + EXPECT_FALSE(perm_set->IsEmpty()); // Try non standard host URLPatternSet non_empty_extent; AddPattern(&non_empty_extent, "http://www.google.com/*"); - perm_set = ExtensionPermissionSet( + perm_set = new ExtensionPermissionSet( empty_apis, non_empty_extent, empty_extent); - EXPECT_FALSE(perm_set.IsEmpty()); + EXPECT_FALSE(perm_set->IsEmpty()); - perm_set = ExtensionPermissionSet( + perm_set = new ExtensionPermissionSet( empty_apis, empty_extent, non_empty_extent); - EXPECT_FALSE(perm_set.IsEmpty()); + EXPECT_FALSE(perm_set->IsEmpty()); } diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index 79cad1a..5115573 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -389,10 +389,10 @@ TEST(ExtensionTest, EffectiveHostPermissions) { hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.MatchesURL(GURL("http://google.com"))); EXPECT_TRUE(hosts.MatchesURL(GURL("http://www.reddit.com"))); - EXPECT_TRUE(extension->permission_set()->HasEffectiveAccessToURL( + EXPECT_TRUE(extension->GetActivePermissions()->HasEffectiveAccessToURL( GURL("http://www.reddit.com"))); EXPECT_TRUE(hosts.MatchesURL(GURL("http://news.ycombinator.com"))); - EXPECT_TRUE(extension->permission_set()->HasEffectiveAccessToURL( + EXPECT_TRUE(extension->GetActivePermissions()->HasEffectiveAccessToURL( GURL("http://news.ycombinator.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); diff --git a/chrome/common/extensions/url_pattern_set.cc b/chrome/common/extensions/url_pattern_set.cc index 3c8f52c..cf2b9a6 100644 --- a/chrome/common/extensions/url_pattern_set.cc +++ b/chrome/common/extensions/url_pattern_set.cc @@ -10,6 +10,29 @@ #include "chrome/common/extensions/url_pattern.h" #include "googleurl/src/gurl.h" + +// static +void URLPatternSet::CreateDifference(const URLPatternSet& set1, + const URLPatternSet& set2, + URLPatternSet* out) { + out->ClearPatterns(); + std::set_difference(set1.patterns_.begin(), set1.patterns_.end(), + set2.patterns_.begin(), set2.patterns_.end(), + std::inserter<std::set<URLPattern> >( + out->patterns_, out->patterns_.begin())); +} + +// static +void URLPatternSet::CreateIntersection(const URLPatternSet& set1, + const URLPatternSet& set2, + URLPatternSet* out) { + out->ClearPatterns(); + std::set_intersection(set1.patterns_.begin(), set1.patterns_.end(), + set2.patterns_.begin(), set2.patterns_.end(), + std::inserter<std::set<URLPattern> >( + out->patterns_, out->patterns_.begin())); +} + // static void URLPatternSet::CreateUnion(const URLPatternSet& set1, const URLPatternSet& set2, @@ -52,6 +75,11 @@ void URLPatternSet::ClearPatterns() { patterns_.clear(); } +bool URLPatternSet::Contains(const URLPatternSet& set) const { + return std::includes(patterns_.begin(), patterns_.end(), + set.patterns_.begin(), set.patterns_.end()); +} + bool URLPatternSet::MatchesURL(const GURL& url) const { for (URLPatternSet::const_iterator pattern = patterns_.begin(); pattern != patterns_.end(); ++pattern) { diff --git a/chrome/common/extensions/url_pattern_set.h b/chrome/common/extensions/url_pattern_set.h index 1848e74..d26321c 100644 --- a/chrome/common/extensions/url_pattern_set.h +++ b/chrome/common/extensions/url_pattern_set.h @@ -18,6 +18,17 @@ class URLPatternSet { typedef std::set<URLPattern>::const_iterator const_iterator; typedef std::set<URLPattern>::iterator iterator; + // Clears |out| and populates the set with |set1| - |set2|. + static void CreateDifference(const URLPatternSet& set1, + const URLPatternSet& set2, + URLPatternSet* out); + + // Clears |out| and populates the set with the intersection of |set1| + // and |set2|. + static void CreateIntersection(const URLPatternSet& set1, + const URLPatternSet& set2, + URLPatternSet* out); + // Clears |out| and populates the set with the union of |set1| and |set2|. static void CreateUnion(const URLPatternSet& set1, const URLPatternSet& set2, @@ -39,6 +50,9 @@ class URLPatternSet { void AddPattern(const URLPattern& pattern); void ClearPatterns(); + // Returns true if the permission |set| is a subset of this. + bool Contains(const URLPatternSet& set) const; + // Test if the extent contains a URL. bool MatchesURL(const GURL& url) const; diff --git a/chrome/common/extensions/url_pattern_set_unittest.cc b/chrome/common/extensions/url_pattern_set_unittest.cc index f9717ef..ff78d90 100644 --- a/chrome/common/extensions/url_pattern_set_unittest.cc +++ b/chrome/common/extensions/url_pattern_set_unittest.cc @@ -7,88 +7,195 @@ #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" -static const int kAllSchemes = - URLPattern::SCHEME_HTTP | - URLPattern::SCHEME_HTTPS | - URLPattern::SCHEME_FILE | - URLPattern::SCHEME_FTP | - URLPattern::SCHEME_CHROMEUI; +namespace { +static void AddPattern(URLPatternSet* set, const std::string& pattern) { + int schemes = URLPattern::SCHEME_ALL; + set->AddPattern(URLPattern(schemes, pattern)); +} + +} TEST(URLPatternSetTest, Empty) { - URLPatternSet extent; - EXPECT_FALSE(extent.MatchesURL(GURL("http://www.foo.com/bar"))); - EXPECT_FALSE(extent.MatchesURL(GURL())); - EXPECT_FALSE(extent.MatchesURL(GURL("invalid"))); + URLPatternSet set; + EXPECT_FALSE(set.MatchesURL(GURL("http://www.foo.com/bar"))); + EXPECT_FALSE(set.MatchesURL(GURL())); + EXPECT_FALSE(set.MatchesURL(GURL("invalid"))); } TEST(URLPatternSetTest, One) { - URLPatternSet extent; - extent.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/*")); + URLPatternSet set; + AddPattern(&set, "http://www.google.com/*"); - EXPECT_TRUE(extent.MatchesURL(GURL("http://www.google.com/"))); - EXPECT_TRUE(extent.MatchesURL(GURL("http://www.google.com/monkey"))); - EXPECT_FALSE(extent.MatchesURL(GURL("https://www.google.com/"))); - EXPECT_FALSE(extent.MatchesURL(GURL("https://www.microsoft.com/"))); + EXPECT_TRUE(set.MatchesURL(GURL("http://www.google.com/"))); + EXPECT_TRUE(set.MatchesURL(GURL("http://www.google.com/monkey"))); + EXPECT_FALSE(set.MatchesURL(GURL("https://www.google.com/"))); + EXPECT_FALSE(set.MatchesURL(GURL("https://www.microsoft.com/"))); } TEST(URLPatternSetTest, Two) { - URLPatternSet extent; - extent.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/*")); - extent.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/*")); + URLPatternSet set; + AddPattern(&set, "http://www.google.com/*"); + AddPattern(&set, "http://www.yahoo.com/*"); - EXPECT_TRUE(extent.MatchesURL(GURL("http://www.google.com/monkey"))); - EXPECT_TRUE(extent.MatchesURL(GURL("http://www.yahoo.com/monkey"))); - EXPECT_FALSE(extent.MatchesURL(GURL("https://www.apple.com/monkey"))); + EXPECT_TRUE(set.MatchesURL(GURL("http://www.google.com/monkey"))); + EXPECT_TRUE(set.MatchesURL(GURL("http://www.yahoo.com/monkey"))); + EXPECT_FALSE(set.MatchesURL(GURL("https://www.apple.com/monkey"))); } TEST(URLPatternSetTest, OverlapsWith) { - URLPatternSet extent1; - extent1.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*")); - extent1.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b*")); + URLPatternSet set1; + AddPattern(&set1, "http://www.google.com/f*"); + AddPattern(&set1, "http://www.yahoo.com/b*"); + + URLPatternSet set2; + AddPattern(&set2, "http://www.reddit.com/f*"); + AddPattern(&set2, "http://www.yahoo.com/z*"); + + URLPatternSet set3; + AddPattern(&set3, "http://www.google.com/q/*"); + AddPattern(&set3, "http://www.yahoo.com/b/*"); + + EXPECT_FALSE(set1.OverlapsWith(set2)); + EXPECT_FALSE(set2.OverlapsWith(set1)); + + EXPECT_TRUE(set1.OverlapsWith(set3)); + EXPECT_TRUE(set3.OverlapsWith(set1)); +} + +TEST(URLPatternSetTest, CreateDifference) { + URLPatternSet expected; + URLPatternSet set1; + URLPatternSet set2; + AddPattern(&set1, "http://www.google.com/f*"); + AddPattern(&set1, "http://www.yahoo.com/b*"); - URLPatternSet extent2; - extent2.AddPattern(URLPattern(kAllSchemes, "http://www.reddit.com/f*")); - extent2.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/z*")); + // Subtract an empty set. + URLPatternSet result; + URLPatternSet::CreateDifference(set1, set2, &result); + EXPECT_EQ(set1, result); - URLPatternSet extent3; - extent3.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/q/*")); - extent3.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b/*")); + // Subtract a real set. + AddPattern(&set2, "http://www.reddit.com/f*"); + AddPattern(&set2, "http://www.yahoo.com/z*"); + AddPattern(&set2, "http://www.google.com/f*"); - EXPECT_FALSE(extent1.OverlapsWith(extent2)); - EXPECT_FALSE(extent2.OverlapsWith(extent1)); + AddPattern(&expected, "http://www.yahoo.com/b*"); - EXPECT_TRUE(extent1.OverlapsWith(extent3)); - EXPECT_TRUE(extent3.OverlapsWith(extent1)); + result.ClearPatterns(); + URLPatternSet::CreateDifference(set1, set2, &result); + EXPECT_EQ(expected, result); + EXPECT_FALSE(result.is_empty()); + EXPECT_TRUE(set1.Contains(result)); + EXPECT_FALSE(result.Contains(set2)); + EXPECT_FALSE(set2.Contains(result)); + + URLPatternSet intersection; + URLPatternSet::CreateIntersection(result, set2, &intersection); + EXPECT_TRUE(intersection.is_empty()); +} + +TEST(URLPatternSetTest, CreateIntersection) { + URLPatternSet empty_set; + URLPatternSet expected; + URLPatternSet set1; + AddPattern(&set1, "http://www.google.com/f*"); + AddPattern(&set1, "http://www.yahoo.com/b*"); + + // Intersection with an empty set. + URLPatternSet result; + URLPatternSet::CreateIntersection(set1, empty_set, &result); + EXPECT_EQ(expected, result); + EXPECT_TRUE(result.is_empty()); + EXPECT_TRUE(empty_set.Contains(result)); + EXPECT_TRUE(result.Contains(empty_set)); + EXPECT_TRUE(set1.Contains(result)); + + // Intersection with a real set. + URLPatternSet set2; + AddPattern(&set2, "http://www.reddit.com/f*"); + AddPattern(&set2, "http://www.yahoo.com/z*"); + AddPattern(&set2, "http://www.google.com/f*"); + + AddPattern(&expected, "http://www.google.com/f*"); + + result.ClearPatterns(); + URLPatternSet::CreateIntersection(set1, set2, &result); + EXPECT_EQ(expected, result); + EXPECT_FALSE(result.is_empty()); + EXPECT_TRUE(set1.Contains(result)); + EXPECT_TRUE(set2.Contains(result)); } TEST(URLPatternSetTest, CreateUnion) { - URLPatternSet empty_extent; + URLPatternSet empty_set; - URLPatternSet extent1; - extent1.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*")); - extent1.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b*")); + URLPatternSet set1; + AddPattern(&set1, "http://www.google.com/f*"); + AddPattern(&set1, "http://www.yahoo.com/b*"); URLPatternSet expected; - expected.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*")); - expected.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b*")); + AddPattern(&expected, "http://www.google.com/f*"); + AddPattern(&expected, "http://www.yahoo.com/b*"); // Union with an empty set. URLPatternSet result; - URLPatternSet::CreateUnion(extent1, empty_extent, &result); + URLPatternSet::CreateUnion(set1, empty_set, &result); EXPECT_EQ(expected, result); - // Union with a real set (including a duplicate). - URLPatternSet extent2; - extent2.AddPattern(URLPattern(kAllSchemes, "http://www.reddit.com/f*")); - extent2.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/z*")); - extent2.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*")); + // Union with a real set. + URLPatternSet set2; + AddPattern(&set2, "http://www.reddit.com/f*"); + AddPattern(&set2, "http://www.yahoo.com/z*"); + AddPattern(&set2, "http://www.google.com/f*"); - expected.AddPattern(URLPattern(kAllSchemes, "http://www.reddit.com/f*")); - expected.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/z*")); - // CreateUnion does not filter out duplicates right now. - expected.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*")); + AddPattern(&expected, "http://www.reddit.com/f*"); + AddPattern(&expected, "http://www.yahoo.com/z*"); result.ClearPatterns(); - URLPatternSet::CreateUnion(extent1, extent2, &result); + URLPatternSet::CreateUnion(set1, set2, &result); EXPECT_EQ(expected, result); } + +TEST(URLPatternSetTest, Contains) { + URLPatternSet set1; + URLPatternSet set2; + URLPatternSet empty_set; + + AddPattern(&set1, "http://www.google.com/*"); + AddPattern(&set1, "http://www.yahoo.com/*"); + + AddPattern(&set2, "http://www.reddit.com/*"); + + EXPECT_FALSE(set1.Contains(set2)); + EXPECT_TRUE(set1.Contains(empty_set)); + EXPECT_FALSE(empty_set.Contains(set1)); + + AddPattern(&set2, "http://www.yahoo.com/*"); + + EXPECT_FALSE(set1.Contains(set2)); + EXPECT_FALSE(set2.Contains(set1)); + + AddPattern(&set2, "http://www.google.com/*"); + + EXPECT_FALSE(set1.Contains(set2)); + EXPECT_TRUE(set2.Contains(set1)); + + // Note that this just checks pattern equality, and not if individual patterns + // contain other patterns. For example: + AddPattern(&set1, "http://*.reddit.com/*"); + EXPECT_FALSE(set1.Contains(set2)); + EXPECT_FALSE(set2.Contains(set1)); +} + +TEST(URLPatternSetTest, Duplicates) { + URLPatternSet set1; + URLPatternSet set2; + + AddPattern(&set1, "http://www.google.com/*"); + AddPattern(&set2, "http://www.google.com/*"); + + AddPattern(&set1, "http://www.google.com/*"); + + // The sets should still be equal after adding a duplicate. + EXPECT_EQ(set2, set1); +} |