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.
4
5 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
6
7 #include "base/lazy_instance.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/values.h"
10 #include "chrome/browser/extensions/active_script_controller.h"
11 #include "chrome/browser/extensions/extension_action_manager.h"
12 #include "chrome/browser/extensions/extension_tab_util.h"
13 #include "chrome/browser/extensions/extension_toolbar_model.h"
14 #include "chrome/browser/extensions/tab_helper.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/sessions/session_tab_helper.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_finder.h"
19 #include "chrome/browser/ui/browser_window.h"
20 #include "chrome/browser/ui/location_bar/location_bar.h"
21 #include "chrome/browser/ui/tabs/tab_strip_model.h"
22 #include "chrome/common/extensions/api/extension_action/action_info.h"
23 #include "chrome/common/render_messages.h"
24 #include "content/public/browser/notification_service.h"
25 #include "extensions/browser/event_router.h"
26 #include "extensions/browser/extension_function_registry.h"
27 #include "extensions/browser/extension_host.h"
28 #include "extensions/browser/extension_registry.h"
29 #include "extensions/browser/image_util.h"
30 #include "extensions/browser/notification_types.h"
31 #include "extensions/common/error_utils.h"
32 #include "extensions/common/feature_switch.h"
33 #include "ui/gfx/image/image.h"
34 #include "ui/gfx/image/image_skia.h"
35
36 using content::WebContents;
37
38 namespace extensions {
39
40 namespace {
41
42 // Whether the browser action is visible in the toolbar.
43 const char kBrowserActionVisible[] = "browser_action_visible";
44
45 // Errors.
46 const char kNoExtensionActionError[] =
47 "This extension has no action specified.";
48 const char kNoTabError[] = "No tab with id: *.";
49 const char kOpenPopupError[] =
50 "Failed to show popup either because there is an existing popup or another "
51 "error occurred.";
52
53 } // namespace
54
55 //
56 // ExtensionActionAPI::Observer
57 //
58
OnExtensionActionUpdated(ExtensionAction * extension_action,content::WebContents * web_contents,content::BrowserContext * browser_context)59 void ExtensionActionAPI::Observer::OnExtensionActionUpdated(
60 ExtensionAction* extension_action,
61 content::WebContents* web_contents,
62 content::BrowserContext* browser_context) {
63 }
64
OnPageActionsUpdated(content::WebContents * web_contents)65 void ExtensionActionAPI::Observer::OnPageActionsUpdated(
66 content::WebContents* web_contents) {
67 }
68
OnExtensionActionAPIShuttingDown()69 void ExtensionActionAPI::Observer::OnExtensionActionAPIShuttingDown() {
70 }
71
~Observer()72 ExtensionActionAPI::Observer::~Observer() {
73 }
74
75 //
76 // ExtensionActionAPI
77 //
78
79 static base::LazyInstance<BrowserContextKeyedAPIFactory<ExtensionActionAPI> >
80 g_factory = LAZY_INSTANCE_INITIALIZER;
81
ExtensionActionAPI(content::BrowserContext * context)82 ExtensionActionAPI::ExtensionActionAPI(content::BrowserContext* context)
83 : browser_context_(context) {
84 ExtensionFunctionRegistry* registry =
85 ExtensionFunctionRegistry::GetInstance();
86
87 // Browser Actions
88 registry->RegisterFunction<BrowserActionSetIconFunction>();
89 registry->RegisterFunction<BrowserActionSetTitleFunction>();
90 registry->RegisterFunction<BrowserActionSetBadgeTextFunction>();
91 registry->RegisterFunction<BrowserActionSetBadgeBackgroundColorFunction>();
92 registry->RegisterFunction<BrowserActionSetPopupFunction>();
93 registry->RegisterFunction<BrowserActionGetTitleFunction>();
94 registry->RegisterFunction<BrowserActionGetBadgeTextFunction>();
95 registry->RegisterFunction<BrowserActionGetBadgeBackgroundColorFunction>();
96 registry->RegisterFunction<BrowserActionGetPopupFunction>();
97 registry->RegisterFunction<BrowserActionEnableFunction>();
98 registry->RegisterFunction<BrowserActionDisableFunction>();
99 registry->RegisterFunction<BrowserActionOpenPopupFunction>();
100
101 // Page Actions
102 registry->RegisterFunction<PageActionShowFunction>();
103 registry->RegisterFunction<PageActionHideFunction>();
104 registry->RegisterFunction<PageActionSetIconFunction>();
105 registry->RegisterFunction<PageActionSetTitleFunction>();
106 registry->RegisterFunction<PageActionSetPopupFunction>();
107 registry->RegisterFunction<PageActionGetTitleFunction>();
108 registry->RegisterFunction<PageActionGetPopupFunction>();
109 }
110
~ExtensionActionAPI()111 ExtensionActionAPI::~ExtensionActionAPI() {
112 }
113
114 // static
115 BrowserContextKeyedAPIFactory<ExtensionActionAPI>*
GetFactoryInstance()116 ExtensionActionAPI::GetFactoryInstance() {
117 return g_factory.Pointer();
118 }
119
120 // static
Get(content::BrowserContext * context)121 ExtensionActionAPI* ExtensionActionAPI::Get(content::BrowserContext* context) {
122 return BrowserContextKeyedAPIFactory<ExtensionActionAPI>::Get(context);
123 }
124
125 // static
GetBrowserActionVisibility(const ExtensionPrefs * prefs,const std::string & extension_id)126 bool ExtensionActionAPI::GetBrowserActionVisibility(
127 const ExtensionPrefs* prefs,
128 const std::string& extension_id) {
129 bool visible = false;
130 if (!prefs || !prefs->ReadPrefAsBoolean(extension_id,
131 kBrowserActionVisible,
132 &visible)) {
133 return true;
134 }
135 return visible;
136 }
137
138 // static
SetBrowserActionVisibility(ExtensionPrefs * prefs,const std::string & extension_id,bool visible)139 void ExtensionActionAPI::SetBrowserActionVisibility(
140 ExtensionPrefs* prefs,
141 const std::string& extension_id,
142 bool visible) {
143 if (GetBrowserActionVisibility(prefs, extension_id) == visible)
144 return;
145
146 prefs->UpdateExtensionPref(extension_id,
147 kBrowserActionVisible,
148 new base::FundamentalValue(visible));
149 content::NotificationService::current()->Notify(
150 NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
151 content::Source<ExtensionPrefs>(prefs),
152 content::Details<const std::string>(&extension_id));
153 }
154
AddObserver(Observer * observer)155 void ExtensionActionAPI::AddObserver(Observer* observer) {
156 observers_.AddObserver(observer);
157 }
158
RemoveObserver(Observer * observer)159 void ExtensionActionAPI::RemoveObserver(Observer* observer) {
160 observers_.RemoveObserver(observer);
161 }
162
ExecuteExtensionAction(const Extension * extension,Browser * browser,bool grant_active_tab_permissions)163 ExtensionAction::ShowAction ExtensionActionAPI::ExecuteExtensionAction(
164 const Extension* extension,
165 Browser* browser,
166 bool grant_active_tab_permissions) {
167 content::WebContents* web_contents =
168 browser->tab_strip_model()->GetActiveWebContents();
169 if (!web_contents)
170 return ExtensionAction::ACTION_NONE;
171
172 int tab_id = SessionTabHelper::IdForTab(web_contents);
173
174 ActiveScriptController* active_script_controller =
175 ActiveScriptController::GetForWebContents(web_contents);
176 bool has_pending_scripts = false;
177 if (active_script_controller &&
178 active_script_controller->WantsToRun(extension)) {
179 has_pending_scripts = true;
180 }
181
182 // Grant active tab if appropriate.
183 if (grant_active_tab_permissions) {
184 TabHelper::FromWebContents(web_contents)->active_tab_permission_granter()->
185 GrantIfRequested(extension);
186 }
187
188 // If this was a request to run a script, it will have been run once active
189 // tab was granted. Return without executing the action, since we should only
190 // run pending scripts OR the extension action, not both.
191 if (has_pending_scripts)
192 return ExtensionAction::ACTION_NONE;
193
194 ExtensionAction* extension_action =
195 ExtensionActionManager::Get(browser_context_)->GetExtensionAction(
196 *extension);
197
198 // Anything that gets here should have a page or browser action.
199 DCHECK(extension_action);
200 if (!extension_action->GetIsVisible(tab_id))
201 return ExtensionAction::ACTION_NONE;
202
203 if (extension_action->HasPopup(tab_id))
204 return ExtensionAction::ACTION_SHOW_POPUP;
205
206 ExtensionActionExecuted(*extension_action, web_contents);
207 return ExtensionAction::ACTION_NONE;
208 }
209
ShowExtensionActionPopup(const Extension * extension,Browser * browser,bool grant_active_tab_permissions)210 bool ExtensionActionAPI::ShowExtensionActionPopup(
211 const Extension* extension,
212 Browser* browser,
213 bool grant_active_tab_permissions) {
214 ExtensionAction* extension_action =
215 ExtensionActionManager::Get(browser_context_)->GetExtensionAction(
216 *extension);
217 if (!extension_action)
218 return false;
219
220 if (extension_action->action_type() == ActionInfo::TYPE_PAGE &&
221 !FeatureSwitch::extension_action_redesign()->IsEnabled()) {
222 // We show page actions in the location bar unless the new toolbar is
223 // enabled.
224 return browser->window()->GetLocationBar()->ShowPageActionPopup(
225 extension, grant_active_tab_permissions);
226 } else {
227 return ExtensionToolbarModel::Get(browser->profile())->
228 ShowExtensionActionPopup(
229 extension, browser, grant_active_tab_permissions);
230 }
231 }
232
NotifyChange(ExtensionAction * extension_action,content::WebContents * web_contents,content::BrowserContext * context)233 void ExtensionActionAPI::NotifyChange(ExtensionAction* extension_action,
234 content::WebContents* web_contents,
235 content::BrowserContext* context) {
236 FOR_EACH_OBSERVER(
237 Observer,
238 observers_,
239 OnExtensionActionUpdated(extension_action, web_contents, context));
240
241 if (extension_action->action_type() == ActionInfo::TYPE_PAGE)
242 NotifyPageActionsChanged(web_contents);
243 }
244
ClearAllValuesForTab(content::WebContents * web_contents)245 void ExtensionActionAPI::ClearAllValuesForTab(
246 content::WebContents* web_contents) {
247 DCHECK(web_contents);
248 int tab_id = SessionTabHelper::IdForTab(web_contents);
249 content::BrowserContext* browser_context = web_contents->GetBrowserContext();
250 const ExtensionSet& enabled_extensions =
251 ExtensionRegistry::Get(browser_context_)->enabled_extensions();
252 ExtensionActionManager* action_manager =
253 ExtensionActionManager::Get(browser_context_);
254
255 for (ExtensionSet::const_iterator iter = enabled_extensions.begin();
256 iter != enabled_extensions.end(); ++iter) {
257 ExtensionAction* extension_action =
258 action_manager->GetBrowserAction(*iter->get());
259 if (!extension_action)
260 extension_action = action_manager->GetPageAction(*iter->get());
261 if (extension_action) {
262 extension_action->ClearAllValuesForTab(tab_id);
263 NotifyChange(extension_action, web_contents, browser_context);
264 }
265 }
266 }
267
DispatchEventToExtension(content::BrowserContext * context,const std::string & extension_id,const std::string & event_name,scoped_ptr<base::ListValue> event_args)268 void ExtensionActionAPI::DispatchEventToExtension(
269 content::BrowserContext* context,
270 const std::string& extension_id,
271 const std::string& event_name,
272 scoped_ptr<base::ListValue> event_args) {
273 if (!EventRouter::Get(context))
274 return;
275
276 scoped_ptr<Event> event(new Event(event_name, event_args.Pass()));
277 event->restrict_to_browser_context = context;
278 event->user_gesture = EventRouter::USER_GESTURE_ENABLED;
279 EventRouter::Get(context)
280 ->DispatchEventToExtension(extension_id, event.Pass());
281 }
282
ExtensionActionExecuted(const ExtensionAction & extension_action,WebContents * web_contents)283 void ExtensionActionAPI::ExtensionActionExecuted(
284 const ExtensionAction& extension_action,
285 WebContents* web_contents) {
286 const char* event_name = NULL;
287 switch (extension_action.action_type()) {
288 case ActionInfo::TYPE_BROWSER:
289 event_name = "browserAction.onClicked";
290 break;
291 case ActionInfo::TYPE_PAGE:
292 event_name = "pageAction.onClicked";
293 break;
294 case ActionInfo::TYPE_SYSTEM_INDICATOR:
295 // The System Indicator handles its own clicks.
296 break;
297 }
298
299 if (event_name) {
300 scoped_ptr<base::ListValue> args(new base::ListValue());
301 base::DictionaryValue* tab_value =
302 ExtensionTabUtil::CreateTabValue(web_contents);
303 args->Append(tab_value);
304
305 DispatchEventToExtension(
306 web_contents->GetBrowserContext(),
307 extension_action.extension_id(),
308 event_name,
309 args.Pass());
310 }
311 }
312
NotifyPageActionsChanged(content::WebContents * web_contents)313 void ExtensionActionAPI::NotifyPageActionsChanged(
314 content::WebContents* web_contents) {
315 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
316 if (!browser)
317 return;
318 LocationBar* location_bar =
319 browser->window() ? browser->window()->GetLocationBar() : NULL;
320 if (!location_bar)
321 return;
322 location_bar->UpdatePageActions();
323
324 FOR_EACH_OBSERVER(Observer, observers_, OnPageActionsUpdated(web_contents));
325 }
326
Shutdown()327 void ExtensionActionAPI::Shutdown() {
328 FOR_EACH_OBSERVER(Observer, observers_, OnExtensionActionAPIShuttingDown());
329 }
330
331 //
332 // ExtensionActionFunction
333 //
334
ExtensionActionFunction()335 ExtensionActionFunction::ExtensionActionFunction()
336 : details_(NULL),
337 tab_id_(ExtensionAction::kDefaultTabId),
338 contents_(NULL),
339 extension_action_(NULL) {
340 }
341
~ExtensionActionFunction()342 ExtensionActionFunction::~ExtensionActionFunction() {
343 }
344
RunSync()345 bool ExtensionActionFunction::RunSync() {
346 ExtensionActionManager* manager = ExtensionActionManager::Get(GetProfile());
347 if (StartsWithASCII(name(), "systemIndicator.", false)) {
348 extension_action_ = manager->GetSystemIndicator(*extension());
349 } else {
350 extension_action_ = manager->GetBrowserAction(*extension());
351 if (!extension_action_) {
352 extension_action_ = manager->GetPageAction(*extension());
353 }
354 }
355 if (!extension_action_) {
356 // TODO(kalman): ideally the browserAction/pageAction APIs wouldn't event
357 // exist for extensions that don't have one declared. This should come as
358 // part of the Feature system.
359 error_ = kNoExtensionActionError;
360 return false;
361 }
362
363 // Populates the tab_id_ and details_ members.
364 EXTENSION_FUNCTION_VALIDATE(ExtractDataFromArguments());
365
366 // Find the WebContents that contains this tab id if one is required.
367 if (tab_id_ != ExtensionAction::kDefaultTabId) {
368 ExtensionTabUtil::GetTabById(tab_id_,
369 GetProfile(),
370 include_incognito(),
371 NULL,
372 NULL,
373 &contents_,
374 NULL);
375 if (!contents_) {
376 error_ = ErrorUtils::FormatErrorMessage(
377 kNoTabError, base::IntToString(tab_id_));
378 return false;
379 }
380 } else {
381 // Only browser actions and system indicators have a default tabId.
382 ActionInfo::Type action_type = extension_action_->action_type();
383 EXTENSION_FUNCTION_VALIDATE(
384 action_type == ActionInfo::TYPE_BROWSER ||
385 action_type == ActionInfo::TYPE_SYSTEM_INDICATOR);
386 }
387 return RunExtensionAction();
388 }
389
ExtractDataFromArguments()390 bool ExtensionActionFunction::ExtractDataFromArguments() {
391 // There may or may not be details (depends on the function).
392 // The tabId might appear in details (if it exists), as the first
393 // argument besides the action type (depends on the function), or be omitted
394 // entirely.
395 base::Value* first_arg = NULL;
396 if (!args_->Get(0, &first_arg))
397 return true;
398
399 switch (first_arg->GetType()) {
400 case base::Value::TYPE_INTEGER:
401 CHECK(first_arg->GetAsInteger(&tab_id_));
402 break;
403
404 case base::Value::TYPE_DICTIONARY: {
405 // Found the details argument.
406 details_ = static_cast<base::DictionaryValue*>(first_arg);
407 // Still need to check for the tabId within details.
408 base::Value* tab_id_value = NULL;
409 if (details_->Get("tabId", &tab_id_value)) {
410 switch (tab_id_value->GetType()) {
411 case base::Value::TYPE_NULL:
412 // OK; tabId is optional, leave it default.
413 return true;
414 case base::Value::TYPE_INTEGER:
415 CHECK(tab_id_value->GetAsInteger(&tab_id_));
416 return true;
417 default:
418 // Boom.
419 return false;
420 }
421 }
422 // Not found; tabId is optional, leave it default.
423 break;
424 }
425
426 case base::Value::TYPE_NULL:
427 // The tabId might be an optional argument.
428 break;
429
430 default:
431 return false;
432 }
433
434 return true;
435 }
436
NotifyChange()437 void ExtensionActionFunction::NotifyChange() {
438 ExtensionActionAPI::Get(GetProfile())->NotifyChange(
439 extension_action_, contents_, GetProfile());
440 }
441
SetVisible(bool visible)442 bool ExtensionActionFunction::SetVisible(bool visible) {
443 if (extension_action_->GetIsVisible(tab_id_) == visible)
444 return true;
445 extension_action_->SetIsVisible(tab_id_, visible);
446 NotifyChange();
447 return true;
448 }
449
RunExtensionAction()450 bool ExtensionActionShowFunction::RunExtensionAction() {
451 return SetVisible(true);
452 }
453
RunExtensionAction()454 bool ExtensionActionHideFunction::RunExtensionAction() {
455 return SetVisible(false);
456 }
457
RunExtensionAction()458 bool ExtensionActionSetIconFunction::RunExtensionAction() {
459 EXTENSION_FUNCTION_VALIDATE(details_);
460
461 // setIcon can take a variant argument: either a dictionary of canvas
462 // ImageData, or an icon index.
463 base::DictionaryValue* canvas_set = NULL;
464 int icon_index;
465 if (details_->GetDictionary("imageData", &canvas_set)) {
466 gfx::ImageSkia icon;
467
468 EXTENSION_FUNCTION_VALIDATE(
469 ExtensionAction::ParseIconFromCanvasDictionary(*canvas_set, &icon));
470
471 extension_action_->SetIcon(tab_id_, gfx::Image(icon));
472 } else if (details_->GetInteger("iconIndex", &icon_index)) {
473 // Obsolete argument: ignore it.
474 return true;
475 } else {
476 EXTENSION_FUNCTION_VALIDATE(false);
477 }
478 NotifyChange();
479 return true;
480 }
481
RunExtensionAction()482 bool ExtensionActionSetTitleFunction::RunExtensionAction() {
483 EXTENSION_FUNCTION_VALIDATE(details_);
484 std::string title;
485 EXTENSION_FUNCTION_VALIDATE(details_->GetString("title", &title));
486 extension_action_->SetTitle(tab_id_, title);
487 NotifyChange();
488 return true;
489 }
490
RunExtensionAction()491 bool ExtensionActionSetPopupFunction::RunExtensionAction() {
492 EXTENSION_FUNCTION_VALIDATE(details_);
493 std::string popup_string;
494 EXTENSION_FUNCTION_VALIDATE(details_->GetString("popup", &popup_string));
495
496 GURL popup_url;
497 if (!popup_string.empty())
498 popup_url = extension()->GetResourceURL(popup_string);
499
500 extension_action_->SetPopupUrl(tab_id_, popup_url);
501 NotifyChange();
502 return true;
503 }
504
RunExtensionAction()505 bool ExtensionActionSetBadgeTextFunction::RunExtensionAction() {
506 EXTENSION_FUNCTION_VALIDATE(details_);
507 std::string badge_text;
508 EXTENSION_FUNCTION_VALIDATE(details_->GetString("text", &badge_text));
509 extension_action_->SetBadgeText(tab_id_, badge_text);
510 NotifyChange();
511 return true;
512 }
513
RunExtensionAction()514 bool ExtensionActionSetBadgeBackgroundColorFunction::RunExtensionAction() {
515 EXTENSION_FUNCTION_VALIDATE(details_);
516 base::Value* color_value = NULL;
517 EXTENSION_FUNCTION_VALIDATE(details_->Get("color", &color_value));
518 SkColor color = 0;
519 if (color_value->IsType(base::Value::TYPE_LIST)) {
520 base::ListValue* list = NULL;
521 EXTENSION_FUNCTION_VALIDATE(details_->GetList("color", &list));
522 EXTENSION_FUNCTION_VALIDATE(list->GetSize() == 4);
523
524 int color_array[4] = {0};
525 for (size_t i = 0; i < arraysize(color_array); ++i) {
526 EXTENSION_FUNCTION_VALIDATE(list->GetInteger(i, &color_array[i]));
527 }
528
529 color = SkColorSetARGB(color_array[3], color_array[0],
530 color_array[1], color_array[2]);
531 } else if (color_value->IsType(base::Value::TYPE_STRING)) {
532 std::string color_string;
533 EXTENSION_FUNCTION_VALIDATE(details_->GetString("color", &color_string));
534 if (!image_util::ParseCSSColorString(color_string, &color))
535 return false;
536 }
537
538 extension_action_->SetBadgeBackgroundColor(tab_id_, color);
539 NotifyChange();
540 return true;
541 }
542
RunExtensionAction()543 bool ExtensionActionGetTitleFunction::RunExtensionAction() {
544 SetResult(new base::StringValue(extension_action_->GetTitle(tab_id_)));
545 return true;
546 }
547
RunExtensionAction()548 bool ExtensionActionGetPopupFunction::RunExtensionAction() {
549 SetResult(
550 new base::StringValue(extension_action_->GetPopupUrl(tab_id_).spec()));
551 return true;
552 }
553
RunExtensionAction()554 bool ExtensionActionGetBadgeTextFunction::RunExtensionAction() {
555 SetResult(new base::StringValue(extension_action_->GetBadgeText(tab_id_)));
556 return true;
557 }
558
RunExtensionAction()559 bool ExtensionActionGetBadgeBackgroundColorFunction::RunExtensionAction() {
560 base::ListValue* list = new base::ListValue();
561 SkColor color = extension_action_->GetBadgeBackgroundColor(tab_id_);
562 list->Append(
563 new base::FundamentalValue(static_cast<int>(SkColorGetR(color))));
564 list->Append(
565 new base::FundamentalValue(static_cast<int>(SkColorGetG(color))));
566 list->Append(
567 new base::FundamentalValue(static_cast<int>(SkColorGetB(color))));
568 list->Append(
569 new base::FundamentalValue(static_cast<int>(SkColorGetA(color))));
570 SetResult(list);
571 return true;
572 }
573
BrowserActionOpenPopupFunction()574 BrowserActionOpenPopupFunction::BrowserActionOpenPopupFunction()
575 : response_sent_(false) {
576 }
577
RunAsync()578 bool BrowserActionOpenPopupFunction::RunAsync() {
579 // We only allow the popup in the active window.
580 Browser* browser = chrome::FindLastActiveWithProfile(
581 GetProfile(), chrome::GetActiveDesktop());
582
583 // If there's no active browser, or the Toolbar isn't visible, abort.
584 // Otherwise, try to open a popup in the active browser.
585 // TODO(justinlin): Remove toolbar check when http://crbug.com/308645 is
586 // fixed.
587 if (!browser ||
588 !browser->window()->IsActive() ||
589 !browser->window()->IsToolbarVisible() ||
590 !ExtensionActionAPI::Get(GetProfile())->ShowExtensionActionPopup(
591 extension_.get(), browser, false)) {
592 error_ = kOpenPopupError;
593 return false;
594 }
595
596 registrar_.Add(this,
597 NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
598 content::Source<Profile>(GetProfile()));
599
600 // Set a timeout for waiting for the notification that the popup is loaded.
601 // Waiting is required so that the popup view can be retrieved by the custom
602 // bindings for the response callback. It's also needed to keep this function
603 // instance around until a notification is observed.
604 base::MessageLoopForUI::current()->PostDelayedTask(
605 FROM_HERE,
606 base::Bind(&BrowserActionOpenPopupFunction::OpenPopupTimedOut, this),
607 base::TimeDelta::FromSeconds(10));
608 return true;
609 }
610
OpenPopupTimedOut()611 void BrowserActionOpenPopupFunction::OpenPopupTimedOut() {
612 if (response_sent_)
613 return;
614
615 DVLOG(1) << "chrome.browserAction.openPopup did not show a popup.";
616 error_ = kOpenPopupError;
617 SendResponse(false);
618 response_sent_ = true;
619 }
620
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)621 void BrowserActionOpenPopupFunction::Observe(
622 int type,
623 const content::NotificationSource& source,
624 const content::NotificationDetails& details) {
625 DCHECK_EQ(NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, type);
626 if (response_sent_)
627 return;
628
629 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
630 if (host->extension_host_type() != VIEW_TYPE_EXTENSION_POPUP ||
631 host->extension()->id() != extension_->id())
632 return;
633
634 SendResponse(true);
635 response_sent_ = true;
636 registrar_.RemoveAll();
637 }
638
639 } // namespace extensions
640