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 "extensions/renderer/user_script_injector.h"
6
7#include <vector>
8
9#include "base/lazy_instance.h"
10#include "content/public/common/url_constants.h"
11#include "extensions/common/extension.h"
12#include "extensions/common/permissions/permissions_data.h"
13#include "extensions/renderer/script_context.h"
14#include "extensions/renderer/scripts_run_info.h"
15#include "grit/extensions_renderer_resources.h"
16#include "third_party/WebKit/public/web/WebDocument.h"
17#include "third_party/WebKit/public/web/WebFrame.h"
18#include "third_party/WebKit/public/web/WebScriptSource.h"
19#include "ui/base/resource/resource_bundle.h"
20#include "url/gurl.h"
21
22namespace extensions {
23
24namespace {
25
26// These two strings are injected before and after the Greasemonkey API and
27// user script to wrap it in an anonymous scope.
28const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
29const char kUserScriptTail[] = "\n})(window);";
30
31// Greasemonkey API source that is injected with the scripts.
32struct GreasemonkeyApiJsString {
33  GreasemonkeyApiJsString();
34  blink::WebScriptSource GetSource() const;
35
36 private:
37  std::string source_;
38};
39
40// The below constructor, monstrous as it is, just makes a WebScriptSource from
41// the GreasemonkeyApiJs resource.
42GreasemonkeyApiJsString::GreasemonkeyApiJsString()
43    : source_(ResourceBundle::GetSharedInstance()
44                  .GetRawDataResource(IDR_GREASEMONKEY_API_JS)
45                  .as_string()) {
46}
47
48blink::WebScriptSource GreasemonkeyApiJsString::GetSource() const {
49  return blink::WebScriptSource(blink::WebString::fromUTF8(source_));
50}
51
52base::LazyInstance<GreasemonkeyApiJsString> g_greasemonkey_api =
53    LAZY_INSTANCE_INITIALIZER;
54
55}  // namespace
56
57UserScriptInjector::UserScriptInjector(
58    const UserScript* script,
59    UserScriptSet* script_list,
60    bool is_declarative)
61    : script_(script),
62      script_id_(script_->id()),
63      extension_id_(script_->extension_id()),
64      is_declarative_(is_declarative),
65      user_script_set_observer_(this) {
66  user_script_set_observer_.Add(script_list);
67}
68
69UserScriptInjector::~UserScriptInjector() {
70}
71
72void UserScriptInjector::OnUserScriptsUpdated(
73    const std::set<std::string>& changed_extensions,
74    const std::vector<UserScript*>& scripts) {
75  // If the extension causing this injection changed, then this injection
76  // will be removed, and there's no guarantee the backing script still exists.
77  if (changed_extensions.count(extension_id_) > 0)
78    return;
79
80  for (std::vector<UserScript*>::const_iterator iter = scripts.begin();
81       iter != scripts.end();
82       ++iter) {
83    // We need to compare to |script_id_| (and not to script_->id()) because the
84    // old |script_| may be deleted by now.
85    if ((*iter)->id() == script_id_) {
86      script_ = *iter;
87      break;
88    }
89  }
90}
91
92UserScript::InjectionType UserScriptInjector::script_type() const {
93  return UserScript::CONTENT_SCRIPT;
94}
95
96bool UserScriptInjector::ShouldExecuteInChildFrames() const {
97  return false;
98}
99
100bool UserScriptInjector::ShouldExecuteInMainWorld() const {
101  return false;
102}
103
104bool UserScriptInjector::IsUserGesture() const {
105  return false;
106}
107
108bool UserScriptInjector::ExpectsResults() const {
109  return false;
110}
111
112bool UserScriptInjector::ShouldInjectJs(
113    UserScript::RunLocation run_location) const {
114  return script_->run_location() == run_location &&
115         !script_->js_scripts().empty();
116}
117
118bool UserScriptInjector::ShouldInjectCss(
119    UserScript::RunLocation run_location) const {
120  return run_location == UserScript::DOCUMENT_START &&
121         !script_->css_scripts().empty();
122}
123
124PermissionsData::AccessType UserScriptInjector::CanExecuteOnFrame(
125    const Extension* extension,
126    blink::WebFrame* web_frame,
127    int tab_id,
128    const GURL& top_url) const {
129  // If we don't have a tab id, we have no UI surface to ask for user consent.
130  // For now, we treat this as an automatic allow.
131  if (tab_id == -1)
132    return PermissionsData::ACCESS_ALLOWED;
133
134  GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL(
135      web_frame, web_frame->document().url(), script_->match_about_blank());
136
137  // Declarative user scripts use "page access" (from "permissions" section in
138  // manifest) whereas non-declarative user scripts use custom
139  // "content script access" logic.
140  if (is_declarative_) {
141    return extension->permissions_data()->GetPageAccess(
142        extension,
143        effective_document_url,
144        top_url,
145        tab_id,
146        -1,  // no process id
147        NULL /* ignore error */);
148  } else {
149    return extension->permissions_data()->GetContentScriptAccess(
150        extension,
151        effective_document_url,
152        top_url,
153        tab_id,
154        -1,  // no process id
155        NULL /* ignore error */);
156  }
157}
158
159std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
160    UserScript::RunLocation run_location) const {
161  DCHECK_EQ(script_->run_location(), run_location);
162
163  std::vector<blink::WebScriptSource> sources;
164  const UserScript::FileList& js_scripts = script_->js_scripts();
165  bool is_standalone_or_emulate_greasemonkey =
166      script_->is_standalone() || script_->emulate_greasemonkey();
167
168  for (UserScript::FileList::const_iterator iter = js_scripts.begin();
169       iter != js_scripts.end();
170       ++iter) {
171    std::string content = iter->GetContent().as_string();
172
173    // We add this dumb function wrapper for standalone user script to
174    // emulate what Greasemonkey does.
175    // TODO(aa): I think that maybe "is_standalone" scripts don't exist
176    // anymore. Investigate.
177    if (is_standalone_or_emulate_greasemonkey) {
178      content.insert(0, kUserScriptHead);
179      content += kUserScriptTail;
180    }
181    sources.push_back(blink::WebScriptSource(
182        blink::WebString::fromUTF8(content), iter->url()));
183  }
184
185  // Emulate Greasemonkey API for scripts that were converted to extensions
186  // and "standalone" user scripts.
187  if (is_standalone_or_emulate_greasemonkey)
188    sources.insert(sources.begin(), g_greasemonkey_api.Get().GetSource());
189
190  return sources;
191}
192
193std::vector<std::string> UserScriptInjector::GetCssSources(
194    UserScript::RunLocation run_location) const {
195  DCHECK_EQ(UserScript::DOCUMENT_START, run_location);
196
197  std::vector<std::string> sources;
198  const UserScript::FileList& css_scripts = script_->css_scripts();
199  for (UserScript::FileList::const_iterator iter = css_scripts.begin();
200       iter != css_scripts.end();
201       ++iter) {
202    sources.push_back(iter->GetContent().as_string());
203  }
204  return sources;
205}
206
207void UserScriptInjector::OnInjectionComplete(
208    scoped_ptr<base::ListValue> execution_results,
209    ScriptsRunInfo* scripts_run_info,
210    UserScript::RunLocation run_location) {
211  if (ShouldInjectJs(run_location)) {
212    const UserScript::FileList& js_scripts = script_->js_scripts();
213    scripts_run_info->num_js += js_scripts.size();
214    for (UserScript::FileList::const_iterator iter = js_scripts.begin();
215         iter != js_scripts.end();
216         ++iter) {
217      scripts_run_info->executing_scripts[extension_id_].insert(
218          iter->url().path());
219    }
220  }
221
222  if (ShouldInjectCss(run_location))
223    scripts_run_info->num_css += script_->css_scripts().size();
224}
225
226void UserScriptInjector::OnWillNotInject(InjectFailureReason reason) {
227}
228
229}  // namespace extensions
230