Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_toolbar_model.cc
index 7dea3ac..1858603 100644 (file)
@@ -4,8 +4,10 @@
 
 #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"
 
 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(
@@ -76,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);
@@ -116,53 +116,46 @@ 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();
 }
 
-ExtensionAction::ShowAction 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 ExtensionAction::ACTION_NONE;
-
-  ExtensionAction* browser_action =
-      ExtensionActionManager::Get(profile_)->GetBrowserAction(*extension);
-
-  // For browser actions, visibility == enabledness.
-  if (!browser_action->GetIsVisible(tab_id))
-    return ExtensionAction::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 ExtensionAction::ACTION_SHOW_POPUP;
-  }
-
-  ExtensionActionAPI::BrowserActionExecuted(
-      browser->profile(), *browser_action, web_contents);
-  return ExtensionAction::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_)
+  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(
@@ -175,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(
@@ -208,18 +199,42 @@ void ExtensionToolbarModel::Observe(
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  DCHECK_EQ(
-      extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
-      type);
+  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);
-  if (ExtensionActionAPI::GetBrowserActionVisibility(extension_prefs_,
-                                                     extension->id()))
-    AddExtension(extension);
-  else
-    RemoveExtension(extension);
+
+  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;
+    }
+    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);
+  }
 }
 
 void ExtensionToolbarModel::OnReady() {
@@ -229,6 +244,7 @@ void ExtensionToolbarModel::OnReady() {
   // changes so that the toolbar buttons can be shown in their stable ordering
   // taken from prefs.
   extension_registry_observer_.Add(registry);
+  extension_action_observer_.Add(ExtensionActionAPI::Get(profile_));
   registrar_.Add(
       this,
       extensions::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
@@ -240,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())
@@ -260,9 +276,25 @@ size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
   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 = toolbar_items_.size();
@@ -287,12 +319,14 @@ void ExtensionToolbarModel::AddExtension(const Extension* extension) {
     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));
   }
 }
 
@@ -312,13 +346,14 @@ 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();
@@ -337,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,
@@ -357,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;
     }
 
@@ -375,13 +409,13 @@ void ExtensionToolbarModel::Populate(const ExtensionIdList& positions,
 
   size_t items_count = toolbar_items_.size();
   for (size_t i = 0; i < items_count; i++) {
-    const Extension* extension = toolbar_items_.back();
+    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(extension));
+        Observer, observers_, ToolbarExtensionRemoved(extension));
   }
   DCHECK(toolbar_items_.empty());
 
@@ -399,8 +433,9 @@ void ExtensionToolbarModel::Populate(const ExtensionIdList& positions,
     if (iter->get() != NULL) {
       toolbar_items_.push_back(*iter);
       FOR_EACH_OBSERVER(
-          Observer, observers_, BrowserActionAdded(
-              *iter, toolbar_items_.size() - 1));
+          Observer,
+          observers_,
+          ToolbarExtensionAdded(iter->get(), toolbar_items_.size() - 1));
     }
   }
   for (ExtensionList::const_iterator iter = unsorted.begin();
@@ -408,8 +443,9 @@ void ExtensionToolbarModel::Populate(const ExtensionIdList& positions,
     if (iter->get() != NULL) {
       toolbar_items_.push_back(*iter);
       FOR_EACH_OBSERVER(
-          Observer, observers_, BrowserActionAdded(
-              *iter, toolbar_items_.size() - 1));
+          Observer,
+          observers_,
+          ToolbarExtensionAdded(iter->get(), toolbar_items_.size() - 1));
     }
   }
 
@@ -439,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();
@@ -497,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;
 }
@@ -519,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)
@@ -532,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;
       }
     }
@@ -562,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;
   }
 
@@ -582,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