Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_toolbar_model.cc
index 7c9d5bf..1858603 100644 (file)
@@ -4,14 +4,15 @@
 
 #include "chrome/browser/extensions/extension_toolbar_model.h"
 
+#include <algorithm>
 #include <string>
 
+#include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_base.h"
 #include "base/prefs/pref_service.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
-#include "chrome/browser/extensions/extension_action.h"
 #include "chrome/browser/extensions/extension_action_manager.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/extensions/extension_toolbar_model_factory.h"
 
 namespace extensions {
 
-bool ExtensionToolbarModel::Observer::BrowserActionShowPopup(
-    const Extension* extension) {
-  return false;
-}
-
 ExtensionToolbarModel::ExtensionToolbarModel(Profile* profile,
                                              ExtensionPrefs* extension_prefs)
     : profile_(profile),
       extension_prefs_(extension_prefs),
       prefs_(profile_->GetPrefs()),
       extensions_initialized_(false),
+      include_all_extensions_(
+          FeatureSwitch::extension_action_redesign()->IsEnabled()),
       is_highlighting_(false),
+      extension_action_observer_(this),
       extension_registry_observer_(this),
       weak_ptr_factory_(this) {
   ExtensionSystem::Get(profile_)->ready().Post(
@@ -77,7 +76,7 @@ void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
   observers_.RemoveObserver(observer);
 }
 
-void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension,
+void ExtensionToolbarModel::MoveExtensionIcon(const Extension* extension,
                                               int index) {
   ExtensionList::iterator pos = std::find(toolbar_items_.begin(),
       toolbar_items_.end(), extension);
@@ -117,57 +116,48 @@ void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension,
     last_known_positions_.push_back(extension->id());
   }
 
-  FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index));
-
+  FOR_EACH_OBSERVER(
+      Observer, observers_, ToolbarExtensionMoved(extension, index));
+  MaybeUpdateVisibilityPref(extension, index);
   UpdatePrefs();
 }
 
-ExtensionToolbarModel::Action ExtensionToolbarModel::ExecuteBrowserAction(
-    const Extension* extension,
-    Browser* browser,
-    GURL* popup_url_out,
-    bool should_grant) {
-  content::WebContents* web_contents = NULL;
-  int tab_id = 0;
-  if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id)) {
-    return ACTION_NONE;
-  }
-
-  ExtensionAction* browser_action =
-      ExtensionActionManager::Get(profile_)->GetBrowserAction(*extension);
-
-  // For browser actions, visibility == enabledness.
-  if (!browser_action->GetIsVisible(tab_id))
-    return ACTION_NONE;
-
-  if (should_grant) {
-    TabHelper::FromWebContents(web_contents)
-        ->active_tab_permission_granter()
-        ->GrantIfRequested(extension);
-  }
-
-  if (browser_action->HasPopup(tab_id)) {
-    if (popup_url_out)
-      *popup_url_out = browser_action->GetPopupUrl(tab_id);
-    return ACTION_SHOW_POPUP;
-  }
-
-  ExtensionActionAPI::BrowserActionExecuted(
-      browser->profile(), *browser_action, web_contents);
-  return ACTION_NONE;
-}
-
 void ExtensionToolbarModel::SetVisibleIconCount(int count) {
   visible_icon_count_ =
       count == static_cast<int>(toolbar_items_.size()) ? -1 : count;
+
   // Only set the prefs if we're not in highlight mode. Highlight mode is
   // designed to be a transitory state, and should not persist across browser
   // restarts (though it may be re-entered).
   if (!is_highlighting_) {
+    // Additionally, if we are using the new toolbar, any icons which are in the
+    // overflow menu are considered "hidden". But it so happens that the times
+    // we are likely to call SetVisibleIconCount() are also those when we are
+    // in flux. So wait for things to cool down before setting the prefs.
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(&ExtensionToolbarModel::MaybeUpdateVisibilityPrefs,
+                   weak_ptr_factory_.GetWeakPtr()));
     prefs_->SetInteger(pref_names::kToolbarSize, visible_icon_count_);
   }
 }
 
