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

Side by Side Diff: extensions/browser/user_script_loader.cc

Issue 1056533002: Implement <webview>.addContentScript/removeContentScript API [2] (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@webview_addremove_contentscripts_2
Patch Set: Another round of comments. Created 5 years, 8 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/browser/user_script_loader.h" 5 #include "extensions/browser/user_script_loader.h"
6 6
7 #include <set> 7 #include <set>
8 #include <string> 8 #include <string>
9 9
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/version.h" 10 #include "base/version.h"
15 #include "content/public/browser/browser_context.h" 11 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/browser_thread.h" 12 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/notification_service.h" 13 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/notification_types.h" 14 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_process_host.h" 15 #include "content/public/browser/render_process_host.h"
20 #include "extensions/browser/content_verifier.h"
21 #include "extensions/browser/extensions_browser_client.h" 16 #include "extensions/browser/extensions_browser_client.h"
22 #include "extensions/browser/notification_types.h" 17 #include "extensions/browser/notification_types.h"
23 #include "extensions/common/extension_messages.h" 18 #include "extensions/common/extension_messages.h"
24 #include "extensions/common/file_util.h"
25 19
26 using content::BrowserThread; 20 using content::BrowserThread;
27 using content::BrowserContext; 21 using content::BrowserContext;
28 22
29 namespace extensions { 23 namespace extensions {
30 24
31 namespace { 25 namespace {
32 26
33 using LoadScriptsCallback =
34 base::Callback<void(scoped_ptr<UserScriptList>,
35 scoped_ptr<base::SharedMemory>)>;
36
37 UserScriptLoader::SubstitutionMap* GetLocalizationMessages(
38 const UserScriptLoader::HostsInfo& hosts_info,
39 const HostID& host_id) {
40 UserScriptLoader::HostsInfo::const_iterator iter = hosts_info.find(host_id);
41 if (iter == hosts_info.end())
42 return nullptr;
43 return file_util::LoadMessageBundleSubstitutionMap(
44 iter->second.first, host_id.id(), iter->second.second);
45 }
46
47 void LoadUserScripts(
48 UserScriptList* user_scripts,
49 const UserScriptLoader::HostsInfo& hosts_info,
50 const std::set<int>& added_script_ids,
51 const scoped_refptr<ContentVerifier>& verifier,
52 UserScriptLoader::LoadUserScriptsContentFunction callback) {
53 for (UserScriptList::iterator script = user_scripts->begin();
54 script != user_scripts->end();
55 ++script) {
56 if (added_script_ids.count(script->id()) == 0)
57 continue;
58 scoped_ptr<UserScriptLoader::SubstitutionMap> localization_messages(
59 GetLocalizationMessages(hosts_info, script->host_id()));
60 for (size_t k = 0; k < script->js_scripts().size(); ++k) {
61 UserScript::File& script_file = script->js_scripts()[k];
62 if (script_file.GetContent().empty())
63 callback.Run(script->host_id(), &script_file, NULL, verifier);
64 }
65 for (size_t k = 0; k < script->css_scripts().size(); ++k) {
66 UserScript::File& script_file = script->css_scripts()[k];
67 if (script_file.GetContent().empty())
68 callback.Run(script->host_id(), &script_file,
69 localization_messages.get(), verifier);
70 }
71 }
72 }
73
74 // Pickle user scripts and return pointer to the shared memory.
75 scoped_ptr<base::SharedMemory> Serialize(const UserScriptList& scripts) {
76 Pickle pickle;
77 pickle.WriteSizeT(scripts.size());
78 for (UserScriptList::const_iterator script = scripts.begin();
79 script != scripts.end();
80 ++script) {
81 // TODO(aa): This can be replaced by sending content script metadata to
82 // renderers along with other extension data in ExtensionMsg_Loaded.
83 // See crbug.com/70516.
84 script->Pickle(&pickle);
85 // Write scripts as 'data' so that we can read it out in the slave without
86 // allocating a new string.
87 for (size_t j = 0; j < script->js_scripts().size(); j++) {
88 base::StringPiece contents = script->js_scripts()[j].GetContent();
89 pickle.WriteData(contents.data(), contents.length());
90 }
91 for (size_t j = 0; j < script->css_scripts().size(); j++) {
92 base::StringPiece contents = script->css_scripts()[j].GetContent();
93 pickle.WriteData(contents.data(), contents.length());
94 }
95 }
96
97 // Create the shared memory object.
98 base::SharedMemory shared_memory;
99
100 base::SharedMemoryCreateOptions options;
101 options.size = pickle.size();
102 options.share_read_only = true;
103 if (!shared_memory.Create(options))
104 return scoped_ptr<base::SharedMemory>();
105
106 if (!shared_memory.Map(pickle.size()))
107 return scoped_ptr<base::SharedMemory>();
108
109 // Copy the pickle to shared memory.
110 memcpy(shared_memory.memory(), pickle.data(), pickle.size());
111
112 base::SharedMemoryHandle readonly_handle;
113 if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(),
114 &readonly_handle))
115 return scoped_ptr<base::SharedMemory>();
116
117 return make_scoped_ptr(new base::SharedMemory(readonly_handle,
118 /*read_only=*/true));
119 }
120
121 void LoadScriptsOnFileThread(
122 scoped_ptr<UserScriptList> user_scripts,
123 const UserScriptLoader::HostsInfo& hosts_info,
124 const std::set<int>& added_script_ids,
125 const scoped_refptr<ContentVerifier>& verifier,
126 UserScriptLoader::LoadUserScriptsContentFunction function,
127 LoadScriptsCallback callback) {
128 DCHECK(user_scripts.get());
129 LoadUserScripts(user_scripts.get(), hosts_info, added_script_ids,
130 verifier, function);
131 scoped_ptr<base::SharedMemory> memory = Serialize(*user_scripts);
132 BrowserThread::PostTask(
133 BrowserThread::UI,
134 FROM_HERE,
135 base::Bind(callback, base::Passed(&user_scripts), base::Passed(&memory)));
136 }
137
138 // Helper function to parse greasesmonkey headers 27 // Helper function to parse greasesmonkey headers
139 bool GetDeclarationValue(const base::StringPiece& line, 28 bool GetDeclarationValue(const base::StringPiece& line,
140 const base::StringPiece& prefix, 29 const base::StringPiece& prefix,
141 std::string* value) { 30 std::string* value) {
142 base::StringPiece::size_type index = line.find(prefix); 31 base::StringPiece::size_type index = line.find(prefix);
143 if (index == base::StringPiece::npos) 32 if (index == base::StringPiece::npos)
144 return false; 33 return false;
145 34
146 std::string temp(line.data() + index + prefix.length(), 35 std::string temp(line.data() + index + prefix.length(),
147 line.length() - index - prefix.length()); 36 line.length() - index - prefix.length());
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 } 132 }
244 133
245 // If no patterns were specified, default to @include *. This is what 134 // If no patterns were specified, default to @include *. This is what
246 // Greasemonkey does. 135 // Greasemonkey does.
247 if (script->globs().empty() && script->url_patterns().is_empty()) 136 if (script->globs().empty() && script->url_patterns().is_empty())
248 script->add_glob("*"); 137 script->add_glob("*");
249 138
250 return true; 139 return true;
251 } 140 }
252 141
253 void UserScriptLoader::LoadScriptsForTest(UserScriptList* user_scripts) { 142 UserScriptLoader::UserScriptLoader(BrowserContext* browser_context,
254 HostsInfo info; 143 const HostID& host_id)
255 std::set<int> added_script_ids;
256 for (UserScriptList::iterator it = user_scripts->begin();
257 it != user_scripts->end();
258 ++it) {
259 added_script_ids.insert(it->id());
260 }
261 LoadUserScripts(user_scripts, info, added_script_ids,
262 NULL /* no verifier for testing */,
263 GetLoadUserScriptsFunction());
264 }
265
266 UserScriptLoader::UserScriptLoader(
267 BrowserContext* browser_context,
268 const HostID& host_id,
269 const scoped_refptr<ContentVerifier>& content_verifier)
270 : user_scripts_(new UserScriptList()), 144 : user_scripts_(new UserScriptList()),
271 clear_scripts_(false), 145 clear_scripts_(false),
272 ready_(false), 146 ready_(false),
273 pending_load_(false), 147 pending_load_(false),
274 browser_context_(browser_context), 148 browser_context_(browser_context),
275 host_id_(host_id), 149 host_id_(host_id) {
276 content_verifier_(content_verifier),
277 weak_factory_(this) {
278 registrar_.Add(this, 150 registrar_.Add(this,
279 content::NOTIFICATION_RENDERER_PROCESS_CREATED, 151 content::NOTIFICATION_RENDERER_PROCESS_CREATED,
280 content::NotificationService::AllBrowserContextsAndSources()); 152 content::NotificationService::AllBrowserContextsAndSources());
281 } 153 }
282 154
283 UserScriptLoader::~UserScriptLoader() { 155 UserScriptLoader::~UserScriptLoader() {
284 } 156 }
285 157
286 void UserScriptLoader::AddScripts(const std::set<UserScript>& scripts) { 158 void UserScriptLoader::AddScripts(const std::set<UserScript>& scripts) {
287 for (std::set<UserScript>::const_iterator it = scripts.begin(); 159 for (std::set<UserScript>::const_iterator it = scripts.begin();
288 it != scripts.end(); 160 it != scripts.end();
289 ++it) { 161 ++it) {
290 removed_scripts_.erase(*it); 162 removed_scripts_.erase(*it);
291 added_scripts_.insert(*it); 163 added_scripts_.insert(*it);
292 } 164 }
293 AttemptLoad(); 165 AttemptLoad();
294 } 166 }
295 167
168 void UserScriptLoader::AddScripts(const std::set<UserScript>& scripts,
169 int render_process_id,
170 int render_view_id) {
171 AddScripts(scripts);
172 }
173
296 void UserScriptLoader::RemoveScripts(const std::set<UserScript>& scripts) { 174 void UserScriptLoader::RemoveScripts(const std::set<UserScript>& scripts) {
297 for (std::set<UserScript>::const_iterator it = scripts.begin(); 175 for (std::set<UserScript>::const_iterator it = scripts.begin();
298 it != scripts.end(); 176 it != scripts.end();
299 ++it) { 177 ++it) {
300 added_scripts_.erase(*it); 178 added_scripts_.erase(*it);
301 removed_scripts_.insert(*it); 179 removed_scripts_.insert(*it);
302 } 180 }
303 AttemptLoad(); 181 AttemptLoad();
304 } 182 }
305 183
306 void UserScriptLoader::ClearScripts() { 184 void UserScriptLoader::ClearScripts(bool is_clear) {
307 clear_scripts_ = true; 185 clear_scripts_ = is_clear;
308 added_scripts_.clear(); 186 added_scripts_.clear();
309 removed_scripts_.clear(); 187 removed_scripts_.clear();
310 AttemptLoad(); 188 if (!clear_scripts_)
189 user_scripts_.reset(nullptr);
190 else
191 AttemptLoad();
311 } 192 }
312 193
313 void UserScriptLoader::Observe(int type, 194 void UserScriptLoader::Observe(int type,
314 const content::NotificationSource& source, 195 const content::NotificationSource& source,
315 const content::NotificationDetails& details) { 196 const content::NotificationDetails& details) {
316 DCHECK_EQ(type, content::NOTIFICATION_RENDERER_PROCESS_CREATED); 197 DCHECK_EQ(type, content::NOTIFICATION_RENDERER_PROCESS_CREATED);
317 content::RenderProcessHost* process = 198 content::RenderProcessHost* process =
318 content::Source<content::RenderProcessHost>(source).ptr(); 199 content::Source<content::RenderProcessHost>(source).ptr();
319 if (!ExtensionsBrowserClient::Get()->IsSameContext( 200 if (!ExtensionsBrowserClient::Get()->IsSameContext(
320 browser_context_, process->GetBrowserContext())) 201 browser_context_, process->GetBrowserContext()))
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
375 } 256 }
376 257
377 // Expand |changed_hosts_| for OnScriptsLoaded, which will use it in 258 // Expand |changed_hosts_| for OnScriptsLoaded, which will use it in
378 // its IPC message. This must be done before we clear |added_scripts_| and 259 // its IPC message. This must be done before we clear |added_scripts_| and
379 // |removed_scripts_| below. 260 // |removed_scripts_| below.
380 std::set<UserScript> changed_scripts(added_scripts_); 261 std::set<UserScript> changed_scripts(added_scripts_);
381 changed_scripts.insert(removed_scripts_.begin(), removed_scripts_.end()); 262 changed_scripts.insert(removed_scripts_.begin(), removed_scripts_.end());
382 for (const UserScript& script : changed_scripts) 263 for (const UserScript& script : changed_scripts)
383 changed_hosts_.insert(script.host_id()); 264 changed_hosts_.insert(script.host_id());
384 265
385 // |changed_hosts_| before passing it to LoadScriptsOnFileThread. 266 LoadScripts(user_scripts_.Pass(), changed_hosts_, added_script_ids);
386 UpdateHostsInfo(changed_hosts_);
387
388 BrowserThread::PostTask(
389 BrowserThread::FILE, FROM_HERE,
390 base::Bind(&LoadScriptsOnFileThread, base::Passed(&user_scripts_),
391 hosts_info_, added_script_ids, content_verifier_,
392 GetLoadUserScriptsFunction(),
393 base::Bind(&UserScriptLoader::OnScriptsLoaded,
394 weak_factory_.GetWeakPtr())));
395
396 clear_scripts_ = false;
Devlin 2015/04/21 22:32:20 Why don't we leave these in here, so we can get ri
Xi Han 2015/04/22 15:32:11 Hmm, right, moved it back after LoadScripts were c
397 added_scripts_.clear();
398 removed_scripts_.clear();
399 user_scripts_.reset(NULL);
400 } 267 }
401 268
402 void UserScriptLoader::AddHostInfo(const HostID& host_id, 269 // static
403 const PathAndDefaultLocale& location) { 270 scoped_ptr<base::SharedMemory> UserScriptLoader::Serialize(
404 if (hosts_info_.find(host_id) != hosts_info_.end()) 271 const UserScriptList& scripts) {
405 return; 272 Pickle pickle;
406 hosts_info_[host_id] = location; 273 pickle.WriteSizeT(scripts.size());
407 } 274 for (UserScriptList::const_iterator script = scripts.begin();
275 script != scripts.end(); ++script) {
276 // TODO(aa): This can be replaced by sending content script metadata to
277 // renderers along with other extension data in ExtensionMsg_Loaded.
278 // See crbug.com/70516.
279 script->Pickle(&pickle);
280 // Write scripts as 'data' so that we can read it out in the slave without
281 // allocating a new string.
282 for (size_t j = 0; j < script->js_scripts().size(); j++) {
283 base::StringPiece contents = script->js_scripts()[j].GetContent();
284 pickle.WriteData(contents.data(), contents.length());
285 }
286 for (size_t j = 0; j < script->css_scripts().size(); j++) {
287 base::StringPiece contents = script->css_scripts()[j].GetContent();
288 pickle.WriteData(contents.data(), contents.length());
289 }
290 }
408 291
409 void UserScriptLoader::RemoveHostInfo(const HostID& host_id) { 292 // Create the shared memory object.
410 hosts_info_.erase(host_id); 293 base::SharedMemory shared_memory;
294
295 base::SharedMemoryCreateOptions options;
296 options.size = pickle.size();
297 options.share_read_only = true;
298 if (!shared_memory.Create(options))
299 return scoped_ptr<base::SharedMemory>();
300
301 if (!shared_memory.Map(pickle.size()))
302 return scoped_ptr<base::SharedMemory>();
303
304 // Copy the pickle to shared memory.
305 memcpy(shared_memory.memory(), pickle.data(), pickle.size());
306
307 base::SharedMemoryHandle readonly_handle;
308 if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(),
309 &readonly_handle))
310 return scoped_ptr<base::SharedMemory>();
311
312 return make_scoped_ptr(new base::SharedMemory(readonly_handle,
313 /*read_only=*/true));
411 } 314 }
412 315
413 void UserScriptLoader::SetReady(bool ready) { 316 void UserScriptLoader::SetReady(bool ready) {
414 bool was_ready = ready_; 317 bool was_ready = ready_;
415 ready_ = ready; 318 ready_ = ready;
416 if (ready_ && !was_ready) 319 if (ready_ && !was_ready)
417 AttemptLoad(); 320 AttemptLoad();
418 } 321 }
419 322
420 void UserScriptLoader::OnScriptsLoaded( 323 void UserScriptLoader::OnScriptsLoaded(
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 if (!shared_memory->ShareToProcess(handle, &handle_for_process)) 382 if (!shared_memory->ShareToProcess(handle, &handle_for_process))
480 return; // This can legitimately fail if the renderer asserts at startup. 383 return; // This can legitimately fail if the renderer asserts at startup.
481 384
482 if (base::SharedMemory::IsHandleValid(handle_for_process)) { 385 if (base::SharedMemory::IsHandleValid(handle_for_process)) {
483 process->Send(new ExtensionMsg_UpdateUserScripts(handle_for_process, 386 process->Send(new ExtensionMsg_UpdateUserScripts(handle_for_process,
484 host_id(), changed_hosts)); 387 host_id(), changed_hosts));
485 } 388 }
486 } 389 }
487 390
488 } // namespace extensions 391 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698