OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/renderer/automation/automation_renderer_helper.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/basictypes.h" | |
10 #include "base/json/json_writer.h" | |
11 #include "base/stringprintf.h" | |
12 #include "base/strings/string_split.h" | |
13 #include "base/utf_string_conversions.h" | |
14 #include "base/values.h" | |
15 #include "chrome/common/automation_events.h" | |
16 #include "chrome/common/automation_messages.h" | |
17 #include "content/public/renderer/render_view.h" | |
18 #include "content/public/renderer/v8_value_converter.h" | |
19 #include "ipc/ipc_channel.h" | |
20 #include "ipc/ipc_message.h" | |
21 #include "skia/ext/platform_canvas.h" | |
22 #include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h" | |
23 #include "third_party/WebKit/Source/Platform/chromium/public/WebURL.h" | |
24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" | |
26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptSource.h" | |
27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
28 #include "ui/gfx/codec/png_codec.h" | |
29 #include "ui/gfx/point.h" | |
30 #include "ui/gfx/rect.h" | |
31 #include "v8/include/v8.h" | |
32 #include "webkit/glue/webkit_glue.h" | |
33 | |
34 #if !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS)) | |
35 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" | |
36 #endif // !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS)) | |
37 | |
38 using WebKit::WebFrame; | |
39 using WebKit::WebSize; | |
40 using WebKit::WebURL; | |
41 using WebKit::WebView; | |
42 | |
43 AutomationRendererHelper::AutomationRendererHelper( | |
44 content::RenderView* render_view) | |
45 : content::RenderViewObserver(render_view) { | |
46 } | |
47 | |
48 AutomationRendererHelper::~AutomationRendererHelper() { } | |
49 | |
50 bool AutomationRendererHelper::SnapshotEntirePage( | |
51 WebView* view, | |
52 std::vector<unsigned char>* png_data, | |
53 std::string* error_msg) { | |
54 WebFrame* frame = view->mainFrame(); | |
55 WebSize old_size = view->size(); | |
56 WebSize new_size = frame->contentsSize(); | |
57 WebSize old_scroll = frame->scrollOffset(); | |
58 bool fixed_layout_enabled = view->isFixedLayoutModeEnabled(); | |
59 WebSize fixed_size = view->fixedLayoutSize(); | |
60 | |
61 frame->setCanHaveScrollbars(false); | |
62 view->setFixedLayoutSize(old_size); | |
63 view->enableFixedLayoutMode(true); | |
64 view->resize(new_size); | |
65 view->layout(); | |
66 frame->setScrollOffset(frame->minimumScrollOffset()); | |
67 | |
68 skia::RefPtr<SkCanvas> canvas = skia::AdoptRef( | |
69 skia::CreatePlatformCanvas(new_size.width, new_size.height, true)); | |
70 | |
71 view->paint(webkit_glue::ToWebCanvas(canvas.get()), | |
72 gfx::Rect(0, 0, new_size.width, new_size.height)); | |
73 | |
74 frame->setCanHaveScrollbars(true); | |
75 view->setFixedLayoutSize(fixed_size); | |
76 view->enableFixedLayoutMode(fixed_layout_enabled); | |
77 view->resize(old_size); | |
78 view->layout(); | |
79 frame->setScrollOffset(old_scroll); | |
80 | |
81 const SkBitmap& bmp = skia::GetTopDevice(*canvas)->accessBitmap(false); | |
82 SkAutoLockPixels lock_pixels(bmp); | |
83 // EncodeBGRA uses FORMAT_SkBitmap, which doesn't work on windows for some | |
84 // cases dealing with transparency. See crbug.com/96317. Use FORMAT_BGRA. | |
85 bool encode_success = gfx::PNGCodec::Encode( | |
86 reinterpret_cast<unsigned char*>(bmp.getPixels()), | |
87 gfx::PNGCodec::FORMAT_BGRA, | |
88 gfx::Size(bmp.width(), bmp.height()), | |
89 bmp.rowBytes(), | |
90 true, // discard_transparency | |
91 std::vector<gfx::PNGCodec::Comment>(), | |
92 png_data); | |
93 if (!encode_success) | |
94 *error_msg = "failed to encode image as png"; | |
95 return encode_success; | |
96 } | |
97 | |
98 void AutomationRendererHelper::OnSnapshotEntirePage() { | |
99 std::vector<unsigned char> png_data; | |
100 std::string error_msg; | |
101 bool success = false; | |
102 if (render_view()->GetWebView()) { | |
103 success = SnapshotEntirePage( | |
104 render_view()->GetWebView(), &png_data, &error_msg); | |
105 } else { | |
106 error_msg = "cannot snapshot page because webview is null"; | |
107 } | |
108 | |
109 // Check that the image is not too large, allowing a 1kb buffer for other | |
110 // message data. | |
111 if (success && png_data.size() > IPC::Channel::kMaximumMessageSize - 1024) { | |
112 png_data.clear(); | |
113 success = false; | |
114 error_msg = "image is too large to be transferred over ipc"; | |
115 } | |
116 Send(new AutomationMsg_SnapshotEntirePageACK( | |
117 routing_id(), success, png_data, error_msg)); | |
118 } | |
119 | |
120 namespace { | |
121 | |
122 scoped_ptr<base::Value> EvaluateScriptInFrame(WebFrame* web_frame, | |
123 const std::string& script) { | |
124 v8::HandleScope handle_scope; | |
125 v8::Local<v8::Value> result = v8::Local<v8::Value>::New( | |
126 web_frame->executeScriptAndReturnValue( | |
127 WebKit::WebScriptSource(UTF8ToUTF16(script)))); | |
128 if (!result.IsEmpty()) { | |
129 v8::Local<v8::Context> context = web_frame->mainWorldScriptContext(); | |
130 v8::Context::Scope context_scope(context); | |
131 scoped_ptr<content::V8ValueConverter> converter( | |
132 content::V8ValueConverter::create()); | |
133 return scoped_ptr<base::Value>(converter->FromV8Value(result, context)); | |
134 } else { | |
135 return scoped_ptr<base::Value>(base::Value::CreateNullValue()); | |
136 } | |
137 } | |
138 | |
139 WebFrame* FrameFromXPath(WebView* view, const string16& frame_xpath) { | |
140 WebFrame* frame = view->mainFrame(); | |
141 if (frame_xpath.empty()) | |
142 return frame; | |
143 | |
144 std::vector<string16> xpaths; | |
145 base::SplitString(frame_xpath, '\n', &xpaths); | |
146 for (std::vector<string16>::const_iterator i = xpaths.begin(); | |
147 frame && i != xpaths.end(); ++i) { | |
148 frame = frame->findChildByExpression(*i); | |
149 } | |
150 return frame; | |
151 } | |
152 | |
153 bool EvaluateScriptChainHelper( | |
154 WebView* web_view, | |
155 const std::string& script, | |
156 const std::string& frame_xpath, | |
157 scoped_ptr<base::DictionaryValue>* result, | |
158 std::string* error_msg) { | |
159 WebFrame* web_frame = FrameFromXPath(web_view, UTF8ToUTF16(frame_xpath)); | |
160 if (!web_frame) { | |
161 *error_msg = "Failed to locate frame by xpath: " + frame_xpath; | |
162 return false; | |
163 } | |
164 scoped_ptr<base::Value> script_value = | |
165 EvaluateScriptInFrame(web_frame, script); | |
166 base::DictionaryValue* dict; | |
167 if (!script_value.get() || !script_value->GetAsDictionary(&dict)) { | |
168 *error_msg = "Script did not return an object"; | |
169 return false; | |
170 } | |
171 base::Value* error_value; | |
172 if (dict->Get("error", &error_value)) { | |
173 base::JSONWriter::Write(error_value, error_msg); | |
174 return false; | |
175 } | |
176 result->reset(static_cast<base::DictionaryValue*>(script_value.release())); | |
177 return true; | |
178 } | |
179 | |
180 } // namespace | |
181 | |
182 bool AutomationRendererHelper::EvaluateScriptChain( | |
183 const std::vector<ScriptEvaluationRequest>& script_chain, | |
184 scoped_ptr<base::DictionaryValue>* result, | |
185 std::string* error_msg) { | |
186 CHECK(!script_chain.empty()); | |
187 WebView* web_view = render_view()->GetWebView(); | |
188 scoped_ptr<base::DictionaryValue> temp_result; | |
189 for (size_t i = 0; i < script_chain.size(); ++i) { | |
190 std::string args_utf8 = "null"; | |
191 if (temp_result.get()) | |
192 base::JSONWriter::Write(temp_result.get(), &args_utf8); | |
193 std::string wrapper_script = base::StringPrintf( | |
194 "(function(){return %s\n}).apply(null, [%s])", | |
195 script_chain[i].script.c_str(), args_utf8.c_str()); | |
196 if (!EvaluateScriptChainHelper(web_view, wrapper_script, | |
197 script_chain[i].frame_xpath, | |
198 &temp_result, error_msg)) { | |
199 return false; | |
200 } | |
201 } | |
202 std::string result_str; | |
203 base::JSONWriter::Write(temp_result.get(), &result_str); | |
204 result->reset(temp_result.release()); | |
205 return true; | |
206 } | |
207 | |
208 bool AutomationRendererHelper::ProcessMouseEvent( | |
209 const AutomationMouseEvent& event, | |
210 std::string* error_msg) { | |
211 WebView* web_view = render_view()->GetWebView(); | |
212 if (!web_view) { | |
213 *error_msg = "Failed to process mouse event because webview does not exist"; | |
214 return false; | |
215 } | |
216 WebKit::WebMouseEvent mouse_event = event.mouse_event; | |
217 if (!event.location_script_chain.empty()) { | |
218 scoped_ptr<base::DictionaryValue> result; | |
219 if (!EvaluateScriptChain(event.location_script_chain, &result, error_msg)) | |
220 return false; | |
221 int x, y; | |
222 if (!result->GetInteger("x", &x) || | |
223 !result->GetInteger("y", &y)) { | |
224 *error_msg = "Script did not return an (x,y) location"; | |
225 return false; | |
226 } | |
227 mouse_event.x = x; | |
228 mouse_event.y = y; | |
229 } | |
230 Send(new AutomationMsg_WillProcessMouseEventAt( | |
231 routing_id(), | |
232 gfx::Point(mouse_event.x, mouse_event.y))); | |
233 web_view->handleInputEvent(mouse_event); | |
234 return true; | |
235 } | |
236 | |
237 #if !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS)) | |
238 void AutomationRendererHelper::OnHeapProfilerDump(const std::string& reason) { | |
239 if (!::IsHeapProfilerRunning()) { | |
240 return; | |
241 } | |
242 ::HeapProfilerDump(reason.c_str()); | |
243 } | |
244 #endif // !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS)) | |
245 | |
246 bool AutomationRendererHelper::OnMessageReceived(const IPC::Message& message) { | |
247 bool handled = true; | |
248 bool deserialize_success = true; | |
249 IPC_BEGIN_MESSAGE_MAP_EX(AutomationRendererHelper, message, | |
250 deserialize_success) | |
251 IPC_MESSAGE_HANDLER(AutomationMsg_SnapshotEntirePage, OnSnapshotEntirePage) | |
252 #if !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS)) | |
253 IPC_MESSAGE_HANDLER(AutomationMsg_HeapProfilerDump, OnHeapProfilerDump) | |
254 #endif // !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS)) | |
255 IPC_MESSAGE_HANDLER(AutomationMsg_ProcessMouseEvent, OnProcessMouseEvent) | |
256 IPC_MESSAGE_UNHANDLED(handled = false) | |
257 IPC_END_MESSAGE_MAP_EX() | |
258 if (!deserialize_success) { | |
259 LOG(ERROR) << "Failed to deserialize an IPC message"; | |
260 } | |
261 return handled; | |
262 } | |
263 | |
264 void AutomationRendererHelper::WillPerformClientRedirect( | |
265 WebFrame* frame, const WebURL& from, const WebURL& to, double interval, | |
266 double fire_time) { | |
267 Send(new AutomationMsg_WillPerformClientRedirect( | |
268 routing_id(), frame->identifier(), interval)); | |
269 } | |
270 | |
271 void AutomationRendererHelper::DidCancelClientRedirect(WebFrame* frame) { | |
272 Send(new AutomationMsg_DidCompleteOrCancelClientRedirect( | |
273 routing_id(), frame->identifier())); | |
274 } | |
275 | |
276 void AutomationRendererHelper::DidCompleteClientRedirect( | |
277 WebFrame* frame, const WebURL& from) { | |
278 Send(new AutomationMsg_DidCompleteOrCancelClientRedirect( | |
279 routing_id(), frame->identifier())); | |
280 } | |
281 | |
282 void AutomationRendererHelper::OnProcessMouseEvent( | |
283 const AutomationMouseEvent& event) { | |
284 std::string error_msg; | |
285 bool success = ProcessMouseEvent(event, &error_msg); | |
286 Send(new AutomationMsg_ProcessMouseEventACK( | |
287 routing_id(), success, error_msg)); | |
288 } | |
OLD | NEW |