+void ExtensionToolbarModel::OnExtensionActionUpdated(
+    ExtensionAction* extension_action,
+    content::WebContents* web_contents,
+    content::BrowserContext* browser_context) {
+  const Extension* extension =
+      ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID(
+          extension_action->extension_id());
+  // Notify observers if the extension exists and is in the model.
+  if (extension &&
+      std::find(toolbar_items_.begin(),
+                toolbar_items_.end(),
+                extension) != toolbar_items_.end()) {
+    FOR_EACH_OBSERVER(Observer, observers_, ToolbarExtensionUpdated(extension));
+  }
+}
+
 void ExtensionToolbarModel::OnExtensionLoaded(
     content::BrowserContext* browser_context,
     const Extension* extension) {
@@ -178,10 +168,8 @@ void ExtensionToolbarModel::OnExtensionLoaded(
     if (toolbar_items_[i].get() == extension)
       return;
   }
-  if (ExtensionActionAPI::GetBrowserActionVisibility(extension_prefs_,
-                                                     extension->id())) {
-    AddExtension(extension);
-  }
+
+  AddExtension(extension);
 }
 
 void ExtensionToolbarModel::OnExtensionUnloaded(
@@ -191,32 +179,61 @@ void ExtensionToolbarModel::OnExtensionUnloaded(
   RemoveExtension(extension);
 }
 
+void ExtensionToolbarModel::OnExtensionUninstalled(
+    content::BrowserContext* browser_context,
+    const Extension* extension,
+    extensions::UninstallReason reason) {
+  // Remove the extension id from the ordered list, if it exists (the extension
+  // might not be represented in the list because it might not have an icon).
+  ExtensionIdList::iterator pos =
+      std::find(last_known_positions_.begin(),
+                last_known_positions_.end(), extension->id());
+
+  if (pos != last_known_positions_.end()) {
+    last_known_positions_.erase(pos);
+    UpdatePrefs();
+  }
+}
+
 void ExtensionToolbarModel::Observe(
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  switch (type) {
-    case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
-      const Extension* extension =
-          content::Details<const Extension>(details).ptr();
-      UninstalledExtension(extension);
-      break;
-    }
-    case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED: {
-      const Extension* extension =
-          ExtensionRegistry::Get(profile_)->GetExtensionById(
-              *content::Details<const std::string>(details).ptr(),
-              ExtensionRegistry::EVERYTHING);
-      if (ExtensionActionAPI::GetBrowserActionVisibility(extension_prefs_,
-                                                         extension->id())) {
-        AddExtension(extension);
-      } else {
-        RemoveExtension(extension);
-      }
-      break;
+  DCHECK_EQ(NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, type);
+  const Extension* extension =
+      ExtensionRegistry::Get(profile_)->GetExtensionById(
+          *content::Details<const std::string>(details).ptr(),
+          ExtensionRegistry::EVERYTHING);
+
+  bool visible = ExtensionActionAPI::GetBrowserActionVisibility(
+                     extension_prefs_, extension->id());
+  // Hiding works differently with the new and old toolbars.
+  if (include_all_extensions_) {
+    int new_size = 0;
+    int new_index = 0;
+    if (visible) {
+      // If this action used to be hidden, we can't possible be showing all.
+      DCHECK_NE(-1, visible_icon_count_);
+      // Grow the bar by one and move the extension to the end of the visibles.
+      new_size = visible_icon_count_ + 1;
+      new_index = new_size - 1;
+    } else {
+      // If we're hiding one, we must be showing at least one.
+      DCHECK_NE(visible_icon_count_, 0);
+      // Shrink the bar by one and move the extension to the beginning of the
+      // overflow menu.
+      new_size = visible_icon_count_ == -1 ?
+                     toolbar_items_.size() - 1 : visible_icon_count_ - 1;
+      new_index = new_size;
     }
-    default:
-      NOTREACHED() << "Received unexpected notification";
+    SetVisibleIconCount(new_size);
+    MoveExtensionIcon(extension, new_index);
+    FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
+  } else {  // Don't include all extensions.
+    if (visible)
+      AddExtension(extension);
+    else
+      RemoveExtension(extension);
   }
 }
 
@@ -227,12 +244,10 @@ void ExtensionToolbarModel::OnReady() {
   // changes so that the toolbar buttons can be shown in their stable ordering
   // taken from prefs.
   extension_registry_observer_.Add(registry);
-  registrar_.Add(this,
-                 chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
-                 content::Source<Profile>(profile_));
+  extension_action_observer_.Add(ExtensionActionAPI::Get(profile_));
   registrar_.Add(
       this,
-      chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
+      extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
       content::Source<ExtensionPrefs>(extension_prefs_));
 }
 
@@ -241,7 +256,7 @@ size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
   // See if we have last known good position for this extension.
   size_t new_index = 0;
   // Loop through the ID list of known positions, to count the number of visible
-  // browser action icons preceding |extension|.
+  // extension icons preceding |extension|.
   for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
        iter_id < last_known_positions_.end(); ++iter_id) {
     if ((*iter_id) == extension->id())
@@ -257,15 +272,32 @@ size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
     }
   }
 
