| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/api/automation_internal/automation_internal_
api.h" | 5 #include "chrome/browser/extensions/api/automation_internal/automation_internal_
api.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include <memory> |
| 9 #include <vector> | 10 #include <vector> |
| 10 | 11 |
| 11 #include "base/macros.h" | 12 #include "base/macros.h" |
| 12 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
| 13 #include "base/strings/string16.h" | 14 #include "base/strings/string16.h" |
| 14 #include "base/strings/string_number_conversions.h" | 15 #include "base/strings/string_number_conversions.h" |
| 15 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" |
| 16 #include "chrome/browser/extensions/api/automation_internal/automation_action_ad
apter.h" | |
| 17 #include "chrome/browser/extensions/api/automation_internal/automation_event_rou
ter.h" | 17 #include "chrome/browser/extensions/api/automation_internal/automation_event_rou
ter.h" |
| 18 #include "chrome/browser/extensions/api/tabs/tabs_constants.h" | 18 #include "chrome/browser/extensions/api/tabs/tabs_constants.h" |
| 19 #include "chrome/browser/extensions/extension_tab_util.h" | 19 #include "chrome/browser/extensions/extension_tab_util.h" |
| 20 #include "chrome/browser/profiles/profile.h" | 20 #include "chrome/browser/profiles/profile.h" |
| 21 #include "chrome/browser/ui/browser.h" | 21 #include "chrome/browser/ui/browser.h" |
| 22 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 22 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 23 #include "chrome/common/extensions/api/automation_api_constants.h" | 23 #include "chrome/common/extensions/api/automation_api_constants.h" |
| 24 #include "chrome/common/extensions/api/automation_internal.h" | 24 #include "chrome/common/extensions/api/automation_internal.h" |
| 25 #include "chrome/common/extensions/chrome_extension_messages.h" | 25 #include "chrome/common/extensions/chrome_extension_messages.h" |
| 26 #include "chrome/common/extensions/manifest_handlers/automation.h" | 26 #include "chrome/common/extensions/manifest_handlers/automation.h" |
| 27 #include "content/public/browser/ax_event_notification_details.h" | 27 #include "content/public/browser/ax_event_notification_details.h" |
| 28 #include "content/public/browser/browser_accessibility_state.h" | 28 #include "content/public/browser/browser_accessibility_state.h" |
| 29 #include "content/public/browser/browser_context.h" | 29 #include "content/public/browser/browser_context.h" |
| 30 #include "content/public/browser/browser_plugin_guest_manager.h" | 30 #include "content/public/browser/browser_plugin_guest_manager.h" |
| 31 #include "content/public/browser/media_session.h" | 31 #include "content/public/browser/media_session.h" |
| 32 #include "content/public/browser/render_frame_host.h" | 32 #include "content/public/browser/render_frame_host.h" |
| 33 #include "content/public/browser/render_view_host.h" | 33 #include "content/public/browser/render_view_host.h" |
| 34 #include "content/public/browser/render_widget_host.h" | 34 #include "content/public/browser/render_widget_host.h" |
| 35 #include "content/public/browser/render_widget_host_view.h" | 35 #include "content/public/browser/render_widget_host_view.h" |
| 36 #include "content/public/browser/web_contents.h" | 36 #include "content/public/browser/web_contents.h" |
| 37 #include "content/public/browser/web_contents_observer.h" | 37 #include "content/public/browser/web_contents_observer.h" |
| 38 #include "content/public/browser/web_contents_user_data.h" | 38 #include "content/public/browser/web_contents_user_data.h" |
| 39 #include "extensions/common/extension_messages.h" | 39 #include "extensions/common/extension_messages.h" |
| 40 #include "extensions/common/permissions/permissions_data.h" | 40 #include "extensions/common/permissions/permissions_data.h" |
| 41 #include "ui/accessibility/ax_action_data.h" | 41 #include "ui/accessibility/ax_action_data.h" |
| 42 #include "ui/accessibility/ax_host_delegate.h" |
| 43 #include "ui/accessibility/ax_tree_id_registry.h" |
| 42 | 44 |
| 43 #if defined(USE_AURA) | 45 #if defined(USE_AURA) |
| 44 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h" | 46 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h" |
| 45 #endif | 47 #endif |
| 46 | 48 |
| 47 namespace extensions { | 49 namespace extensions { |
| 48 class AutomationWebContentsObserver; | 50 class AutomationWebContentsObserver; |
| 49 } // namespace extensions | 51 } // namespace extensions |
| 50 | 52 |
| 51 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::AutomationWebContentsObserver); | 53 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::AutomationWebContentsObserver); |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 148 // TODO(aboxhall): check for webstore URL | 150 // TODO(aboxhall): check for webstore URL |
| 149 if (automation_info->matches.MatchesURL(url)) | 151 if (automation_info->matches.MatchesURL(url)) |
| 150 return true; | 152 return true; |
| 151 | 153 |
| 152 int tab_id = ExtensionTabUtil::GetTabId(contents); | 154 int tab_id = ExtensionTabUtil::GetTabId(contents); |
| 153 std::string unused_error; | 155 std::string unused_error; |
| 154 return extension->permissions_data()->CanAccessPage(extension, url, tab_id, | 156 return extension->permissions_data()->CanAccessPage(extension, url, tab_id, |
| 155 &unused_error); | 157 &unused_error); |
| 156 } | 158 } |
| 157 | 159 |
| 158 // Helper class that implements an action adapter for a |RenderFrameHost|. | |
| 159 class RenderFrameHostActionAdapter : public AutomationActionAdapter { | |
| 160 public: | |
| 161 explicit RenderFrameHostActionAdapter(content::RenderFrameHost* rfh) | |
| 162 : rfh_(rfh) {} | |
| 163 | |
| 164 virtual ~RenderFrameHostActionAdapter() {} | |
| 165 | |
| 166 // AutomationActionAdapter implementation. | |
| 167 void PerformAction(const ui::AXActionData& data) override { | |
| 168 rfh_->AccessibilityPerformAction(data); | |
| 169 } | |
| 170 | |
| 171 private: | |
| 172 content::RenderFrameHost* rfh_; | |
| 173 | |
| 174 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostActionAdapter); | |
| 175 }; | |
| 176 | |
| 177 } // namespace | 160 } // namespace |
| 178 | 161 |
| 179 // Helper class that receives accessibility data from |WebContents|. | 162 // Helper class that receives accessibility data from |WebContents|. |
| 180 class AutomationWebContentsObserver | 163 class AutomationWebContentsObserver |
| 181 : public content::WebContentsObserver, | 164 : public content::WebContentsObserver, |
| 182 public content::WebContentsUserData<AutomationWebContentsObserver> { | 165 public content::WebContentsUserData<AutomationWebContentsObserver> { |
| 183 public: | 166 public: |
| 184 ~AutomationWebContentsObserver() override {} | 167 ~AutomationWebContentsObserver() override {} |
| 185 | 168 |
| 186 // content::WebContentsObserver overrides. | 169 // content::WebContentsObserver overrides. |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 | 321 |
| 339 // Only call this if this is the root of a frame tree, to avoid resetting | 322 // Only call this if this is the root of a frame tree, to avoid resetting |
| 340 // the accessibility state multiple times. | 323 // the accessibility state multiple times. |
| 341 if (!rfh->GetParent()) | 324 if (!rfh->GetParent()) |
| 342 contents->EnableWebContentsOnlyAccessibilityMode(); | 325 contents->EnableWebContentsOnlyAccessibilityMode(); |
| 343 | 326 |
| 344 return RespondNow(NoArguments()); | 327 return RespondNow(NoArguments()); |
| 345 } | 328 } |
| 346 | 329 |
| 347 ExtensionFunction::ResponseAction | 330 ExtensionFunction::ResponseAction |
| 331 AutomationInternalPerformActionFunction::ConvertToAXActionData( |
| 332 api::automation_internal::PerformAction::Params* params, |
| 333 ui::AXActionData* action) { |
| 334 action->target_node_id = params->args.automation_node_id; |
| 335 switch (params->args.action_type) { |
| 336 case api::automation_internal::ACTION_TYPE_DODEFAULT: |
| 337 action->action = ui::AX_ACTION_DO_DEFAULT; |
| 338 break; |
| 339 case api::automation_internal::ACTION_TYPE_FOCUS: |
| 340 action->action = ui::AX_ACTION_FOCUS; |
| 341 break; |
| 342 case api::automation_internal::ACTION_TYPE_GETIMAGEDATA: { |
| 343 api::automation_internal::GetImageDataParams get_image_data_params; |
| 344 EXTENSION_FUNCTION_VALIDATE( |
| 345 api::automation_internal::GetImageDataParams::Populate( |
| 346 params->opt_args.additional_properties, &get_image_data_params)); |
| 347 action->action = ui::AX_ACTION_GET_IMAGE_DATA; |
| 348 action->target_rect = gfx::Rect(0, 0, get_image_data_params.max_width, |
| 349 get_image_data_params.max_height); |
| 350 break; |
| 351 } |
| 352 case api::automation_internal::ACTION_TYPE_MAKEVISIBLE: |
| 353 action->action = ui::AX_ACTION_SCROLL_TO_MAKE_VISIBLE; |
| 354 break; |
| 355 case api::automation_internal::ACTION_TYPE_SETSELECTION: { |
| 356 api::automation_internal::SetSelectionParams selection_params; |
| 357 EXTENSION_FUNCTION_VALIDATE( |
| 358 api::automation_internal::SetSelectionParams::Populate( |
| 359 params->opt_args.additional_properties, &selection_params)); |
| 360 action->anchor_node_id = params->args.automation_node_id; |
| 361 action->anchor_offset = selection_params.anchor_offset; |
| 362 action->focus_node_id = selection_params.focus_node_id; |
| 363 action->focus_offset = selection_params.focus_offset; |
| 364 action->action = ui::AX_ACTION_SET_SELECTION; |
| 365 break; |
| 366 } |
| 367 case api::automation_internal::ACTION_TYPE_SHOWCONTEXTMENU: { |
| 368 action->action = ui::AX_ACTION_SHOW_CONTEXT_MENU; |
| 369 break; |
| 370 } |
| 371 case api::automation_internal::ACTION_TYPE_SETACCESSIBILITYFOCUS: { |
| 372 action->action = ui::AX_ACTION_SET_ACCESSIBILITY_FOCUS; |
| 373 break; |
| 374 } |
| 375 case api::automation_internal:: |
| 376 ACTION_TYPE_SETSEQUENTIALFOCUSNAVIGATIONSTARTINGPOINT: { |
| 377 action->action = |
| 378 ui::AX_ACTION_SET_SEQUENTIAL_FOCUS_NAVIGATION_STARTING_POINT; |
| 379 break; |
| 380 } |
| 381 default: |
| 382 NOTREACHED(); |
| 383 } |
| 384 return RespondNow(NoArguments()); |
| 385 } |
| 386 |
| 387 ExtensionFunction::ResponseAction |
| 348 AutomationInternalPerformActionFunction::Run() { | 388 AutomationInternalPerformActionFunction::Run() { |
| 349 const AutomationInfo* automation_info = AutomationInfo::Get(extension()); | 389 const AutomationInfo* automation_info = AutomationInfo::Get(extension()); |
| 350 EXTENSION_FUNCTION_VALIDATE(automation_info && automation_info->interact); | 390 EXTENSION_FUNCTION_VALIDATE(automation_info && automation_info->interact); |
| 351 | 391 |
| 352 using api::automation_internal::PerformAction::Params; | 392 using api::automation_internal::PerformAction::Params; |
| 353 std::unique_ptr<Params> params(Params::Create(*args_)); | 393 std::unique_ptr<Params> params(Params::Create(*args_)); |
| 354 EXTENSION_FUNCTION_VALIDATE(params.get()); | 394 EXTENSION_FUNCTION_VALIDATE(params.get()); |
| 355 | 395 ui::AXTreeIDRegistry* registry = ui::AXTreeIDRegistry::GetInstance(); |
| 356 if (params->args.tree_id == api::automation::kDesktopTreeID) { | 396 ui::AXHostDelegate* delegate = |
| 397 registry->GetHostDelegate(params->args.tree_id); |
| 398 if (delegate) { |
| 357 #if defined(USE_AURA) | 399 #if defined(USE_AURA) |
| 358 return RouteActionToAdapter(params.get(), | 400 ui::AXActionData data; |
| 359 AutomationManagerAura::GetInstance()); | 401 ConvertToAXActionData(params.get(), &data); |
| 402 delegate->PerformAction(data); |
| 360 #else | 403 #else |
| 361 NOTREACHED(); | 404 NOTREACHED(); |
| 362 return RespondNow(Error("Unexpected action on desktop automation tree;" | 405 return RespondNow(Error("Unexpected action on desktop automation tree;" |
| 363 " platform does not support desktop automation")); | 406 " platform does not support desktop automation")); |
| 364 #endif // defined(USE_AURA) | 407 #endif // defined(USE_AURA) |
| 365 } | 408 } |
| 366 content::RenderFrameHost* rfh = | 409 content::RenderFrameHost* rfh = |
| 367 content::RenderFrameHost::FromAXTreeID(params->args.tree_id); | 410 content::RenderFrameHost::FromAXTreeID(params->args.tree_id); |
| 368 if (!rfh) | 411 if (!rfh) |
| 369 return RespondNow(Error("Ignoring action on destroyed node")); | 412 return RespondNow(Error("Ignoring action on destroyed node")); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 390 api::automation_internal::ACTION_TYPE_RESUMEMEDIA) { | 433 api::automation_internal::ACTION_TYPE_RESUMEMEDIA) { |
| 391 content::MediaSession* session = content::MediaSession::Get(contents); | 434 content::MediaSession* session = content::MediaSession::Get(contents); |
| 392 session->Resume(content::MediaSession::SuspendType::SYSTEM); | 435 session->Resume(content::MediaSession::SuspendType::SYSTEM); |
| 393 return RespondNow(NoArguments()); | 436 return RespondNow(NoArguments()); |
| 394 } else if (params->args.action_type == | 437 } else if (params->args.action_type == |
| 395 api::automation_internal::ACTION_TYPE_SUSPENDMEDIA) { | 438 api::automation_internal::ACTION_TYPE_SUSPENDMEDIA) { |
| 396 content::MediaSession* session = content::MediaSession::Get(contents); | 439 content::MediaSession* session = content::MediaSession::Get(contents); |
| 397 session->Suspend(content::MediaSession::SuspendType::SYSTEM); | 440 session->Suspend(content::MediaSession::SuspendType::SYSTEM); |
| 398 return RespondNow(NoArguments()); | 441 return RespondNow(NoArguments()); |
| 399 } | 442 } |
| 400 | 443 ui::AXActionData data; |
| 401 RenderFrameHostActionAdapter adapter(rfh); | 444 ExtensionFunction::ResponseAction result = |
| 402 return RouteActionToAdapter(params.get(), &adapter); | 445 ConvertToAXActionData(params.get(), &data); |
| 446 rfh->AccessibilityPerformAction(data); |
| 447 return result; |
| 403 } | 448 } |
| 404 | 449 |
| 405 ExtensionFunction::ResponseAction | 450 ExtensionFunction::ResponseAction |
| 406 AutomationInternalPerformActionFunction::RouteActionToAdapter( | |
| 407 api::automation_internal::PerformAction::Params* params, | |
| 408 AutomationActionAdapter* adapter) { | |
| 409 ui::AXActionData action; | |
| 410 action.target_node_id = params->args.automation_node_id; | |
| 411 switch (params->args.action_type) { | |
| 412 case api::automation_internal::ACTION_TYPE_DODEFAULT: | |
| 413 action.action = ui::AX_ACTION_DO_DEFAULT; | |
| 414 adapter->PerformAction(action); | |
| 415 break; | |
| 416 case api::automation_internal::ACTION_TYPE_FOCUS: | |
| 417 action.action = ui::AX_ACTION_FOCUS; | |
| 418 adapter->PerformAction(action); | |
| 419 break; | |
| 420 case api::automation_internal::ACTION_TYPE_GETIMAGEDATA: { | |
| 421 api::automation_internal::GetImageDataParams get_image_data_params; | |
| 422 EXTENSION_FUNCTION_VALIDATE( | |
| 423 api::automation_internal::GetImageDataParams::Populate( | |
| 424 params->opt_args.additional_properties, &get_image_data_params)); | |
| 425 action.action = ui::AX_ACTION_GET_IMAGE_DATA; | |
| 426 action.target_rect = gfx::Rect( | |
| 427 0, 0, get_image_data_params.max_width, | |
| 428 get_image_data_params.max_height); | |
| 429 adapter->PerformAction(action); | |
| 430 break; | |
| 431 } | |
| 432 case api::automation_internal::ACTION_TYPE_MAKEVISIBLE: | |
| 433 action.action = ui::AX_ACTION_SCROLL_TO_MAKE_VISIBLE; | |
| 434 adapter->PerformAction(action); | |
| 435 break; | |
| 436 case api::automation_internal::ACTION_TYPE_SETSELECTION: { | |
| 437 api::automation_internal::SetSelectionParams selection_params; | |
| 438 EXTENSION_FUNCTION_VALIDATE( | |
| 439 api::automation_internal::SetSelectionParams::Populate( | |
| 440 params->opt_args.additional_properties, &selection_params)); | |
| 441 action.anchor_node_id = params->args.automation_node_id; | |
| 442 action.anchor_offset = selection_params.anchor_offset; | |
| 443 action.focus_node_id = selection_params.focus_node_id; | |
| 444 action.focus_offset = selection_params.focus_offset; | |
| 445 action.action = ui::AX_ACTION_SET_SELECTION; | |
| 446 adapter->PerformAction(action); | |
| 447 break; | |
| 448 } | |
| 449 case api::automation_internal::ACTION_TYPE_SHOWCONTEXTMENU: { | |
| 450 action.action = ui::AX_ACTION_SHOW_CONTEXT_MENU; | |
| 451 adapter->PerformAction(action); | |
| 452 break; | |
| 453 } | |
| 454 case api::automation_internal::ACTION_TYPE_SETACCESSIBILITYFOCUS: { | |
| 455 action.action = ui::AX_ACTION_SET_ACCESSIBILITY_FOCUS; | |
| 456 adapter->PerformAction(action); | |
| 457 break; | |
| 458 } | |
| 459 case api::automation_internal:: | |
| 460 ACTION_TYPE_SETSEQUENTIALFOCUSNAVIGATIONSTARTINGPOINT: { | |
| 461 action.action = | |
| 462 ui::AX_ACTION_SET_SEQUENTIAL_FOCUS_NAVIGATION_STARTING_POINT; | |
| 463 adapter->PerformAction(action); | |
| 464 break; | |
| 465 } | |
| 466 default: | |
| 467 NOTREACHED(); | |
| 468 } | |
| 469 return RespondNow(NoArguments()); | |
| 470 } | |
| 471 | |
| 472 ExtensionFunction::ResponseAction | |
| 473 AutomationInternalEnableDesktopFunction::Run() { | 451 AutomationInternalEnableDesktopFunction::Run() { |
| 474 #if defined(USE_AURA) | 452 #if defined(USE_AURA) |
| 475 const AutomationInfo* automation_info = AutomationInfo::Get(extension()); | 453 const AutomationInfo* automation_info = AutomationInfo::Get(extension()); |
| 476 if (!automation_info || !automation_info->desktop) | 454 if (!automation_info || !automation_info->desktop) |
| 477 return RespondNow(Error("desktop permission must be requested")); | 455 return RespondNow(Error("desktop permission must be requested")); |
| 478 | 456 |
| 479 using api::automation_internal::EnableDesktop::Params; | 457 using api::automation_internal::EnableDesktop::Params; |
| 480 std::unique_ptr<Params> params(Params::Create(*args_)); | 458 std::unique_ptr<Params> params(Params::Create(*args_)); |
| 481 EXTENSION_FUNCTION_VALIDATE(params.get()); | 459 EXTENSION_FUNCTION_VALIDATE(params.get()); |
| 482 | 460 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 498 | 476 |
| 499 ExtensionFunction::ResponseAction | 477 ExtensionFunction::ResponseAction |
| 500 AutomationInternalQuerySelectorFunction::Run() { | 478 AutomationInternalQuerySelectorFunction::Run() { |
| 501 const AutomationInfo* automation_info = AutomationInfo::Get(extension()); | 479 const AutomationInfo* automation_info = AutomationInfo::Get(extension()); |
| 502 EXTENSION_FUNCTION_VALIDATE(automation_info); | 480 EXTENSION_FUNCTION_VALIDATE(automation_info); |
| 503 | 481 |
| 504 using api::automation_internal::QuerySelector::Params; | 482 using api::automation_internal::QuerySelector::Params; |
| 505 std::unique_ptr<Params> params(Params::Create(*args_)); | 483 std::unique_ptr<Params> params(Params::Create(*args_)); |
| 506 EXTENSION_FUNCTION_VALIDATE(params.get()); | 484 EXTENSION_FUNCTION_VALIDATE(params.get()); |
| 507 | 485 |
| 508 if (params->args.tree_id == api::automation::kDesktopTreeID) { | |
| 509 return RespondNow( | |
| 510 Error("domQuerySelector queries may not be used on the desktop.")); | |
| 511 } | |
| 512 content::RenderFrameHost* rfh = | 486 content::RenderFrameHost* rfh = |
| 513 content::RenderFrameHost::FromAXTreeID(params->args.tree_id); | 487 content::RenderFrameHost::FromAXTreeID(params->args.tree_id); |
| 514 if (!rfh) | 488 if (!rfh) { |
| 515 return RespondNow(Error("domQuerySelector query sent on destroyed tree.")); | 489 return RespondNow( |
| 490 Error("domQuerySelector query sent on non-web or destroyed tree.")); |
| 491 } |
| 516 | 492 |
| 517 content::WebContents* contents = | 493 content::WebContents* contents = |
| 518 content::WebContents::FromRenderFrameHost(rfh); | 494 content::WebContents::FromRenderFrameHost(rfh); |
| 519 | 495 |
| 520 int request_id = query_request_id_counter_++; | 496 int request_id = query_request_id_counter_++; |
| 521 base::string16 selector = base::UTF8ToUTF16(params->args.selector); | 497 base::string16 selector = base::UTF8ToUTF16(params->args.selector); |
| 522 | 498 |
| 523 // QuerySelectorHandler handles IPCs and deletes itself on completion. | 499 // QuerySelectorHandler handles IPCs and deletes itself on completion. |
| 524 new QuerySelectorHandler( | 500 new QuerySelectorHandler( |
| 525 contents, request_id, params->args.automation_node_id, selector, | 501 contents, request_id, params->args.automation_node_id, selector, |
| 526 base::Bind(&AutomationInternalQuerySelectorFunction::OnResponse, this)); | 502 base::Bind(&AutomationInternalQuerySelectorFunction::OnResponse, this)); |
| 527 | 503 |
| 528 return RespondLater(); | 504 return RespondLater(); |
| 529 } | 505 } |
| 530 | 506 |
| 531 void AutomationInternalQuerySelectorFunction::OnResponse( | 507 void AutomationInternalQuerySelectorFunction::OnResponse( |
| 532 const std::string& error, | 508 const std::string& error, |
| 533 int result_acc_obj_id) { | 509 int result_acc_obj_id) { |
| 534 if (!error.empty()) { | 510 if (!error.empty()) { |
| 535 Respond(Error(error)); | 511 Respond(Error(error)); |
| 536 return; | 512 return; |
| 537 } | 513 } |
| 538 | 514 |
| 539 Respond( | 515 Respond( |
| 540 OneArgument(base::MakeUnique<base::FundamentalValue>(result_acc_obj_id))); | 516 OneArgument(base::MakeUnique<base::FundamentalValue>(result_acc_obj_id))); |
| 541 } | 517 } |
| 542 | 518 |
| 543 } // namespace extensions | 519 } // namespace extensions |
| OLD | NEW |