summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views/app_launcher.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/views/app_launcher.cc')
-rw-r--r--chrome/browser/views/app_launcher.cc397
1 files changed, 397 insertions, 0 deletions
diff --git a/chrome/browser/views/app_launcher.cc b/chrome/browser/views/app_launcher.cc
new file mode 100644
index 0000000..65f2eda
--- /dev/null
+++ b/chrome/browser/views/app_launcher.cc
@@ -0,0 +1,397 @@
+// Copyright (c) 2010 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/views/app_launcher.h"
+
+#include <string>
+#include <vector>
+
+#include "app/resource_bundle.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/view_ids.h"
+#include "chrome/browser/views/dom_view.h"
+#include "chrome/browser/views/info_bubble.h"
+#include "chrome/browser/views/frame/browser_view.h"
+#include "chrome/browser/views/location_bar/location_bar_view.h"
+#include "chrome/common/url_constants.h"
+#include "views/widget/root_view.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/status/status_area_view.h"
+#endif
+
+namespace {
+
+// Padding between the navigation bar and the render view contents.
+const int kNavigationBarBottomPadding = 3;
+
+// NavigationBar constants.
+const int kNavigationBarBorderThickness = 1;
+
+// The speed in pixels per milli-second at which the animation should progress.
+// It is easier to use a speed than a duration as the contents may report
+// several changes in size over-time.
+const double kAnimationSpeedPxPerMS = 1.5;
+
+const SkColor kBorderColor = SkColorSetRGB(205, 201, 201);
+
+// Command line switch for specifying url of the page.
+// TODO: nuke when we convert to the real app page. Also nuke code in
+// AddNewContents
+const wchar_t kURLSwitch[] = L"main-menu-url";
+
+// Returns the URL of the menu.
+static GURL GetMenuURL() {
+ std::wstring url_string =
+ CommandLine::ForCurrentProcess()->GetSwitchValue(kURLSwitch);
+ if (!url_string.empty())
+ return GURL(WideToUTF8(url_string));
+ return GURL(chrome::kChromeUIAppLauncherURL);
+}
+
+// Returns the location bar view of |browser|.
+static views::View* GetBrowserLocationBar(Browser* browser) {
+ BrowserView* browser_view = static_cast<BrowserView*>(browser->window());
+ views::RootView* root_view = views::Widget::GetWidgetFromNativeWindow(
+ browser_view->GetNativeHandle())->GetRootView();
+ return root_view->GetViewByID(VIEW_ID_LOCATION_BAR);
+}
+
+} // namespace
+
+// InfoBubbleContentsView
+//
+// The view that contains the navigation bar and DOMUI.
+// It is displayed in an info-bubble.
+
+class InfoBubbleContentsView : public views::View,
+ public LocationBarView::Delegate,
+ public CommandUpdater::CommandUpdaterDelegate {
+ public:
+ explicit InfoBubbleContentsView(AppLauncher* app_launcher);
+ ~InfoBubbleContentsView();
+
+ // Computes and sets the preferred size for the InfoBubbleContentsView based
+ // on the preferred size of the DOMUI contents specified.
+ void ComputePreferredSize(const gfx::Size& dom_view_preferred_size);
+
+ // Sets the initial focus.
+ // Should be called when the bubble that contains us is shown.
+ void BubbleShown();
+
+ // Returns the TabContents displaying the contents for this bubble.
+ TabContents* GetBubbleTabContents();
+
+ // views::View override:
+ virtual gfx::Size GetPreferredSize();
+ virtual void Layout();
+ virtual void ViewHierarchyChanged(bool is_add,
+ views::View* parent,
+ views::View* child);
+
+ // LocationBarView::Delegate implementation:
+
+ // WARNING: this is not the TabContents of the bubble! Use
+ // GetBubbleTabContents() to get the bubble's TabContents.
+ virtual TabContents* GetTabContents();
+ virtual void OnInputInProgress(bool in_progress) {}
+
+ // CommandUpdater::CommandUpdaterDelegate implementation:
+ virtual void ExecuteCommand(int id);
+
+ private:
+ // The application launcher displaying this info bubble.
+ AppLauncher* app_launcher_;
+
+ // The location bar.
+ LocationBarView* location_bar_;
+
+ // The view containing the renderer view.
+ DOMView* dom_view_;
+
+ // The preferred size for this view (at which it fits its contents).
+ gfx::Size preferred_size_;
+
+ // CommandUpdater the location bar sends commands to.
+ CommandUpdater command_updater_;
+
+ // The width of the browser's location bar.
+ int browser_location_bar_width_;
+
+ DISALLOW_COPY_AND_ASSIGN(InfoBubbleContentsView);
+};
+
+InfoBubbleContentsView::InfoBubbleContentsView(AppLauncher* app_launcher)
+ : app_launcher_(app_launcher),
+ location_bar_(NULL),
+ dom_view_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(command_updater_(this)) {
+ // Allow the location bar to open URLs.
+ command_updater_.UpdateCommandEnabled(IDC_OPEN_CURRENT_URL, true);
+ DCHECK(app_launcher);
+
+ browser_location_bar_width_ =
+ GetBrowserLocationBar(app_launcher->browser())->width();
+}
+
+InfoBubbleContentsView::~InfoBubbleContentsView() {
+}
+
+void InfoBubbleContentsView::ComputePreferredSize(
+ const gfx::Size& dom_view_preferred_size) {
+ preferred_size_ = dom_view_preferred_size;
+
+ // Add the padding and location bar height.
+ preferred_size_.Enlarge(
+ 0, location_bar_->height() + kNavigationBarBottomPadding);
+
+ // Make sure the width is at least the browser location bar width.
+ if (preferred_size_.width() < browser_location_bar_width_)
+ preferred_size_.set_width(browser_location_bar_width_);
+}
+
+void InfoBubbleContentsView::BubbleShown() {
+ location_bar_->RequestFocus();
+}
+
+TabContents* InfoBubbleContentsView::GetBubbleTabContents() {
+ return dom_view_->tab_contents();
+}
+
+void InfoBubbleContentsView::ViewHierarchyChanged(
+ bool is_add, views::View* parent, views::View* child) {
+ if (!is_add || child != this)
+ return;
+
+ DCHECK(!dom_view_);
+ dom_view_ = new DOMView();
+ AddChildView(dom_view_);
+ // We pass NULL for site instance so the renderer uses its own process.
+ dom_view_->Init(app_launcher_->browser()->profile(), NULL);
+ // We make the AppLauncher the TabContents delegate so we get notifications
+ // from the page to open links.
+ dom_view_->tab_contents()->set_delegate(app_launcher_);
+ GURL url = GetMenuURL();
+ std::string ref = url.ref();
+ if (!app_launcher_->hash_params().empty()) {
+ if (!ref.empty())
+ ref += "&";
+ ref += app_launcher_->hash_params();
+
+ url_canon::Replacements<char> replacements;
+ replacements.SetRef(ref.c_str(), url_parse::Component(0, ref.size()));
+ url = url.ReplaceComponents(replacements);
+ }
+ dom_view_->LoadURL(url);
+
+ Browser* browser = app_launcher_->browser();
+ location_bar_ = new LocationBarView(browser->profile(),
+ &command_updater_,
+ browser->toolbar_model(),
+ this,
+ LocationBarView::APP_LAUNCHER);
+
+ location_bar_->set_border(
+ views::Border::CreateSolidBorder(kNavigationBarBorderThickness,
+ kBorderColor));
+ AddChildView(location_bar_);
+ location_bar_->Init();
+ // Size the location to its preferred size so ComputePreferredSize() computes
+ // the right size.
+ location_bar_->SizeToPreferredSize();
+ ComputePreferredSize(gfx::Size(browser_location_bar_width_, 0));
+ Layout();
+}
+
+TabContents* InfoBubbleContentsView::GetTabContents() {
+ return app_launcher_->browser()->GetSelectedTabContents();
+}
+
+gfx::Size InfoBubbleContentsView::GetPreferredSize() {
+ return preferred_size_;
+}
+
+void InfoBubbleContentsView::Layout() {
+ if (bounds().IsEmpty() || GetChildViewCount() == 0)
+ return;
+
+ gfx::Rect bounds = GetLocalBounds(false);
+ // The browser's location bar use a vertical padding that we need to take into
+ // account to match its height.
+ int location_bar_height =
+ location_bar_->GetPreferredSize().height() - LocationBarView::kVertMargin;
+ location_bar_->SetBounds(bounds.x(), bounds.y(), bounds.width(),
+ location_bar_height);
+ int render_y = location_bar_->bounds().bottom() + kNavigationBarBottomPadding;
+ dom_view_->SetBounds(0, render_y,
+ width(), app_launcher_->contents_pref_size_.height());
+}
+
+void InfoBubbleContentsView::ExecuteCommand(int id) {
+ // The user navigated by typing or selecting an entry in the location bar.
+ DCHECK_EQ(IDC_OPEN_CURRENT_URL, id);
+ GURL url(WideToUTF8(location_bar_->GetInputString()));
+ app_launcher_->AddTabWithURL(url, location_bar_->GetPageTransition());
+ app_launcher_->Hide();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AppLauncher
+
+AppLauncher::AppLauncher(Browser* browser)
+ : browser_(browser),
+ info_bubble_(NULL) {
+ DCHECK(browser);
+ info_bubble_content_ = new InfoBubbleContentsView(this);
+#if defined(OS_WIN)
+ animate_ = true;
+ animation_.reset(new SlideAnimation(this));
+ animation_->SetTweenType(Tween::LINEAR);
+#else
+ animate_ = false;
+#endif
+}
+
+AppLauncher::~AppLauncher() {
+}
+
+// static
+AppLauncher* AppLauncher::Show(Browser* browser,
+ const gfx::Rect& bounds,
+ const gfx::Point& bubble_anchor,
+ const std::string& hash_params) {
+ AppLauncher* app_launcher = new AppLauncher(browser);
+ BrowserView* browser_view = static_cast<BrowserView*>(browser->window());
+ app_launcher->hash_params_ = hash_params;
+ app_launcher->info_bubble_ =
+ PinnedContentsInfoBubble::Show(browser_view->GetWidget(),
+ bounds, BubbleBorder::TOP_LEFT, bubble_anchor,
+ app_launcher->info_bubble_content_, app_launcher);
+ app_launcher->info_bubble_content_->BubbleShown();
+
+ // TODO(finnur): Change this so that we only fade out when the user launches
+ // something from the bubble. This will fade out on dismiss as well.
+ app_launcher->info_bubble_->set_fade_away_on_close(true);
+ return app_launcher;
+}
+
+// static
+AppLauncher* AppLauncher::ShowForNewTab(Browser* browser,
+ const std::string& hash_params) {
+ BrowserView* browser_view = static_cast<BrowserView*>(browser->window());
+ TabStrip* tabstrip = browser_view->tabstrip()->AsTabStrip();
+ if (!tabstrip)
+ return NULL;
+ gfx::Rect bounds = tabstrip->GetNewTabButtonBounds();
+ gfx::Point origin = bounds.origin();
+ views::RootView::ConvertPointToScreen(tabstrip, &origin);
+ bounds.set_origin(origin);
+
+ // Figure out where the location bar is, so we can pin the bubble to
+ // make our url bar appear exactly over it.
+ views::View* location_bar = GetBrowserLocationBar(browser);
+ gfx::Point location_bar_origin = location_bar->bounds().origin();
+ views::RootView::ConvertPointToScreen(location_bar->GetParent(),
+ &location_bar_origin);
+
+ return Show(browser, bounds, location_bar_origin, hash_params);
+}
+
+void AppLauncher::Hide() {
+ info_bubble_->Close();
+}
+
+void AppLauncher::OpenURLFromTab(TabContents* source,
+ const GURL& url, const GURL& referrer,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition) {
+ // TODO(jcivelli): we should call Browser::OpenApplicationTab(), we would need
+ // to access the app for this URL.
+ // The user clicked an item in the app launcher contents.
+ AddTabWithURL(url, PageTransition::AUTO_BOOKMARK);
+ Hide();
+}
+
+void AppLauncher::AddNewContents(TabContents* source,
+ TabContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture) {
+#if defined(OS_CHROMEOS)
+ // ChromeOS uses the kURLSwitch to specify a page that opens popups. We need
+ // to do this so the popups are opened when the user clicks on the page.
+ // TODO: nuke when convert to the real app page.
+ new_contents->set_delegate(NULL);
+ browser_->GetSelectedTabContents()->AddNewContents(
+ new_contents, disposition, initial_pos, user_gesture);
+ Hide();
+#endif
+}
+
+void AppLauncher::UpdatePreferredSize(const gfx::Size& pref_size) {
+ if (pref_size.width() == 0 || pref_size.height() == 0)
+ return;
+
+ previous_contents_pref_size_ = contents_pref_size_;
+ contents_pref_size_ = pref_size;
+
+ if (!animate_) {
+ info_bubble_content_->ComputePreferredSize(pref_size);
+ info_bubble_->SizeToContents();
+ return;
+ }
+
+ int original_height = previous_contents_pref_size_.height();
+ int new_height = contents_pref_size_.height();
+ int new_duration;
+ if (animation_->is_animating()) {
+ // Modify the animation duration so that the current running animation does
+ // not appear janky.
+ new_duration = static_cast<int>(new_height / kAnimationSpeedPxPerMS);
+ } else {
+ // The animation is not running.
+ animation_->Reset(); // It may have already been run.
+ new_duration = static_cast<int>(abs(new_height - original_height) /
+ kAnimationSpeedPxPerMS);
+ }
+ animation_->SetSlideDuration(new_duration);
+ animation_->Show(); // No-op if already showing.
+}
+
+void AppLauncher::AnimationProgressed(const Animation* animation) {
+ gfx::Size contents_size(contents_pref_size_.width(),
+ animation->CurrentValueBetween(previous_contents_pref_size_.height(),
+ contents_pref_size_.height()));
+ info_bubble_content_->ComputePreferredSize(contents_size);
+ info_bubble_->SizeToContents();
+}
+
+void AppLauncher::InfoBubbleClosing(InfoBubble* info_bubble,
+ bool closed_by_escape) {
+ // Delay deleting to be safe (we, and our tabcontents may be on the stack).
+ // Remove ourself as a delegate as on GTK the Widget destruction is
+ // asynchronous and will happen after the AppLauncher has been deleted (and it
+ // might notify us after we have been deleted).
+ info_bubble_content_->GetBubbleTabContents()->set_delegate(NULL);
+ MessageLoop::current()->PostTask(FROM_HERE,
+ new DeleteTask<AppLauncher>(this));
+}
+
+void AppLauncher::AddTabWithURL(const GURL& url,
+ PageTransition::Type transition) {
+ browser_->AddTabWithURL(
+ url, GURL(), transition, -1,
+ TabStripModel::ADD_SELECTED | TabStripModel::ADD_FORCE_INDEX, NULL,
+ std::string());
+}
+
+void AppLauncher::Resize(const gfx::Size& contents_size) {
+ info_bubble_content_->ComputePreferredSize(contents_size);
+ info_bubble_->SizeToContents();
+}