-  return -1;
+  // Position not found.
+  return toolbar_items_.size();
+}
+
+bool ExtensionToolbarModel::ShouldAddExtension(const Extension* extension) {
+  ExtensionActionManager* action_manager =
+      ExtensionActionManager::Get(profile_);
+  if (include_all_extensions_) {
+    // In this case, we don't care about the browser action visibility, because
+    // we want to show each extension regardless.
+    // TODO(devlin): Extension actions which are not visible should be moved to
+    // the overflow menu by default.
+    return action_manager->GetBrowserAction(*extension) ||
+           action_manager->GetPageAction(*extension);
+  }
+
+  return action_manager->GetBrowserAction(*extension) &&
+         ExtensionActionAPI::GetBrowserActionVisibility(
+             extension_prefs_, extension->id());
 }
 
 void ExtensionToolbarModel::AddExtension(const Extension* extension) {
-  // We only care about extensions with browser actions.
-  if (!ExtensionActionManager::Get(profile_)->GetBrowserAction(*extension))
+  if (!ShouldAddExtension(extension))
     return;
 
-  size_t new_index = -1;
+  size_t new_index = toolbar_items_.size();
 
   // See if we have a last known good position for this extension.
   ExtensionIdList::iterator last_pos = std::find(last_known_positions_.begin(),
@@ -281,19 +313,20 @@ void ExtensionToolbarModel::AddExtension(const Extension* extension) {
     }
   } else {
     // This is a never before seen extension, that was added to the end. Make
-    // sure to reflect that.
+    // sure to reflect that. (|new_index| was set above.)
     toolbar_items_.push_back(make_scoped_refptr(extension));
     last_known_positions_.push_back(extension->id());
-    new_index = toolbar_items_.size() - 1;
     UpdatePrefs();
   }
 
+  MaybeUpdateVisibilityPref(extension, new_index);
+
   // If we're currently highlighting, then even though we add a browser action
   // to the full list (|toolbar_items_|, there won't be another *visible*
   // browser action, which was what the observers care about.
   if (!is_highlighting_) {
-    FOR_EACH_OBSERVER(Observer, observers_,
-                      BrowserActionAdded(extension, new_index));
+    FOR_EACH_OBSERVER(
+        Observer, observers_, ToolbarExtensionAdded(extension, new_index));
   }
 }
 
@@ -313,31 +346,19 @@ void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
                     extension);
     if (pos != highlighted_items_.end()) {
       highlighted_items_.erase(pos);
-      FOR_EACH_OBSERVER(Observer, observers_, BrowserActionRemoved(extension));
+      FOR_EACH_OBSERVER(
+          Observer, observers_, ToolbarExtensionRemoved(extension));
       // If the highlighted list is now empty, we stop highlighting.
       if (highlighted_items_.empty())
         StopHighlighting();
     }
   } else {
-    FOR_EACH_OBSERVER(Observer, observers_, BrowserActionRemoved(extension));
+    FOR_EACH_OBSERVER(Observer, observers_, ToolbarExtensionRemoved(extension));
   }
 
   UpdatePrefs();
 }
 
-void ExtensionToolbarModel::UninstalledExtension(const Extension* extension) {
-  // Remove the extension id from the ordered list, if it exists (the extension
-  // might not be represented in the list because it might not have an icon).
-  ExtensionIdList::iterator pos =
-      std::find(last_known_positions_.begin(),
-                last_known_positions_.end(), extension->id());
-
-  if (pos != last_known_positions_.end()) {
-    last_known_positions_.erase(pos);
-    UpdatePrefs();
-  }
-}
-
 // Combine the currently enabled extensions that have browser actions (which
 // we get from the ExtensionRegistry) with the ordering we get from the
 // pref service. For robustness we use a somewhat inefficient process:
