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

Unified Diff: chrome/browser/extensions/user_script_loader.cc

Issue 955473002: Move DeclarativeUserScriptManager/Master and UserScriptLoader to //extensions. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/extensions/user_script_loader.h ('k') | chrome/chrome_browser_extensions.gypi » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/extensions/user_script_loader.cc
diff --git a/chrome/browser/extensions/user_script_loader.cc b/chrome/browser/extensions/user_script_loader.cc
deleted file mode 100644
index 24e042ba48284f2202405c3f71331797f9392ce4..0000000000000000000000000000000000000000
--- a/chrome/browser/extensions/user_script_loader.cc
+++ /dev/null
@@ -1,492 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/user_script_loader.h"
-
-#include <set>
-#include <string>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/version.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/profiles/profile.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/render_process_host.h"
-#include "extensions/browser/content_verifier.h"
-#include "extensions/common/extension_messages.h"
-#include "extensions/common/file_util.h"
-
-using content::BrowserThread;
-
-namespace extensions {
-
-namespace {
-
-using LoadScriptsCallback =
- base::Callback<void(scoped_ptr<UserScriptList>,
- scoped_ptr<base::SharedMemory>)>;
-
-UserScriptLoader::SubstitutionMap* GetLocalizationMessages(
- const UserScriptLoader::HostsInfo& hosts_info,
- const HostID& host_id) {
- UserScriptLoader::HostsInfo::const_iterator iter = hosts_info.find(host_id);
- if (iter == hosts_info.end())
- return nullptr;
- return file_util::LoadMessageBundleSubstitutionMap(
- iter->second.first, host_id.id(), iter->second.second);
-}
-
-void LoadUserScripts(
- UserScriptList* user_scripts,
- const UserScriptLoader::HostsInfo& hosts_info,
- const std::set<int>& added_script_ids,
- const scoped_refptr<ContentVerifier>& verifier,
- UserScriptLoader::LoadUserScriptsContentFunction callback) {
- for (UserScriptList::iterator script = user_scripts->begin();
- script != user_scripts->end();
- ++script) {
- if (added_script_ids.count(script->id()) == 0)
- continue;
- scoped_ptr<UserScriptLoader::SubstitutionMap> localization_messages(
- GetLocalizationMessages(hosts_info, script->host_id()));
- for (size_t k = 0; k < script->js_scripts().size(); ++k) {
- UserScript::File& script_file = script->js_scripts()[k];
- if (script_file.GetContent().empty())
- callback.Run(script->host_id(), &script_file, NULL, verifier);
- }
- for (size_t k = 0; k < script->css_scripts().size(); ++k) {
- UserScript::File& script_file = script->css_scripts()[k];
- if (script_file.GetContent().empty())
- callback.Run(script->host_id(), &script_file,
- localization_messages.get(), verifier);
- }
- }
-}
-
-// Pickle user scripts and return pointer to the shared memory.
-scoped_ptr<base::SharedMemory> Serialize(const UserScriptList& scripts) {
- Pickle pickle;
- pickle.WriteSizeT(scripts.size());
- for (UserScriptList::const_iterator script = scripts.begin();
- script != scripts.end();
- ++script) {
- // TODO(aa): This can be replaced by sending content script metadata to
- // renderers along with other extension data in ExtensionMsg_Loaded.
- // See crbug.com/70516.
- script->Pickle(&pickle);
- // Write scripts as 'data' so that we can read it out in the slave without
- // allocating a new string.
- for (size_t j = 0; j < script->js_scripts().size(); j++) {
- base::StringPiece contents = script->js_scripts()[j].GetContent();
- pickle.WriteData(contents.data(), contents.length());
- }
- for (size_t j = 0; j < script->css_scripts().size(); j++) {
- base::StringPiece contents = script->css_scripts()[j].GetContent();
- pickle.WriteData(contents.data(), contents.length());
- }
- }
-
- // Create the shared memory object.
- base::SharedMemory shared_memory;
-
- base::SharedMemoryCreateOptions options;
- options.size = pickle.size();
- options.share_read_only = true;
- if (!shared_memory.Create(options))
- return scoped_ptr<base::SharedMemory>();
-
- if (!shared_memory.Map(pickle.size()))
- return scoped_ptr<base::SharedMemory>();
-
- // Copy the pickle to shared memory.
- memcpy(shared_memory.memory(), pickle.data(), pickle.size());
-
- base::SharedMemoryHandle readonly_handle;
- if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(),
- &readonly_handle))
- return scoped_ptr<base::SharedMemory>();
-
- return make_scoped_ptr(new base::SharedMemory(readonly_handle,
- /*read_only=*/true));
-}
-
-void LoadScriptsOnFileThread(
- scoped_ptr<UserScriptList> user_scripts,
- const UserScriptLoader::HostsInfo& hosts_info,
- const std::set<int>& added_script_ids,
- const scoped_refptr<ContentVerifier>& verifier,
- UserScriptLoader::LoadUserScriptsContentFunction function,
- LoadScriptsCallback callback) {
- DCHECK(user_scripts.get());
- LoadUserScripts(user_scripts.get(), hosts_info, added_script_ids,
- verifier, function);
- scoped_ptr<base::SharedMemory> memory = Serialize(*user_scripts);
- BrowserThread::PostTask(
- BrowserThread::UI,
- FROM_HERE,
- base::Bind(callback, base::Passed(&user_scripts), base::Passed(&memory)));
-}
-
-// Helper function to parse greasesmonkey headers
-bool GetDeclarationValue(const base::StringPiece& line,
- const base::StringPiece& prefix,
- std::string* value) {
- base::StringPiece::size_type index = line.find(prefix);
- if (index == base::StringPiece::npos)
- return false;
-
- std::string temp(line.data() + index + prefix.length(),
- line.length() - index - prefix.length());
-
- if (temp.empty() || !IsWhitespace(temp[0]))
- return false;
-
- base::TrimWhitespaceASCII(temp, base::TRIM_ALL, value);
- return true;
-}
-
-} // namespace
-
-// static
-bool UserScriptLoader::ParseMetadataHeader(const base::StringPiece& script_text,
- UserScript* script) {
- // http://wiki.greasespot.net/Metadata_block
- base::StringPiece line;
- size_t line_start = 0;
- size_t line_end = line_start;
- bool in_metadata = false;
-
- static const base::StringPiece kUserScriptBegin("// ==UserScript==");
- static const base::StringPiece kUserScriptEng("// ==/UserScript==");
- static const base::StringPiece kNamespaceDeclaration("// @namespace");
- static const base::StringPiece kNameDeclaration("// @name");
- static const base::StringPiece kVersionDeclaration("// @version");
- static const base::StringPiece kDescriptionDeclaration("// @description");
- static const base::StringPiece kIncludeDeclaration("// @include");
- static const base::StringPiece kExcludeDeclaration("// @exclude");
- static const base::StringPiece kMatchDeclaration("// @match");
- static const base::StringPiece kExcludeMatchDeclaration("// @exclude_match");
- static const base::StringPiece kRunAtDeclaration("// @run-at");
- static const base::StringPiece kRunAtDocumentStartValue("document-start");
- static const base::StringPiece kRunAtDocumentEndValue("document-end");
- static const base::StringPiece kRunAtDocumentIdleValue("document-idle");
-
- while (line_start < script_text.length()) {
- line_end = script_text.find('\n', line_start);
-
- // Handle the case where there is no trailing newline in the file.
- if (line_end == std::string::npos)
- line_end = script_text.length() - 1;
-
- line.set(script_text.data() + line_start, line_end - line_start);
-
- if (!in_metadata) {
- if (line.starts_with(kUserScriptBegin))
- in_metadata = true;
- } else {
- if (line.starts_with(kUserScriptEng))
- break;
-
- std::string value;
- if (GetDeclarationValue(line, kIncludeDeclaration, &value)) {
- // We escape some characters that MatchPattern() considers special.
- ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
- ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
- script->add_glob(value);
- } else if (GetDeclarationValue(line, kExcludeDeclaration, &value)) {
- ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
- ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
- script->add_exclude_glob(value);
- } else if (GetDeclarationValue(line, kNamespaceDeclaration, &value)) {
- script->set_name_space(value);
- } else if (GetDeclarationValue(line, kNameDeclaration, &value)) {
- script->set_name(value);
- } else if (GetDeclarationValue(line, kVersionDeclaration, &value)) {
- Version version(value);
- if (version.IsValid())
- script->set_version(version.GetString());
- } else if (GetDeclarationValue(line, kDescriptionDeclaration, &value)) {
- script->set_description(value);
- } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) {
- URLPattern pattern(UserScript::ValidUserScriptSchemes());
- if (URLPattern::PARSE_SUCCESS != pattern.Parse(value))
- return false;
- script->add_url_pattern(pattern);
- } else if (GetDeclarationValue(line, kExcludeMatchDeclaration, &value)) {
- URLPattern exclude(UserScript::ValidUserScriptSchemes());
- if (URLPattern::PARSE_SUCCESS != exclude.Parse(value))
- return false;
- script->add_exclude_url_pattern(exclude);
- } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) {
- if (value == kRunAtDocumentStartValue)
- script->set_run_location(UserScript::DOCUMENT_START);
- else if (value == kRunAtDocumentEndValue)
- script->set_run_location(UserScript::DOCUMENT_END);
- else if (value == kRunAtDocumentIdleValue)
- script->set_run_location(UserScript::DOCUMENT_IDLE);
- else
- return false;
- }
-
- // TODO(aa): Handle more types of metadata.
- }
-
- line_start = line_end + 1;
- }
-
- // If no patterns were specified, default to @include *. This is what
- // Greasemonkey does.
- if (script->globs().empty() && script->url_patterns().is_empty())
- script->add_glob("*");
-
- return true;
-}
-
-void UserScriptLoader::LoadScriptsForTest(UserScriptList* user_scripts) {
- HostsInfo info;
- std::set<int> added_script_ids;
- for (UserScriptList::iterator it = user_scripts->begin();
- it != user_scripts->end();
- ++it) {
- added_script_ids.insert(it->id());
- }
- LoadUserScripts(user_scripts, info, added_script_ids,
- NULL /* no verifier for testing */,
- GetLoadUserScriptsFunction());
-}
-
-UserScriptLoader::UserScriptLoader(
- Profile* profile,
- const HostID& host_id,
- const scoped_refptr<ContentVerifier>& content_verifier)
- : user_scripts_(new UserScriptList()),
- clear_scripts_(false),
- ready_(false),
- pending_load_(false),
- profile_(profile),
- host_id_(host_id),
- content_verifier_(content_verifier),
- weak_factory_(this) {
- registrar_.Add(this,
- content::NOTIFICATION_RENDERER_PROCESS_CREATED,
- content::NotificationService::AllBrowserContextsAndSources());
-}
-
-UserScriptLoader::~UserScriptLoader() {
-}
-
-void UserScriptLoader::AddScripts(const std::set<UserScript>& scripts) {
- for (std::set<UserScript>::const_iterator it = scripts.begin();
- it != scripts.end();
- ++it) {
- removed_scripts_.erase(*it);
- added_scripts_.insert(*it);
- }
- AttemptLoad();
-}
-
-void UserScriptLoader::RemoveScripts(const std::set<UserScript>& scripts) {
- for (std::set<UserScript>::const_iterator it = scripts.begin();
- it != scripts.end();
- ++it) {
- added_scripts_.erase(*it);
- removed_scripts_.insert(*it);
- }
- AttemptLoad();
-}
-
-void UserScriptLoader::ClearScripts() {
- clear_scripts_ = true;
- added_scripts_.clear();
- removed_scripts_.clear();
- AttemptLoad();
-}
-
-void UserScriptLoader::Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
- DCHECK_EQ(type, content::NOTIFICATION_RENDERER_PROCESS_CREATED);
- content::RenderProcessHost* process =
- content::Source<content::RenderProcessHost>(source).ptr();
- Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
- if (!profile_->IsSameProfile(profile))
- return;
- if (scripts_ready()) {
- SendUpdate(process, shared_memory_.get(),
- std::set<HostID>()); // Include all hosts.
- }
-}
-
-bool UserScriptLoader::ScriptsMayHaveChanged() const {
- // Scripts may have changed if there are scripts added, scripts removed, or
- // if scripts were cleared and either:
- // (1) A load is in progress (which may result in a non-zero number of
- // scripts that need to be cleared), or
- // (2) The current set of scripts is non-empty (so they need to be cleared).
- return (added_scripts_.size() ||
- removed_scripts_.size() ||
- (clear_scripts_ &&
- (is_loading() || user_scripts_->size())));
-}
-
-void UserScriptLoader::AttemptLoad() {
- if (ready_ && ScriptsMayHaveChanged()) {
- if (is_loading())
- pending_load_ = true;
- else
- StartLoad();
- }
-}
-
-void UserScriptLoader::StartLoad() {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- DCHECK(!is_loading());
-
- // If scripts were marked for clearing before adding and removing, then clear
- // them.
- if (clear_scripts_) {
- user_scripts_->clear();
- } else {
- for (UserScriptList::iterator it = user_scripts_->begin();
- it != user_scripts_->end();) {
- if (removed_scripts_.count(*it))
- it = user_scripts_->erase(it);
- else
- ++it;
- }
- }
-
- user_scripts_->insert(
- user_scripts_->end(), added_scripts_.begin(), added_scripts_.end());
-
- std::set<int> added_script_ids;
- for (std::set<UserScript>::const_iterator it = added_scripts_.begin();
- it != added_scripts_.end();
- ++it) {
- added_script_ids.insert(it->id());
- }
-
- // Expand |changed_hosts_| for OnScriptsLoaded, which will use it in
- // its IPC message. This must be done before we clear |added_scripts_| and
- // |removed_scripts_| below.
- std::set<UserScript> changed_scripts(added_scripts_);
- changed_scripts.insert(removed_scripts_.begin(), removed_scripts_.end());
- for (const UserScript& script : changed_scripts)
- changed_hosts_.insert(script.host_id());
-
- // |changed_hosts_| before passing it to LoadScriptsOnFileThread.
- UpdateHostsInfo(changed_hosts_);
-
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- base::Bind(&LoadScriptsOnFileThread, base::Passed(&user_scripts_),
- hosts_info_, added_script_ids, content_verifier_,
- GetLoadUserScriptsFunction(),
- base::Bind(&UserScriptLoader::OnScriptsLoaded,
- weak_factory_.GetWeakPtr())));
-
- clear_scripts_ = false;
- added_scripts_.clear();
- removed_scripts_.clear();
- user_scripts_.reset(NULL);
-}
-
-void UserScriptLoader::AddHostInfo(const HostID& host_id,
- const PathAndDefaultLocale& location) {
- if (hosts_info_.find(host_id) != hosts_info_.end())
- return;
- hosts_info_[host_id] = location;
-}
-
-void UserScriptLoader::RemoveHostInfo(const HostID& host_id) {
- hosts_info_.erase(host_id);
-}
-
-void UserScriptLoader::SetReady(bool ready) {
- bool was_ready = ready_;
- ready_ = ready;
- if (ready_ && !was_ready)
- AttemptLoad();
-}
-
-void UserScriptLoader::OnScriptsLoaded(
- scoped_ptr<UserScriptList> user_scripts,
- scoped_ptr<base::SharedMemory> shared_memory) {
- user_scripts_.reset(user_scripts.release());
- if (pending_load_) {
- // While we were loading, there were further changes. Don't bother
- // notifying about these scripts and instead just immediately reload.
- pending_load_ = false;
- StartLoad();
- return;
- }
-
- if (shared_memory.get() == NULL) {
- // This can happen if we run out of file descriptors. In that case, we
- // have a choice between silently omitting all user scripts for new tabs,
- // by nulling out shared_memory_, or only silently omitting new ones by
- // leaving the existing object in place. The second seems less bad, even
- // though it removes the possibility that freeing the shared memory block
- // would open up enough FDs for long enough for a retry to succeed.
-
- // Pretend the extension change didn't happen.
- return;
- }
-
- // We've got scripts ready to go.
- shared_memory_.reset(shared_memory.release());
-
- for (content::RenderProcessHost::iterator i(
- content::RenderProcessHost::AllHostsIterator());
- !i.IsAtEnd();
- i.Advance()) {
- SendUpdate(i.GetCurrentValue(), shared_memory_.get(), changed_hosts_);
- }
- changed_hosts_.clear();
-
- content::NotificationService::current()->Notify(
- extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
- content::Source<Profile>(profile_),
- content::Details<base::SharedMemory>(shared_memory_.get()));
-}
-
-void UserScriptLoader::SendUpdate(content::RenderProcessHost* process,
- base::SharedMemory* shared_memory,
- const std::set<HostID>& changed_hosts) {
- // Don't allow injection of content scripts into <webview>.
- if (process->IsIsolatedGuest())
- return;
-
- Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
- // Make sure we only send user scripts to processes in our profile.
- if (!profile_->IsSameProfile(profile))
- return;
-
- // If the process is being started asynchronously, early return. We'll end up
- // calling InitUserScripts when it's created which will call this again.
- base::ProcessHandle handle = process->GetHandle();
- if (!handle)
- return;
-
- base::SharedMemoryHandle handle_for_process;
- if (!shared_memory->ShareToProcess(handle, &handle_for_process))
- return; // This can legitimately fail if the renderer asserts at startup.
-
- // TODO(hanxi): update the IPC message to send a set of HostIDs to render.
- // Also, remove this function when the refactor is done on render side.
- std::set<std::string> changed_ids_set;
- for (const HostID& id : changed_hosts)
- changed_ids_set.insert(id.id());
-
- if (base::SharedMemory::IsHandleValid(handle_for_process)) {
- process->Send(new ExtensionMsg_UpdateUserScripts(
- handle_for_process, host_id().id(), changed_ids_set));
- }
-}
-
-} // namespace extensions
« no previous file with comments | « chrome/browser/extensions/user_script_loader.h ('k') | chrome/chrome_browser_extensions.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698