1// Copyright 2013 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 "extensions/common/user_script.h"
6
7#include "base/command_line.h"
8#include "base/pickle.h"
9#include "base/strings/string_util.h"
10#include "extensions/common/switches.h"
11
12namespace {
13
14bool UrlMatchesGlobs(const std::vector<std::string>* globs,
15                     const GURL& url) {
16  for (std::vector<std::string>::const_iterator glob = globs->begin();
17       glob != globs->end(); ++glob) {
18    if (MatchPattern(url.spec(), *glob))
19      return true;
20  }
21
22  return false;
23}
24
25}  // namespace
26
27namespace extensions {
28
29// The bitmask for valid user script injectable schemes used by URLPattern.
30enum {
31  kValidUserScriptSchemes = URLPattern::SCHEME_CHROMEUI |
32                            URLPattern::SCHEME_HTTP |
33                            URLPattern::SCHEME_HTTPS |
34                            URLPattern::SCHEME_FILE |
35                            URLPattern::SCHEME_FTP
36};
37
38// static
39const char UserScript::kFileExtension[] = ".user.js";
40
41bool UserScript::IsURLUserScript(const GURL& url,
42                                 const std::string& mime_type) {
43  return EndsWith(url.ExtractFileName(), kFileExtension, false) &&
44      mime_type != "text/html";
45}
46
47// static
48int UserScript::ValidUserScriptSchemes(bool canExecuteScriptEverywhere) {
49  if (canExecuteScriptEverywhere)
50    return URLPattern::SCHEME_ALL;
51  int valid_schemes = kValidUserScriptSchemes;
52  if (!CommandLine::ForCurrentProcess()->HasSwitch(
53      switches::kExtensionsOnChromeURLs)) {
54    valid_schemes &= ~URLPattern::SCHEME_CHROMEUI;
55  }
56  return valid_schemes;
57}
58
59UserScript::File::File(const base::FilePath& extension_root,
60                       const base::FilePath& relative_path,
61                       const GURL& url)
62    : extension_root_(extension_root),
63      relative_path_(relative_path),
64      url_(url) {
65}
66
67UserScript::File::File() {}
68
69UserScript::File::~File() {}
70
71UserScript::UserScript()
72    : run_location_(DOCUMENT_IDLE), emulate_greasemonkey_(false),
73      match_all_frames_(false), incognito_enabled_(false) {
74}
75
76UserScript::~UserScript() {
77}
78
79void UserScript::add_url_pattern(const URLPattern& pattern) {
80  url_set_.AddPattern(pattern);
81}
82
83void UserScript::add_exclude_url_pattern(const URLPattern& pattern) {
84  exclude_url_set_.AddPattern(pattern);
85}
86
87bool UserScript::MatchesURL(const GURL& url) const {
88  if (!url_set_.is_empty()) {
89    if (!url_set_.MatchesURL(url))
90      return false;
91  }
92
93  if (!exclude_url_set_.is_empty()) {
94    if (exclude_url_set_.MatchesURL(url))
95      return false;
96  }
97
98  if (!globs_.empty()) {
99    if (!UrlMatchesGlobs(&globs_, url))
100      return false;
101  }
102
103  if (!exclude_globs_.empty()) {
104    if (UrlMatchesGlobs(&exclude_globs_, url))
105      return false;
106  }
107
108  return true;
109}
110
111void UserScript::File::Pickle(::Pickle* pickle) const {
112  pickle->WriteString(url_.spec());
113  // Do not write path. It's not needed in the renderer.
114  // Do not write content. It will be serialized by other means.
115}
116
117void UserScript::File::Unpickle(const ::Pickle& pickle, PickleIterator* iter) {
118  // Read the url from the pickle.
119  std::string url;
120  CHECK(pickle.ReadString(iter, &url));
121  set_url(GURL(url));
122}
123
124void UserScript::Pickle(::Pickle* pickle) const {
125  // Write the simple types to the pickle.
126  pickle->WriteInt(run_location());
127  pickle->WriteString(extension_id());
128  pickle->WriteBool(emulate_greasemonkey());
129  pickle->WriteBool(match_all_frames());
130  pickle->WriteBool(is_incognito_enabled());
131
132  PickleGlobs(pickle, globs_);
133  PickleGlobs(pickle, exclude_globs_);
134  PickleURLPatternSet(pickle, url_set_);
135  PickleURLPatternSet(pickle, exclude_url_set_);
136  PickleScripts(pickle, js_scripts_);
137  PickleScripts(pickle, css_scripts_);
138}
139
140void UserScript::PickleGlobs(::Pickle* pickle,
141                             const std::vector<std::string>& globs) const {
142  pickle->WriteUInt64(globs.size());
143  for (std::vector<std::string>::const_iterator glob = globs.begin();
144       glob != globs.end(); ++glob) {
145    pickle->WriteString(*glob);
146  }
147}
148
149void UserScript::PickleURLPatternSet(::Pickle* pickle,
150                                     const URLPatternSet& pattern_list) const {
151  pickle->WriteUInt64(pattern_list.patterns().size());
152  for (URLPatternSet::const_iterator pattern = pattern_list.begin();
153       pattern != pattern_list.end(); ++pattern) {
154    pickle->WriteInt(pattern->valid_schemes());
155    pickle->WriteString(pattern->GetAsString());
156  }
157}
158
159void UserScript::PickleScripts(::Pickle* pickle,
160                               const FileList& scripts) const {
161  pickle->WriteUInt64(scripts.size());
162  for (FileList::const_iterator file = scripts.begin();
163       file != scripts.end(); ++file) {
164    file->Pickle(pickle);
165  }
166}
167
168void UserScript::Unpickle(const ::Pickle& pickle, PickleIterator* iter) {
169  // Read the run location.
170  int run_location = 0;
171  CHECK(pickle.ReadInt(iter, &run_location));
172  CHECK(run_location >= 0 && run_location < RUN_LOCATION_LAST);
173  run_location_ = static_cast<RunLocation>(run_location);
174
175  CHECK(pickle.ReadString(iter, &extension_id_));
176  CHECK(pickle.ReadBool(iter, &emulate_greasemonkey_));
177  CHECK(pickle.ReadBool(iter, &match_all_frames_));
178  CHECK(pickle.ReadBool(iter, &incognito_enabled_));
179
180  UnpickleGlobs(pickle, iter, &globs_);
181  UnpickleGlobs(pickle, iter, &exclude_globs_);
182  UnpickleURLPatternSet(pickle, iter, &url_set_);
183  UnpickleURLPatternSet(pickle, iter, &exclude_url_set_);
184  UnpickleScripts(pickle, iter, &js_scripts_);
185  UnpickleScripts(pickle, iter, &css_scripts_);
186}
187
188void UserScript::UnpickleGlobs(const ::Pickle& pickle, PickleIterator* iter,
189                               std::vector<std::string>* globs) {
190  uint64 num_globs = 0;
191  CHECK(pickle.ReadUInt64(iter, &num_globs));
192  globs->clear();
193  for (uint64 i = 0; i < num_globs; ++i) {
194    std::string glob;
195    CHECK(pickle.ReadString(iter, &glob));
196    globs->push_back(glob);
197  }
198}
199
200void UserScript::UnpickleURLPatternSet(const ::Pickle& pickle,
201                                       PickleIterator* iter,
202                                       URLPatternSet* pattern_list) {
203  uint64 num_patterns = 0;
204  CHECK(pickle.ReadUInt64(iter, &num_patterns));
205
206  pattern_list->ClearPatterns();
207  for (uint64 i = 0; i < num_patterns; ++i) {
208    int valid_schemes;
209    CHECK(pickle.ReadInt(iter, &valid_schemes));
210
211    std::string pattern_str;
212    CHECK(pickle.ReadString(iter, &pattern_str));
213
214    URLPattern pattern(kValidUserScriptSchemes);
215    URLPattern::ParseResult result = pattern.Parse(pattern_str);
216    CHECK(URLPattern::PARSE_SUCCESS == result) <<
217        URLPattern::GetParseResultString(result) << " " << pattern_str.c_str();
218
219    pattern.SetValidSchemes(valid_schemes);
220    pattern_list->AddPattern(pattern);
221  }
222}
223
224void UserScript::UnpickleScripts(const ::Pickle& pickle, PickleIterator* iter,
225                                 FileList* scripts) {
226  uint64 num_files = 0;
227  CHECK(pickle.ReadUInt64(iter, &num_files));
228  scripts->clear();
229  for (uint64 i = 0; i < num_files; ++i) {
230    File file;
231    file.Unpickle(pickle, iter);
232    scripts->push_back(file);
233  }
234}
235
236}  // namespace extensions
237