@@ -351,7 +372,8 @@ void ExtensionToolbarModel::InitializeExtensionList(
   Populate(last_known_positions_, extensions);
 
   extensions_initialized_ = true;
-  FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged());
+  MaybeUpdateVisibilityPrefs();
+  FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
 }
 
 void ExtensionToolbarModel::Populate(const ExtensionIdList& positions,
@@ -371,11 +393,9 @@ void ExtensionToolbarModel::Populate(const ExtensionIdList& positions,
        it != extensions.end();
        ++it) {
     const Extension* extension = it->get();
-    if (!extension_action_manager->GetBrowserAction(*extension))
-      continue;
-    if (!ExtensionActionAPI::GetBrowserActionVisibility(
-            extension_prefs_, extension->id())) {
-      ++hidden;
+    if (!ShouldAddExtension(extension)) {
+      if (extension_action_manager->GetBrowserAction(*extension))
+        ++hidden;
       continue;
     }
 
@@ -387,15 +407,21 @@ void ExtensionToolbarModel::Populate(const ExtensionIdList& positions,
       unsorted.push_back(make_scoped_refptr(extension));
   }
 
-  // Erase current icons.
-  for (size_t i = 0; i < toolbar_items_.size(); i++) {
+  size_t items_count = toolbar_items_.size();
+  for (size_t i = 0; i < items_count; i++) {
+    const Extension* extension = toolbar_items_.back().get();
+    // By popping the extension here (before calling BrowserActionRemoved),
+    // we will not shrink visible count by one after BrowserActionRemoved
+    // calls SetVisibleCount.
+    toolbar_items_.pop_back();
     FOR_EACH_OBSERVER(
-        Observer, observers_, BrowserActionRemoved(toolbar_items_[i].get()));
+        Observer, observers_, ToolbarExtensionRemoved(extension));
   }
-  toolbar_items_.clear();
+  DCHECK(toolbar_items_.empty());
 
   // Merge the lists.
   toolbar_items_.reserve(sorted.size() + unsorted.size());
+
   for (ExtensionList::const_iterator iter = sorted.begin();
        iter != sorted.end(); ++iter) {
     // It's possible for the extension order to contain items that aren't
@@ -404,11 +430,24 @@ void ExtensionToolbarModel::Populate(const ExtensionIdList& positions,
     // syncing NPAPI-containing extensions, so if one of those is not actually
     // synced, we'll get a NULL in the list.  This sort of case can also happen
     // if some error prevents an extension from loading.
-    if (iter->get() != NULL)
+    if (iter->get() != NULL) {
+      toolbar_items_.push_back(*iter);
+      FOR_EACH_OBSERVER(
+          Observer,
+          observers_,
+          ToolbarExtensionAdded(iter->get(), toolbar_items_.size() - 1));
+    }
+  }
+  for (ExtensionList::const_iterator iter = unsorted.begin();
+       iter != unsorted.end(); ++iter) {
+    if (iter->get() != NULL) {
       toolbar_items_.push_back(*iter);
+      FOR_EACH_OBSERVER(
+          Observer,
+          observers_,
+          ToolbarExtensionAdded(iter->get(), toolbar_items_.size() - 1));
+    }
   }
-  toolbar_items_.insert(toolbar_items_.end(), unsorted.begin(),
-                        unsorted.end());
 
   UMA_HISTOGRAM_COUNTS_100(
       "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden);
@@ -424,12 +463,6 @@ void ExtensionToolbarModel::Populate(const ExtensionIdList& positions,
                                  base::HistogramBase::kSampleType_MAX :
                                  visible_icon_count_);
   }
-
-  // Inform observers.
-  for (size_t i = 0; i < toolbar_items_.size(); i++) {
-    FOR_EACH_OBSERVER(
-        Observer, observers_, BrowserActionAdded(toolbar_items_[i].get(), i));
-  }
 }
 
 void ExtensionToolbarModel::UpdatePrefs() {
@@ -442,6 +475,42 @@ void ExtensionToolbarModel::UpdatePrefs() {
   pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
 }
 
+void ExtensionToolbarModel::MaybeUpdateVisibilityPref(
+    const Extension* extension, int index) {
+  // We only update the visibility pref for hidden/not hidden based on the
+  // overflow menu with the new toolbar design.
+  if (include_all_extensions_) {
+    bool visible = index < visible_icon_count_ || visible_icon_count_ == -1;
+    if (visible != ExtensionActionAPI::GetBrowserActionVisibility(
+                       extension_prefs_, extension->id())) {
+      // Don't observe changes caused by ourselves.
+      bool was_registered = false;
+      if (registrar_.IsRegistered(
+              this,
+              NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
+              content::Source<ExtensionPrefs>(extension_prefs_))) {
+        was_registered = true;
+        registrar_.Remove(
+            this,
+            NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
+            content::Source<ExtensionPrefs>(extension_prefs_));
+      }
+      ExtensionActionAPI::SetBrowserActionVisibility(
+          extension_prefs_, extension->id(), visible);
+      if (was_registered) {
+        registrar_.Add(this,
+                       NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
+                       content::Source<ExtensionPrefs>(extension_prefs_));
+      }
+    }
+  }
+}
+
+void ExtensionToolbarModel::MaybeUpdateVisibilityPrefs() {
+  for (size_t i = 0u; i < toolbar_items_.size(); ++i)
+    MaybeUpdateVisibilityPref(toolbar_items_[i].get(), i);
+}
+
 int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) {
   int original_index = 0, i = 0;
   for (ExtensionList::iterator iter = toolbar_items_.begin();
@@ -500,13 +569,18 @@ void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
   }
 }
 
-bool ExtensionToolbarModel::ShowBrowserActionPopup(const Extension* extension) {
+bool ExtensionToolbarModel::ShowExtensionActionPopup(
+    const Extension* extension,
+    Browser* browser,
+    bool grant_active_tab) {
   ObserverListBase<Observer>::Iterator it(observers_);
   Observer* obs = NULL;
+  // Look for the Observer associated with the browser.
+  // This would be cleaner if we had an abstract class for the Toolbar UI
+  // (like we do for LocationBar), but sadly, we don't.
   while ((obs = it.GetNext()) != NULL) {
-    // Stop after first popup since it should only show in the active window.
-    if (obs->BrowserActionShowPopup(extension))
-      return true;
+    if (obs->GetBrowser() == browser)
+      return obs->ShowExtensionActionPopup(extension, grant_active_tab);
   }
   return false;
 }
@@ -522,7 +596,7 @@ void ExtensionToolbarModel::EnsureVisibility(
     SetVisibleIconCount(extension_ids.size());
 
     // Inform observers.
-    FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged());
+    FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
   }
 
   if (visible_icon_count_ == -1)
@@ -535,7 +609,7 @@ void ExtensionToolbarModel::EnsureVisibility(
          extension != toolbar_items_.end(); ++extension) {
       if ((*extension)->id() == (*it)) {
         if (extension - toolbar_items_.begin() >= visible_icon_count_)
-          MoveBrowserAction(*extension, 0);
+          MoveExtensionIcon(extension->get(), 0);
         break;
       }
     }
@@ -565,10 +639,10 @@ bool ExtensionToolbarModel::HighlightExtensions(
     if (visible_icon_count_ != -1 &&
         visible_icon_count_ < static_cast<int>(extension_ids.size())) {
       SetVisibleIconCount(extension_ids.size());
-      FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged());
+      FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
     }
 
-    FOR_EACH_OBSERVER(Observer, observers_, HighlightModeChanged(true));
+    FOR_EACH_OBSERVER(Observer, observers_, ToolbarHighlightModeChanged(true));
     return true;
   }
 
@@ -585,10 +659,15 @@ void ExtensionToolbarModel::StopHighlighting() {
     is_highlighting_ = false;
     if (old_visible_icon_count_ != visible_icon_count_) {
       SetVisibleIconCount(old_visible_icon_count_);
-      FOR_EACH_OBSERVER(Observer, observers_, VisibleCountChanged());
+      FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
     }
-    FOR_EACH_OBSERVER(Observer, observers_, HighlightModeChanged(false));
+    FOR_EACH_OBSERVER(Observer, observers_, ToolbarHighlightModeChanged(false));
   }
-};
+}
+
+void ExtensionToolbarModel::SetVisibleIconCountForTest(size_t visible_icons) {
+  SetVisibleIconCount(visible_icons);
+  FOR_EACH_OBSERVER(Observer, observers_, ToolbarVisibleCountChanged());
+}
 
 }  // namespace extensions