Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / api / extension_action / extension_action_api.cc
blobf316a6bd71a9c6d78f33024238514b09ca55cd46
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.
5 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
7 #include "base/lazy_instance.h"
8 #include "base/location.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/values.h"
13 #include "chrome/browser/extensions/active_script_controller.h"
14 #include "chrome/browser/extensions/extension_action_manager.h"
15 #include "chrome/browser/extensions/extension_tab_util.h"
16 #include "chrome/browser/extensions/extension_util.h"
17 #include "chrome/browser/extensions/tab_helper.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/sessions/session_tab_helper.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/browser_finder.h"
22 #include "chrome/browser/ui/browser_window.h"
23 #include "chrome/browser/ui/location_bar/location_bar.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
25 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
26 #include "chrome/common/extensions/api/extension_action/action_info.h"
27 #include "content/public/browser/notification_service.h"
28 #include "extensions/browser/event_router.h"
29 #include "extensions/browser/extension_function_registry.h"
30 #include "extensions/browser/extension_host.h"
31 #include "extensions/browser/extension_registry.h"
32 #include "extensions/browser/notification_types.h"
33 #include "extensions/common/error_utils.h"
34 #include "extensions/common/feature_switch.h"
35 #include "extensions/common/image_util.h"
36 #include "ui/gfx/image/image.h"
37 #include "ui/gfx/image/image_skia.h"
39 using content::WebContents;
41 namespace extensions {
43 namespace {
45 // Whether the browser action is visible in the toolbar.
46 const char kBrowserActionVisible[] = "browser_action_visible";
48 // Errors.
49 const char kNoExtensionActionError[] =
50 "This extension has no action specified.";
51 const char kNoTabError[] = "No tab with id: *.";
52 const char kOpenPopupError[] =
53 "Failed to show popup either because there is an existing popup or another "
54 "error occurred.";
56 } // namespace
59 // ExtensionActionAPI::Observer
62 void ExtensionActionAPI::Observer::OnExtensionActionUpdated(
63 ExtensionAction* extension_action,
64 content::WebContents* web_contents,
65 content::BrowserContext* browser_context) {
68 void ExtensionActionAPI::Observer::OnExtensionActionVisibilityChanged(
69 const std::string& extension_id,
70 bool is_now_visible) {
73 void ExtensionActionAPI::Observer::OnPageActionsUpdated(
74 content::WebContents* web_contents) {
77 void ExtensionActionAPI::Observer::OnExtensionActionAPIShuttingDown() {
80 ExtensionActionAPI::Observer::~Observer() {
84 // ExtensionActionAPI
87 static base::LazyInstance<BrowserContextKeyedAPIFactory<ExtensionActionAPI> >
88 g_factory = LAZY_INSTANCE_INITIALIZER;
90 ExtensionActionAPI::ExtensionActionAPI(content::BrowserContext* context)
91 : browser_context_(context),
92 extension_prefs_(nullptr) {
93 ExtensionFunctionRegistry* registry =
94 ExtensionFunctionRegistry::GetInstance();
96 // Browser Actions
97 registry->RegisterFunction<BrowserActionSetIconFunction>();
98 registry->RegisterFunction<BrowserActionSetTitleFunction>();
99 registry->RegisterFunction<BrowserActionSetBadgeTextFunction>();
100 registry->RegisterFunction<BrowserActionSetBadgeBackgroundColorFunction>();
101 registry->RegisterFunction<BrowserActionSetPopupFunction>();
102 registry->RegisterFunction<BrowserActionGetTitleFunction>();
103 registry->RegisterFunction<BrowserActionGetBadgeTextFunction>();
104 registry->RegisterFunction<BrowserActionGetBadgeBackgroundColorFunction>();
105 registry->RegisterFunction<BrowserActionGetPopupFunction>();
106 registry->RegisterFunction<BrowserActionEnableFunction>();
107 registry->RegisterFunction<BrowserActionDisableFunction>();
108 registry->RegisterFunction<BrowserActionOpenPopupFunction>();
110 // Page Actions
111 registry->RegisterFunction<PageActionShowFunction>();
112 registry->RegisterFunction<PageActionHideFunction>();
113 registry->RegisterFunction<PageActionSetIconFunction>();
114 registry->RegisterFunction<PageActionSetTitleFunction>();
115 registry->RegisterFunction<PageActionSetPopupFunction>();
116 registry->RegisterFunction<PageActionGetTitleFunction>();
117 registry->RegisterFunction<PageActionGetPopupFunction>();
120 ExtensionActionAPI::~ExtensionActionAPI() {
123 // static
124 BrowserContextKeyedAPIFactory<ExtensionActionAPI>*
125 ExtensionActionAPI::GetFactoryInstance() {
126 return g_factory.Pointer();
129 // static
130 ExtensionActionAPI* ExtensionActionAPI::Get(content::BrowserContext* context) {
131 return BrowserContextKeyedAPIFactory<ExtensionActionAPI>::Get(context);
134 void ExtensionActionAPI::AddObserver(Observer* observer) {
135 observers_.AddObserver(observer);
138 void ExtensionActionAPI::RemoveObserver(Observer* observer) {
139 observers_.RemoveObserver(observer);
142 bool ExtensionActionAPI::GetBrowserActionVisibility(
143 const std::string& extension_id) {
144 bool visible = false;
145 ExtensionPrefs* prefs = GetExtensionPrefs();
146 if (!prefs || !prefs->ReadPrefAsBoolean(extension_id,
147 kBrowserActionVisible,
148 &visible)) {
149 return true;
151 return visible;
154 void ExtensionActionAPI::SetBrowserActionVisibility(
155 const std::string& extension_id,
156 bool visible) {
157 if (GetBrowserActionVisibility(extension_id) == visible)
158 return;
160 GetExtensionPrefs()->UpdateExtensionPref(extension_id,
161 kBrowserActionVisible,
162 new base::FundamentalValue(visible));
163 FOR_EACH_OBSERVER(Observer, observers_, OnExtensionActionVisibilityChanged(
164 extension_id, visible));
167 ExtensionAction::ShowAction ExtensionActionAPI::ExecuteExtensionAction(
168 const Extension* extension,
169 Browser* browser,
170 bool grant_active_tab_permissions) {
171 content::WebContents* web_contents =
172 browser->tab_strip_model()->GetActiveWebContents();
173 if (!web_contents)
174 return ExtensionAction::ACTION_NONE;
176 int tab_id = SessionTabHelper::IdForTab(web_contents);
178 ActiveScriptController* active_script_controller =
179 ActiveScriptController::GetForWebContents(web_contents);
180 bool has_pending_scripts = false;
181 if (active_script_controller &&
182 active_script_controller->WantsToRun(extension)) {
183 has_pending_scripts = true;
186 // Grant active tab if appropriate.
187 if (grant_active_tab_permissions) {
188 TabHelper::FromWebContents(web_contents)->active_tab_permission_granter()->
189 GrantIfRequested(extension);
192 // If this was a request to run a script, it will have been run once active
193 // tab was granted. Return without executing the action, since we should only
194 // run pending scripts OR the extension action, not both.
195 if (has_pending_scripts)
196 return ExtensionAction::ACTION_NONE;
198 ExtensionAction* extension_action =
199 ExtensionActionManager::Get(browser_context_)->GetExtensionAction(
200 *extension);
202 // Anything that gets here should have a page or browser action.
203 DCHECK(extension_action);
204 if (!extension_action->GetIsVisible(tab_id))
205 return ExtensionAction::ACTION_NONE;
207 if (extension_action->HasPopup(tab_id))
208 return ExtensionAction::ACTION_SHOW_POPUP;
210 ExtensionActionExecuted(*extension_action, web_contents);
211 return ExtensionAction::ACTION_NONE;
214 bool ExtensionActionAPI::ShowExtensionActionPopup(
215 const Extension* extension,
216 Browser* browser,
217 bool grant_active_tab_permissions) {
218 ExtensionAction* extension_action =
219 ExtensionActionManager::Get(browser_context_)->GetExtensionAction(
220 *extension);
221 if (!extension_action)
222 return false;
224 if (extension_action->action_type() == ActionInfo::TYPE_PAGE &&
225 !FeatureSwitch::extension_action_redesign()->IsEnabled()) {
226 // We show page actions in the location bar unless the new toolbar is
227 // enabled.
228 return browser->window()->GetLocationBar()->ShowPageActionPopup(
229 extension, grant_active_tab_permissions);
231 return browser->window()->GetToolbarActionsBar()->ShowToolbarActionPopup(
232 extension->id(), grant_active_tab_permissions);
235 bool ExtensionActionAPI::ExtensionWantsToRun(
236 const Extension* extension, content::WebContents* web_contents) {
237 // An extension wants to act if it has a visible page action on the given
238 // page...
239 ExtensionAction* page_action =
240 ExtensionActionManager::Get(browser_context_)->GetPageAction(*extension);
241 if (page_action &&
242 page_action->GetIsVisible(SessionTabHelper::IdForTab(web_contents)))
243 return true;
245 // ... Or if it has pending scripts that need approval for execution.
246 ActiveScriptController* active_script_controller =
247 ActiveScriptController::GetForWebContents(web_contents);
248 if (active_script_controller &&
249 active_script_controller->WantsToRun(extension))
250 return true;
252 return false;
255 void ExtensionActionAPI::NotifyChange(ExtensionAction* extension_action,
256 content::WebContents* web_contents,
257 content::BrowserContext* context) {
258 FOR_EACH_OBSERVER(
259 Observer,
260 observers_,
261 OnExtensionActionUpdated(extension_action, web_contents, context));
263 if (extension_action->action_type() == ActionInfo::TYPE_PAGE)
264 NotifyPageActionsChanged(web_contents);
267 void ExtensionActionAPI::ClearAllValuesForTab(
268 content::WebContents* web_contents) {
269 DCHECK(web_contents);
270 int tab_id = SessionTabHelper::IdForTab(web_contents);
271 content::BrowserContext* browser_context = web_contents->GetBrowserContext();
272 const ExtensionSet& enabled_extensions =
273 ExtensionRegistry::Get(browser_context_)->enabled_extensions();
274 ExtensionActionManager* action_manager =
275 ExtensionActionManager::Get(browser_context_);
277 for (ExtensionSet::const_iterator iter = enabled_extensions.begin();
278 iter != enabled_extensions.end(); ++iter) {
279 ExtensionAction* extension_action =
280 action_manager->GetExtensionAction(**iter);
281 if (extension_action) {
282 extension_action->ClearAllValuesForTab(tab_id);
283 NotifyChange(extension_action, web_contents, browser_context);
288 ExtensionPrefs* ExtensionActionAPI::GetExtensionPrefs() {
289 // This lazy initialization is more than just an optimization, because it
290 // allows tests to associate a new ExtensionPrefs with the browser context
291 // before we access it.
292 if (!extension_prefs_)
293 extension_prefs_ = ExtensionPrefs::Get(browser_context_);
294 return extension_prefs_;
297 void ExtensionActionAPI::DispatchEventToExtension(
298 content::BrowserContext* context,
299 const std::string& extension_id,
300 events::HistogramValue histogram_value,
301 const std::string& event_name,
302 scoped_ptr<base::ListValue> event_args) {
303 if (!EventRouter::Get(context))
304 return;
306 scoped_ptr<Event> event(
307 new Event(histogram_value, event_name, event_args.Pass()));
308 event->restrict_to_browser_context = context;
309 event->user_gesture = EventRouter::USER_GESTURE_ENABLED;
310 EventRouter::Get(context)
311 ->DispatchEventToExtension(extension_id, event.Pass());
314 void ExtensionActionAPI::ExtensionActionExecuted(
315 const ExtensionAction& extension_action,
316 WebContents* web_contents) {
317 events::HistogramValue histogram_value = events::UNKNOWN;
318 const char* event_name = NULL;
319 switch (extension_action.action_type()) {
320 case ActionInfo::TYPE_BROWSER:
321 histogram_value = events::BROWSER_ACTION_ON_CLICKED;
322 event_name = "browserAction.onClicked";
323 break;
324 case ActionInfo::TYPE_PAGE:
325 histogram_value = events::PAGE_ACTION_ON_CLICKED;
326 event_name = "pageAction.onClicked";
327 break;
328 case ActionInfo::TYPE_SYSTEM_INDICATOR:
329 // The System Indicator handles its own clicks.
330 NOTREACHED();
331 break;
334 if (event_name) {
335 scoped_ptr<base::ListValue> args(new base::ListValue());
336 base::DictionaryValue* tab_value =
337 ExtensionTabUtil::CreateTabValue(web_contents);
338 args->Append(tab_value);
340 DispatchEventToExtension(web_contents->GetBrowserContext(),
341 extension_action.extension_id(), histogram_value,
342 event_name, args.Pass());
346 void ExtensionActionAPI::NotifyPageActionsChanged(
347 content::WebContents* web_contents) {
348 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
349 if (!browser)
350 return;
351 LocationBar* location_bar =
352 browser->window() ? browser->window()->GetLocationBar() : NULL;
353 if (!location_bar)
354 return;
355 location_bar->UpdatePageActions();
357 FOR_EACH_OBSERVER(Observer, observers_, OnPageActionsUpdated(web_contents));
360 void ExtensionActionAPI::Shutdown() {
361 FOR_EACH_OBSERVER(Observer, observers_, OnExtensionActionAPIShuttingDown());
365 // ExtensionActionFunction
368 ExtensionActionFunction::ExtensionActionFunction()
369 : details_(NULL),
370 tab_id_(ExtensionAction::kDefaultTabId),
371 contents_(NULL),
372 extension_action_(NULL) {
375 ExtensionActionFunction::~ExtensionActionFunction() {
378 bool ExtensionActionFunction::RunSync() {
379 ExtensionActionManager* manager = ExtensionActionManager::Get(GetProfile());
380 if (base::StartsWith(name(), "systemIndicator.",
381 base::CompareCase::INSENSITIVE_ASCII)) {
382 extension_action_ = manager->GetSystemIndicator(*extension());
383 } else {
384 extension_action_ = manager->GetBrowserAction(*extension());
385 if (!extension_action_) {
386 extension_action_ = manager->GetPageAction(*extension());
389 if (!extension_action_) {
390 // TODO(kalman): ideally the browserAction/pageAction APIs wouldn't event
391 // exist for extensions that don't have one declared. This should come as
392 // part of the Feature system.
393 error_ = kNoExtensionActionError;
394 return false;
397 // Populates the tab_id_ and details_ members.
398 EXTENSION_FUNCTION_VALIDATE(ExtractDataFromArguments());
400 // Find the WebContents that contains this tab id if one is required.
401 if (tab_id_ != ExtensionAction::kDefaultTabId) {
402 ExtensionTabUtil::GetTabById(tab_id_,
403 GetProfile(),
404 include_incognito(),
405 NULL,
406 NULL,
407 &contents_,
408 NULL);
409 if (!contents_) {
410 error_ = ErrorUtils::FormatErrorMessage(
411 kNoTabError, base::IntToString(tab_id_));
412 return false;
414 } else {
415 // Only browser actions and system indicators have a default tabId.
416 ActionInfo::Type action_type = extension_action_->action_type();
417 EXTENSION_FUNCTION_VALIDATE(
418 action_type == ActionInfo::TYPE_BROWSER ||
419 action_type == ActionInfo::TYPE_SYSTEM_INDICATOR);
421 return RunExtensionAction();
424 bool ExtensionActionFunction::ExtractDataFromArguments() {
425 // There may or may not be details (depends on the function).
426 // The tabId might appear in details (if it exists), as the first
427 // argument besides the action type (depends on the function), or be omitted
428 // entirely.
429 base::Value* first_arg = NULL;
430 if (!args_->Get(0, &first_arg))
431 return true;
433 switch (first_arg->GetType()) {
434 case base::Value::TYPE_INTEGER:
435 CHECK(first_arg->GetAsInteger(&tab_id_));
436 break;
438 case base::Value::TYPE_DICTIONARY: {
439 // Found the details argument.
440 details_ = static_cast<base::DictionaryValue*>(first_arg);
441 // Still need to check for the tabId within details.
442 base::Value* tab_id_value = NULL;
443 if (details_->Get("tabId", &tab_id_value)) {
444 switch (tab_id_value->GetType()) {
445 case base::Value::TYPE_NULL:
446 // OK; tabId is optional, leave it default.
447 return true;
448 case base::Value::TYPE_INTEGER:
449 CHECK(tab_id_value->GetAsInteger(&tab_id_));
450 return true;
451 default:
452 // Boom.
453 return false;
456 // Not found; tabId is optional, leave it default.
457 break;
460 case base::Value::TYPE_NULL:
461 // The tabId might be an optional argument.
462 break;
464 default:
465 return false;
468 return true;
471 void ExtensionActionFunction::NotifyChange() {
472 ExtensionActionAPI::Get(GetProfile())->NotifyChange(
473 extension_action_, contents_, GetProfile());
476 bool ExtensionActionFunction::SetVisible(bool visible) {
477 if (extension_action_->GetIsVisible(tab_id_) == visible)
478 return true;
479 extension_action_->SetIsVisible(tab_id_, visible);
480 NotifyChange();
481 return true;
484 bool ExtensionActionShowFunction::RunExtensionAction() {
485 return SetVisible(true);
488 bool ExtensionActionHideFunction::RunExtensionAction() {
489 return SetVisible(false);
492 bool ExtensionActionSetIconFunction::RunExtensionAction() {
493 EXTENSION_FUNCTION_VALIDATE(details_);
495 // setIcon can take a variant argument: either a dictionary of canvas
496 // ImageData, or an icon index.
497 base::DictionaryValue* canvas_set = NULL;
498 int icon_index;
499 if (details_->GetDictionary("imageData", &canvas_set)) {
500 gfx::ImageSkia icon;
502 EXTENSION_FUNCTION_VALIDATE(
503 ExtensionAction::ParseIconFromCanvasDictionary(*canvas_set, &icon));
505 extension_action_->SetIcon(tab_id_, gfx::Image(icon));
506 } else if (details_->GetInteger("iconIndex", &icon_index)) {
507 // Obsolete argument: ignore it.
508 return true;
509 } else {
510 EXTENSION_FUNCTION_VALIDATE(false);
512 NotifyChange();
513 return true;
516 bool ExtensionActionSetTitleFunction::RunExtensionAction() {
517 EXTENSION_FUNCTION_VALIDATE(details_);
518 std::string title;
519 EXTENSION_FUNCTION_VALIDATE(details_->GetString("title", &title));
520 extension_action_->SetTitle(tab_id_, title);
521 NotifyChange();
522 return true;
525 bool ExtensionActionSetPopupFunction::RunExtensionAction() {
526 EXTENSION_FUNCTION_VALIDATE(details_);
527 std::string popup_string;
528 EXTENSION_FUNCTION_VALIDATE(details_->GetString("popup", &popup_string));
530 GURL popup_url;
531 if (!popup_string.empty())
532 popup_url = extension()->GetResourceURL(popup_string);
534 extension_action_->SetPopupUrl(tab_id_, popup_url);
535 NotifyChange();
536 return true;
539 bool ExtensionActionSetBadgeTextFunction::RunExtensionAction() {
540 EXTENSION_FUNCTION_VALIDATE(details_);
541 std::string badge_text;
542 EXTENSION_FUNCTION_VALIDATE(details_->GetString("text", &badge_text));
543 extension_action_->SetBadgeText(tab_id_, badge_text);
544 NotifyChange();
545 return true;
548 bool ExtensionActionSetBadgeBackgroundColorFunction::RunExtensionAction() {
549 EXTENSION_FUNCTION_VALIDATE(details_);
550 base::Value* color_value = NULL;
551 EXTENSION_FUNCTION_VALIDATE(details_->Get("color", &color_value));
552 SkColor color = 0;
553 if (color_value->IsType(base::Value::TYPE_LIST)) {
554 base::ListValue* list = NULL;
555 EXTENSION_FUNCTION_VALIDATE(details_->GetList("color", &list));
556 EXTENSION_FUNCTION_VALIDATE(list->GetSize() == 4);
558 int color_array[4] = {0};
559 for (size_t i = 0; i < arraysize(color_array); ++i) {
560 EXTENSION_FUNCTION_VALIDATE(list->GetInteger(i, &color_array[i]));
563 color = SkColorSetARGB(color_array[3], color_array[0],
564 color_array[1], color_array[2]);
565 } else if (color_value->IsType(base::Value::TYPE_STRING)) {
566 std::string color_string;
567 EXTENSION_FUNCTION_VALIDATE(details_->GetString("color", &color_string));
568 if (!image_util::ParseCSSColorString(color_string, &color))
569 return false;
572 extension_action_->SetBadgeBackgroundColor(tab_id_, color);
573 NotifyChange();
574 return true;
577 bool ExtensionActionGetTitleFunction::RunExtensionAction() {
578 SetResult(new base::StringValue(extension_action_->GetTitle(tab_id_)));
579 return true;
582 bool ExtensionActionGetPopupFunction::RunExtensionAction() {
583 SetResult(
584 new base::StringValue(extension_action_->GetPopupUrl(tab_id_).spec()));
585 return true;
588 bool ExtensionActionGetBadgeTextFunction::RunExtensionAction() {
589 SetResult(new base::StringValue(extension_action_->GetBadgeText(tab_id_)));
590 return true;
593 bool ExtensionActionGetBadgeBackgroundColorFunction::RunExtensionAction() {
594 base::ListValue* list = new base::ListValue();
595 SkColor color = extension_action_->GetBadgeBackgroundColor(tab_id_);
596 list->Append(
597 new base::FundamentalValue(static_cast<int>(SkColorGetR(color))));
598 list->Append(
599 new base::FundamentalValue(static_cast<int>(SkColorGetG(color))));
600 list->Append(
601 new base::FundamentalValue(static_cast<int>(SkColorGetB(color))));
602 list->Append(
603 new base::FundamentalValue(static_cast<int>(SkColorGetA(color))));
604 SetResult(list);
605 return true;
608 BrowserActionOpenPopupFunction::BrowserActionOpenPopupFunction()
609 : response_sent_(false) {
612 bool BrowserActionOpenPopupFunction::RunAsync() {
613 // We only allow the popup in the active window.
614 Profile* profile = GetProfile();
615 Browser* browser = chrome::FindLastActiveWithProfile(
616 profile, chrome::GetActiveDesktop());
617 // It's possible that the last active browser actually corresponds to the
618 // associated incognito profile, and this won't be returned by
619 // FindLastActiveWithProfile. If the browser we found isn't active and the
620 // extension can operate incognito, then check the last active incognito, too.
621 if ((!browser || !browser->window()->IsActive()) &&
622 util::IsIncognitoEnabled(extension()->id(), profile) &&
623 profile->HasOffTheRecordProfile()) {
624 browser = chrome::FindLastActiveWithProfile(
625 profile->GetOffTheRecordProfile(), chrome::GetActiveDesktop());
628 // If there's no active browser, or the Toolbar isn't visible, abort.
629 // Otherwise, try to open a popup in the active browser.
630 // TODO(justinlin): Remove toolbar check when http://crbug.com/308645 is
631 // fixed.
632 if (!browser ||
633 !browser->window()->IsActive() ||
634 !browser->window()->IsToolbarVisible() ||
635 !ExtensionActionAPI::Get(GetProfile())->ShowExtensionActionPopup(
636 extension_.get(), browser, false)) {
637 error_ = kOpenPopupError;
638 return false;
641 // Even if this is for an incognito window, we want to use the normal profile.
642 // If the extension is spanning, then extension hosts are created with the
643 // original profile, and if it's split, then we know the api call came from
644 // the right profile.
645 registrar_.Add(this, NOTIFICATION_EXTENSION_HOST_DID_STOP_FIRST_LOAD,
646 content::Source<Profile>(profile));
648 // Set a timeout for waiting for the notification that the popup is loaded.
649 // Waiting is required so that the popup view can be retrieved by the custom
650 // bindings for the response callback. It's also needed to keep this function
651 // instance around until a notification is observed.
652 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
653 FROM_HERE,
654 base::Bind(&BrowserActionOpenPopupFunction::OpenPopupTimedOut, this),
655 base::TimeDelta::FromSeconds(10));
656 return true;
659 void BrowserActionOpenPopupFunction::OpenPopupTimedOut() {
660 if (response_sent_)
661 return;
663 DVLOG(1) << "chrome.browserAction.openPopup did not show a popup.";
664 error_ = kOpenPopupError;
665 SendResponse(false);
666 response_sent_ = true;
669 void BrowserActionOpenPopupFunction::Observe(
670 int type,
671 const content::NotificationSource& source,
672 const content::NotificationDetails& details) {
673 DCHECK_EQ(NOTIFICATION_EXTENSION_HOST_DID_STOP_FIRST_LOAD, type);
674 if (response_sent_)
675 return;
677 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
678 if (host->extension_host_type() != VIEW_TYPE_EXTENSION_POPUP ||
679 host->extension()->id() != extension_->id())
680 return;
682 SendResponse(true);
683 response_sent_ = true;
684 registrar_.RemoveAll();
687 } // namespace extensions