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
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/user_script_loader.h"
6
7#include <set>
8#include <string>
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/memory/shared_memory.h"
15#include "base/version.h"
16#include "chrome/browser/chrome_notification_types.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/common/extensions/api/i18n/default_locale_handler.h"
19#include "content/public/browser/browser_thread.h"
20#include "content/public/browser/notification_service.h"
21#include "content/public/browser/render_process_host.h"
22#include "extensions/browser/component_extension_resource_manager.h"
23#include "extensions/browser/content_verifier.h"
24#include "extensions/browser/extension_registry.h"
25#include "extensions/browser/extension_system.h"
26#include "extensions/browser/extensions_browser_client.h"
27#include "extensions/common/extension_messages.h"
28#include "extensions/common/file_util.h"
29#include "extensions/common/message_bundle.h"
30#include "extensions/common/one_shot_event.h"
31#include "ui/base/resource/resource_bundle.h"
32
33using content::BrowserThread;
34using extensions::ExtensionsBrowserClient;
35
36namespace extensions {
37
38namespace {
39
40typedef base::Callback<
41    void(scoped_ptr<UserScriptList>, scoped_ptr<base::SharedMemory>)>
42    LoadScriptsCallback;
43
44void VerifyContent(scoped_refptr<ContentVerifier> verifier,
45                   const ExtensionId& extension_id,
46                   const base::FilePath& extension_root,
47                   const base::FilePath& relative_path,
48                   const std::string& content) {
49  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
50  scoped_refptr<ContentVerifyJob> job(
51      verifier->CreateJobFor(extension_id, extension_root, relative_path));
52  if (job.get()) {
53    job->Start();
54    job->BytesRead(content.size(), content.data());
55    job->DoneReading();
56  }
57}
58
59bool LoadScriptContent(const ExtensionId& extension_id,
60                       UserScript::File* script_file,
61                       const SubstitutionMap* localization_messages,
62                       scoped_refptr<ContentVerifier> verifier) {
63  std::string content;
64  const base::FilePath& path = ExtensionResource::GetFilePath(
65      script_file->extension_root(),
66      script_file->relative_path(),
67      ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT);
68  if (path.empty()) {
69    int resource_id;
70    if (ExtensionsBrowserClient::Get()->GetComponentExtensionResourceManager()->
71        IsComponentExtensionResource(script_file->extension_root(),
72                                     script_file->relative_path(),
73                                     &resource_id)) {
74      const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
75      content = rb.GetRawDataResource(resource_id).as_string();
76    } else {
77      LOG(WARNING) << "Failed to get file path to "
78                   << script_file->relative_path().value() << " from "
79                   << script_file->extension_root().value();
80      return false;
81    }
82  } else {
83    if (!base::ReadFileToString(path, &content)) {
84      LOG(WARNING) << "Failed to load user script file: " << path.value();
85      return false;
86    }
87    if (verifier.get()) {
88      content::BrowserThread::PostTask(content::BrowserThread::IO,
89                                       FROM_HERE,
90                                       base::Bind(&VerifyContent,
91                                                  verifier,
92                                                  extension_id,
93                                                  script_file->extension_root(),
94                                                  script_file->relative_path(),
95                                                  content));
96    }
97  }
98
99  // Localize the content.
100  if (localization_messages) {
101    std::string error;
102    MessageBundle::ReplaceMessagesWithExternalDictionary(
103        *localization_messages, &content, &error);
104    if (!error.empty()) {
105      LOG(WARNING) << "Failed to replace messages in script: " << error;
106    }
107  }
108
109  // Remove BOM from the content.
110  std::string::size_type index = content.find(base::kUtf8ByteOrderMark);
111  if (index == 0) {
112    script_file->set_content(content.substr(strlen(base::kUtf8ByteOrderMark)));
113  } else {
114    script_file->set_content(content);
115  }
116
117  return true;
118}
119
120SubstitutionMap* GetLocalizationMessages(const ExtensionsInfo& extensions_info,
121                                         const ExtensionId& extension_id) {
122  ExtensionsInfo::const_iterator iter = extensions_info.find(extension_id);
123  if (iter == extensions_info.end())
124    return NULL;
125  return file_util::LoadMessageBundleSubstitutionMap(
126      iter->second.first, extension_id, iter->second.second);
127}
128
129void LoadUserScripts(UserScriptList* user_scripts,
130                     const ExtensionsInfo& extensions_info,
131                     const std::set<int>& added_script_ids,
132                     ContentVerifier* verifier) {
133  for (UserScriptList::iterator script = user_scripts->begin();
134       script != user_scripts->end();
135       ++script) {
136    if (added_script_ids.count(script->id()) == 0)
137      continue;
138    scoped_ptr<SubstitutionMap> localization_messages(
139        GetLocalizationMessages(extensions_info, script->extension_id()));
140    for (size_t k = 0; k < script->js_scripts().size(); ++k) {
141      UserScript::File& script_file = script->js_scripts()[k];
142      if (script_file.GetContent().empty())
143        LoadScriptContent(script->extension_id(), &script_file, NULL, verifier);
144    }
145    for (size_t k = 0; k < script->css_scripts().size(); ++k) {
146      UserScript::File& script_file = script->css_scripts()[k];
147      if (script_file.GetContent().empty())
148        LoadScriptContent(script->extension_id(),
149                          &script_file,
150                          localization_messages.get(),
151                          verifier);
152    }
153  }
154}
155
156// Pickle user scripts and return pointer to the shared memory.
157scoped_ptr<base::SharedMemory> Serialize(const UserScriptList& scripts) {
158  Pickle pickle;
159  pickle.WriteUInt64(scripts.size());
160  for (UserScriptList::const_iterator script = scripts.begin();
161       script != scripts.end();
162       ++script) {
163    // TODO(aa): This can be replaced by sending content script metadata to
164    // renderers along with other extension data in ExtensionMsg_Loaded.
165    // See crbug.com/70516.
166    script->Pickle(&pickle);
167    // Write scripts as 'data' so that we can read it out in the slave without
168    // allocating a new string.
169    for (size_t j = 0; j < script->js_scripts().size(); j++) {
170      base::StringPiece contents = script->js_scripts()[j].GetContent();
171      pickle.WriteData(contents.data(), contents.length());
172    }
173    for (size_t j = 0; j < script->css_scripts().size(); j++) {
174      base::StringPiece contents = script->css_scripts()[j].GetContent();
175      pickle.WriteData(contents.data(), contents.length());
176    }
177  }
178
179  // Create the shared memory object.
180  base::SharedMemory shared_memory;
181
182  base::SharedMemoryCreateOptions options;
183  options.size = pickle.size();
184  options.share_read_only = true;
185  if (!shared_memory.Create(options))
186    return scoped_ptr<base::SharedMemory>();
187
188  if (!shared_memory.Map(pickle.size()))
189    return scoped_ptr<base::SharedMemory>();
190
191  // Copy the pickle to shared memory.
192  memcpy(shared_memory.memory(), pickle.data(), pickle.size());
193
194  base::SharedMemoryHandle readonly_handle;
195  if (!shared_memory.ShareReadOnlyToProcess(base::GetCurrentProcessHandle(),
196                                            &readonly_handle))
197    return scoped_ptr<base::SharedMemory>();
198
199  return make_scoped_ptr(new base::SharedMemory(readonly_handle,
200                                                /*read_only=*/true));
201}
202
203void LoadScriptsOnFileThread(scoped_ptr<UserScriptList> user_scripts,
204                             const ExtensionsInfo& extensions_info,
205                             const std::set<int>& added_script_ids,
206                             scoped_refptr<ContentVerifier> verifier,
207                             LoadScriptsCallback callback) {
208  DCHECK(user_scripts.get());
209  LoadUserScripts(
210      user_scripts.get(), extensions_info, added_script_ids, verifier.get());
211  scoped_ptr<base::SharedMemory> memory = Serialize(*user_scripts);
212  BrowserThread::PostTask(
213      BrowserThread::UI,
214      FROM_HERE,
215      base::Bind(callback, base::Passed(&user_scripts), base::Passed(&memory)));
216}
217
218// Helper function to parse greasesmonkey headers
219bool GetDeclarationValue(const base::StringPiece& line,
220                         const base::StringPiece& prefix,
221                         std::string* value) {
222  base::StringPiece::size_type index = line.find(prefix);
223  if (index == base::StringPiece::npos)
224    return false;
225
226  std::string temp(line.data() + index + prefix.length(),
227                   line.length() - index - prefix.length());
228
229  if (temp.empty() || !IsWhitespace(temp[0]))
230    return false;
231
232  base::TrimWhitespaceASCII(temp, base::TRIM_ALL, value);
233  return true;
234}
235
236}  // namespace
237
238// static
239bool UserScriptLoader::ParseMetadataHeader(const base::StringPiece& script_text,
240                                           UserScript* script) {
241  // http://wiki.greasespot.net/Metadata_block
242  base::StringPiece line;
243  size_t line_start = 0;
244  size_t line_end = line_start;
245  bool in_metadata = false;
246
247  static const base::StringPiece kUserScriptBegin("// ==UserScript==");
248  static const base::StringPiece kUserScriptEng("// ==/UserScript==");
249  static const base::StringPiece kNamespaceDeclaration("// @namespace");
250  static const base::StringPiece kNameDeclaration("// @name");
251  static const base::StringPiece kVersionDeclaration("// @version");
252  static const base::StringPiece kDescriptionDeclaration("// @description");
253  static const base::StringPiece kIncludeDeclaration("// @include");
254  static const base::StringPiece kExcludeDeclaration("// @exclude");
255  static const base::StringPiece kMatchDeclaration("// @match");
256  static const base::StringPiece kExcludeMatchDeclaration("// @exclude_match");
257  static const base::StringPiece kRunAtDeclaration("// @run-at");
258  static const base::StringPiece kRunAtDocumentStartValue("document-start");
259  static const base::StringPiece kRunAtDocumentEndValue("document-end");
260  static const base::StringPiece kRunAtDocumentIdleValue("document-idle");
261
262  while (line_start < script_text.length()) {
263    line_end = script_text.find('\n', line_start);
264
265    // Handle the case where there is no trailing newline in the file.
266    if (line_end == std::string::npos)
267      line_end = script_text.length() - 1;
268
269    line.set(script_text.data() + line_start, line_end - line_start);
270
271    if (!in_metadata) {
272      if (line.starts_with(kUserScriptBegin))
273        in_metadata = true;
274    } else {
275      if (line.starts_with(kUserScriptEng))
276        break;
277
278      std::string value;
279      if (GetDeclarationValue(line, kIncludeDeclaration, &value)) {
280        // We escape some characters that MatchPattern() considers special.
281        ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
282        ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
283        script->add_glob(value);
284      } else if (GetDeclarationValue(line, kExcludeDeclaration, &value)) {
285        ReplaceSubstringsAfterOffset(&value, 0, "\\", "\\\\");
286        ReplaceSubstringsAfterOffset(&value, 0, "?", "\\?");
287        script->add_exclude_glob(value);
288      } else if (GetDeclarationValue(line, kNamespaceDeclaration, &value)) {
289        script->set_name_space(value);
290      } else if (GetDeclarationValue(line, kNameDeclaration, &value)) {
291        script->set_name(value);
292      } else if (GetDeclarationValue(line, kVersionDeclaration, &value)) {
293        Version version(value);
294        if (version.IsValid())
295          script->set_version(version.GetString());
296      } else if (GetDeclarationValue(line, kDescriptionDeclaration, &value)) {
297        script->set_description(value);
298      } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) {
299        URLPattern pattern(UserScript::ValidUserScriptSchemes());
300        if (URLPattern::PARSE_SUCCESS != pattern.Parse(value))
301          return false;
302        script->add_url_pattern(pattern);
303      } else if (GetDeclarationValue(line, kExcludeMatchDeclaration, &value)) {
304        URLPattern exclude(UserScript::ValidUserScriptSchemes());
305        if (URLPattern::PARSE_SUCCESS != exclude.Parse(value))
306          return false;
307        script->add_exclude_url_pattern(exclude);
308      } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) {
309        if (value == kRunAtDocumentStartValue)
310          script->set_run_location(UserScript::DOCUMENT_START);
311        else if (value == kRunAtDocumentEndValue)
312          script->set_run_location(UserScript::DOCUMENT_END);
313        else if (value == kRunAtDocumentIdleValue)
314          script->set_run_location(UserScript::DOCUMENT_IDLE);
315        else
316          return false;
317      }
318
319      // TODO(aa): Handle more types of metadata.
320    }
321
322    line_start = line_end + 1;
323  }
324
325  // If no patterns were specified, default to @include *. This is what
326  // Greasemonkey does.
327  if (script->globs().empty() && script->url_patterns().is_empty())
328    script->add_glob("*");
329
330  return true;
331}
332
333// static
334void UserScriptLoader::LoadScriptsForTest(UserScriptList* user_scripts) {
335  ExtensionsInfo info;
336  std::set<int> added_script_ids;
337  for (UserScriptList::iterator it = user_scripts->begin();
338       it != user_scripts->end();
339       ++it) {
340    added_script_ids.insert(it->id());
341  }
342  LoadUserScripts(
343      user_scripts, info, added_script_ids, NULL /* no verifier for testing */);
344}
345
346UserScriptLoader::UserScriptLoader(Profile* profile,
347                                   const ExtensionId& owner_extension_id,
348                                   bool listen_for_extension_system_loaded)
349    : user_scripts_(new UserScriptList()),
350      clear_scripts_(false),
351      extension_system_ready_(false),
352      pending_load_(false),
353      profile_(profile),
354      owner_extension_id_(owner_extension_id),
355      extension_registry_observer_(this),
356      weak_factory_(this) {
357  extension_registry_observer_.Add(ExtensionRegistry::Get(profile));
358  if (listen_for_extension_system_loaded) {
359    ExtensionSystem::Get(profile_)->ready().Post(
360        FROM_HERE,
361        base::Bind(&UserScriptLoader::OnExtensionSystemReady,
362                   weak_factory_.GetWeakPtr()));
363  } else {
364    extension_system_ready_ = true;
365  }
366  registrar_.Add(this,
367                 content::NOTIFICATION_RENDERER_PROCESS_CREATED,
368                 content::NotificationService::AllBrowserContextsAndSources());
369}
370
371UserScriptLoader::~UserScriptLoader() {
372}
373
374void UserScriptLoader::AddScripts(const std::set<UserScript>& scripts) {
375  for (std::set<UserScript>::const_iterator it = scripts.begin();
376       it != scripts.end();
377       ++it) {
378    removed_scripts_.erase(*it);
379    added_scripts_.insert(*it);
380  }
381  AttemptLoad();
382}
383
384void UserScriptLoader::RemoveScripts(const std::set<UserScript>& scripts) {
385  for (std::set<UserScript>::const_iterator it = scripts.begin();
386       it != scripts.end();
387       ++it) {
388    added_scripts_.erase(*it);
389    removed_scripts_.insert(*it);
390  }
391  AttemptLoad();
392}
393
394void UserScriptLoader::ClearScripts() {
395  clear_scripts_ = true;
396  added_scripts_.clear();
397  removed_scripts_.clear();
398  AttemptLoad();
399}
400
401void UserScriptLoader::Observe(int type,
402                               const content::NotificationSource& source,
403                               const content::NotificationDetails& details) {
404  DCHECK_EQ(type, content::NOTIFICATION_RENDERER_PROCESS_CREATED);
405  content::RenderProcessHost* process =
406      content::Source<content::RenderProcessHost>(source).ptr();
407  Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
408  if (!profile_->IsSameProfile(profile))
409    return;
410  if (scripts_ready()) {
411    SendUpdate(process,
412               shared_memory_.get(),
413               std::set<ExtensionId>());  // Include all extensions.
414  }
415}
416
417void UserScriptLoader::OnExtensionUnloaded(
418    content::BrowserContext* browser_context,
419    const Extension* extension,
420    UnloadedExtensionInfo::Reason reason) {
421  extensions_info_.erase(extension->id());
422}
423
424void UserScriptLoader::OnExtensionSystemReady() {
425  extension_system_ready_ = true;
426  AttemptLoad();
427}
428
429bool UserScriptLoader::ScriptsMayHaveChanged() const {
430  // Scripts may have changed if there are scripts added, scripts removed, or
431  // if scripts were cleared and either:
432  // (1) A load is in progress (which may result in a non-zero number of
433  //     scripts that need to be cleared), or
434  // (2) The current set of scripts is non-empty (so they need to be cleared).
435  return (added_scripts_.size() ||
436          removed_scripts_.size() ||
437          (clear_scripts_ &&
438           (is_loading() || user_scripts_->size())));
439}
440
441void UserScriptLoader::AttemptLoad() {
442  if (extension_system_ready_ && ScriptsMayHaveChanged()) {
443    if (is_loading())
444      pending_load_ = true;
445    else
446      StartLoad();
447  }
448}
449
450void UserScriptLoader::StartLoad() {
451  DCHECK_CURRENTLY_ON(BrowserThread::UI);
452  DCHECK(!is_loading());
453
454  // If scripts were marked for clearing before adding and removing, then clear
455  // them.
456  if (clear_scripts_) {
457    user_scripts_->clear();
458  } else {
459    for (UserScriptList::iterator it = user_scripts_->begin();
460         it != user_scripts_->end();) {
461      if (removed_scripts_.count(*it))
462        it = user_scripts_->erase(it);
463      else
464        ++it;
465    }
466  }
467
468  user_scripts_->insert(
469      user_scripts_->end(), added_scripts_.begin(), added_scripts_.end());
470
471  std::set<int> added_script_ids;
472  for (std::set<UserScript>::const_iterator it = added_scripts_.begin();
473       it != added_scripts_.end();
474       ++it) {
475    added_script_ids.insert(it->id());
476  }
477
478  // Expand |changed_extensions_| for OnScriptsLoaded, which will use it in
479  // its IPC message. This must be done before we clear |added_scripts_| and
480  // |removed_scripts_| below.
481  std::set<UserScript> changed_scripts(added_scripts_);
482  changed_scripts.insert(removed_scripts_.begin(), removed_scripts_.end());
483  ExpandChangedExtensions(changed_scripts);
484
485  // Update |extensions_info_| to contain info from every extension in
486  // |changed_extensions_| before passing it to LoadScriptsOnFileThread.
487  UpdateExtensionsInfo();
488
489  BrowserThread::PostTask(
490      BrowserThread::FILE,
491      FROM_HERE,
492      base::Bind(&LoadScriptsOnFileThread,
493                 base::Passed(&user_scripts_),
494                 extensions_info_,
495                 added_script_ids,
496                 make_scoped_refptr(
497                     ExtensionSystem::Get(profile_)->content_verifier()),
498                 base::Bind(&UserScriptLoader::OnScriptsLoaded,
499                            weak_factory_.GetWeakPtr())));
500
501  clear_scripts_ = false;
502  added_scripts_.clear();
503  removed_scripts_.clear();
504  user_scripts_.reset(NULL);
505}
506
507void UserScriptLoader::OnScriptsLoaded(
508    scoped_ptr<UserScriptList> user_scripts,
509    scoped_ptr<base::SharedMemory> shared_memory) {
510  user_scripts_.reset(user_scripts.release());
511  if (pending_load_) {
512    // While we were loading, there were further changes. Don't bother
513    // notifying about these scripts and instead just immediately reload.
514    pending_load_ = false;
515    StartLoad();
516    return;
517  }
518
519  if (shared_memory.get() == NULL) {
520    // This can happen if we run out of file descriptors.  In that case, we
521    // have a choice between silently omitting all user scripts for new tabs,
522    // by nulling out shared_memory_, or only silently omitting new ones by
523    // leaving the existing object in place. The second seems less bad, even
524    // though it removes the possibility that freeing the shared memory block
525    // would open up enough FDs for long enough for a retry to succeed.
526
527    // Pretend the extension change didn't happen.
528    return;
529  }
530
531  // We've got scripts ready to go.
532  shared_memory_.reset(shared_memory.release());
533
534  for (content::RenderProcessHost::iterator i(
535           content::RenderProcessHost::AllHostsIterator());
536       !i.IsAtEnd();
537       i.Advance()) {
538    SendUpdate(i.GetCurrentValue(), shared_memory_.get(), changed_extensions_);
539  }
540  changed_extensions_.clear();
541
542  content::NotificationService::current()->Notify(
543      extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
544      content::Source<Profile>(profile_),
545      content::Details<base::SharedMemory>(shared_memory_.get()));
546}
547
548void UserScriptLoader::SendUpdate(
549    content::RenderProcessHost* process,
550    base::SharedMemory* shared_memory,
551    const std::set<ExtensionId>& changed_extensions) {
552  // Don't allow injection of content scripts into <webview>.
553  if (process->IsIsolatedGuest())
554    return;
555
556  Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
557  // Make sure we only send user scripts to processes in our profile.
558  if (!profile_->IsSameProfile(profile))
559    return;
560
561  // If the process is being started asynchronously, early return.  We'll end up
562  // calling InitUserScripts when it's created which will call this again.
563  base::ProcessHandle handle = process->GetHandle();
564  if (!handle)
565    return;
566
567  base::SharedMemoryHandle handle_for_process;
568  if (!shared_memory->ShareToProcess(handle, &handle_for_process))
569    return;  // This can legitimately fail if the renderer asserts at startup.
570
571  if (base::SharedMemory::IsHandleValid(handle_for_process)) {
572    process->Send(new ExtensionMsg_UpdateUserScripts(
573        handle_for_process, owner_extension_id_, changed_extensions));
574  }
575}
576
577void UserScriptLoader::ExpandChangedExtensions(
578    const std::set<UserScript>& scripts) {
579  for (std::set<UserScript>::const_iterator it = scripts.begin();
580       it != scripts.end();
581       ++it) {
582    changed_extensions_.insert(it->extension_id());
583  }
584}
585
586void UserScriptLoader::UpdateExtensionsInfo() {
587  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
588  for (std::set<ExtensionId>::const_iterator it = changed_extensions_.begin();
589       it != changed_extensions_.end();
590       ++it) {
591    if (extensions_info_.find(*it) == extensions_info_.end()) {
592      const Extension* extension =
593          registry->GetExtensionById(*it, ExtensionRegistry::EVERYTHING);
594      // |changed_extensions_| may include extensions that have been removed,
595      // which leads to the above lookup failing. In this case, just continue.
596      if (!extension)
597        continue;
598      extensions_info_[*it] = ExtensionSet::ExtensionPathAndDefaultLocale(
599          extension->path(), LocaleInfo::GetDefaultLocale(extension));
600    }
601  }
602}
603
604}  // namespace extensions
605