Index: chrome/browser/ui/search/other_device_menu_controller.cc |
diff --git a/chrome/browser/ui/search/other_device_menu_controller.cc b/chrome/browser/ui/search/other_device_menu_controller.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a9b3ec25515253315822801983b4398382c38d17 |
--- /dev/null |
+++ b/chrome/browser/ui/search/other_device_menu_controller.cc |
@@ -0,0 +1,198 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/ui/search/other_device_menu_controller.h" |
+ |
+#include "base/metrics/histogram.h" |
+#include "base/string16.h" |
+#include "chrome/browser/sync/glue/session_model_associator.h" |
+#include "chrome/browser/ui/browser.h" |
+#include "chrome/browser/ui/browser_finder.h" |
+#include "chrome/browser/ui/browser_window.h" |
+#include "chrome/browser/ui/webui/ntp/foreign_session_handler.h" |
+#include "content/public/browser/web_contents.h" |
+#include "content/public/browser/web_ui.h" |
+#include "grit/generated_resources.h" |
+#include "ui/base/l10n/l10n_util.h" |
+#include "ui/base/text/text_elider.h" |
+#include "webkit/glue/window_open_disposition.h" |
+ |
+namespace { |
+ |
+// The max number of tabs that will be added to the menu. |
+const size_t kMaxTabsToShow = 18; |
+ |
+// The max width of a menu. Menu text exceeding this will be elided. |
+const int kMaxWidth = 375; |
+ |
+// Enumerates the different menu item types. |
+enum ItemType { |
+ // An item for restoring a single tab. |
+ TAB, |
+ // An item for restoring all tabs in this session. |
+ OPEN_ALL, |
+ // Number of enum entries, used for UMA histogram reporting macros. |
+ ITEM_TYPE_ENUM_COUNT, |
+}; |
+ |
+// Helper function that returns the largest tab timestamp for the window. |
+double GetMostRecentTabTimestamp(const SessionWindow* window) { |
+ double max_timestamp = 0; |
+ for (size_t i = 0, num_tabs = window->tabs.size(); i < num_tabs; ++i) { |
+ linked_ptr<DictionaryValue> tab_value = |
+ linked_ptr<DictionaryValue>(new DictionaryValue()); |
+ if (browser_sync::ForeignSessionHandler::SessionTabToValue( |
+ *window->tabs[i], tab_value.get())) { |
+ double timestamp; |
+ tab_value->GetDouble("timestamp", ×tamp); |
+ if (timestamp > max_timestamp) |
+ max_timestamp = timestamp; |
+ } |
+ } |
+ return max_timestamp; |
+} |
+ |
+// Comparator function to sort windows by last-modified time. Windows in a |
+// session share the same timestamp, so we need to use tab timestamps instead. |
+// instead. |
+bool SortWindowsByRecency(const SessionWindow* w1, const SessionWindow* w2) { |
+ return GetMostRecentTabTimestamp(w1) > GetMostRecentTabTimestamp(w2); |
+} |
+ |
+} // namespace |
+ |
+OtherDeviceMenuController::OtherDeviceMenuController( |
+ content::WebUI* web_ui, |
+ const std::string& session_id, |
+ const gfx::Point& location) |
+ : web_ui_(web_ui), |
+ session_id_(session_id), |
+ location_(location), |
+ ALLOW_THIS_IN_INITIALIZER_LIST(menu_model_(this)) { |
+ |
+ // Initialize the model. |
+ AddDeviceTabs(); |
+ |
+ // Add a "Open all" menu item if there is more than one tab. |
+ if (!tab_data_.empty()) { |
+ linked_ptr<DictionaryValue> open_all_tab_value = |
+ linked_ptr<DictionaryValue>(new DictionaryValue()); |
+ // kInvalidId signifies that the entire session should be opened. |
+ open_all_tab_value->SetInteger( |
+ "sessionId", |
+ browser_sync::ForeignSessionHandler::kInvalidId); |
+ open_all_tab_value->SetInteger( |
+ "windowId", |
+ browser_sync::ForeignSessionHandler::kInvalidId); |
+ menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
+ menu_model_.AddItem( |
+ tab_data_.size(), |
+ l10n_util::GetStringUTF16(IDS_NEW_TAB_OTHER_SESSIONS_OPEN_ALL)); |
+ tab_data_.push_back(open_all_tab_value); |
+ } |
+ |
+ // Create the view. |
+ view_.reset(OtherDeviceMenu::Create(&menu_model_)); |
+} |
+ |
+OtherDeviceMenuController::~OtherDeviceMenuController() { |
+} |
+ |
+void OtherDeviceMenuController::ShowMenu() { |
+ content::WebContents* web_contents = web_ui_->GetWebContents(); |
+ Browser* browser = browser::FindBrowserWithWebContents(web_contents); |
+ if (!browser) |
+ return; |
+ |
+ view_->ShowMenu(browser->window()->GetNativeWindow(), location_); |
+} |
+ |
+bool OtherDeviceMenuController::IsCommandIdChecked(int command_id) const { |
+ return false; |
+} |
+ |
+bool OtherDeviceMenuController::IsCommandIdEnabled(int command_id) const { |
+ return true; |
+} |
+ |
+void OtherDeviceMenuController::ExecuteCommand(int command_id) { |
+ ExecuteCommand(command_id, 0); |
+} |
+ |
+// TODO(jeremycho): Figure out why mouse wheel clicks don't trigger this. |
+void OtherDeviceMenuController::ExecuteCommand(int command_id, |
+ int event_flags) { |
+ DCHECK_GT(tab_data_.size(), static_cast<size_t>(command_id)) << |
+ "Invalid command_id from other device menu."; |
+ |
+ linked_ptr<DictionaryValue> tab_data = tab_data_[command_id]; |
+ // This is not a mistake - sessionId actually refers to the tab id. |
+ // See http://crbug.com/154865. |
+ int tab_id = browser_sync::ForeignSessionHandler::kInvalidId; |
+ tab_data->GetInteger("sessionId", &tab_id); |
+ |
+ int window_id = browser_sync::ForeignSessionHandler::kInvalidId; |
+ tab_data->GetInteger("windowId", &window_id); |
+ |
+ WindowOpenDisposition disposition = |
+ chrome::DispositionFromEventFlags(event_flags); |
+ browser_sync::ForeignSessionHandler::OpenForeignSession( |
+ web_ui_, session_id_, window_id, tab_id, disposition); |
+ |
+ ItemType itemType = tab_id == |
+ browser_sync::ForeignSessionHandler::kInvalidId ? OPEN_ALL : TAB; |
+ UMA_HISTOGRAM_ENUMERATION("NewTabPage.OtherDeviceMenu", |
+ itemType, ITEM_TYPE_ENUM_COUNT); |
+} |
+ |
+bool OtherDeviceMenuController::GetAcceleratorForCommandId( |
+ int command_id, |
+ ui::Accelerator* accelerator) { |
+ return false; |
+} |
+ |
+void OtherDeviceMenuController::AddDeviceTabs() { |
+ browser_sync::SessionModelAssociator* associator = |
+ browser_sync::ForeignSessionHandler::GetModelAssociator(web_ui_); |
+ std::vector<const SessionWindow*> windows; |
+ |
+ // Populate the menu with the device's tabs, using separators between windows. |
+ if (associator && associator->GetForeignSession(session_id_, &windows)) { |
+ // Show windows by descending last-modified time. |
+ std::sort(windows.begin(), windows.end(), SortWindowsByRecency); |
+ bool last_window_has_tabs = false; |
+ for (std::vector<const SessionWindow*>::const_iterator it = |
+ windows.begin(); it != windows.end(); ++it) { |
+ if (last_window_has_tabs) |
+ menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); |
+ last_window_has_tabs = false; |
+ |
+ const SessionWindow* window = *it; |
+ for (size_t i = 0, num_tabs = window->tabs.size(); i < num_tabs; ++i) { |
+ linked_ptr<DictionaryValue> tab_value = |
+ linked_ptr<DictionaryValue>(new DictionaryValue()); |
+ if (!browser_sync::ForeignSessionHandler::SessionTabToValue( |
+ *window->tabs[i], tab_value.get())) { |
+ continue; |
+ } |
+ last_window_has_tabs = true; |
+ tab_value->SetInteger("windowId", window->window_id.id()); |
+ string16 title; |
+ tab_value->GetString("title", &title); |
+ title = ui::ElideText( |
+ title, gfx::Font(), kMaxWidth, ui::ELIDE_AT_END); |
+ menu_model_.AddItem(tab_data_.size(), title); |
+ // TODO(jeremycho): Use tab_value.GetString("url", &url) to request |
+ // favicons. http://crbug.com/153410. |
+ tab_data_.push_back(tab_value); |
+ if (tab_data_.size() >= kMaxTabsToShow) |
+ return; |
+ } |
+ } |
+ } |
+} |
+ |
+// OtherDevice menu ---------------------------------------------------------- |
+ |
+OtherDeviceMenu::~OtherDeviceMenu() {} |