Home | History | Annotate | Download | only in webview
      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 {
     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 
     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*).
     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 
     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 
    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 
    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 
    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 
    175 WebviewClearDataFunction::WebviewClearDataFunction()
    176     : remove_mask_(0), bad_message_(false) {}
    177 
    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.
    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?
    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 
    253 void WebviewClearDataFunction::ClearDataDone() {
    254   Release();  // Balanced in RunAsync().
    255   SendResponse(true);
    256 }
    257 
    258 WebviewExecuteCodeFunction::WebviewExecuteCodeFunction()
    259     : guest_instance_id_(0), guest_src_(GURL::EmptyGURL()) {}
    260 
    261 WebviewExecuteCodeFunction::~WebviewExecuteCodeFunction() {
    262 }
    263 
    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 
    293 bool WebviewExecuteCodeFunction::ShouldInsertCSS() const {
    294   return false;
    295 }
    296 
    297 bool WebviewExecuteCodeFunction::CanExecuteScriptOnPage() {
    298   return true;
    299 }
    300 
    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 
    310 bool WebviewExecuteCodeFunction::IsWebView() const {
    311   return true;
    312 }
    313 
    314 const GURL& WebviewExecuteCodeFunction::GetWebViewSrc() const {
    315   return guest_src_;
    316 }
    317 
    318 WebviewExecuteScriptFunction::WebviewExecuteScriptFunction() {
    319 }
    320 
    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 
    332 WebviewInsertCSSFunction::WebviewInsertCSSFunction() {
    333 }
    334 
    335 bool WebviewInsertCSSFunction::ShouldInsertCSS() const {
    336   return true;
    337 }
    338 
    339 WebviewCaptureVisibleRegionFunction::WebviewCaptureVisibleRegionFunction() {
    340 }
    341 
    342 WebviewCaptureVisibleRegionFunction::~WebviewCaptureVisibleRegionFunction() {
    343 }
    344 
    345 bool WebviewCaptureVisibleRegionFunction::IsScreenshotEnabled() {
    346   return true;
    347 }
    348 
    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 
    356 void WebviewCaptureVisibleRegionFunction::OnCaptureFailure(
    357     FailureReason reason) {
    358   SendResponse(false);
    359 }
    360 
    361 WebviewSetNameFunction::WebviewSetNameFunction() {
    362 }
    363 
    364 WebviewSetNameFunction::~WebviewSetNameFunction() {
    365 }
    366 
    367 WebviewSetZoomFunction::WebviewSetZoomFunction() {
    368 }
    369 
    370 WebviewSetZoomFunction::~WebviewSetZoomFunction() {
    371 }
    372 
    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 
    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 
    392 WebviewGetZoomFunction::WebviewGetZoomFunction() {
    393 }
    394 
    395 WebviewGetZoomFunction::~WebviewGetZoomFunction() {
    396 }
    397 
    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 
    409 WebviewFindFunction::WebviewFindFunction() {
    410 }
    411 
    412 WebviewFindFunction::~WebviewFindFunction() {
    413 }
    414 
    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 
    439 WebviewStopFindingFunction::WebviewStopFindingFunction() {
    440 }
    441 
    442 WebviewStopFindingFunction::~WebviewStopFindingFunction() {
    443 }
    444 
    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 
    470 WebviewGoFunction::WebviewGoFunction() {
    471 }
    472 
    473 WebviewGoFunction::~WebviewGoFunction() {
    474 }
    475 
    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 
    484 WebviewReloadFunction::WebviewReloadFunction() {
    485 }
    486 
    487 WebviewReloadFunction::~WebviewReloadFunction() {
    488 }
    489 
    490 bool WebviewReloadFunction::RunAsyncSafe(WebViewGuest* guest) {
    491   guest->Reload();
    492   return true;
    493 }
    494 
    495 WebviewSetPermissionFunction::WebviewSetPermissionFunction() {
    496 }
    497 
    498 WebviewSetPermissionFunction::~WebviewSetPermissionFunction() {
    499 }
    500 
    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 
    535 WebviewShowContextMenuFunction::WebviewShowContextMenuFunction() {
    536 }
    537 
    538 WebviewShowContextMenuFunction::~WebviewShowContextMenuFunction() {
    539 }
    540 
    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 
    554 WebviewOverrideUserAgentFunction::WebviewOverrideUserAgentFunction() {
    555 }
    556 
    557 WebviewOverrideUserAgentFunction::~WebviewOverrideUserAgentFunction() {
    558 }
    559 
    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 
    569 WebviewStopFunction::WebviewStopFunction() {
    570 }
    571 
    572 WebviewStopFunction::~WebviewStopFunction() {
    573 }
    574 
    575 bool WebviewStopFunction::RunAsyncSafe(WebViewGuest* guest) {
    576   guest->Stop();
    577   return true;
    578 }
    579 
    580 WebviewTerminateFunction::WebviewTerminateFunction() {
    581 }
    582 
    583 WebviewTerminateFunction::~WebviewTerminateFunction() {
    584 }
    585 
    586 bool WebviewTerminateFunction::RunAsyncSafe(WebViewGuest* guest) {
    587   guest->Terminate();
    588   return true;
    589 }
    590 
    591 }  // namespace extensions
    592