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/browser/extensions/api/webview/webview_api.h"
6
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/extensions/api/browsing_data/browsing_data_api.h"
9 #include "chrome/browser/extensions/api/context_menus/context_menus_api.h"
10 #include "chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h"
11 #include "chrome/browser/extensions/tab_helper.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/common/extensions/api/webview.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/render_view_host.h"
16 #include "content/public/browser/storage_partition.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/common/stop_find_action.h"
19 #include "extensions/common/error_utils.h"
20 #include "third_party/WebKit/public/web/WebFindOptions.h"
21
22 using content::WebContents;
23 using extensions::api::tabs::InjectDetails;
24 using extensions::api::webview::SetPermission::Params;
25 namespace helpers = extensions::context_menus_api_helpers;
26 namespace webview = extensions::api::webview;
27
28 namespace extensions {
29
30 namespace {
MaskForKey(const char * key)31 int MaskForKey(const char* key) {
32 if (strcmp(key, extension_browsing_data_api_constants::kAppCacheKey) == 0)
33 return content::StoragePartition::REMOVE_DATA_MASK_APPCACHE;
34 if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0)
35 return content::StoragePartition::REMOVE_DATA_MASK_COOKIES;
36 if (strcmp(key, extension_browsing_data_api_constants::kFileSystemsKey) == 0)
37 return content::StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
38 if (strcmp(key, extension_browsing_data_api_constants::kIndexedDBKey) == 0)
39 return content::StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
40 if (strcmp(key, extension_browsing_data_api_constants::kLocalStorageKey) == 0)
41 return content::StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
42 if (strcmp(key, extension_browsing_data_api_constants::kWebSQLKey) == 0)
43 return content::StoragePartition::REMOVE_DATA_MASK_WEBSQL;
44 return 0;
45 }
46
47 } // namespace
48
RunAsync()49 bool WebviewExtensionFunction::RunAsync() {
50 int instance_id = 0;
51 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &instance_id));
52 WebViewGuest* guest = WebViewGuest::From(
53 render_view_host()->GetProcess()->GetID(), instance_id);
54 if (!guest)
55 return false;
56
57 return RunAsyncSafe(guest);
58 }
59
60 // TODO(lazyboy): Add checks similar to
61 // WebviewExtensionFunction::RunAsyncSafe(WebViewGuest*).
RunAsync()62 bool WebviewContextMenusCreateFunction::RunAsync() {
63 scoped_ptr<webview::ContextMenusCreate::Params> params(
64 webview::ContextMenusCreate::Params::Create(*args_));
65 EXTENSION_FUNCTION_VALIDATE(params.get());
66
67 MenuItem::Id id(
68 Profile::FromBrowserContext(browser_context())->IsOffTheRecord(),
69 MenuItem::ExtensionKey(extension_id(), params->instance_id));
70
71 if (params->create_properties.id.get()) {
72 id.string_uid = *params->create_properties.id;
73 } else {
74 // The Generated Id is added by webview_custom_bindings.js.
75 base::DictionaryValue* properties = NULL;
76 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &properties));
77 EXTENSION_FUNCTION_VALIDATE(
78 properties->GetInteger(helpers::kGeneratedIdKey, &id.uid));
79 }
80
81 bool success = extensions::context_menus_api_helpers::CreateMenuItem(
82 params->create_properties,
83 Profile::FromBrowserContext(browser_context()),
84 GetExtension(),
85 id,
86 &error_);
87
88 SendResponse(success);
89 return success;
90 }
91
RunAsyncSafe(WebViewGuest * guest)92 bool WebviewNavigateFunction::RunAsyncSafe(WebViewGuest* guest) {
93 scoped_ptr<webview::Navigate::Params> params(
94 webview::Navigate::Params::Create(*args_));
95 EXTENSION_FUNCTION_VALIDATE(params.get());
96 std::string src = params->src;
97 guest->NavigateGuest(src);
98 return true;
99 }
100
RunAsync()101 bool WebviewContextMenusUpdateFunction::RunAsync() {
102 scoped_ptr<webview::ContextMenusUpdate::Params> params(
103 webview::ContextMenusUpdate::Params::Create(*args_));
104 EXTENSION_FUNCTION_VALIDATE(params.get());
105
106 Profile* profile = Profile::FromBrowserContext(browser_context());
107 MenuItem::Id item_id(
108 profile->IsOffTheRecord(),
109 MenuItem::ExtensionKey(extension_id(), params->instance_id));
110
111 if (params->id.as_string)
112 item_id.string_uid = *params->id.as_string;
113 else if (params->id.as_integer)
114 item_id.uid = *params->id.as_integer;
115 else
116 NOTREACHED();
117
118 bool success = extensions::context_menus_api_helpers::UpdateMenuItem(
119 params->update_properties, profile, GetExtension(), item_id, &error_);
120 SendResponse(success);
121 return success;
122 }
123
RunAsync()124 bool WebviewContextMenusRemoveFunction::RunAsync() {
125 scoped_ptr<webview::ContextMenusRemove::Params> params(
126 webview::ContextMenusRemove::Params::Create(*args_));
127 EXTENSION_FUNCTION_VALIDATE(params.get());
128
129 MenuManager* menu_manager =
130 MenuManager::Get(Profile::FromBrowserContext(browser_context()));
131
132 MenuItem::Id id(
133 Profile::FromBrowserContext(browser_context())->IsOffTheRecord(),
134 MenuItem::ExtensionKey(extension_id(), params->instance_id));
135
136 if (params->menu_item_id.as_string) {
137 id.string_uid = *params->menu_item_id.as_string;
138 } else if (params->menu_item_id.as_integer) {
139 id.uid = *params->menu_item_id.as_integer;
140 } else {
141 NOTREACHED();
142 }
143
144 bool success = true;
145 MenuItem* item = menu_manager->GetItemById(id);
146 // Ensure one <webview> can't remove another's menu items.
147 if (!item || item->id().extension_key != id.extension_key) {
148 error_ = ErrorUtils::FormatErrorMessage(
149 context_menus_api_helpers::kCannotFindItemError,
150 context_menus_api_helpers::GetIDString(id));
151 success = false;
152 } else if (!menu_manager->RemoveContextMenuItem(id)) {
153 success = false;
154 }
155
156 SendResponse(success);
157 return success;
158 }
159
RunAsync()160 bool WebviewContextMenusRemoveAllFunction::RunAsync() {
161 scoped_ptr<webview::ContextMenusRemoveAll::Params> params(
162 webview::ContextMenusRemoveAll::Params::Create(*args_));
163 EXTENSION_FUNCTION_VALIDATE(params.get());
164
165 MenuManager* menu_manager =
166 MenuManager::Get(Profile::FromBrowserContext(browser_context()));
167
168 int webview_instance_id = params->instance_id;
169 menu_manager->RemoveAllContextItems(
170 MenuItem::ExtensionKey(GetExtension()->id(), webview_instance_id));
171 SendResponse(true);
172 return true;
173 }
174
WebviewClearDataFunction()175 WebviewClearDataFunction::WebviewClearDataFunction()
176 : remove_mask_(0), bad_message_(false) {}
177
~WebviewClearDataFunction()178 WebviewClearDataFunction::~WebviewClearDataFunction() {}
179
180 // Parses the |dataToRemove| argument to generate the remove mask. Sets
181 // |bad_message_| (like EXTENSION_FUNCTION_VALIDATE would if this were a bool
182 // method) if 'dataToRemove' is not present.
GetRemovalMask()183 uint32 WebviewClearDataFunction::GetRemovalMask() {
184 base::DictionaryValue* data_to_remove;
185 if (!args_->GetDictionary(2, &data_to_remove)) {
186 bad_message_ = true;
187 return 0;
188 }
189
190 uint32 remove_mask = 0;
191 for (base::DictionaryValue::Iterator i(*data_to_remove);
192 !i.IsAtEnd();
193 i.Advance()) {
194 bool selected = false;
195 if (!i.value().GetAsBoolean(&selected)) {
196 bad_message_ = true;
197 return 0;
198 }
199 if (selected)
200 remove_mask |= MaskForKey(i.key().c_str());
201 }
202
203 return remove_mask;
204 }
205
206 // TODO(lazyboy): Parameters in this extension function are similar (or a
207 // sub-set) to BrowsingDataRemoverFunction. How can we share this code?
RunAsyncSafe(WebViewGuest * guest)208 bool WebviewClearDataFunction::RunAsyncSafe(WebViewGuest* guest) {
209 // Grab the initial |options| parameter, and parse out the arguments.
210 base::DictionaryValue* options;
211 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options));
212 DCHECK(options);
213
214 // If |ms_since_epoch| isn't set, default it to 0.
215 double ms_since_epoch;
216 if (!options->GetDouble(extension_browsing_data_api_constants::kSinceKey,
217 &ms_since_epoch)) {
218 ms_since_epoch = 0;
219 }
220
221 // base::Time takes a double that represents seconds since epoch. JavaScript
222 // gives developers milliseconds, so do a quick conversion before populating
223 // the object. Also, Time::FromDoubleT converts double time 0 to empty Time
224 // object. So we need to do special handling here.
225 remove_since_ = (ms_since_epoch == 0) ?
226 base::Time::UnixEpoch() :
227 base::Time::FromDoubleT(ms_since_epoch / 1000.0);
228
229 remove_mask_ = GetRemovalMask();
230 if (bad_message_)
231 return false;
232
233 AddRef(); // Balanced below or in WebviewClearDataFunction::Done().
234
235 bool scheduled = false;
236 if (remove_mask_) {
237 scheduled = guest->ClearData(
238 remove_since_,
239 remove_mask_,
240 base::Bind(&WebviewClearDataFunction::ClearDataDone,
241 this));
242 }
243 if (!remove_mask_ || !scheduled) {
244 SendResponse(false);
245 Release(); // Balanced above.
246 return false;
247 }
248
249 // Will finish asynchronously.
250 return true;
251 }
252
ClearDataDone()253 void WebviewClearDataFunction::ClearDataDone() {
254 Release(); // Balanced in RunAsync().
255 SendResponse(true);
256 }
257
WebviewExecuteCodeFunction()258 WebviewExecuteCodeFunction::WebviewExecuteCodeFunction()
259 : guest_instance_id_(0), guest_src_(GURL::EmptyGURL()) {}
260
~WebviewExecuteCodeFunction()261 WebviewExecuteCodeFunction::~WebviewExecuteCodeFunction() {
262 }
263
Init()264 bool WebviewExecuteCodeFunction::Init() {
265 if (details_.get())
266 return true;
267
268 if (!args_->GetInteger(0, &guest_instance_id_))
269 return false;
270
271 if (!guest_instance_id_)
272 return false;
273
274 std::string src;
275 if (!args_->GetString(1, &src))
276 return false;
277
278 guest_src_ = GURL(src);
279 if (!guest_src_.is_valid())
280 return false;
281
282 base::DictionaryValue* details_value = NULL;
283 if (!args_->GetDictionary(2, &details_value))
284 return false;
285 scoped_ptr<InjectDetails> details(new InjectDetails());
286 if (!InjectDetails::Populate(*details_value, details.get()))
287 return false;
288
289 details_ = details.Pass();
290 return true;
291 }
292
ShouldInsertCSS() const293 bool WebviewExecuteCodeFunction::ShouldInsertCSS() const {
294 return false;
295 }
296
CanExecuteScriptOnPage()297 bool WebviewExecuteCodeFunction::CanExecuteScriptOnPage() {
298 return true;
299 }
300
GetScriptExecutor()301 extensions::ScriptExecutor* WebviewExecuteCodeFunction::GetScriptExecutor() {
302 WebViewGuest* guest = WebViewGuest::From(
303 render_view_host()->GetProcess()->GetID(), guest_instance_id_);
304 if (!guest)
305 return NULL;
306
307 return guest->script_executor();
308 }
309
IsWebView() const310 bool WebviewExecuteCodeFunction::IsWebView() const {
311 return true;
312 }
313
GetWebViewSrc() const314 const GURL& WebviewExecuteCodeFunction::GetWebViewSrc() const {
315 return guest_src_;
316 }
317
WebviewExecuteScriptFunction()318 WebviewExecuteScriptFunction::WebviewExecuteScriptFunction() {
319 }
320
OnExecuteCodeFinished(const std::string & error,int32 on_page_id,const GURL & on_url,const base::ListValue & result)321 void WebviewExecuteScriptFunction::OnExecuteCodeFinished(
322 const std::string& error,
323 int32 on_page_id,
324 const GURL& on_url,
325 const base::ListValue& result) {
326 if (error.empty())
327 SetResult(result.DeepCopy());
328 WebviewExecuteCodeFunction::OnExecuteCodeFinished(error, on_page_id, on_url,
329 result);
330 }
331
WebviewInsertCSSFunction()332 WebviewInsertCSSFunction::WebviewInsertCSSFunction() {
333 }
334
ShouldInsertCSS() const335 bool WebviewInsertCSSFunction::ShouldInsertCSS() const {
336 return true;
337 }
338
WebviewCaptureVisibleRegionFunction()339 WebviewCaptureVisibleRegionFunction::WebviewCaptureVisibleRegionFunction() {
340 }
341
~WebviewCaptureVisibleRegionFunction()342 WebviewCaptureVisibleRegionFunction::~WebviewCaptureVisibleRegionFunction() {
343 }
344
IsScreenshotEnabled()345 bool WebviewCaptureVisibleRegionFunction::IsScreenshotEnabled() {
346 return true;
347 }
348
GetWebContentsForID(int instance_id)349 WebContents* WebviewCaptureVisibleRegionFunction::GetWebContentsForID(
350 int instance_id) {
351 WebViewGuest* guest = WebViewGuest::From(
352 render_view_host()->GetProcess()->GetID(), instance_id);
353 return guest ? guest->guest_web_contents() : NULL;
354 }
355
OnCaptureFailure(FailureReason reason)356 void WebviewCaptureVisibleRegionFunction::OnCaptureFailure(
357 FailureReason reason) {
358 SendResponse(false);
359 }
360
WebviewSetNameFunction()361 WebviewSetNameFunction::WebviewSetNameFunction() {
362 }
363
~WebviewSetNameFunction()364 WebviewSetNameFunction::~WebviewSetNameFunction() {
365 }
366
WebviewSetZoomFunction()367 WebviewSetZoomFunction::WebviewSetZoomFunction() {
368 }
369
~WebviewSetZoomFunction()370 WebviewSetZoomFunction::~WebviewSetZoomFunction() {
371 }
372
RunAsyncSafe(WebViewGuest * guest)373 bool WebviewSetNameFunction::RunAsyncSafe(WebViewGuest* guest) {
374 scoped_ptr<webview::SetName::Params> params(
375 webview::SetName::Params::Create(*args_));
376 EXTENSION_FUNCTION_VALIDATE(params.get());
377 guest->SetName(params->frame_name);
378 SendResponse(true);
379 return true;
380 }
381
RunAsyncSafe(WebViewGuest * guest)382 bool WebviewSetZoomFunction::RunAsyncSafe(WebViewGuest* guest) {
383 scoped_ptr<webview::SetZoom::Params> params(
384 webview::SetZoom::Params::Create(*args_));
385 EXTENSION_FUNCTION_VALIDATE(params.get());
386 guest->SetZoom(params->zoom_factor);
387
388 SendResponse(true);
389 return true;
390 }
391
WebviewGetZoomFunction()392 WebviewGetZoomFunction::WebviewGetZoomFunction() {
393 }
394
~WebviewGetZoomFunction()395 WebviewGetZoomFunction::~WebviewGetZoomFunction() {
396 }
397
RunAsyncSafe(WebViewGuest * guest)398 bool WebviewGetZoomFunction::RunAsyncSafe(WebViewGuest* guest) {
399 scoped_ptr<webview::GetZoom::Params> params(
400 webview::GetZoom::Params::Create(*args_));
401 EXTENSION_FUNCTION_VALIDATE(params.get());
402
403 double zoom_factor = guest->GetZoom();
404 SetResult(base::Value::CreateDoubleValue(zoom_factor));
405 SendResponse(true);
406 return true;
407 }
408
WebviewFindFunction()409 WebviewFindFunction::WebviewFindFunction() {
410 }
411
~WebviewFindFunction()412 WebviewFindFunction::~WebviewFindFunction() {
413 }
414
RunAsyncSafe(WebViewGuest * guest)415 bool WebviewFindFunction::RunAsyncSafe(WebViewGuest* guest) {
416 scoped_ptr<webview::Find::Params> params(
417 webview::Find::Params::Create(*args_));
418 EXTENSION_FUNCTION_VALIDATE(params.get());
419
420 // Convert the std::string search_text to string16.
421 base::string16 search_text;
422 base::UTF8ToUTF16(params->search_text.c_str(),
423 params->search_text.length(),
424 &search_text);
425
426 // Set the find options to their default values.
427 blink::WebFindOptions options;
428 if (params->options) {
429 options.forward =
430 params->options->backward ? !*params->options->backward : true;
431 options.matchCase =
432 params->options->match_case ? *params->options->match_case : false;
433 }
434
435 guest->Find(search_text, options, this);
436 return true;
437 }
438
WebviewStopFindingFunction()439 WebviewStopFindingFunction::WebviewStopFindingFunction() {
440 }
441
~WebviewStopFindingFunction()442 WebviewStopFindingFunction::~WebviewStopFindingFunction() {
443 }
444
RunAsyncSafe(WebViewGuest * guest)445 bool WebviewStopFindingFunction::RunAsyncSafe(WebViewGuest* guest) {
446 scoped_ptr<webview::StopFinding::Params> params(
447 webview::StopFinding::Params::Create(*args_));
448 EXTENSION_FUNCTION_VALIDATE(params.get());
449
450 // Set the StopFindAction.
451 content::StopFindAction action;
452 switch (params->action) {
453 case webview::StopFinding::Params::ACTION_CLEAR:
454 action = content::STOP_FIND_ACTION_CLEAR_SELECTION;
455 break;
456 case webview::StopFinding::Params::ACTION_KEEP:
457 action = content::STOP_FIND_ACTION_KEEP_SELECTION;
458 break;
459 case webview::StopFinding::Params::ACTION_ACTIVATE:
460 action = content::STOP_FIND_ACTION_ACTIVATE_SELECTION;
461 break;
462 default:
463 action = content::STOP_FIND_ACTION_KEEP_SELECTION;
464 }
465
466 guest->StopFinding(action);
467 return true;
468 }
469
WebviewGoFunction()470 WebviewGoFunction::WebviewGoFunction() {
471 }
472
~WebviewGoFunction()473 WebviewGoFunction::~WebviewGoFunction() {
474 }
475
RunAsyncSafe(WebViewGuest * guest)476 bool WebviewGoFunction::RunAsyncSafe(WebViewGuest* guest) {
477 scoped_ptr<webview::Go::Params> params(webview::Go::Params::Create(*args_));
478 EXTENSION_FUNCTION_VALIDATE(params.get());
479
480 guest->Go(params->relative_index);
481 return true;
482 }
483
WebviewReloadFunction()484 WebviewReloadFunction::WebviewReloadFunction() {
485 }
486
~WebviewReloadFunction()487 WebviewReloadFunction::~WebviewReloadFunction() {
488 }
489
RunAsyncSafe(WebViewGuest * guest)490 bool WebviewReloadFunction::RunAsyncSafe(WebViewGuest* guest) {
491 guest->Reload();
492 return true;
493 }
494
WebviewSetPermissionFunction()495 WebviewSetPermissionFunction::WebviewSetPermissionFunction() {
496 }
497
~WebviewSetPermissionFunction()498 WebviewSetPermissionFunction::~WebviewSetPermissionFunction() {
499 }
500
RunAsyncSafe(WebViewGuest * guest)501 bool WebviewSetPermissionFunction::RunAsyncSafe(WebViewGuest* guest) {
502 scoped_ptr<webview::SetPermission::Params> params(
503 webview::SetPermission::Params::Create(*args_));
504 EXTENSION_FUNCTION_VALIDATE(params.get());
505
506 WebViewGuest::PermissionResponseAction action = WebViewGuest::DEFAULT;
507 switch (params->action) {
508 case Params::ACTION_ALLOW:
509 action = WebViewGuest::ALLOW;
510 break;
511 case Params::ACTION_DENY:
512 action = WebViewGuest::DENY;
513 break;
514 case Params::ACTION_DEFAULT:
515 break;
516 default:
517 NOTREACHED();
518 }
519
520 std::string user_input;
521 if (params->user_input)
522 user_input = *params->user_input;
523
524 WebViewGuest::SetPermissionResult result =
525 guest->SetPermission(params->request_id, action, user_input);
526
527 EXTENSION_FUNCTION_VALIDATE(result != WebViewGuest::SET_PERMISSION_INVALID);
528
529 SetResult(base::Value::CreateBooleanValue(
530 result == WebViewGuest::SET_PERMISSION_ALLOWED));
531 SendResponse(true);
532 return true;
533 }
534
WebviewShowContextMenuFunction()535 WebviewShowContextMenuFunction::WebviewShowContextMenuFunction() {
536 }
537
~WebviewShowContextMenuFunction()538 WebviewShowContextMenuFunction::~WebviewShowContextMenuFunction() {
539 }
540
RunAsyncSafe(WebViewGuest * guest)541 bool WebviewShowContextMenuFunction::RunAsyncSafe(WebViewGuest* guest) {
542 scoped_ptr<webview::ShowContextMenu::Params> params(
543 webview::ShowContextMenu::Params::Create(*args_));
544 EXTENSION_FUNCTION_VALIDATE(params.get());
545
546 // TODO(lazyboy): Actually implement filtering menu items, we pass NULL for
547 // now.
548 guest->ShowContextMenu(params->request_id, NULL);
549
550 SendResponse(true);
551 return true;
552 }
553
WebviewOverrideUserAgentFunction()554 WebviewOverrideUserAgentFunction::WebviewOverrideUserAgentFunction() {
555 }
556
~WebviewOverrideUserAgentFunction()557 WebviewOverrideUserAgentFunction::~WebviewOverrideUserAgentFunction() {
558 }
559
RunAsyncSafe(WebViewGuest * guest)560 bool WebviewOverrideUserAgentFunction::RunAsyncSafe(WebViewGuest* guest) {
561 scoped_ptr<extensions::api::webview::OverrideUserAgent::Params> params(
562 extensions::api::webview::OverrideUserAgent::Params::Create(*args_));
563 EXTENSION_FUNCTION_VALIDATE(params.get());
564
565 guest->SetUserAgentOverride(params->user_agent_override);
566 return true;
567 }
568
WebviewStopFunction()569 WebviewStopFunction::WebviewStopFunction() {
570 }
571
~WebviewStopFunction()572 WebviewStopFunction::~WebviewStopFunction() {
573 }
574
RunAsyncSafe(WebViewGuest * guest)575 bool WebviewStopFunction::RunAsyncSafe(WebViewGuest* guest) {
576 guest->Stop();
577 return true;
578 }
579
WebviewTerminateFunction()580 WebviewTerminateFunction::WebviewTerminateFunction() {
581 }
582
~WebviewTerminateFunction()583 WebviewTerminateFunction::~WebviewTerminateFunction() {
584 }
585
RunAsyncSafe(WebViewGuest * guest)586 bool WebviewTerminateFunction::RunAsyncSafe(WebViewGuest* guest) {
587 guest->Terminate();
588 return true;
589 }
590
591 } // namespace extensions
592