OLD | NEW |
| (Empty) |
1 // Copyright (c) 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 "chrome/test/chromedriver/web_view_impl.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/json/json_writer.h" | |
9 #include "base/logging.h" | |
10 #include "base/stringprintf.h" | |
11 #include "base/values.h" | |
12 #include "chrome/test/chromedriver/devtools_client_impl.h" | |
13 #include "chrome/test/chromedriver/dom_tracker.h" | |
14 #include "chrome/test/chromedriver/frame_tracker.h" | |
15 #include "chrome/test/chromedriver/javascript_dialog_manager.h" | |
16 #include "chrome/test/chromedriver/js.h" | |
17 #include "chrome/test/chromedriver/navigation_tracker.h" | |
18 #include "chrome/test/chromedriver/status.h" | |
19 #include "chrome/test/chromedriver/ui_events.h" | |
20 #include "chrome/test/chromedriver/web_view_delegate.h" | |
21 | |
22 namespace { | |
23 | |
24 Status GetContextIdForFrame(FrameTracker* tracker, | |
25 const std::string& frame, | |
26 int* context_id) { | |
27 if (frame.empty()) { | |
28 *context_id = 0; | |
29 return Status(kOk); | |
30 } | |
31 Status status = tracker->GetContextIdForFrame(frame, context_id); | |
32 if (status.IsError()) | |
33 return status; | |
34 return Status(kOk); | |
35 } | |
36 | |
37 const char* GetAsString(MouseEventType type) { | |
38 switch (type) { | |
39 case kPressedMouseEventType: | |
40 return "mousePressed"; | |
41 case kReleasedMouseEventType: | |
42 return "mouseReleased"; | |
43 case kMovedMouseEventType: | |
44 return "mouseMoved"; | |
45 default: | |
46 return ""; | |
47 } | |
48 } | |
49 | |
50 const char* GetAsString(MouseButton button) { | |
51 switch (button) { | |
52 case kLeftMouseButton: | |
53 return "left"; | |
54 case kMiddleMouseButton: | |
55 return "middle"; | |
56 case kRightMouseButton: | |
57 return "right"; | |
58 case kNoneMouseButton: | |
59 return "none"; | |
60 default: | |
61 return ""; | |
62 } | |
63 } | |
64 | |
65 Status IsNotPendingNavigation(NavigationTracker* tracker, | |
66 const std::string& frame_id, | |
67 bool* is_not_pending) { | |
68 bool is_pending; | |
69 Status status = tracker->IsPendingNavigation(frame_id, &is_pending); | |
70 if (status.IsError()) | |
71 return status; | |
72 *is_not_pending = !is_pending; | |
73 return Status(kOk); | |
74 } | |
75 | |
76 const char* GetAsString(KeyEventType type) { | |
77 switch (type) { | |
78 case kKeyDownEventType: | |
79 return "keyDown"; | |
80 case kKeyUpEventType: | |
81 return "keyUp"; | |
82 case kRawKeyDownEventType: | |
83 return "rawKeyDown"; | |
84 case kCharEventType: | |
85 return "char"; | |
86 default: | |
87 return ""; | |
88 } | |
89 } | |
90 | |
91 } // namespace | |
92 | |
93 WebViewImpl::WebViewImpl(const std::string& id, | |
94 DevToolsClient* client, | |
95 WebViewDelegate* delegate, | |
96 const CloserFunc& closer_func) | |
97 : id_(id), | |
98 dom_tracker_(new DomTracker(client)), | |
99 frame_tracker_(new FrameTracker(client)), | |
100 navigation_tracker_(new NavigationTracker(client)), | |
101 dialog_manager_(new JavaScriptDialogManager(client)), | |
102 client_(client), | |
103 delegate_(delegate), | |
104 closer_func_(closer_func) {} | |
105 | |
106 WebViewImpl::~WebViewImpl() {} | |
107 | |
108 std::string WebViewImpl::GetId() { | |
109 return id_; | |
110 } | |
111 | |
112 Status WebViewImpl::ConnectIfNecessary() { | |
113 return client_->ConnectIfNecessary(); | |
114 } | |
115 | |
116 Status WebViewImpl::Close() { | |
117 Status status = closer_func_.Run(); | |
118 if (status.IsOk()) | |
119 delegate_->OnWebViewClose(this); | |
120 return status; | |
121 } | |
122 | |
123 Status WebViewImpl::Load(const std::string& url) { | |
124 base::DictionaryValue params; | |
125 params.SetString("url", url); | |
126 return client_->SendCommand("Page.navigate", params); | |
127 } | |
128 | |
129 Status WebViewImpl::Reload() { | |
130 base::DictionaryValue params; | |
131 params.SetBoolean("ignoreCache", false); | |
132 return client_->SendCommand("Page.reload", params); | |
133 } | |
134 | |
135 Status WebViewImpl::EvaluateScript(const std::string& frame, | |
136 const std::string& expression, | |
137 scoped_ptr<base::Value>* result) { | |
138 int context_id; | |
139 Status status = GetContextIdForFrame(frame_tracker_.get(), frame, | |
140 &context_id); | |
141 if (status.IsError()) | |
142 return status; | |
143 return internal::EvaluateScriptAndGetValue( | |
144 client_.get(), context_id, expression, result); | |
145 } | |
146 | |
147 Status WebViewImpl::CallFunction(const std::string& frame, | |
148 const std::string& function, | |
149 const base::ListValue& args, | |
150 scoped_ptr<base::Value>* result) { | |
151 std::string json; | |
152 base::JSONWriter::Write(&args, &json); | |
153 std::string expression = base::StringPrintf( | |
154 "(%s).apply(null, [%s, %s])", | |
155 kCallFunctionScript, | |
156 function.c_str(), | |
157 json.c_str()); | |
158 scoped_ptr<base::Value> temp_result; | |
159 Status status = EvaluateScript(frame, expression, &temp_result); | |
160 if (status.IsError()) | |
161 return status; | |
162 | |
163 return internal::ParseCallFunctionResult(*temp_result, result); | |
164 } | |
165 | |
166 Status WebViewImpl::GetFrameByFunction(const std::string& frame, | |
167 const std::string& function, | |
168 const base::ListValue& args, | |
169 std::string* out_frame) { | |
170 int context_id; | |
171 Status status = GetContextIdForFrame(frame_tracker_.get(), frame, | |
172 &context_id); | |
173 if (status.IsError()) | |
174 return status; | |
175 int node_id; | |
176 status = internal::GetNodeIdFromFunction( | |
177 client_.get(), context_id, function, args, &node_id); | |
178 if (status.IsError()) | |
179 return status; | |
180 return dom_tracker_->GetFrameIdForNode(node_id, out_frame); | |
181 } | |
182 | |
183 Status WebViewImpl::DispatchMouseEvents(const std::list<MouseEvent>& events) { | |
184 for (std::list<MouseEvent>::const_iterator it = events.begin(); | |
185 it != events.end(); ++it) { | |
186 base::DictionaryValue params; | |
187 params.SetString("type", GetAsString(it->type)); | |
188 params.SetInteger("x", it->x); | |
189 params.SetInteger("y", it->y); | |
190 params.SetString("button", GetAsString(it->button)); | |
191 params.SetInteger("clickCount", it->click_count); | |
192 Status status = client_->SendCommand("Input.dispatchMouseEvent", params); | |
193 if (status.IsError()) | |
194 return status; | |
195 } | |
196 return Status(kOk); | |
197 } | |
198 | |
199 Status WebViewImpl::DispatchKeyEvents(const std::list<KeyEvent>& events) { | |
200 for (std::list<KeyEvent>::const_iterator it = events.begin(); | |
201 it != events.end(); ++it) { | |
202 base::DictionaryValue params; | |
203 params.SetString("type", GetAsString(it->type)); | |
204 if (it->modifiers & kNumLockKeyModifierMask) { | |
205 params.SetBoolean("isKeypad", true); | |
206 params.SetInteger("modifiers", | |
207 it->modifiers & ~kNumLockKeyModifierMask); | |
208 } else { | |
209 params.SetInteger("modifiers", it->modifiers); | |
210 } | |
211 params.SetString("text", it->modified_text); | |
212 params.SetString("unmodifiedText", it->unmodified_text); | |
213 params.SetInteger("nativeVirtualKeyCode", it->key_code); | |
214 params.SetInteger("windowsVirtualKeyCode", it->key_code); | |
215 Status status = client_->SendCommand("Input.dispatchKeyEvent", params); | |
216 if (status.IsError()) | |
217 return status; | |
218 } | |
219 return Status(kOk); | |
220 } | |
221 | |
222 Status WebViewImpl::GetCookies(scoped_ptr<base::ListValue>* cookies) { | |
223 base::DictionaryValue params; | |
224 scoped_ptr<base::DictionaryValue> result; | |
225 Status status = client_->SendCommandAndGetResult( | |
226 "Page.getCookies", params, &result); | |
227 if (status.IsError()) | |
228 return status; | |
229 base::ListValue* cookies_tmp; | |
230 if (!result->GetList("cookies", &cookies_tmp)) | |
231 return Status(kUnknownError, "DevTools didn't return cookies"); | |
232 cookies->reset(cookies_tmp->DeepCopy()); | |
233 return Status(kOk); | |
234 } | |
235 | |
236 Status WebViewImpl::DeleteCookie(const std::string& name, | |
237 const std::string& url) { | |
238 base::DictionaryValue params; | |
239 params.SetString("cookieName", name); | |
240 params.SetString("url", url); | |
241 return client_->SendCommand("Page.deleteCookie", params); | |
242 } | |
243 | |
244 Status WebViewImpl::WaitForPendingNavigations(const std::string& frame_id) { | |
245 std::string full_frame_id(frame_id); | |
246 if (full_frame_id.empty()) { | |
247 Status status = GetMainFrame(&full_frame_id); | |
248 if (status.IsError()) | |
249 return status; | |
250 } | |
251 return client_->HandleEventsUntil( | |
252 base::Bind(IsNotPendingNavigation, navigation_tracker_.get(), | |
253 full_frame_id)); | |
254 } | |
255 | |
256 Status WebViewImpl::IsPendingNavigation(const std::string& frame_id, | |
257 bool* is_pending) { | |
258 std::string full_frame_id(frame_id); | |
259 if (full_frame_id.empty()) { | |
260 Status status = GetMainFrame(&full_frame_id); | |
261 if (status.IsError()) | |
262 return status; | |
263 } | |
264 return navigation_tracker_->IsPendingNavigation(frame_id, is_pending); | |
265 } | |
266 | |
267 Status WebViewImpl::GetMainFrame(std::string* out_frame) { | |
268 base::DictionaryValue params; | |
269 scoped_ptr<base::DictionaryValue> result; | |
270 Status status = client_->SendCommandAndGetResult( | |
271 "Page.getResourceTree", params, &result); | |
272 if (status.IsError()) | |
273 return status; | |
274 if (!result->GetString("frameTree.frame.id", out_frame)) | |
275 return Status(kUnknownError, "missing 'frameTree.frame.id' in response"); | |
276 return Status(kOk); | |
277 } | |
278 | |
279 JavaScriptDialogManager* WebViewImpl::GetJavaScriptDialogManager() { | |
280 return dialog_manager_.get(); | |
281 } | |
282 | |
283 Status WebViewImpl::CaptureScreenshot(std::string* screenshot) { | |
284 base::DictionaryValue params; | |
285 scoped_ptr<base::DictionaryValue> result; | |
286 Status status = client_->SendCommandAndGetResult( | |
287 "Page.captureScreenshot", params, &result); | |
288 if (status.IsError()) | |
289 return status; | |
290 if (!result->GetString("data", screenshot)) | |
291 return Status(kUnknownError, "expected string 'data' in response"); | |
292 return Status(kOk); | |
293 } | |
294 | |
295 namespace internal { | |
296 | |
297 Status EvaluateScript(DevToolsClient* client, | |
298 int context_id, | |
299 const std::string& expression, | |
300 EvaluateScriptReturnType return_type, | |
301 scoped_ptr<base::DictionaryValue>* result) { | |
302 base::DictionaryValue params; | |
303 params.SetString("expression", expression); | |
304 if (context_id) | |
305 params.SetInteger("contextId", context_id); | |
306 params.SetBoolean("returnByValue", return_type == ReturnByValue); | |
307 scoped_ptr<base::DictionaryValue> cmd_result; | |
308 Status status = client->SendCommandAndGetResult( | |
309 "Runtime.evaluate", params, &cmd_result); | |
310 if (status.IsError()) | |
311 return status; | |
312 | |
313 bool was_thrown; | |
314 if (!cmd_result->GetBoolean("wasThrown", &was_thrown)) | |
315 return Status(kUnknownError, "Runtime.evaluate missing 'wasThrown'"); | |
316 if (was_thrown) { | |
317 std::string description = "unknown"; | |
318 cmd_result->GetString("result.description", &description); | |
319 return Status(kUnknownError, | |
320 "Runtime.evaluate threw exception: " + description); | |
321 } | |
322 | |
323 base::DictionaryValue* unscoped_result; | |
324 if (!cmd_result->GetDictionary("result", &unscoped_result)) | |
325 return Status(kUnknownError, "evaluate missing dictionary 'result'"); | |
326 result->reset(unscoped_result->DeepCopy()); | |
327 return Status(kOk); | |
328 } | |
329 | |
330 Status EvaluateScriptAndGetObject(DevToolsClient* client, | |
331 int context_id, | |
332 const std::string& expression, | |
333 std::string* object_id) { | |
334 scoped_ptr<base::DictionaryValue> result; | |
335 Status status = EvaluateScript(client, context_id, expression, ReturnByObject, | |
336 &result); | |
337 if (status.IsError()) | |
338 return status; | |
339 if (!result->GetString("objectId", object_id)) | |
340 return Status(kUnknownError, "evaluate missing string 'objectId'"); | |
341 return Status(kOk); | |
342 } | |
343 | |
344 Status EvaluateScriptAndGetValue(DevToolsClient* client, | |
345 int context_id, | |
346 const std::string& expression, | |
347 scoped_ptr<base::Value>* result) { | |
348 scoped_ptr<base::DictionaryValue> temp_result; | |
349 Status status = EvaluateScript(client, context_id, expression, ReturnByValue, | |
350 &temp_result); | |
351 if (status.IsError()) | |
352 return status; | |
353 | |
354 std::string type; | |
355 if (!temp_result->GetString("type", &type)) | |
356 return Status(kUnknownError, "Runtime.evaluate missing string 'type'"); | |
357 | |
358 if (type == "undefined") { | |
359 result->reset(base::Value::CreateNullValue()); | |
360 } else { | |
361 base::Value* value; | |
362 if (!temp_result->Get("value", &value)) | |
363 return Status(kUnknownError, "Runtime.evaluate missing 'value'"); | |
364 result->reset(value->DeepCopy()); | |
365 } | |
366 return Status(kOk); | |
367 } | |
368 | |
369 Status ParseCallFunctionResult(const base::Value& temp_result, | |
370 scoped_ptr<base::Value>* result) { | |
371 const base::DictionaryValue* dict; | |
372 if (!temp_result.GetAsDictionary(&dict)) | |
373 return Status(kUnknownError, "call function result must be a dictionary"); | |
374 int status_code; | |
375 if (!dict->GetInteger("status", &status_code)) { | |
376 return Status(kUnknownError, | |
377 "call function result missing int 'status'"); | |
378 } | |
379 if (status_code != kOk) { | |
380 std::string message; | |
381 dict->GetString("value", &message); | |
382 return Status(static_cast<StatusCode>(status_code), message); | |
383 } | |
384 const base::Value* unscoped_value; | |
385 if (!dict->Get("value", &unscoped_value)) { | |
386 return Status(kUnknownError, | |
387 "call function result missing 'value'"); | |
388 } | |
389 result->reset(unscoped_value->DeepCopy()); | |
390 return Status(kOk); | |
391 } | |
392 | |
393 Status GetNodeIdFromFunction(DevToolsClient* client, | |
394 int context_id, | |
395 const std::string& function, | |
396 const base::ListValue& args, | |
397 int* node_id) { | |
398 std::string json; | |
399 base::JSONWriter::Write(&args, &json); | |
400 std::string expression = base::StringPrintf( | |
401 "(%s).apply(null, [%s, %s, true])", | |
402 kCallFunctionScript, | |
403 function.c_str(), | |
404 json.c_str()); | |
405 | |
406 std::string element_id; | |
407 Status status = internal::EvaluateScriptAndGetObject( | |
408 client, context_id, expression, &element_id); | |
409 if (status.IsError()) | |
410 return status; | |
411 | |
412 scoped_ptr<base::DictionaryValue> cmd_result; | |
413 { | |
414 base::DictionaryValue params; | |
415 params.SetString("objectId", element_id); | |
416 status = client->SendCommandAndGetResult( | |
417 "DOM.requestNode", params, &cmd_result); | |
418 } | |
419 { | |
420 // Release the remote object before doing anything else. | |
421 base::DictionaryValue params; | |
422 params.SetString("objectId", element_id); | |
423 Status release_status = | |
424 client->SendCommand("Runtime.releaseObject", params); | |
425 if (release_status.IsError()) { | |
426 LOG(ERROR) << "Failed to release remote object: " | |
427 << release_status.message(); | |
428 } | |
429 } | |
430 if (status.IsError()) | |
431 return status; | |
432 | |
433 if (!cmd_result->GetInteger("nodeId", node_id)) | |
434 return Status(kUnknownError, "DOM.requestNode missing int 'nodeId'"); | |
435 return Status(kOk); | |
436 } | |
437 | |
438 } // namespace internal | |
OLD | NEW |