summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/generated_resources.grd3
-rw-r--r--chrome/browser/extensions/execute_code_in_tab_function.cc4
-rw-r--r--chrome/browser/extensions/extension_prefs.cc15
-rw-r--r--chrome/browser/extensions/extension_prefs.h5
-rw-r--r--chrome/browser/extensions/extension_startup_browsertest.cc18
-rw-r--r--chrome/browser/extensions/extension_tabs_module.cc3
-rw-r--r--chrome/browser/extensions/extensions_service.cc37
-rw-r--r--chrome/browser/extensions/extensions_service.h11
-rw-r--r--chrome/browser/extensions/extensions_ui.cc38
-rw-r--r--chrome/browser/extensions/extensions_ui.h3
-rw-r--r--chrome/browser/extensions/user_script_master.cc22
-rw-r--r--chrome/browser/resources/extensions_ui.html23
-rw-r--r--chrome/common/chrome_switches.cc5
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/common/extensions/extension.cc21
-rw-r--r--chrome/common/extensions/extension.h6
-rw-r--r--chrome/common/extensions/user_script.cc2
-rw-r--r--chrome/common/extensions/user_script.h9
-rw-r--r--chrome/common/notification_type.h4
-rw-r--r--chrome/renderer/user_script_slave.cc3
-rw-r--r--chrome/test/data/extensions/good/Preferences1
-rw-r--r--chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json2
-rw-r--r--chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json2
-rw-r--r--chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json2
24 files changed, 206 insertions, 34 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 7aebf70..b72ba48 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3547,6 +3547,9 @@ each locale. -->
<message name="IDS_EXTENSIONS_ENABLE_INCOGNITO" desc="The checkbox for enabling extensions in incognito.">
Allow in incognito
</message>
+ <message name="IDS_EXTENSIONS_ALLOW_FILE_ACCESS" desc="The checkbox for allowing an extension access to run scripts on file URLs.">
+ Allow access to file URLs
+ </message>
<message name="IDS_EXTENSIONS_INCOGNITO_WARNING" desc="Warns the user that Chrome cannot prevent extensions from recording history in incognito mode. Displayed in extensions management UI after an extension is selected to be run in incognito mode.">
<ph name="BEGIN_BOLD">&lt;b&gt;</ph>Warning:<ph name="END_BOLD">&lt;/b&gt;</ph> Google Chrome cannot prevent extensions from recording your browsing history. To disable this extension in incognito mode, unselect this option.
</message>
diff --git a/chrome/browser/extensions/execute_code_in_tab_function.cc b/chrome/browser/extensions/execute_code_in_tab_function.cc
index c391db8..44de6bf 100644
--- a/chrome/browser/extensions/execute_code_in_tab_function.cc
+++ b/chrome/browser/extensions/execute_code_in_tab_function.cc
@@ -9,6 +9,7 @@
#include "chrome/browser/browser.h"
#include "chrome/browser/extensions/extension_tabs_module.h"
#include "chrome/browser/extensions/extension_tabs_module_constants.h"
+#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/extensions/file_reader.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/extensions/extension.h"
@@ -66,7 +67,8 @@ bool ExecuteCodeInTabFunction::RunImpl() {
// NOTE: This can give the wrong answer due to race conditions, but it is OK,
// we check again in the renderer.
- if (!GetExtension()->CanExecuteScriptOnHost(contents->GetURL(), &error_))
+ if (!profile()->GetExtensionsService()->CanExecuteScriptOnHost(
+ GetExtension(), contents->GetURL(), &error_))
return false;
if (script_info->HasKey(keys::kAllFramesKey)) {
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index e73b2f6..0f9661c 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -67,6 +67,10 @@ const wchar_t kIdleInstallInfoFetchTime[] = L"fetch_time";
// A preference that, if true, will allow this extension to run in incognito
// mode.
const wchar_t kPrefIncognitoEnabled[] = L"incognito";
+
+// A preference to control whether an extension is allowed to inject script in
+// pages with file URLs.
+const wchar_t kPrefAllowFileAccess[] = L"allowFileAccess";
}
////////////////////////////////////////////////////////////////////////////////
@@ -352,6 +356,17 @@ void ExtensionPrefs::SetIsIncognitoEnabled(const std::string& extension_id,
prefs_->SavePersistentPrefs();
}
+bool ExtensionPrefs::AllowFileAccess(const std::string& extension_id) {
+ return ReadExtensionPrefBoolean(extension_id, kPrefAllowFileAccess);
+}
+
+void ExtensionPrefs::SetAllowFileAccess(const std::string& extension_id,
+ bool allow) {
+ UpdateExtensionPref(extension_id, kPrefAllowFileAccess,
+ Value::CreateBooleanValue(allow));
+ prefs_->SavePersistentPrefs();
+}
+
void ExtensionPrefs::GetKilledExtensionIds(std::set<std::string>* killed_ids) {
const DictionaryValue* dict = prefs_->GetDictionary(kExtensionsPref);
if (!dict || dict->empty())
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index 3f352e7ad..77487e9 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -105,6 +105,11 @@ class ExtensionPrefs {
bool IsIncognitoEnabled(const std::string& extension_id);
void SetIsIncognitoEnabled(const std::string& extension_id, bool enabled);
+ // Returns true if the user has chosen to allow this extension to inject
+ // scripts into pages with file URLs.
+ bool AllowFileAccess(const std::string& extension_id);
+ void SetAllowFileAccess(const std::string& extension_id, bool allow);
+
// Saves ExtensionInfo for each installed extension with the path to the
// version directory and the location. Blacklisted extensions won't be saved
// and neither will external extensions the user has explicitly uninstalled.
diff --git a/chrome/browser/extensions/extension_startup_browsertest.cc b/chrome/browser/extensions/extension_startup_browsertest.cc
index ed62a40..7566caa 100644
--- a/chrome/browser/extensions/extension_startup_browsertest.cc
+++ b/chrome/browser/extensions/extension_startup_browsertest.cc
@@ -63,6 +63,7 @@ class ExtensionStartupTestBase : public InProcessBrowserTest {
if (!load_extension_.value().empty()) {
command_line->AppendSwitchWithValue(switches::kLoadExtension,
load_extension_.ToWStringHack());
+ command_line->AppendSwitch(switches::kDisableExtensionsFileAccessCheck);
}
}
@@ -143,6 +144,23 @@ IN_PROC_BROWSER_TEST_F(ExtensionsStartupTest, Test) {
TestInjection(true, true);
}
+// Tests that disallowing file access on an extension prevents it from injecting
+// script into a page with a file URL.
+IN_PROC_BROWSER_TEST_F(ExtensionsStartupTest, NoFileAccess) {
+ WaitForServicesToStart(4, true); // 1 component extension and 3 others.
+
+ ExtensionsService* service = browser()->profile()->GetExtensionsService();
+ for (size_t i = 0; i < service->extensions()->size(); ++i) {
+ if (service->AllowFileAccess(service->extensions()->at(i))) {
+ service->SetAllowFileAccess(service->extensions()->at(i), false);
+ ui_test_utils::WaitForNotification(
+ NotificationType::USER_SCRIPTS_UPDATED);
+ }
+ }
+
+ TestInjection(false, false);
+}
+
// ExtensionsLoadTest
// Ensures that we can startup the browser with --load-extension and see them
// run.
diff --git a/chrome/browser/extensions/extension_tabs_module.cc b/chrome/browser/extensions/extension_tabs_module.cc
index 71a8753..0f58360 100644
--- a/chrome/browser/extensions/extension_tabs_module.cc
+++ b/chrome/browser/extensions/extension_tabs_module.cc
@@ -646,7 +646,8 @@ bool UpdateTabFunction::RunImpl() {
// JavaScript URLs can do the same kinds of things as cross-origin XHR, so
// we need to check host permissions before allowing them.
if (url.SchemeIs(chrome::kJavaScriptScheme)) {
- if (!GetExtension()->CanExecuteScriptOnHost(contents->GetURL(), &error_))
+ if (!profile()->GetExtensionsService()->CanExecuteScriptOnHost(
+ GetExtension(), contents->GetURL(), &error_))
return false;
// TODO(aa): How does controller queue URLs? Is there any chance that this
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 99dcd9c..02a7366 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -38,6 +38,7 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/extensions/extension_file_util.h"
#include "chrome/common/extensions/extension_l10n_util.h"
#include "chrome/common/notification_service.h"
@@ -675,6 +676,42 @@ void ExtensionsService::SetIsIncognitoEnabled(Extension* extension,
NotifyExtensionLoaded(extension);
}
+bool ExtensionsService::AllowFileAccess(const Extension* extension) {
+ return (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableExtensionsFileAccessCheck) ||
+ extension_prefs_->AllowFileAccess(extension->id()));
+}
+
+void ExtensionsService::SetAllowFileAccess(Extension* extension, bool allow) {
+ extension_prefs_->SetAllowFileAccess(extension->id(), allow);
+ NotificationService::current()->Notify(
+ NotificationType::EXTENSION_USER_SCRIPTS_UPDATED,
+ Source<Profile>(profile_),
+ Details<Extension>(extension));
+}
+
+bool ExtensionsService::CanExecuteScriptOnHost(Extension* extension,
+ const GURL& url,
+ std::string* error) const {
+ // No extensions are allowed to execute script on the gallery because that
+ // would allow extensions to manipulate their own install pages.
+ if (url.host() == GURL(Extension::ChromeStoreURL()).host()) {
+ if (error)
+ *error = errors::kCannotScriptGallery;
+ return false;
+ }
+
+ if (extension->HasHostPermission(url))
+ return true;
+
+ if (error) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
+ url.spec());
+ }
+
+ return false;
+}
+
void ExtensionsService::CheckForExternalUpdates() {
// This installs or updates externally provided extensions.
// TODO(aa): Why pass this list into the provider, why not just filter it
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
index c7e0609..07bfa8b 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -155,6 +155,17 @@ class ExtensionsService
bool IsIncognitoEnabled(const Extension* extension);
void SetIsIncognitoEnabled(Extension* extension, bool enabled);
+ // Whether this extension can inject scripts into pages with file URLs.
+ bool AllowFileAccess(const Extension* extension);
+ void SetAllowFileAccess(Extension* extension, bool allow);
+
+ // Returns true if the extension has permission to execute script on a
+ // particular host.
+ // TODO(aa): Also use this in the renderer, for normal content script
+ // injection. Currently, that has its own copy of this code.
+ bool CanExecuteScriptOnHost(Extension* extension,
+ const GURL& url, std::string* error) const;
+
const FilePath& install_directory() const { return install_directory_; }
// Initialize and start all installed extensions.
diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc
index 9ad2e78..84a6f33c 100644
--- a/chrome/browser/extensions/extensions_ui.cc
+++ b/chrome/browser/extensions/extensions_ui.cc
@@ -125,6 +125,8 @@ void ExtensionsUIHTMLSource::StartDataRequest(const std::string& path,
l10n_util::GetString(IDS_EXTENSIONS_ENABLE));
localized_strings.SetString(L"enableIncognito",
l10n_util::GetString(IDS_EXTENSIONS_ENABLE_INCOGNITO));
+ localized_strings.SetString(L"allowFileAccess",
+ l10n_util::GetString(IDS_EXTENSIONS_ALLOW_FILE_ACCESS));
localized_strings.SetString(L"incognitoWarning",
l10n_util::GetString(IDS_EXTENSIONS_INCOGNITO_WARNING));
localized_strings.SetString(L"reload",
@@ -282,6 +284,8 @@ void ExtensionsDOMHandler::RegisterMessages() {
NewCallback(this, &ExtensionsDOMHandler::HandleEnableMessage));
dom_ui_->RegisterMessageCallback("enableIncognito",
NewCallback(this, &ExtensionsDOMHandler::HandleEnableIncognitoMessage));
+ dom_ui_->RegisterMessageCallback("allowFileAccess",
+ NewCallback(this, &ExtensionsDOMHandler::HandleAllowFileAccessMessage));
dom_ui_->RegisterMessageCallback("uninstall",
NewCallback(this, &ExtensionsDOMHandler::HandleUninstallMessage));
dom_ui_->RegisterMessageCallback("options",
@@ -493,6 +497,20 @@ void ExtensionsDOMHandler::HandleEnableIncognitoMessage(const Value* value) {
ignore_notifications_ = false;
}
+void ExtensionsDOMHandler::HandleAllowFileAccessMessage(const Value* value) {
+ CHECK(value->IsType(Value::TYPE_LIST));
+ const ListValue* list = static_cast<const ListValue*>(value);
+ CHECK(list->GetSize() == 2);
+ std::string extension_id, allow_str;
+ CHECK(list->GetString(0, &extension_id));
+ CHECK(list->GetString(1, &allow_str));
+ Extension* extension = extensions_service_->GetExtensionById(extension_id,
+ true);
+ DCHECK(extension);
+
+ extensions_service_->SetAllowFileAccess(extension, allow_str == "true");
+}
+
void ExtensionsDOMHandler::HandleUninstallMessage(const Value* value) {
CHECK(value->IsType(Value::TYPE_LIST));
const ListValue* list = static_cast<const ListValue*>(value);
@@ -767,9 +785,23 @@ DictionaryValue* ExtensionsDOMHandler::CreateContentScriptDetailValue(
return script_data;
}
+static bool ExtensionWantsFileAccess(const Extension* extension) {
+ for (UserScriptList::const_iterator it = extension->content_scripts().begin();
+ it != extension->content_scripts().end(); ++it) {
+ for (UserScript::PatternList::const_iterator pattern =
+ it->url_patterns().begin();
+ pattern != it->url_patterns().end(); ++pattern) {
+ if (pattern->scheme() == chrome::kFileScheme)
+ return true;
+ }
+ }
+
+ return false;
+}
+
// Static
DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue(
- ExtensionsService* service, const Extension *extension,
+ ExtensionsService* service, const Extension* extension,
const std::vector<ExtensionPage>& pages, bool enabled) {
DictionaryValue* extension_data = new DictionaryValue();
@@ -780,6 +812,10 @@ DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue(
extension_data->SetBoolean(L"enabled", enabled);
extension_data->SetBoolean(L"enabledIncognito",
service ? service->IsIncognitoEnabled(extension) : false);
+ extension_data->SetBoolean(L"wantsFileAccess",
+ ExtensionWantsFileAccess(extension));
+ extension_data->SetBoolean(L"allowFileAccess",
+ service ? service->AllowFileAccess(extension) : false);
extension_data->SetBoolean(L"allow_reload",
extension->location() == Extension::LOAD);
diff --git a/chrome/browser/extensions/extensions_ui.h b/chrome/browser/extensions/extensions_ui.h
index e85e586..120f08a 100644
--- a/chrome/browser/extensions/extensions_ui.h
+++ b/chrome/browser/extensions/extensions_ui.h
@@ -149,6 +149,9 @@ class ExtensionsDOMHandler
// Callback for "enableIncognito" message.
void HandleEnableIncognitoMessage(const Value* value);
+ // Callback for "allowFileAcces" message.
+ void HandleAllowFileAccessMessage(const Value* value);
+
// Callback for "uninstall" message.
void HandleUninstallMessage(const Value* value);
diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc
index 608f9db..c651c2e 100644
--- a/chrome/browser/extensions/user_script_master.cc
+++ b/chrome/browser/extensions/user_script_master.cc
@@ -306,6 +306,8 @@ UserScriptMaster::UserScriptMaster(const FilePath& script_dir, Profile* profile)
Source<Profile>(profile_));
registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
Source<Profile>(profile_));
+ registrar_.Add(this, NotificationType::EXTENSION_USER_SCRIPTS_UPDATED,
+ Source<Profile>(profile_));
}
UserScriptMaster::~UserScriptMaster() {
@@ -348,11 +350,14 @@ void UserScriptMaster::Observe(NotificationType type,
Extension* extension = Details<Extension>(details).ptr();
bool incognito_enabled = profile_->GetExtensionsService()->
IsIncognitoEnabled(extension);
+ bool allow_file_access = profile_->GetExtensionsService()->
+ AllowFileAccess(extension);
const UserScriptList& scripts = extension->content_scripts();
for (UserScriptList::const_iterator iter = scripts.begin();
iter != scripts.end(); ++iter) {
lone_scripts_.push_back(*iter);
lone_scripts_.back().set_incognito_enabled(incognito_enabled);
+ lone_scripts_.back().set_allow_file_access(allow_file_access);
}
if (extensions_service_ready_)
StartScan();
@@ -375,6 +380,23 @@ void UserScriptMaster::Observe(NotificationType type,
break;
}
+ case NotificationType::EXTENSION_USER_SCRIPTS_UPDATED: {
+ Extension* extension = Details<Extension>(details).ptr();
+ UserScriptList new_lone_scripts;
+ bool incognito_enabled = profile_->GetExtensionsService()->
+ IsIncognitoEnabled(extension);
+ bool allow_file_access = profile_->GetExtensionsService()->
+ AllowFileAccess(extension);
+ for (UserScriptList::iterator iter = lone_scripts_.begin();
+ iter != lone_scripts_.end(); ++iter) {
+ if (iter->extension_id() == extension->id()) {
+ iter->set_incognito_enabled(incognito_enabled);
+ iter->set_allow_file_access(allow_file_access);
+ }
+ }
+ StartScan();
+ break;
+ }
default:
DCHECK(false);
diff --git a/chrome/browser/resources/extensions_ui.html b/chrome/browser/resources/extensions_ui.html
index 3ae9d1e..1db6d5a 100644
--- a/chrome/browser/resources/extensions_ui.html
+++ b/chrome/browser/resources/extensions_ui.html
@@ -352,6 +352,8 @@ var extensionDataFormat = {
'version': '1.0.231',
'enabled': 'true',
'enabledIncognito': 'false',
+ 'wantsFileAccess': 'false',
+ 'allowFileAccess': 'false',
'allow_reload': true,
'order': 1,
'options_url': 'options.html',
@@ -392,6 +394,8 @@ var extensionDataFormat = {
'version': '1.0.231',
'enabled': 'true',
'enabledIncognito': 'false',
+ 'wantsFileAccess': 'false',
+ 'allowFileAccess': 'false',
'allow_reload': false,
'order': 2,
'icon': '',
@@ -580,10 +584,16 @@ function handleToggleExtensionIncognito(node) {
}
/**
+ * Handles the 'allowFileAccess' checkbox getting changed.
+ */
+function handleToggleAllowFileAccess(node) {
+ chrome.send('allowFileAccess', [node.extensionId, String(node.checked)]);
+}
+
+/**
* Handles an 'uninstall' button getting clicked.
*/
function handleUninstallExtension(node) {
- // Tell the C++ ExtensionDOMHandler to uninstall an extension.
chrome.send('uninstall', [node.extensionId]);
}
@@ -882,14 +892,21 @@ document.addEventListener('DOMContentLoaded', requestExtensionsData);
i18n-content="options"
>OPTIONS</a>
<label
- jsdisplay="enabled"
- onclick="handleToggleExtensionIncognito(this.getElementsByTagName('input')[0])">
+ jsdisplay="enabled">
<input type="checkbox"
jsvalues=".extensionId:id;.enabled:enabled"
jsdisplay="enabled"
jseval="this.checked = enabledIncognito"
onchange="handleToggleExtensionIncognito(this)">
<span i18n-content="enableIncognito">ALLOW THIS EXTENSION TO RUN IN INCOGNITO</span></label>
+ <label
+ jsdisplay="enabled && wantsFileAccess">
+ <input type="checkbox"
+ jsvalues=".extensionId:id;.enabled:enabled;.wantsFileAccess:wantsFileAccess"
+ jsdisplay="enabled && wantsFileAccess"
+ jseval="this.checked = allowFileAccess"
+ onchange="handleToggleAllowFileAccess(this)">
+ <span i18n-content="allowFileAccess">ALLOW THIS EXTENSION ACCESS TO FILE URLS</span></label>
</span>
</div>
<div class="incognitoWarning">
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 9852c97..c6bb2fe 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -136,6 +136,11 @@ const char kDisableDevTools[] = "disable-dev-tools";
// Disable extensions.
const char kDisableExtensions[] = "disable-extensions";
+// Disable checking for user opt-in for extensions that want to inject script
+// into file URLs (ie, always allow it). This is used during automated testing.
+const char kDisableExtensionsFileAccessCheck[] =
+ "disable-extensions-file-access-check";
+
// Suppresses support for the Geolocation javascript API.
const char kDisableGeolocation[] = "disable-geolocation";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 80df849..6aa3b40 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -53,6 +53,7 @@ extern const char kDisableDatabases[];
extern const char kDisableDesktopNotifications[];
extern const char kDisableDevTools[];
extern const char kDisableExtensions[];
+extern const char kDisableExtensionsFileAccessCheck[];
extern const char kDisableGeolocation[];
extern const char kDisableHangMonitor[];
extern const char kDisableInternalFlash[];
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 1460ac5..47216fc 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -1654,27 +1654,6 @@ bool Extension::HasHostPermission(const GURL& url) const {
return false;
}
-bool Extension::CanExecuteScriptOnHost(const GURL& url,
- std::string* error) const {
- // No extensions are allowed to execute script on the gallery because that
- // would allow extensions to manipulate their own install pages.
- if (url.host() == GURL(ChromeStoreURL()).host()) {
- if (error)
- *error = errors::kCannotScriptGallery;
- return false;
- }
-
- if (HasHostPermission(url))
- return true;
-
- if (error) {
- *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
- url.spec());
- }
-
- return false;
-}
-
const std::set<std::string> Extension::GetEffectiveHostPermissions() const {
std::set<std::string> effective_hosts;
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index d0642dc..1af1d23 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -253,12 +253,6 @@ class Extension {
return host_permissions_;
}
- // Returns true if the extension has permission to execute script on a
- // particular host.
- // TODO(aa): Also use this in the renderer, for normal content script
- // injection. Currently, that has its own copy of this code.
- bool CanExecuteScriptOnHost(const GURL& url, std::string* error) const;
-
// Returns true if the extension has the specified API permission.
bool HasApiPermission(const std::string& permission) const {
return std::find(api_permissions_.begin(), api_permissions_.end(),
diff --git a/chrome/common/extensions/user_script.cc b/chrome/common/extensions/user_script.cc
index 5ed2040..b55a10c 100644
--- a/chrome/common/extensions/user_script.cc
+++ b/chrome/common/extensions/user_script.cc
@@ -81,6 +81,7 @@ void UserScript::Pickle(::Pickle* pickle) const {
pickle->WriteBool(emulate_greasemonkey());
pickle->WriteBool(match_all_frames());
pickle->WriteBool(is_incognito_enabled());
+ pickle->WriteBool(allow_file_access());
// Write globs.
std::vector<std::string>::const_iterator glob;
@@ -126,6 +127,7 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) {
CHECK(pickle.ReadBool(iter, &emulate_greasemonkey_));
CHECK(pickle.ReadBool(iter, &match_all_frames_));
CHECK(pickle.ReadBool(iter, &incognito_enabled_));
+ CHECK(pickle.ReadBool(iter, &allow_file_access_));
// Read globs.
size_t num_globs = 0;
diff --git a/chrome/common/extensions/user_script.h b/chrome/common/extensions/user_script.h
index 6bf30ee..ac8f836 100644
--- a/chrome/common/extensions/user_script.h
+++ b/chrome/common/extensions/user_script.h
@@ -102,7 +102,8 @@ class UserScript {
// Greasemonkey and probably more useful for typical scripts.
UserScript()
: run_location_(DOCUMENT_IDLE), emulate_greasemonkey_(false),
- match_all_frames_(false), incognito_enabled_(false) {
+ match_all_frames_(false), incognito_enabled_(false),
+ allow_file_access_(false) {
}
const std::string& name_space() const { return name_space_; }
@@ -170,6 +171,9 @@ class UserScript {
bool is_incognito_enabled() const { return incognito_enabled_; }
void set_incognito_enabled(bool enabled) { incognito_enabled_ = enabled; }
+ bool allow_file_access() const { return allow_file_access_; }
+ void set_allow_file_access(bool allowed) { allow_file_access_ = allowed; }
+
bool is_standalone() const { return extension_id_.empty(); }
// Returns true if the script should be applied to the specified URL, false
@@ -232,6 +236,9 @@ class UserScript {
// True if the script should be injected into an incognito tab.
bool incognito_enabled_;
+
+ // True if the user agreed to allow this script access to file URLs.
+ bool allow_file_access_;
};
typedef std::vector<UserScript> UserScriptList;
diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h
index 464f629..3c96a1a 100644
--- a/chrome/common/notification_type.h
+++ b/chrome/common/notification_type.h
@@ -787,6 +787,10 @@ class NotificationType {
// Same as above, but for a disabled extension.
EXTENSION_UNLOADED_DISABLED,
+ // Sent when an extension has updated its user scripts. The details are an
+ // Extension, and the source is a Profile.
+ EXTENSION_USER_SCRIPTS_UPDATED,
+
// Sent after a new ExtensionFunctionDispatcher is created. The details are
// an ExtensionFunctionDispatcher* and the source is a Profile*. This is
// similar in timing to EXTENSION_HOST_CREATED, but also fires when an
diff --git a/chrome/renderer/user_script_slave.cc b/chrome/renderer/user_script_slave.cc
index 11ad1f2..c5812ad 100644
--- a/chrome/renderer/user_script_slave.cc
+++ b/chrome/renderer/user_script_slave.cc
@@ -167,6 +167,9 @@ bool UserScriptSlave::InjectScripts(WebFrame* frame,
if (!script->MatchesUrl(frame->url()))
continue; // This frame doesn't match the script url pattern, skip it.
+ if (frame_url.SchemeIsFile() && !script->allow_file_access())
+ continue; // This script isn't allowed to run on file URLs.
+
// CSS files are always injected on document start before js scripts.
if (location == UserScript::DOCUMENT_START) {
num_css += script->css_scripts().size();
diff --git a/chrome/test/data/extensions/good/Preferences b/chrome/test/data/extensions/good/Preferences
index 7aad8c2..6d5c369 100644
--- a/chrome/test/data/extensions/good/Preferences
+++ b/chrome/test/data/extensions/good/Preferences
@@ -5,6 +5,7 @@
"location": 1,
"path": "behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0",
"state": 1,
+ "allowFileAccess": true,
"manifest": {
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuUZGKCDbff6IRaxa4Pue7PPkxwPaNhGT3JEqppEsNWFjM80imEdqMbf3lrWqEfaHgaNku7nlpwPO1mu3/4Hr+XdNa5MhfnOnuPee4hyTLwOs3Vzz81wpbdzUxZSi2OmqMyI5oTaBYICfNHLwcuc65N5dbt6WKGeKgTpp4v7j7zwIDAQAB",
"version": "1.0.0.0",
diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json
index be1fbd2..6de443b 100644
--- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json
+++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json
@@ -8,6 +8,8 @@
"allow_reload": false,
"order": 2,
"enabledIncognito": false,
+ "wantsFileAccess": true,
+ "allowFileAccess": false,
"content_scripts": [
{
"matches": ["file://*", "http://*.google.com/*", "https://*.google.com/*"],
diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json
index 6b5f26f..cbbc5fc 100644
--- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json
+++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json
@@ -8,6 +8,8 @@
"allow_reload": false,
"order": 2,
"enabledIncognito": false,
+ "wantsFileAccess": false,
+ "allowFileAccess": false,
"content_scripts": [],
"views": [
{
diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
index df765e2..aef2f0d 100644
--- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
+++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
@@ -8,6 +8,8 @@
"allow_reload": false,
"order": 2,
"enabledIncognito": false,
+ "wantsFileAccess": false,
+ "allowFileAccess": false,
"content_scripts": [],
"views": [],
"hasPopupAction": false,