summaryrefslogtreecommitdiffstats
path: root/chrome/browser/tab_contents/native_ui_contents.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/tab_contents/native_ui_contents.cc')
-rw-r--r--chrome/browser/tab_contents/native_ui_contents.cc671
1 files changed, 671 insertions, 0 deletions
diff --git a/chrome/browser/tab_contents/native_ui_contents.cc b/chrome/browser/tab_contents/native_ui_contents.cc
new file mode 100644
index 0000000..9ff65ce
--- /dev/null
+++ b/chrome/browser/tab_contents/native_ui_contents.cc
@@ -0,0 +1,671 @@
+// Copyright (c) 2006-2008 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/tab_contents/native_ui_contents.h"
+
+#include "chrome/browser/browser.h"
+#include "chrome/browser/history_tab_ui.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/views/download_tab_view.h"
+#include "chrome/common/drag_drop_types.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/os_exchange_data.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/background.h"
+#include "chrome/views/checkbox.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/image_view.h"
+#include "chrome/views/root_view.h"
+#include "chrome/views/scroll_view.h"
+#include "chrome/views/throbber.h"
+#include "chrome/views/widget_win.h"
+
+#include "generated_resources.h"
+
+using views::ColumnSet;
+using views::GridLayout;
+
+//static
+bool NativeUIContents::g_ui_factories_initialized = false;
+
+// The URL scheme currently used.
+static const char kNativeUIContentsScheme[] = "chrome-nativeui";
+
+// Unique page id generator.
+static int g_next_page_id = 0;
+
+// The x-position of the title.
+static const int kDestinationTitleOffset = 38;
+
+// The x-position of the search field.
+static const int kDestinationSearchOffset = 128;
+
+// The width of the search field.
+static const int kDestinationSearchWidth = 360;
+
+// Padding between columns
+static const int kDestinationSmallerMargin = 8;
+
+// The background color.
+static const SkColor kBackground = SkColorSetRGB(255, 255, 255);
+
+// The color of the bottom margin.
+static const SkColor kBottomMarginColor = SkColorSetRGB(246, 249, 255);
+
+// The height of the bottom margin.
+static const int kBottomMargin = 5;
+
+// The Chrome product logo.
+static const SkBitmap* kProductLogo = NULL;
+
+// Padding around the product logo.
+static const int kProductLogoPadding = 8;
+
+namespace {
+
+// NativeRootView --------------------------------------------------------------
+
+// NativeRootView is a trivial RootView subclass that allows URL drops and
+// forwards them to the NavigationController to open.
+
+class NativeRootView : public views::RootView {
+ public:
+ explicit NativeRootView(NativeUIContents* host)
+ : RootView(host),
+ host_(host) { }
+
+ virtual ~NativeRootView() { }
+
+ virtual bool CanDrop(const OSExchangeData& data) {
+ return data.HasURL();
+ }
+
+ virtual int OnDragUpdated(const views::DropTargetEvent& event) {
+ if (event.GetSourceOperations() & DragDropTypes::DRAG_COPY)
+ return DragDropTypes::DRAG_COPY;
+ if (event.GetSourceOperations() & DragDropTypes::DRAG_LINK)
+ return DragDropTypes::DRAG_LINK;
+ return DragDropTypes::DRAG_NONE;
+ }
+
+ virtual int OnPerformDrop(const views::DropTargetEvent& event) {
+ GURL url;
+ std::wstring title;
+ if (!event.GetData().GetURLAndTitle(&url, &title) || !url.is_valid())
+ return DragDropTypes::DRAG_NONE;
+ host_->controller()->LoadURL(url, GURL(), PageTransition::GENERATED);
+ return OnDragUpdated(event);
+ }
+
+ private:
+ NativeUIContents* host_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(NativeRootView);
+};
+
+} // namespace
+
+
+// Returns the end of the scheme and end of the host. This is temporary until
+// bug 772411 is fixed.
+static void GetSchemeAndHostEnd(const GURL& url,
+ size_t* scheme_end,
+ size_t* host_end) {
+ const std::string spec = url.spec();
+ *scheme_end = spec.find("//");
+ DCHECK(*scheme_end != std::string::npos);
+
+ *host_end = spec.find('/', *scheme_end + 2);
+ if (*host_end == std::string::npos)
+ *host_end = spec.size();
+}
+
+NativeUIContents::NativeUIContents(Profile* profile)
+ : TabContents(TAB_CONTENTS_NATIVE_UI),
+ is_visible_(false),
+ current_ui_(NULL),
+ current_view_(NULL),
+ state_(new PageState()) {
+ if (!g_ui_factories_initialized) {
+ InitializeNativeUIFactories();
+ g_ui_factories_initialized = true;
+ }
+}
+
+NativeUIContents::~NativeUIContents() {
+ if (current_ui_) {
+ views::RootView* root_view = GetRootView();
+ current_ui_->WillBecomeInvisible(this);
+ root_view->RemoveChildView(current_view_);
+ current_ui_ = NULL;
+ current_view_ = NULL;
+ }
+
+ STLDeleteContainerPairSecondPointers(path_to_native_uis_.begin(),
+ path_to_native_uis_.end());
+}
+
+void NativeUIContents::CreateView() {
+ set_delete_on_destroy(false);
+ WidgetWin::Init(GetDesktopWindow(), gfx::Rect(), false);
+}
+
+LRESULT NativeUIContents::OnCreate(LPCREATESTRUCT create_struct) {
+ // Set the view container initial size.
+ CRect tmp;
+ ::GetWindowRect(GetHWND(), &tmp);
+ tmp.right = tmp.Width();
+ tmp.bottom = tmp.Height();
+ tmp.left = tmp.top = 0;
+
+ // Install the focus manager so we get notified of Tab key events.
+ views::FocusManager::InstallFocusSubclass(GetHWND(), NULL);
+ GetRootView()->set_background(new NativeUIBackground);
+ return 0;
+}
+
+void NativeUIContents::OnDestroy() {
+ views::FocusManager::UninstallFocusSubclass(GetHWND());
+}
+
+void NativeUIContents::OnSize(UINT size_command, const CSize& new_size) {
+ Layout();
+ ::RedrawWindow(GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
+}
+
+void NativeUIContents::OnWindowPosChanged(WINDOWPOS* position) {
+ // NOTE: this may be invoked even when the visbility didn't change, in which
+ // case hiding and showing are both false.
+ const bool hiding = (position->flags & SWP_HIDEWINDOW) == SWP_HIDEWINDOW;
+ const bool showing = (position->flags & SWP_SHOWWINDOW) == SWP_SHOWWINDOW;
+ if (hiding || showing) {
+ if (is_visible_ != showing) {
+ is_visible_ = showing;
+ if (current_ui_) {
+ if (is_visible_)
+ current_ui_->WillBecomeVisible(this);
+ else
+ current_ui_->WillBecomeInvisible(this);
+ }
+ }
+ }
+ ChangeSize(0, CSize(position->cx, position->cy));
+
+ SetMsgHandled(FALSE);
+}
+
+void NativeUIContents::GetContainerBounds(gfx::Rect* out) const {
+ GetBounds(out, false);
+}
+
+void NativeUIContents::SetPageState(PageState* page_state) {
+ if (!page_state)
+ page_state = new PageState();
+ state_.reset(page_state);
+ NavigationController* ctrl = controller();
+ if (ctrl) {
+ int ne_index = ctrl->GetLastCommittedEntryIndex();
+ NavigationEntry* ne = ctrl->GetEntryAtIndex(ne_index);
+ if (ne) {
+ // NavigationEntry is null if we're being restored.
+ DCHECK(ne);
+ std::string rep;
+ state_->GetByteRepresentation(&rep);
+ ne->set_content_state(rep);
+ ctrl->NotifyEntryChanged(ne, ne_index);
+ }
+ }
+}
+
+bool NativeUIContents::NavigateToPendingEntry(bool reload) {
+ views::RootView* root_view = GetRootView();
+ DCHECK(root_view);
+
+ if (current_ui_) {
+ current_ui_->WillBecomeInvisible(this);
+ root_view->RemoveChildView(current_view_);
+ current_ui_ = NULL;
+ current_view_ = NULL;
+ }
+
+ NavigationEntry* pending_entry = controller()->GetPendingEntry();
+ NativeUI* new_ui = GetNativeUIForURL(pending_entry->url());
+ if (new_ui) {
+ current_ui_ = new_ui;
+ is_visible_ = true;
+ current_ui_->WillBecomeVisible(this);
+ current_view_ = new_ui->GetView();
+ root_view->AddChildView(current_view_);
+
+ std::string s = pending_entry->content_state();
+ if (s.empty())
+ state_->InitWithURL(pending_entry->url());
+ else
+ state_->InitWithBytes(s);
+
+ current_ui_->Navigate(*state_);
+ Layout();
+ }
+
+ // Commit the new load in the navigation controller. If the ID of the
+ // NavigationEntry we were given was -1, that means this is a new load, so
+ // we have to generate a new ID.
+ controller()->CommitPendingEntry();
+
+ // Populate the committed entry.
+ NavigationEntry* committed_entry = controller()->GetLastCommittedEntry();
+ committed_entry->set_title(GetDefaultTitle());
+ committed_entry->favicon().set_bitmap(GetFavIcon());
+ committed_entry->favicon().set_is_valid(true);
+ if (new_ui) {
+ // Strip out the query params, they should have moved to state.
+ // TODO(sky): use GURL methods for replacements once bug is fixed.
+ size_t scheme_end, host_end;
+ GetSchemeAndHostEnd(committed_entry->url(), &scheme_end, &host_end);
+ committed_entry->set_url(
+ GURL(committed_entry->url().spec().substr(0, host_end)));
+ }
+ std::string content_state;
+ state_->GetByteRepresentation(&content_state);
+ committed_entry->set_content_state(content_state);
+
+ // Broadcast the fact that we just updated all that crap.
+ controller()->NotifyEntryChanged(
+ committed_entry,
+ controller()->GetIndexOfEntry(committed_entry));
+ return true;
+}
+
+void NativeUIContents::Layout() {
+ if (current_view_) {
+ views::RootView* root_view = GetRootView();
+ current_view_->SetBounds(0, 0, root_view->width(),
+ root_view->height());
+ current_view_->Layout();
+ }
+}
+
+const std::wstring NativeUIContents::GetDefaultTitle() const {
+ if (current_ui_)
+ return current_ui_->GetTitle();
+ else
+ return std::wstring();
+}
+
+SkBitmap NativeUIContents::GetFavIcon() const {
+ int icon_id;
+
+ if (current_ui_)
+ icon_id = current_ui_->GetFavIconID();
+ else
+ icon_id = IDR_DEFAULT_FAVICON;
+
+ return *ResourceBundle::GetSharedInstance().GetBitmapNamed(icon_id);
+}
+
+void NativeUIContents::DidBecomeSelected() {
+ TabContents::DidBecomeSelected();
+ Layout();
+}
+
+void NativeUIContents::SetInitialFocus() {
+ if (!current_ui_ || !current_ui_->SetInitialFocus()) {
+ int tab_index;
+ Browser* browser = Browser::GetBrowserForController(
+ this->controller(), &tab_index);
+ if (browser)
+ browser->FocusLocationBar();
+ else
+ TabContents::SetInitialFocus(); // Will set focus to our HWND.
+ }
+}
+
+void NativeUIContents::SetIsLoading(bool is_loading,
+ LoadNotificationDetails* details) {
+ TabContents::SetIsLoading(is_loading, details);
+}
+
+// FocusTraversable Implementation
+views::View* NativeUIContents::FindNextFocusableView(
+ views::View* starting_view, bool reverse,
+ views::FocusTraversable::Direction direction, bool dont_loop,
+ views::FocusTraversable** focus_traversable,
+ views::View** focus_traversable_view) {
+ return GetRootView()->FindNextFocusableView(
+ starting_view, reverse, direction, dont_loop,
+ focus_traversable, focus_traversable_view);
+}
+
+//static
+std::string NativeUIContents::GetScheme() {
+ return kNativeUIContentsScheme;
+}
+
+//static
+void NativeUIContents::InitializeNativeUIFactories() {
+ RegisterNativeUIFactory(DownloadTabUI::GetURL(),
+ DownloadTabUI::GetNativeUIFactory());
+ RegisterNativeUIFactory(HistoryTabUI::GetURL(),
+ HistoryTabUI::GetNativeUIFactory());
+}
+
+// static
+std::string NativeUIContents::GetFactoryKey(const GURL& url) {
+ size_t scheme_end;
+ size_t host_end;
+ GetSchemeAndHostEnd(url, &scheme_end, &host_end);
+ return url.spec().substr(scheme_end + 2, host_end - scheme_end - 2);
+}
+
+typedef std::map<std::string, NativeUIFactory*> PathToFactoryMap;
+
+static PathToFactoryMap* g_path_to_factory = NULL;
+
+//static
+void NativeUIContents::RegisterNativeUIFactory(const GURL& url,
+ NativeUIFactory* factory) {
+ const std::string key = GetFactoryKey(url);
+
+ if (!g_path_to_factory)
+ g_path_to_factory = new PathToFactoryMap;
+
+ PathToFactoryMap::iterator i = g_path_to_factory->find(key);
+ if (i != g_path_to_factory->end()) {
+ delete i->second;
+ g_path_to_factory->erase(i);
+ }
+ (*g_path_to_factory)[key] = factory;
+}
+
+views::RootView* NativeUIContents::CreateRootView() {
+ return new NativeRootView(this);
+}
+
+//static
+NativeUI* NativeUIContents::InstantiateNativeUIForURL(
+ const GURL& url, NativeUIContents* contents) {
+ if (!g_path_to_factory)
+ return NULL;
+
+ const std::string key = GetFactoryKey(url);
+
+ NativeUIFactory* factory = (*g_path_to_factory)[key];
+ if (factory)
+ return factory->CreateNativeUIForURL(url, contents);
+ else
+ return NULL;
+}
+
+NativeUI* NativeUIContents::GetNativeUIForURL(const GURL& url) {
+ const std::string key = GetFactoryKey(url);
+
+ PathToUI::iterator i = path_to_native_uis_.find(key);
+ if (i != path_to_native_uis_.end())
+ return i->second;
+
+ NativeUI* ui = InstantiateNativeUIForURL(url, this);
+ if (ui)
+ path_to_native_uis_[key] = ui;
+ return ui;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Standard NativeUI background implementation.
+//
+////////////////////////////////////////////////////////////////////////////////
+NativeUIBackground::NativeUIBackground() {
+}
+
+NativeUIBackground::~NativeUIBackground() {
+}
+
+void NativeUIBackground::Paint(ChromeCanvas* canvas,
+ views::View* view) const {
+ static const SkColor kBackground = SkColorSetRGB(255, 255, 255);
+ canvas->FillRectInt(kBackground, 0, 0, view->width(), view->height());
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// SearchableUIBackground
+// A Background subclass to be used with SearchableUIContainer objects.
+// Paint() is overridden to do nothing here; the background of the bar is
+// painted in SearchableUIContainer::Paint. This class is necessary
+// only for native controls to be able to get query the background
+// brush.
+
+class SearchableUIBackground : public views::Background {
+ public:
+ explicit SearchableUIBackground(SkColor native_control_color) {
+ SetNativeControlColor(native_control_color);
+ }
+ virtual ~SearchableUIBackground() {};
+
+ // Empty implementation.
+ // The actual painting of the bar happens in SearchableUIContainer::Paint.
+ virtual void Paint(ChromeCanvas* canvas, views::View* view) const { }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(SearchableUIBackground);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// SearchableUIContainer implementation.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+SearchableUIContainer::SearchableUIContainer(
+ SearchableUIContainer::Delegate* delegate)
+ : delegate_(delegate),
+ search_field_(NULL),
+ title_link_(NULL),
+ title_image_(NULL),
+ scroll_view_(NULL) {
+ title_link_ = new views::Link;
+ ResourceBundle& resource_bundle = ResourceBundle::GetSharedInstance();
+ ChromeFont title_font(resource_bundle
+ .GetFont(ResourceBundle::WebFont).DeriveFont(2));
+ title_link_->SetFont(title_font);
+ title_link_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ title_link_->SetController(this);
+
+ title_image_ = new views::ImageView();
+ title_image_->SetVisible(false);
+
+ // Get the product logo
+ if (!kProductLogo) {
+ kProductLogo = resource_bundle.GetBitmapNamed(IDR_PRODUCT_LOGO);
+ }
+
+ product_logo_ = new views::ImageView();
+ product_logo_->SetVisible(true);
+ product_logo_->SetImage(*kProductLogo);
+ AddChildView(product_logo_);
+
+ search_field_ = new views::TextField;
+ search_field_->SetFont(ResourceBundle::GetSharedInstance().GetFont(
+ ResourceBundle::WebFont));
+ search_field_->SetController(this);
+
+ scroll_view_ = new views::ScrollView;
+ scroll_view_->set_background(
+ views::Background::CreateSolidBackground(kBackground));
+
+ // Set background class so that native controls can get a color.
+ set_background(new SearchableUIBackground(kBackground));
+
+ throbber_ = new views::SmoothedThrobber(50);
+
+ GridLayout* layout = new GridLayout(this);
+ // View owns the LayoutManager and will delete it along with all the columns
+ // we create here.
+ SetLayoutManager(layout);
+
+ search_button_ =
+ new views::NativeButton(std::wstring());
+ search_button_->SetFont(resource_bundle.GetFont(ResourceBundle::WebFont));
+ search_button_->SetListener(this);
+
+ // Set a background color for the search button. If SearchableUIContainer
+ // provided a background, then the search button could inherit that instead.
+ search_button_->set_background(new SearchableUIBackground(kBackground));
+
+ // For the first row (icon, title/text field, search button and throbber).
+ ColumnSet* column_set = layout->AddColumnSet(0);
+ column_set->AddPaddingColumn(0, kDestinationTitleOffset);
+
+ // Add the icon column.
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF,
+ kDestinationSearchOffset - kDestinationTitleOffset -
+ kDestinationSmallerMargin,
+ kDestinationSearchOffset - kDestinationTitleOffset -
+ kDestinationSmallerMargin);
+ column_set->AddPaddingColumn(0, kDestinationSmallerMargin);
+
+ // Add the title/search field column.
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, kDestinationSearchWidth,
+ kDestinationSearchWidth);
+ column_set->AddPaddingColumn(0, kDestinationSmallerMargin);
+
+ // Add the search button column.
+ column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kDestinationSmallerMargin);
+
+ // Add the throbber column.
+ column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ // For the scroll view.
+ column_set = layout->AddColumnSet(1);
+ column_set->AddPaddingColumn(0, 1);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+
+ layout->AddPaddingRow(0, kDestinationSmallerMargin);
+ layout->StartRow(0, 0);
+ layout->AddView(title_image_, 1, 2);
+ layout->AddView(title_link_);
+
+ layout->StartRow(0, 0);
+ layout->SkipColumns(1);
+ layout->AddView(search_field_);
+ layout->AddView(search_button_);
+ layout->AddView(throbber_);
+
+ layout->AddPaddingRow(0, kDestinationSmallerMargin);
+ layout->StartRow(1, 1);
+ layout->AddView(scroll_view_);
+}
+
+SearchableUIContainer::~SearchableUIContainer() {
+}
+
+void SearchableUIContainer::SetContents(views::View* contents) {
+ // The column view will resize to accomodate long titles.
+ title_link_->SetText(delegate_->GetTitle());
+
+ int section_icon_id = delegate_->GetSectionIconID();
+ if (section_icon_id != 0) {
+ title_image_->SetImage(*ResourceBundle::GetSharedInstance().
+ GetBitmapNamed(section_icon_id));
+ title_image_->SetVisible(true);
+ }
+
+ search_button_->SetLabel(delegate_->GetSearchButtonText());
+ scroll_view_->SetContents(contents);
+}
+
+views::View* SearchableUIContainer::GetContents() {
+ return scroll_view_->GetContents();
+}
+
+void SearchableUIContainer::Layout() {
+ View::Layout();
+
+ gfx::Size search_button_size = search_button_->GetPreferredSize();
+ gfx::Size product_logo_size = product_logo_->GetPreferredSize();
+
+ int field_width = kDestinationSearchOffset +
+ kDestinationSearchWidth +
+ kDestinationSmallerMargin +
+ static_cast<int>(search_button_size.width()) +
+ kDestinationSmallerMargin;
+
+ product_logo_->SetBounds(std::max(width() - kProductLogo->width() -
+ kProductLogoPadding,
+ field_width),
+ kProductLogoPadding,
+ product_logo_size.width(),
+ product_logo_size.height());
+}
+
+void SearchableUIContainer::Paint(ChromeCanvas* canvas) {
+ SkColor top_color(kBackground);
+ canvas->FillRectInt(top_color, 0, 0,
+ width(), scroll_view_->y());
+
+ canvas->FillRectInt(kBottomMarginColor, 0, scroll_view_->y() -
+ kBottomMargin, width(), kBottomMargin);
+
+ canvas->FillRectInt(SkColorSetRGB(196, 196, 196),
+ 0, scroll_view_->y() - 1, width(), 1);
+}
+
+views::TextField* SearchableUIContainer::GetSearchField() const {
+ return search_field_;
+}
+
+views::ScrollView* SearchableUIContainer::GetScrollView() const {
+ return scroll_view_;
+}
+
+void SearchableUIContainer::SetSearchEnabled(bool enabled) {
+ search_field_->SetReadOnly(!enabled);
+ search_button_->SetEnabled(enabled);
+}
+
+void SearchableUIContainer::StartThrobber() {
+ throbber_->Start();
+}
+
+void SearchableUIContainer::StopThrobber() {
+ throbber_->Stop();
+}
+
+void SearchableUIContainer::ButtonPressed(views::NativeButton* sender) {
+ DoSearch();
+}
+
+void SearchableUIContainer::LinkActivated(views::Link *link,
+ int event_flags) {
+ if (link == title_link_) {
+ search_field_->SetText(std::wstring());
+ DoSearch();
+ }
+}
+
+void SearchableUIContainer::HandleKeystroke(views::TextField* sender,
+ UINT message,
+ TCHAR key,
+ UINT repeat_count,
+ UINT flags) {
+ if (key == VK_RETURN)
+ DoSearch();
+}
+
+void SearchableUIContainer::DoSearch() {
+ if (delegate_)
+ delegate_->DoSearch(search_field_->GetText());
+
+ scroll_view_->ScrollToPosition(scroll_view_->vertical_scroll_bar(), 0);
+}
+