1 // Copyright 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/ui/extensions/extension_installed_bubble.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/time/time.h" 12 #include "chrome/browser/chrome_notification_types.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/ui/browser.h" 15 #include "chrome/common/extensions/api/extension_action/action_info.h" 16 #include "chrome/common/extensions/api/omnibox/omnibox_handler.h" 17 #include "content/public/browser/notification_details.h" 18 #include "content/public/browser/notification_source.h" 19 #include "extensions/browser/extension_registry.h" 20 #include "extensions/common/extension.h" 21 22 using content::Details; 23 using extensions::Extension; 24 25 namespace { 26 27 // How long to wait for browser action animations to complete before retrying. 28 const int kAnimationWaitMs = 50; 29 // How often we retry when waiting for browser action animation to end. 30 const int kAnimationWaitRetries = 10; 31 32 } // namespace 33 34 ExtensionInstalledBubble::ExtensionInstalledBubble(Delegate* delegate, 35 const Extension* extension, 36 Browser* browser, 37 const SkBitmap& icon) 38 : delegate_(delegate), 39 extension_(extension), 40 browser_(browser), 41 icon_(icon), 42 extension_registry_observer_(this), 43 animation_wait_retries_(0), 44 weak_factory_(this) { 45 if (!extensions::OmniboxInfo::GetKeyword(extension).empty()) 46 type_ = OMNIBOX_KEYWORD; 47 else if (extensions::ActionInfo::GetBrowserActionInfo(extension)) 48 type_ = BROWSER_ACTION; 49 else if (extensions::ActionInfo::GetPageActionInfo(extension) && 50 extensions::ActionInfo::IsVerboseInstallMessage(extension)) 51 type_ = PAGE_ACTION; 52 else 53 type_ = GENERIC; 54 55 // |extension| has been initialized but not loaded at this point. We need 56 // to wait on showing the Bubble until not only the EXTENSION_LOADED gets 57 // fired, but all of the EXTENSION_LOADED Observers have run. Only then can we 58 // be sure that a BrowserAction or PageAction has had views created which we 59 // can inspect for the purpose of previewing of pointing to them. 60 extension_registry_observer_.Add( 61 extensions::ExtensionRegistry::Get(browser->profile())); 62 63 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSING, 64 content::Source<Browser>(browser)); 65 } 66 67 ExtensionInstalledBubble::~ExtensionInstalledBubble() {} 68 69 void ExtensionInstalledBubble::IgnoreBrowserClosing() { 70 registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_CLOSING, 71 content::Source<Browser>(browser_)); 72 } 73 74 void ExtensionInstalledBubble::ShowInternal() { 75 if (delegate_->MaybeShowNow()) 76 return; 77 if (animation_wait_retries_++ < kAnimationWaitRetries) { 78 base::MessageLoopForUI::current()->PostDelayedTask( 79 FROM_HERE, 80 base::Bind(&ExtensionInstalledBubble::ShowInternal, 81 weak_factory_.GetWeakPtr()), 82 base::TimeDelta::FromMilliseconds(kAnimationWaitMs)); 83 } 84 } 85 86 void ExtensionInstalledBubble::OnExtensionLoaded( 87 content::BrowserContext* browser_context, 88 const extensions::Extension* extension) { 89 if (extension == extension_) { 90 animation_wait_retries_ = 0; 91 // PostTask to ourself to allow all EXTENSION_LOADED Observers to run. 92 base::MessageLoopForUI::current()->PostTask( 93 FROM_HERE, 94 base::Bind(&ExtensionInstalledBubble::ShowInternal, 95 weak_factory_.GetWeakPtr())); 96 } 97 } 98 99 void ExtensionInstalledBubble::OnExtensionUnloaded( 100 content::BrowserContext* browser_context, 101 const extensions::Extension* extension, 102 extensions::UnloadedExtensionInfo::Reason reason) { 103 if (extension == extension_) { 104 // Extension is going away, make sure ShowInternal won't be called. 105 weak_factory_.InvalidateWeakPtrs(); 106 extension_ = NULL; 107 } 108 } 109 110 void ExtensionInstalledBubble::Observe( 111 int type, 112 const content::NotificationSource& source, 113 const content::NotificationDetails& details) { 114 DCHECK_EQ(type, chrome::NOTIFICATION_BROWSER_CLOSING) 115 << "Received unexpected notification"; 116 delete delegate_; 117 } 118