1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
// Copyright (c) 2009 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/renderer/user_script_slave.h"
#include "app/resource_bundle.h"
#include "base/histogram.h"
#include "base/logging.h"
#include "base/perftimer.h"
#include "base/pickle.h"
#include "base/shared_memory.h"
#include "base/string_util.h"
#include "googleurl/src/gurl.h"
#include "webkit/api/public/WebScriptSource.h"
#include "webkit/glue/webframe.h"
#include "grit/renderer_resources.h"
using WebKit::WebScriptSource;
using WebKit::WebString;
// These two strings are injected before and after the Greasemonkey API and
// user script to wrap it in an anonymous scope.
static const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
static const char kUserScriptTail[] = "\n})(window);";
// Creates a convenient reference to a content script's parent extension.
static const char kInitExtension[] =
"chrome.extension = new chrome.Extension('%s')";
UserScriptSlave::UserScriptSlave()
: shared_memory_(NULL),
script_deleter_(&scripts_),
user_script_start_line_(0) {
// TODO: Only windows supports resources and only windows supports user
// scrips, so only load the Greasemonkey API on windows. Fix this when
// better cross platofrm support is available.
#if defined(OS_WIN)
api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_GREASEMONKEY_API_JS);
#endif
// Count the number of lines that will be injected before the user script.
StringPiece::size_type pos = 0;
while ((pos = api_js_.find('\n', pos)) != StringPiece::npos) {
user_script_start_line_++;
pos++;
}
// NOTE: There is actually one extra line in the injected script because the
// function header includes a newline as well. But WebKit expects the
// numbering to be one-based, not zero-based, so actually *not* accounting for
// this extra line ends us up with the right offset.
}
bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
scripts_.clear();
// Create the shared memory object (read only).
shared_memory_.reset(new base::SharedMemory(shared_memory, true));
if (!shared_memory_.get())
return false;
// First get the size of the memory block.
if (!shared_memory_->Map(sizeof(Pickle::Header)))
return false;
Pickle::Header* pickle_header =
reinterpret_cast<Pickle::Header*>(shared_memory_->memory());
// Now map in the rest of the block.
int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size;
shared_memory_->Unmap();
if (!shared_memory_->Map(pickle_size))
return false;
// Unpickle scripts.
void* iter = NULL;
size_t num_scripts = 0;
Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()),
pickle_size);
pickle.ReadSize(&iter, &num_scripts);
scripts_.reserve(num_scripts);
for (size_t i = 0; i < num_scripts; ++i) {
scripts_.push_back(new UserScript());
UserScript* script = scripts_.back();
script->Unpickle(pickle, &iter);
// Note that this is a pointer into shared memory. We don't own it. It gets
// cleared up when the last renderer or browser process drops their
// reference to the shared memory.
for (size_t j = 0; j < script->js_scripts().size(); ++j) {
const char* body = NULL;
int body_length = 0;
CHECK(pickle.ReadData(&iter, &body, &body_length));
script->js_scripts()[j].set_external_content(
StringPiece(body, body_length));
}
for (size_t j = 0; j < script->css_scripts().size(); ++j) {
const char* body = NULL;
int body_length = 0;
CHECK(pickle.ReadData(&iter, &body, &body_length));
script->css_scripts()[j].set_external_content(
StringPiece(body, body_length));
}
}
return true;
}
bool UserScriptSlave::InjectScripts(WebFrame* frame,
UserScript::RunLocation location) {
PerfTimer timer;
int num_matched = 0;
for (size_t i = 0; i < scripts_.size(); ++i) {
std::vector<WebScriptSource> sources;
UserScript* script = scripts_[i];
if (!script->MatchesUrl(frame->GetURL()))
continue; // This frame doesn't match the script url pattern, skip it.
++num_matched;
// CSS files are always injected on document start before js scripts.
if (location == UserScript::DOCUMENT_START) {
for (size_t j = 0; j < script->css_scripts().size(); ++j) {
UserScript::File& file = script->css_scripts()[j];
frame->InsertCSSStyles(file.GetContent().as_string());
}
}
if (script->run_location() == location) {
for (size_t j = 0; j < script->js_scripts().size(); ++j) {
UserScript::File &file = script->js_scripts()[j];
std::string content = file.GetContent().as_string();
// We add this dumb function wrapper for standalone user script to
// emulate what Greasemonkey does.
if (script->is_standalone()) {
content.insert(0, kUserScriptHead);
content += kUserScriptTail;
}
sources.push_back(WebScriptSource(
WebString::fromUTF8(content.c_str(), content.length()),
file.url()));
}
}
if (!sources.empty()) {
if (script->is_standalone()) {
// For standalone scripts, we try to emulate the Greasemonkey API.
sources.insert(sources.begin(),
WebScriptSource(WebString::fromUTF8(api_js_.as_string())));
} else {
// Setup chrome.self to contain an Extension object with the correct
// ID.
sources.insert(sources.begin(),
WebScriptSource(WebString::fromUTF8(
StringPrintf(kInitExtension, script->extension_id().c_str()))));
}
frame->ExecuteScriptInNewContext(&sources.front(), sources.size());
}
}
// Log debug info.
if (location == UserScript::DOCUMENT_START) {
HISTOGRAM_COUNTS_100("UserScripts:DocStart:Count", num_matched);
HISTOGRAM_TIMES("UserScripts:DocStart:Time", timer.Elapsed());
} else {
HISTOGRAM_COUNTS_100("UserScripts:DocEnd:Count", num_matched);
HISTOGRAM_TIMES("UserScripts:DocEnd:Time", timer.Elapsed());
}
LOG(INFO) << "Injected " << num_matched << " user scripts into " <<
frame->GetURL().spec();
return true;
}
|