diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-14 21:38:30 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-14 21:38:30 +0000 |
commit | 03bb953da2a51fa35e7735ce711c1be6946312ea (patch) | |
tree | bd9fe31bd34036670ccc5247dcdba72cb9609400 /chrome/browser | |
parent | 037d1e192cfbad001946fe026d0460e636fa8e76 (diff) | |
download | chromium_src-03bb953da2a51fa35e7735ce711c1be6946312ea.zip chromium_src-03bb953da2a51fa35e7735ce711c1be6946312ea.tar.gz chromium_src-03bb953da2a51fa35e7735ce711c1be6946312ea.tar.bz2 |
Bunch of match preview tweaks:
. Makes MatchPreview owned by Browser rather than each TabContents.
. Makes MatchPreview dismiss when the omnibox closes.
. Supports the ability to send script to the page rather than
reloading on every keystroke.
. Supports receiving results from the page that drives the suggest
text in the omnbox.
BUG=54833
TEST=none
Review URL: http://codereview.chromium.org/3332022
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@59428 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
33 files changed, 1071 insertions, 283 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_edit.cc b/chrome/browser/autocomplete/autocomplete_edit.cc index 1162943..442ef5c 100644 --- a/chrome/browser/autocomplete/autocomplete_edit.cc +++ b/chrome/browser/autocomplete/autocomplete_edit.cc @@ -129,11 +129,10 @@ void AutocompleteEditModel::RestoreState(const State& state) { } } -GURL AutocompleteEditModel::CurrentURL(PageTransition::Type* transition_type) { +AutocompleteMatch AutocompleteEditModel::CurrentMatch() { AutocompleteMatch match; GetInfoForCurrentText(&match, NULL); - *transition_type = match.transition; - return match.destination_url; + return match; } bool AutocompleteEditModel::UpdatePermanentText( diff --git a/chrome/browser/autocomplete/autocomplete_edit.h b/chrome/browser/autocomplete/autocomplete_edit.h index 8376cb5..1454111 100644 --- a/chrome/browser/autocomplete/autocomplete_edit.h +++ b/chrome/browser/autocomplete/autocomplete_edit.h @@ -134,10 +134,9 @@ class AutocompleteEditModel : public NotificationObserver { // Restores local state from the saved |state|. void RestoreState(const State& state); - // Returns the url and transition type for the current text. If the user has - // not edited the text this is the permanent url, otherwise it is the url the - // user would navigate to if they accept the current edit. - GURL CurrentURL(PageTransition::Type* transition_type); + // Returns the match for the current text. If the user has not edited the text + // this is the match corresponding to the permanent text. + AutocompleteMatch CurrentMatch(); // Called when the user wants to export the entire current text as a URL. // Sets the url, and if known, the title and favicon. diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc index a597915..1432000 100644 --- a/chrome/browser/browser.cc +++ b/chrome/browser/browser.cc @@ -254,6 +254,11 @@ Browser::Browser(Type type, Profile* profile) if (profile_->GetProfileSyncService()) profile_->GetProfileSyncService()->AddObserver(this); + + if (type == TYPE_NORMAL && MatchPreview::IsEnabled() && + !profile->IsOffTheRecord()) { + match_preview_.reset(new MatchPreview(this)); + } } Browser::~Browser() { @@ -1263,6 +1268,13 @@ void Browser::OpenCurrentURL() { LocationBar* location_bar = window_->GetLocationBar(); WindowOpenDisposition open_disposition = location_bar->GetWindowOpenDisposition(); + // TODO(sky): support other dispositions. + if (open_disposition == CURRENT_TAB && match_preview() && + match_preview()->is_active()) { + match_preview()->CommitCurrentPreview(); + return; + } + GURL url(WideToUTF8(location_bar->GetInputString())); // Use ADD_INHERIT_OPENER so that all pages opened by the omnibox at least @@ -2500,6 +2512,9 @@ void Browser::TabDetachedAt(TabContents* contents, int index) { } void Browser::TabDeselectedAt(TabContents* contents, int index) { + if (match_preview()) + match_preview()->DestroyPreviewContents(); + // Save what the user's currently typing, so it can be restored when we // switch back to this tab. window_->GetLocationBar()->SaveStateToContents(contents); @@ -3039,16 +3054,6 @@ void Browser::ContentTypeChanged(TabContents* source) { UpdateZoomCommandsForTabState(); } -void Browser::CommitMatchPreview(TabContents* source) { - int index = tabstrip_model_.GetIndexOfTabContents(source); - DCHECK_NE(-1, index); - TabContents* preview_contents = - source->match_preview()->ReleasePreviewContents(); - // TabStripModel takes ownership of preview_contents. - tabstrip_model_.ReplaceTabContentsAt( - index, preview_contents, TabStripModelObserver::REPLACE_MATCH_PREVIEW); -} - /////////////////////////////////////////////////////////////////////////////// // Browser, SelectFileDialog::Listener implementation: @@ -3218,6 +3223,37 @@ void Browser::OnStateChanged() { } /////////////////////////////////////////////////////////////////////////////// +// Browser, MatchPreviewDelegate implementation: + +void Browser::ShowMatchPreview() { + DCHECK(match_preview_->tab_contents() == GetSelectedTabContents()); + window_->ShowMatchPreview(); +} + +void Browser::HideMatchPreview() { + if (match_preview_->tab_contents() == GetSelectedTabContents()) + window_->HideMatchPreview(); +} + +void Browser::CommitMatchPreview() { + TabContents* tab_contents = match_preview_->tab_contents(); + int index = tabstrip_model_.GetIndexOfTabContents(tab_contents); + DCHECK_NE(-1, index); + scoped_ptr<TabContents> preview_contents( + match_preview()->ReleasePreviewContents(true)); + preview_contents->controller().CopyStateFromAndPrune( + tab_contents->controller()); + // TabStripModel takes ownership of preview_contents. + tabstrip_model_.ReplaceTabContentsAt( + index, preview_contents.release(), + TabStripModelObserver::REPLACE_MATCH_PREVIEW); +} + +void Browser::SetSuggestedText(const string16& text) { + window()->GetLocationBar()->SetSuggestedText(text); +} + +/////////////////////////////////////////////////////////////////////////////// // Browser, Command and state updating (private): void Browser::InitCommandState() { diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h index 3ff0647..eceaa39 100644 --- a/chrome/browser/browser.h +++ b/chrome/browser/browser.h @@ -14,6 +14,7 @@ #include "base/basictypes.h" #include "base/gtest_prod_util.h" #include "base/scoped_ptr.h" +#include "base/string16.h" #include "base/task.h" #include "chrome/browser/command_updater.h" #include "chrome/browser/debugger/devtools_toggle_action.h" @@ -23,6 +24,7 @@ #include "chrome/browser/shell_dialogs.h" #include "chrome/browser/sync/profile_sync_service_observer.h" #include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/browser/tab_contents/match_preview_delegate.h" #include "chrome/browser/tab_contents/page_navigator.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" #include "chrome/browser/toolbar_model.h" @@ -35,6 +37,7 @@ class BrowserWindow; class Extension; class FindBarController; +class MatchPreview; class PrefService; class Profile; class SessionStorageNamespace; @@ -53,7 +56,8 @@ class Browser : public TabStripModelDelegate, public NotificationObserver, public SelectFileDialog::Listener, public TabRestoreServiceObserver, - public ProfileSyncServiceObserver { + public ProfileSyncServiceObserver, + public MatchPreviewDelegate { public: // If you change the values in this enum you'll need to update browser_proxy. // TODO(sky): move into a common place that is referenced by both ui_tests @@ -163,6 +167,10 @@ class Browser : public TabStripModelDelegate, Profile* profile() const { return profile_; } const std::vector<std::wstring>& user_data_dir_profiles() const; + // Returns the MatchPreview or NULL if there is no MatchPreview for this + // Browser. + MatchPreview* match_preview() const { return match_preview_.get(); } + #if defined(UNIT_TEST) // Sets the BrowserWindow. This is intended for testing and generally not // useful outside of testing. Use CreateBrowserWindow outside of testing, or @@ -758,7 +766,6 @@ class Browser : public TabStripModelDelegate, virtual void OnDidGetApplicationInfo(TabContents* tab_contents, int32 page_id); virtual void ContentTypeChanged(TabContents* source); - virtual void CommitMatchPreview(TabContents* source); // Overridden from SelectFileDialog::Listener: virtual void FileSelected(const FilePath& path, int index, void* params); @@ -771,6 +778,12 @@ class Browser : public TabStripModelDelegate, // Overridden from ProfileSyncServiceObserver: virtual void OnStateChanged(); + // Overriden from MatchPreviewDelegate: + virtual void ShowMatchPreview(); + virtual void HideMatchPreview(); + virtual void CommitMatchPreview(); + virtual void SetSuggestedText(const string16& text); + // Command and state updating /////////////////////////////////////////////// // Initialize state for all browser commands. @@ -1081,6 +1094,8 @@ class Browser : public TabStripModelDelegate, // and we install ourselves as an observer. TabRestoreService* tab_restore_service_; + scoped_ptr<MatchPreview> match_preview_; + DISALLOW_COPY_AND_ASSIGN(Browser); }; diff --git a/chrome/browser/browser_window.h b/chrome/browser/browser_window.h index 98cd042..9009327 100644 --- a/chrome/browser/browser_window.h +++ b/chrome/browser/browser_window.h @@ -312,6 +312,12 @@ class BrowserWindow { virtual void OpenTabpose() = 0; #endif + // Invoked when the match preview's tab contents should be shown. + virtual void ShowMatchPreview() = 0; + + // Invoked when the match preview's tab contents should be hidden. + virtual void HideMatchPreview() = 0; + // Construct a BrowserWindow implementation for the specified |browser|. static BrowserWindow* CreateBrowserWindow(Browser* browser); diff --git a/chrome/browser/cocoa/browser_window_cocoa.h b/chrome/browser/cocoa/browser_window_cocoa.h index 10b9fd3..226e573 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.h +++ b/chrome/browser/cocoa/browser_window_cocoa.h @@ -106,6 +106,8 @@ class BrowserWindowCocoa : public BrowserWindow, virtual void Paste(); virtual void ToggleTabStripMode(); virtual void OpenTabpose(); + virtual void ShowMatchPreview(); + virtual void HideMatchPreview(); // Overridden from NotificationObserver virtual void Observe(NotificationType type, diff --git a/chrome/browser/cocoa/browser_window_cocoa.mm b/chrome/browser/cocoa/browser_window_cocoa.mm index fa9d6c6..f889a3c 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.mm +++ b/chrome/browser/cocoa/browser_window_cocoa.mm @@ -564,6 +564,16 @@ void BrowserWindowCocoa::OpenTabpose() { [controller_ openTabpose]; } +void BrowserWindowCocoa::ShowMatchPreview() { + // TODO: implement me + NOTIMPLEMENTED(); +} + +void BrowserWindowCocoa::HideMatchPreview() { + // TODO: implement me + NOTIMPLEMENTED(); +} + void BrowserWindowCocoa::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { diff --git a/chrome/browser/cocoa/location_bar/location_bar_view_mac.h b/chrome/browser/cocoa/location_bar/location_bar_view_mac.h index 6a7a598..0ec9058 100644 --- a/chrome/browser/cocoa/location_bar/location_bar_view_mac.h +++ b/chrome/browser/cocoa/location_bar/location_bar_view_mac.h @@ -54,6 +54,7 @@ class LocationBarViewMac : public AutocompleteEditController, // Overridden from LocationBar: virtual void ShowFirstRunBubble(FirstRun::BubbleType bubble_type); + virtual void SetSuggestedText(const string16& text); virtual std::wstring GetInputString() const; virtual WindowOpenDisposition GetWindowOpenDisposition() const; virtual PageTransition::Type GetPageTransition() const; diff --git a/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm b/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm index 9bc4d1d..d8b8b80 100644 --- a/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm +++ b/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm @@ -124,6 +124,11 @@ std::wstring LocationBarViewMac::GetInputString() const { return location_input_; } +void LocationBarViewMac::SetSuggestedText(const string16& text) { + // TODO: implement me. + NOTIMPLEMENTED(); +} + WindowOpenDisposition LocationBarViewMac::GetWindowOpenDisposition() const { return disposition_; } diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc index da1a13d..1c8418f 100644 --- a/chrome/browser/gtk/browser_window_gtk.cc +++ b/chrome/browser/gtk/browser_window_gtk.cc @@ -1129,6 +1129,16 @@ void BrowserWindowGtk::Paste() { DoCutCopyPaste(this, &RenderViewHost::Paste, "paste-clipboard"); } +void BrowserWindowGtk::ShowMatchPreview() { + // TODO: implement me + NOTIMPLEMENTED(); +} + +void BrowserWindowGtk::HideMatchPreview() { + // TODO: implement me + NOTIMPLEMENTED(); +} + void BrowserWindowGtk::ConfirmBrowserCloseWithPendingDownloads() { new DownloadInProgressDialogGtk(browser()); } diff --git a/chrome/browser/gtk/browser_window_gtk.h b/chrome/browser/gtk/browser_window_gtk.h index 44dedb4..f80be5e 100644 --- a/chrome/browser/gtk/browser_window_gtk.h +++ b/chrome/browser/gtk/browser_window_gtk.h @@ -123,6 +123,8 @@ class BrowserWindowGtk : public BrowserWindow, virtual void Copy(); virtual void Paste(); virtual void ToggleTabStripMode() {} + virtual void ShowMatchPreview(); + virtual void HideMatchPreview(); // Overridden from NotificationObserver: virtual void Observe(NotificationType type, diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc index 705be84..3d3cefd 100644 --- a/chrome/browser/gtk/location_bar_view_gtk.cc +++ b/chrome/browser/gtk/location_bar_view_gtk.cc @@ -544,6 +544,11 @@ void LocationBarViewGtk::ShowFirstRunBubble(FirstRun::BubbleType bubble_type) { MessageLoop::current()->PostTask(FROM_HERE, task); } +void LocationBarViewGtk::SetSuggestedText(const string16& text) { + // TODO: implement me. + NOTIMPLEMENTED(); +} + std::wstring LocationBarViewGtk::GetInputString() const { return location_input_; } diff --git a/chrome/browser/gtk/location_bar_view_gtk.h b/chrome/browser/gtk/location_bar_view_gtk.h index c0fef25..8c820d2 100644 --- a/chrome/browser/gtk/location_bar_view_gtk.h +++ b/chrome/browser/gtk/location_bar_view_gtk.h @@ -101,6 +101,7 @@ class LocationBarViewGtk : public AutocompleteEditController, // Implement the LocationBar interface. virtual void ShowFirstRunBubble(FirstRun::BubbleType bubble_type); + virtual void SetSuggestedText(const string16& text); virtual std::wstring GetInputString() const; virtual WindowOpenDisposition GetWindowOpenDisposition() const; virtual PageTransition::Type GetPageTransition() const; diff --git a/chrome/browser/location_bar.h b/chrome/browser/location_bar.h index d16bd8f..b6a6d0d 100644 --- a/chrome/browser/location_bar.h +++ b/chrome/browser/location_bar.h @@ -14,6 +14,7 @@ #include <string> +#include "base/string16.h" #include "chrome/browser/first_run/first_run.h" #include "chrome/common/page_transition_types.h" #include "webkit/glue/window_open_disposition.h" @@ -28,12 +29,9 @@ class LocationBar { // Shows the first run information bubble anchored to the location bar. virtual void ShowFirstRunBubble(FirstRun::BubbleType bubble_type) = 0; - // TODO: port -#if defined(TOOLKIT_VIEWS) // Sets the suggested text to show in the omnibox. This is shown in addition // to the current text of the omnibox. - virtual void SetSuggestedText(const std::wstring& text) = 0; -#endif + virtual void SetSuggestedText(const string16& text) = 0; // Returns the string of text entered in the location bar. virtual std::wstring GetInputString() const = 0; diff --git a/chrome/browser/search_engines/template_url.h b/chrome/browser/search_engines/template_url.h index 42eb0bf..ceaa0ecd2 100644 --- a/chrome/browser/search_engines/template_url.h +++ b/chrome/browser/search_engines/template_url.h @@ -277,7 +277,7 @@ class TemplateURL { // Generates a favicon URL from the specified url. static GURL GenerateFaviconURL(const GURL& url); - // Returns true if |true| is non-null and has a search URL that supports + // Returns true if |turl| is non-null and has a search URL that supports // replacement. static bool SupportsReplacement(const TemplateURL* turl); @@ -297,6 +297,10 @@ class TemplateURL { } const std::wstring& short_name() const { return short_name_; } + // Returns true if this search engine supports showing instant results. + // TODO(sky): make this real. + bool supports_instant() const { return false; } + // An accessor for the short_name, but adjusted so it can be appropriately // displayed even if it is LTR and the UI is RTL. std::wstring AdjustedShortNameForLocaleDirection() const; diff --git a/chrome/browser/tab_contents/match_preview.cc b/chrome/browser/tab_contents/match_preview.cc index 709a582..d167ceb 100644 --- a/chrome/browser/tab_contents/match_preview.cc +++ b/chrome/browser/tab_contents/match_preview.cc @@ -7,20 +7,204 @@ #include <algorithm> #include "base/command_line.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/favicon_service.h" +#include "chrome/browser/history/history_marshaling.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" +#include "chrome/browser/search_engines/template_url.h" +#include "chrome/browser/search_engines/template_url_model.h" +#include "chrome/browser/tab_contents/match_preview_delegate.h" #include "chrome/browser/tab_contents/navigation_controller.h" #include "chrome/browser/tab_contents/navigation_entry.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" #include "chrome/common/page_transition_types.h" +#include "chrome/common/render_messages.h" #include "chrome/common/renderer_preferences.h" +#include "gfx/codec/png_codec.h" #include "ipc/ipc_message.h" +namespace { + +const char kUserInputScript[] = + "if (window.chrome.userInput) window.chrome.userInput(\"$1\");"; + +// Sends the user input script to |tab_contents|. |text| is the text the user +// input into the omnibox. +void SendUserInputScript(TabContents* tab_contents, + const string16& text, + bool done) { + // TODO: support done. + string16 escaped_text(text); + ReplaceSubstringsAfterOffset(&escaped_text, 0L, ASCIIToUTF16("\""), + ASCIIToUTF16("\\\"")); + string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserInputScript), + escaped_text, NULL); + tab_contents->render_view_host()->ExecuteJavascriptInWebFrame( + std::wstring(), + UTF16ToWide(script)); +} + +} // namespace + +// FrameLoadObserver is responsible for waiting for the TabContents to finish +// loading and when done sending the necessary script down to the page. +class MatchPreview::FrameLoadObserver : public NotificationObserver { + public: + FrameLoadObserver(MatchPreview* match_preview, const string16& text) + : match_preview_(match_preview), + tab_contents_(match_preview->preview_contents()), + unique_id_(tab_contents_->controller().pending_entry()->unique_id()), + text_(text), + send_done_(false) { + registrar_.Add(this, NotificationType::LOAD_COMPLETED_MAIN_FRAME, + Source<TabContents>(tab_contents_)); + registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(tab_contents_)); + } + + // Sets the text to send to the page. + void set_text(const string16& text) { text_ = text; } + + // Invoked when the MatchPreview releases ownership of the TabContents and + // the page hasn't finished loading. + void DetachFromPreview() { + match_preview_ = NULL; + send_done_ = true; + } + + // NotificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::LOAD_COMPLETED_MAIN_FRAME: { + int page_id = *(Details<int>(details).ptr()); + NavigationEntry* active_entry = + tab_contents_->controller().GetActiveEntry(); + if (!active_entry || active_entry->page_id() != page_id || + active_entry->unique_id() != unique_id_) { + return; + } + + SendUserInputScript(tab_contents_, text_, send_done_); + + if (match_preview_) + match_preview_->PageFinishedLoading(); + + delete this; + return; + } + + case NotificationType::TAB_CONTENTS_DESTROYED: + delete this; + return; + + default: + NOTREACHED(); + break; + } + } + + private: + // MatchPreview that created us. + MatchPreview* match_preview_; + + // The TabContents we're listening for changes on. + TabContents* tab_contents_; + + // unique_id of the NavigationEntry we're waiting on. + const int unique_id_; + + // Text to send down to the page. + string16 text_; + + // Passed to SendScript. + bool send_done_; + + // Registers and unregisters us for notifications. + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(FrameLoadObserver); +}; + +// PaintObserver implementation. When the RenderWidgetHost paints itself this +// notifies MatchPreview, which makes the TabContents active. +class MatchPreview::PaintObserverImpl : public RenderWidgetHost::PaintObserver { + public: + explicit PaintObserverImpl(MatchPreview* preview) + : match_preview_(preview) { + } + + virtual void RenderWidgetHostWillPaint(RenderWidgetHost* rwh) { + } + + virtual void RenderWidgetHostDidPaint(RenderWidgetHost* rwh) { + match_preview_->PreviewDidPaint(); + rwh->set_paint_observer(NULL); + // WARNING: we've been deleted. + } + + private: + MatchPreview* match_preview_; + + DISALLOW_COPY_AND_ASSIGN(PaintObserverImpl); +}; + class MatchPreview::TabContentsDelegateImpl : public TabContentsDelegate { public: explicit TabContentsDelegateImpl(MatchPreview* match_preview) - : match_preview_(match_preview) { + : match_preview_(match_preview), + installed_paint_observer_(false), + waiting_for_new_page_(true) { + } + + // Invoked prior to loading a new URL. + void PrepareForNewLoad() { + waiting_for_new_page_ = true; + add_page_vector_.clear(); + } + + // Invoked when removed as the delegate. Gives a chance to do any necessary + // cleanup. + void Reset() { + installed_paint_observer_ = false; + } + + // Commits the currently buffered history. + void CommitHistory() { + TabContents* tab = match_preview_->preview_contents(); + if (tab->profile()->IsOffTheRecord()) + return; + + for (size_t i = 0; i < add_page_vector_.size(); ++i) + tab->UpdateHistoryForNavigation(add_page_vector_[i].get()); + + NavigationEntry* active_entry = tab->controller().GetActiveEntry(); + DCHECK(active_entry); + tab->UpdateHistoryPageTitle(*active_entry); + + FaviconService* favicon_service = + tab->profile()->GetFaviconService(Profile::EXPLICIT_ACCESS); + + if (favicon_service && active_entry->favicon().is_valid() && + !active_entry->favicon().bitmap().empty()) { + std::vector<unsigned char> image_data; + gfx::PNGCodec::EncodeBGRASkBitmap(active_entry->favicon().bitmap(), false, + &image_data); + favicon_service->SetFavicon(active_entry->url(), + active_entry->favicon().url(), + image_data); + } } virtual void OpenURLFromTab(TabContents* source, @@ -28,14 +212,24 @@ class MatchPreview::TabContentsDelegateImpl : public TabContentsDelegate { WindowOpenDisposition disposition, PageTransition::Type transition) {} virtual void NavigationStateChanged(const TabContents* source, - unsigned changed_flags) {} + unsigned changed_flags) { + if (!installed_paint_observer_ && source->controller().entry_count()) { + // The load has been committed. Install an observer that waits for the + // first paint then makes the preview active. We wait for the load to be + // committed before waiting on paint as there is always an initial paint + // when a new renderer is created from the resize so that if we showed the + // preview after the first paint we would end up with a white rect. + installed_paint_observer_ = true; + source->GetRenderWidgetHostView()->GetRenderWidgetHost()-> + set_paint_observer(new PaintObserverImpl(match_preview_)); + } + } virtual void AddNewContents(TabContents* source, TabContents* new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture) {} virtual void ActivateContents(TabContents* contents) { - match_preview_->CommitCurrentPreview(); } virtual void DeactivateContents(TabContents* contents) {} virtual void LoadingStateChanged(TabContents* source) {} @@ -59,9 +253,7 @@ class MatchPreview::TabContentsDelegateImpl : public TabContentsDelegate { virtual void ConvertContentsToApplication(TabContents* source) {} virtual bool CanReloadContents(TabContents* source) const { return true; } virtual gfx::Rect GetRootWindowResizerRect() const { - return match_preview_->host_->delegate() ? - match_preview_->host_->delegate()->GetRootWindowResizerRect() : - gfx::Rect(); + return gfx::Rect(); } virtual void ShowHtmlDialog(HtmlDialogUIDelegate* delegate, gfx::NativeWindow parent_window) {} @@ -82,7 +274,6 @@ class MatchPreview::TabContentsDelegateImpl : public TabContentsDelegate { virtual bool TakeFocus(bool reverse) { return false; } virtual void SetTabContentBlocked(TabContents* contents, bool blocked) {} virtual void TabContentsFocused(TabContents* tab_content) { - match_preview_->CommitCurrentPreview(); } virtual int GetExtraRenderViewHeight() const { return 0; } virtual bool CanDownload(int request_id) { return false; } @@ -108,16 +299,22 @@ class MatchPreview::TabContentsDelegateImpl : public TabContentsDelegate { virtual void ShowContentSettingsWindow(ContentSettingsType content_type) {} virtual void ShowCollectedCookiesDialog(TabContents* tab_contents) {} virtual bool OnGoToEntryOffset(int offset) { return false; } - virtual bool ShouldAddNavigationToHistory( + virtual bool ShouldAddNavigationsToHistory( const history::HistoryAddPageArgs& add_page_args, NavigationType::Type navigation_type) { + if (waiting_for_new_page_ && navigation_type == NavigationType::NEW_PAGE) + waiting_for_new_page_ = false; + + if (!waiting_for_new_page_) { + add_page_vector_.push_back( + scoped_refptr<history::HistoryAddPageArgs>(add_page_args.Clone())); + } return false; } virtual void OnDidGetApplicationInfo(TabContents* tab_contents, int32 page_id) {} virtual gfx::NativeWindow GetFrameNativeWindow() { - return match_preview_->host_->delegate() ? - match_preview_->host_->delegate()->GetFrameNativeWindow() : NULL; + return NULL; } virtual void TabContentsCreated(TabContents* new_contents) {} virtual bool infobars_enabled() { return false; } @@ -125,21 +322,37 @@ class MatchPreview::TabContentsDelegateImpl : public TabContentsDelegate { virtual void UpdatePreferredSize(const gfx::Size& pref_size) {} virtual void ContentTypeChanged(TabContents* source) {} + virtual void OnSetSuggestResult(int32 page_id, const std::string& result) { + TabContents* source = match_preview_->preview_contents(); + // TODO: only allow for default search provider. + if (source->controller().GetActiveEntry() && + page_id == source->controller().GetActiveEntry()->page_id()) { + match_preview_->SetCompleteSuggestedText(UTF8ToUTF16(result)); + } + } + private: + typedef std::vector<scoped_refptr<history::HistoryAddPageArgs> > + AddPageVector; + MatchPreview* match_preview_; - DISALLOW_COPY_AND_ASSIGN(TabContentsDelegateImpl); -}; + // Has the paint observer been installed? See comment in + // NavigationStateChanged for details on this. + bool installed_paint_observer_; -MatchPreview::MatchPreview(TabContents* host) : host_(host) { - delegate_.reset(new TabContentsDelegateImpl(this)); -} + // Used to cache data that needs to be added to history. Normally entries are + // added to history as the user types, but for match preview we only want to + // add the items to history if the user commits the match preview. So, we + // cache them here and if committed then add the items to history. + AddPageVector add_page_vector_; -MatchPreview::~MatchPreview() { - // Delete the TabContents before the delegate as the TabContents holds a - // reference to the delegate. - preview_contents_.reset(NULL); -} + // Are we we waiting for a NavigationType of NEW_PAGE? If we're waiting for + // NEW_PAGE navigation we don't add history items to add_page_vector_. + bool waiting_for_new_page_; + + DISALLOW_COPY_AND_ASSIGN(TabContentsDelegateImpl); +}; // static bool MatchPreview::IsEnabled() { @@ -153,44 +366,163 @@ bool MatchPreview::IsEnabled() { return enabled; } -void MatchPreview::Update(const GURL& url) { - if (url_ == url) +MatchPreview::MatchPreview(MatchPreviewDelegate* delegate) + : delegate_(delegate), + tab_contents_(NULL), + is_active_(false), + template_url_id_(0) { + preview_tab_contents_delegate_.reset(new TabContentsDelegateImpl(this)); +} + +MatchPreview::~MatchPreview() { + // Delete the TabContents before the delegate as the TabContents holds a + // reference to the delegate. + preview_contents_.reset(NULL); +} + +void MatchPreview::Update(TabContents* tab_contents, + const AutocompleteMatch& match, + const string16& user_text, + string16* suggested_text) { + if (tab_contents != tab_contents_) + DestroyPreviewContents(); + + tab_contents_ = tab_contents; + + if (url_ == match.destination_url) return; - url_ = url; + url_ = match.destination_url; if (url_.is_empty() || !url_.is_valid()) { DestroyPreviewContents(); return; } - if (!preview_contents_.get()) { + user_text_ = user_text; + + if (preview_contents_.get() == NULL) { preview_contents_.reset( - new TabContents(host_->profile(), NULL, MSG_ROUTING_NONE, NULL, NULL)); - preview_contents_->set_delegate(delegate_.get()); - NotificationService::current()->Notify( - NotificationType::MATCH_PREVIEW_TAB_CONTENTS_CREATED, - Source<TabContents>(host_), - NotificationService::NoDetails()); + new TabContents(tab_contents_->profile(), NULL, MSG_ROUTING_NONE, + NULL, NULL)); + // Propagate the max page id. That way if we end up merging the two + // NavigationControllers (which happens if we commit) none of the page ids + // will overlap. + int32 max_page_id = tab_contents_->GetMaxPageID(); + if (max_page_id != -1) + preview_contents_->controller().set_max_restored_page_id(max_page_id + 1); + + preview_contents_->set_delegate(preview_tab_contents_delegate_.get()); + + gfx::Rect tab_bounds; + tab_contents_->view()->GetContainerBounds(&tab_bounds); + preview_contents_->view()->SizeContents(tab_bounds.size()); + + preview_contents_->ShowContents(); } + preview_tab_contents_delegate_->PrepareForNewLoad(); - // TODO: figure out transition type. - preview_contents_->controller().LoadURL(url, GURL(), - PageTransition::GENERATED); + const TemplateURL* template_url = match.template_url; + if (match.type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED || + match.type == AutocompleteMatch::SEARCH_HISTORY || + match.type == AutocompleteMatch::SEARCH_SUGGEST) { + TemplateURLModel* model = tab_contents->profile()->GetTemplateURLModel(); + template_url = model ? model->GetDefaultSearchProvider() : NULL; + } + TemplateURLID template_url_id = template_url ? template_url->id() : 0; + + if (template_url && template_url->supports_instant() && + TemplateURL::SupportsReplacement(template_url)) { + if (template_url_id == template_url_id_) { + if (frame_load_observer_.get()) { + // The page hasn't loaded yet. We'll send the script down when it does. + frame_load_observer_->set_text(user_text_); + return; + } + SendUserInputScript(preview_contents_.get(), user_text_, false); + if (complete_suggested_text_.size() > user_text_.size() && + !complete_suggested_text_.compare(0, user_text_.size(), user_text_)) { + *suggested_text = complete_suggested_text_.substr(user_text_.size()); + } + } else { + // TODO: should we use a different url for instant? + GURL url = GURL(template_url->url()->ReplaceSearchTerms( + *template_url, std::wstring(), + TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring())); + // user_text_ is sent once the page finishes loading by FrameLoadObserver. + preview_contents_->controller().LoadURL(url, GURL(), match.transition); + frame_load_observer_.reset(new FrameLoadObserver(this, user_text_)); + } + } else { + frame_load_observer_.reset(NULL); + preview_contents_->controller().LoadURL(url_, GURL(), match.transition); + } + + template_url_id_ = template_url_id; } void MatchPreview::DestroyPreviewContents() { - url_ = GURL(); - preview_contents_.reset(NULL); + delegate_->HideMatchPreview(); + delete ReleasePreviewContents(false); } void MatchPreview::CommitCurrentPreview() { DCHECK(preview_contents_.get()); - if (host_->delegate()) - host_->delegate()->CommitMatchPreview(host_); + delegate_->CommitMatchPreview(); } -TabContents* MatchPreview::ReleasePreviewContents() { +TabContents* MatchPreview::ReleasePreviewContents(bool commit_history) { + template_url_id_ = 0; url_ = GURL(); + user_text_.clear(); + complete_suggested_text_.clear(); + if (frame_load_observer_.get()) { + frame_load_observer_->DetachFromPreview(); + // FrameLoadObserver will delete itself either when the TabContents is + // deleted, or when the page finishes loading. + FrameLoadObserver* unused ALLOW_UNUSED = frame_load_observer_.release(); + } + if (preview_contents_.get()) { + if (commit_history) + preview_tab_contents_delegate_->CommitHistory(); + // Destroy the paint observer. + if (preview_contents_->GetRenderWidgetHostView()) { + // RenderWidgetHostView may be null during shutdown. + preview_contents_->GetRenderWidgetHostView()->GetRenderWidgetHost()-> + set_paint_observer(NULL); + } + preview_contents_->set_delegate(NULL); + preview_tab_contents_delegate_->Reset(); + is_active_ = false; + } return preview_contents_.release(); } + +void MatchPreview::SetCompleteSuggestedText( + const string16& complete_suggested_text) { + if (complete_suggested_text == complete_suggested_text_) + return; + + if (user_text_.compare(0, user_text_.size(), complete_suggested_text, + 0, user_text_.size())) { + // The user text no longer contains the suggested text, ignore it. + complete_suggested_text_.clear(); + delegate_->SetSuggestedText(string16()); + return; + } + + complete_suggested_text_ = complete_suggested_text; + delegate_->SetSuggestedText( + complete_suggested_text_.substr(user_text_.size())); +} + +void MatchPreview::PreviewDidPaint() { + DCHECK(!is_active_); + is_active_ = true; + delegate_->ShowMatchPreview(); +} + +void MatchPreview::PageFinishedLoading() { + // FrameLoadObserver deletes itself after this call. + FrameLoadObserver* unused ALLOW_UNUSED = frame_load_observer_.release(); +} diff --git a/chrome/browser/tab_contents/match_preview.h b/chrome/browser/tab_contents/match_preview.h index cc6605b..f699f89 100644 --- a/chrome/browser/tab_contents/match_preview.h +++ b/chrome/browser/tab_contents/match_preview.h @@ -4,31 +4,31 @@ #ifndef CHROME_BROWSER_TAB_CONTENTS_MATCH_PREVIEW_H_ #define CHROME_BROWSER_TAB_CONTENTS_MATCH_PREVIEW_H_ +#pragma once #include "base/basictypes.h" #include "base/scoped_ptr.h" +#include "base/string16.h" +#include "base/timer.h" +#include "chrome/browser/search_engines/template_url_id.h" +#include "chrome/common/page_transition_types.h" #include "googleurl/src/gurl.h" +struct AutocompleteMatch; +class MatchPreviewDelegate; class TabContents; // MatchPreview maintains a TabContents that is intended to give a preview of -// a URL. MatchPreview is owned by TabContents. -// -// As the user types in the omnibox the LocationBar updates MatchPreview by -// way of 'Update'. If the user does a gesture on the preview, say clicks a -// link, the TabContentsDelegate of the hosting TabContents is notified with -// CommitMatchPreview and the TabContents maintained by MatchPreview replaces -// the current TabContents in the TabStripModel. +// a URL. MatchPreview is owned by Browser. // // At any time the TabContents maintained by MatchPreview may be destroyed by -// way of 'DestroyPreviewContents'. Consumers of MatchPreview can detect the -// preview TabContents was destroyed by observing TAB_CONTENTS_DESTROYED. -// -// Consumers of MatchPreview can detect a new TabContents was created by -// MatchPreview by listening for MATCH_PREVIEW_TAB_CONTENTS_CREATED. +// way of |DestroyPreviewContents|, which results in |HideMatchPreview| being +// invoked on the delegate. Similarly the preview may be committed at any time +// by invoking |CommitCurrentPreview|, which results in |CommitMatchPreview| +// being invoked on the delegate. class MatchPreview { public: - explicit MatchPreview(TabContents* host); + explicit MatchPreview(MatchPreviewDelegate* delegate); ~MatchPreview(); // Is MatchPreview enabled? @@ -38,7 +38,10 @@ class MatchPreview { // the url is empty and there is a preview TabContents it is destroyed. If url // is non-empty and the preview TabContents has not been created it is // created. - void Update(const GURL& url); + void Update(TabContents* tab_contents, + const AutocompleteMatch& match, + const string16& user_text, + string16* suggested_text); // Destroys the preview TabContents. Does nothing if the preview TabContents // has not been created. @@ -49,31 +52,70 @@ class MatchPreview { void CommitCurrentPreview(); // Releases the preview TabContents passing ownership to the caller. This is - // intended to be called when the preview TabContents is committed. - TabContents* ReleasePreviewContents(); + // intended to be called when the preview TabContents is committed. This does + // not notify the delegate. + TabContents* ReleasePreviewContents(bool commit_history); - // The TabContents we're maintaining the preview for. - TabContents* host() { return host_; } + // TabContents the match is being shown for. + TabContents* tab_contents() const { return tab_contents_; } // The preview TabContents; may be null. - TabContents* preview_contents() { return preview_contents_.get(); } + TabContents* preview_contents() const { return preview_contents_.get(); } + + // Returns true if the preview TabContents is active. In some situations this + // may return false yet preview_contents() returns non-NULL. + bool is_active() const { return is_active_; } + + const GURL& url() const { return url_; } private: + class FrameLoadObserver; + class PaintObserverImpl; class TabContentsDelegateImpl; + // Invoked when the page wants to update the suggested text. If |user_text_| + // starts with |suggested_text|, then the delegate is notified of the change, + // which results in updating the omnibox. + void SetCompleteSuggestedText(const string16& suggested_text); + + // Invoked when the preview paints. This notifies the delegate the preview is + // ready to be shown. + void PreviewDidPaint(); + + // Invoked once the page has finished loading and the script has been sent. + void PageFinishedLoading(); + + MatchPreviewDelegate* delegate_; + + // The TabContents last passed to |Update|. + TabContents* tab_contents_; + // The url we're displaying. GURL url_; - // The TabContents we're providing the preview for. - TabContents* host_; - // Delegate of the preview TabContents. Used to detect when the user does some // gesture on the TabContents and the preview needs to be activated. - scoped_ptr<TabContentsDelegateImpl> delegate_; + scoped_ptr<TabContentsDelegateImpl> preview_tab_contents_delegate_; // The preview TabContents; may be null. scoped_ptr<TabContents> preview_contents_; + // Has notification been sent out that the preview TabContents is ready to be + // shown? + bool is_active_; + + // The text the user typed in the omnibox. + string16 user_text_; + + // The latest suggestion from the page. + string16 complete_suggested_text_; + + // If we're showing instant results this is the ID of the TemplateURL driving + // the results. A value of 0 means there is no TemplateURL. + TemplateURLID template_url_id_; + + scoped_ptr<FrameLoadObserver> frame_load_observer_; + DISALLOW_COPY_AND_ASSIGN(MatchPreview); }; diff --git a/chrome/browser/tab_contents/match_preview_delegate.h b/chrome/browser/tab_contents/match_preview_delegate.h new file mode 100644 index 0000000..9d12cf8 --- /dev/null +++ b/chrome/browser/tab_contents/match_preview_delegate.h @@ -0,0 +1,32 @@ +// 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. + +#ifndef CHROME_BROWSER_TAB_CONTENTS_MATCH_PREVIEW_DELEGATE_H_ +#define CHROME_BROWSER_TAB_CONTENTS_MATCH_PREVIEW_DELEGATE_H_ +#pragma once + +#include "base/string16.h" + +// MatchPreview's delegate. Normally the Browser implements this. See +// MatchPreview for details. +class MatchPreviewDelegate { + public: + // Invoked when the preview TabContents should be shown. + virtual void ShowMatchPreview() = 0; + + // Invoked when the preview TabContents should be hidden. + virtual void HideMatchPreview() = 0; + + // Invoked when the user does something that should result in the preview + // TabContents becoming the active TabContents. + virtual void CommitMatchPreview() = 0; + + // Invoked when the suggested text is to change to |text|. + virtual void SetSuggestedText(const string16& text) = 0; + + protected: + virtual ~MatchPreviewDelegate() {} +}; + +#endif // CHROME_BROWSER_TAB_CONTENTS_MATCH_PREVIEW_DELEGATE_H_ diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc index b5b4d49..eb3826d 100644 --- a/chrome/browser/tab_contents/tab_contents.cc +++ b/chrome/browser/tab_contents/tab_contents.cc @@ -77,7 +77,6 @@ #include "chrome/browser/sessions/session_types.h" #include "chrome/browser/tab_contents/infobar_delegate.h" #include "chrome/browser/tab_contents/interstitial_page.h" -#include "chrome/browser/tab_contents/match_preview.h" #include "chrome/browser/tab_contents/navigation_entry.h" #include "chrome/browser/tab_contents/provisional_load_details.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" @@ -434,9 +433,6 @@ TabContents::TabContents(Profile* profile, // Set-up the showing of the omnibox search infobar if applicable. if (OmniboxSearchHint::IsEnabled(profile)) omnibox_search_hint_.reset(new OmniboxSearchHint(this)); - - if (MatchPreview::IsEnabled()) - match_preview_.reset(new MatchPreview(this)); } TabContents::~TabContents() { diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h index 39596f3..63759b4 100644 --- a/chrome/browser/tab_contents/tab_contents.h +++ b/chrome/browser/tab_contents/tab_contents.h @@ -72,7 +72,6 @@ class Extension; class FileSelectHelper; class InfoBarDelegate; class LoadNotificationDetails; -class MatchPreview; class OmniboxSearchHint; class PasswordManager; class PluginInstaller; @@ -168,9 +167,6 @@ class TabContents : public PageNavigator, // Returns the TabContentsSSLHelper, creating it if necessary. TabContentsSSLHelper* GetSSLHelper(); - // Returns the MatchPreview. Returns NULL if MatchPreview is not enabled. - MatchPreview* match_preview() { return match_preview_.get(); } - // Returns the SavePackage which manages the page saving job. May be NULL. SavePackage* save_package() const { return save_package_.get(); } @@ -1277,8 +1273,6 @@ class TabContents : public PageNavigator, // See description in RenderViewHostDelegate::SetDisplayingPDFContent. bool displaying_pdf_content_; - scoped_ptr<MatchPreview> match_preview_; - // --------------------------------------------------------------------------- DISALLOW_COPY_AND_ASSIGN(TabContents); diff --git a/chrome/browser/tab_contents/tab_contents_delegate.cc b/chrome/browser/tab_contents/tab_contents_delegate.cc index 615a438..7e07ed6 100644 --- a/chrome/browser/tab_contents/tab_contents_delegate.cc +++ b/chrome/browser/tab_contents/tab_contents_delegate.cc @@ -165,9 +165,6 @@ void TabContentsDelegate::UpdatePreferredSize(const gfx::Size& pref_size) { void TabContentsDelegate::ContentTypeChanged(TabContents* source) { } -void TabContentsDelegate::CommitMatchPreview(TabContents* source) { -} - void TabContentsDelegate::OnSetSuggestResult(int32 page_id, const std::string& result) { } diff --git a/chrome/browser/tab_contents/tab_contents_delegate.h b/chrome/browser/tab_contents/tab_contents_delegate.h index 56b4c9f..6baa57c 100644 --- a/chrome/browser/tab_contents/tab_contents_delegate.h +++ b/chrome/browser/tab_contents/tab_contents_delegate.h @@ -285,11 +285,6 @@ class TabContentsDelegate : public AutomationResourceRoutingDelegate { // PDF using the internal PDF plugin. virtual void ContentTypeChanged(TabContents* source); - // Sent when the user does a gesture that results in committing the match - // preview. The delegate should replace |source| with the |source|'s match - // preview TabContents. - virtual void CommitMatchPreview(TabContents* source); - // Notifies the delegate that the page has a suggest result. virtual void OnSetSuggestResult(int32 page_id, const std::string& result); diff --git a/chrome/browser/views/app_launcher.cc b/chrome/browser/views/app_launcher.cc index 3f8bdbf..880b085 100644 --- a/chrome/browser/views/app_launcher.cc +++ b/chrome/browser/views/app_launcher.cc @@ -85,6 +85,7 @@ class InfoBubbleContentsView : public views::View, // WARNING: this is not the TabContents of the bubble! Use // GetBubbleTabContents() to get the bubble's TabContents. virtual TabContents* GetTabContents(); + virtual MatchPreview* GetMatchPreview() { return NULL; } virtual void OnInputInProgress(bool in_progress) {} // CommandUpdater::CommandUpdaterDelegate implementation: diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc index c0ba2cc..29330d1 100644 --- a/chrome/browser/views/frame/browser_view.cc +++ b/chrome/browser/views/frame/browser_view.cc @@ -43,6 +43,7 @@ #include "chrome/browser/views/browser_dialogs.h" #include "chrome/browser/views/download_shelf_view.h" #include "chrome/browser/views/frame/browser_view_layout.h" +#include "chrome/browser/views/frame/contents_container.h" #include "chrome/browser/views/fullscreen_exit_bubble.h" #include "chrome/browser/views/status_bubble_views.h" #include "chrome/browser/views/tab_contents/tab_contents_container.h" @@ -135,75 +136,6 @@ static gfx::NativeWindow GetNormalBrowserWindowForBrowser(Browser* browser, } #endif // defined(OS_CHROMEOS) -// ContentsContainer is responsible for managing the TabContents views. -// ContentsContainer has up to two children: one for the currently active -// TabContents and one for the match preview TabContents. -class BrowserView::ContentsContainer : public views::View { - public: - ContentsContainer(BrowserView* browser_view, views::View* active) - : browser_view_(browser_view), - active_(active), - preview_(NULL) { - AddChildView(active_); - } - - // Makes the preview view the active view and nulls out the old active view. - // It's assumed the caller will delete or remove the old active view - // separately. - void MakePreviewContentsActiveContents() { - active_ = preview_; - preview_ = NULL; - Layout(); - } - - // Sets the preview view. This does not delete the old. - void SetPreview(views::View* preview) { - if (preview == preview_) - return; - - if (preview_) - RemoveChildView(preview_); - preview_ = preview; - if (preview_) - AddChildView(preview_); - - Layout(); - } - - virtual void Layout() { - // The active view always gets the full bounds. - active_->SetBounds(0, 0, width(), height()); - - if (preview_) { - // The preview view gets the full width and is positioned beneath the - // bottom of the autocompleted popup. - int max_autocomplete_y = browser_view_->toolbar()->location_bar()-> - location_entry()->model()->popup_model()->view()->GetMaxYCoordinate(); - gfx::Point screen_origin; - views::View::ConvertPointToScreen(this, &screen_origin); - DCHECK_GT(max_autocomplete_y, screen_origin.y()); - int preview_origin = max_autocomplete_y - screen_origin.y(); - if (preview_origin < height()) { - preview_->SetBounds(0, preview_origin, width(), - height() - preview_origin); - } else { - preview_->SetBounds(0, 0, 0, 0); - } - } - - // Need to invoke views::View in case any views whose bounds didn't change - // still need a layout. - views::View::Layout(); - } - - private: - BrowserView* browser_view_; - views::View* active_; - views::View* preview_; - - DISALLOW_COPY_AND_ASSIGN(ContentsContainer); -}; - /////////////////////////////////////////////////////////////////////////////// // BookmarkExtensionBackground, private: // This object serves as the views::Background object which is used to layout @@ -1422,6 +1354,26 @@ void BrowserView::ToggleTabStripMode() { frame_->TabStripDisplayModeChanged(); } +void BrowserView::ShowMatchPreview() { + if (!preview_container_) + preview_container_ = new TabContentsContainer(); + TabContents* preview_tab_contents = + browser_->match_preview()->preview_contents(); + contents_->SetPreview(preview_container_, preview_tab_contents); + preview_container_->ChangeTabContents(preview_tab_contents); +} + +void BrowserView::HideMatchPreview() { + if (!preview_container_) + return; + + // The contents must be changed before SetPreview is invoked. + preview_container_->ChangeTabContents(NULL); + contents_->SetPreview(NULL, NULL); + delete preview_container_; + preview_container_ = NULL; +} + /////////////////////////////////////////////////////////////////////////////// // BrowserView, BrowserWindowTesting implementation: @@ -1461,25 +1413,6 @@ void BrowserView::Observe(NotificationType type, } break; - case NotificationType::MATCH_PREVIEW_TAB_CONTENTS_CREATED: - if (Source<TabContents>(source).ptr() == - browser_->GetSelectedTabContents()) { - ShowMatchPreview(); - } - break; - - case NotificationType::TAB_CONTENTS_DESTROYED: { - if (MatchPreview::IsEnabled()) { - TabContents* selected_contents = browser_->GetSelectedTabContents(); - if (selected_contents && - selected_contents->match_preview()->preview_contents() == - Source<TabContents>(source).ptr()) { - HideMatchPreview(); - } - } - break; - } - case NotificationType::SIDEBAR_CHANGED: if (Details<SidebarContainer>(details)->tab_contents() == browser_->GetSelectedTabContents()) { @@ -1983,13 +1916,6 @@ void BrowserView::Init() { // We're now initialized and ready to process Layout requests. ignore_layout_ = false; - - registrar_.Add(this, - NotificationType::MATCH_PREVIEW_TAB_CONTENTS_CREATED, - NotificationService::AllSources()); - registrar_.Add(this, - NotificationType::TAB_CONTENTS_DESTROYED, - NotificationService::AllSources()); } #if defined(OS_WIN) @@ -2472,25 +2398,6 @@ void BrowserView::InitHangMonitor() { #endif } -void BrowserView::ShowMatchPreview() { - if (!preview_container_) - preview_container_ = new TabContentsContainer(); - contents_->SetPreview(preview_container_); - preview_container_->ChangeTabContents( - browser_->GetSelectedTabContents()->match_preview()->preview_contents()); -} - -void BrowserView::HideMatchPreview() { - if (!preview_container_) - return; - - // The contents must be changed before SetPreview is invoked. - preview_container_->ChangeTabContents(NULL); - contents_->SetPreview(NULL); - delete preview_container_; - preview_container_ = NULL; -} - void BrowserView::ProcessTabSelected(TabContents* new_contents, bool change_tab_contents) { // Update various elements that are interested in knowing the current diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h index 1d77dff..0ae1122 100644 --- a/chrome/browser/views/frame/browser_view.h +++ b/chrome/browser/views/frame/browser_view.h @@ -43,6 +43,7 @@ class BookmarkBarView; class Browser; class BrowserBubble; class BrowserViewLayout; +class ContentsContainer; class DownloadShelfView; class EncodingMenuModel; class FullscreenExitBubble; @@ -319,6 +320,8 @@ class BrowserView : public BrowserBubbleHost, virtual void Copy(); virtual void Paste(); virtual void ToggleTabStripMode(); + virtual void ShowMatchPreview(); + virtual void HideMatchPreview(); // Overridden from BrowserWindowTesting: virtual BookmarkBarView* GetBookmarkBarView() const; @@ -420,8 +423,6 @@ class BrowserView : public BrowserBubbleHost, private: friend class BrowserViewLayout; - class ContentsContainer; - #if defined(OS_WIN) // Creates the system menu. void InitSystemMenu(); @@ -493,12 +494,6 @@ class BrowserView : public BrowserBubbleHost, // Initialize the hung plugin detector. void InitHangMonitor(); - // Shows the match preview for the selected tab contents. - void ShowMatchPreview(); - - // Hides the match preview for the selected tab contents. - void HideMatchPreview(); - // Invoked from TabSelectedAt or when the match preview is made active. Is // |change_tab_contents| is true, |new_contents| is added to the view // hierarchy, if |change_tab_contents| is false, it's assumed |new_contents| @@ -521,13 +516,13 @@ class BrowserView : public BrowserBubbleHost, // | |--------------------------------------------------------------| // | | Navigation buttons, menus and the address bar (toolbar_) | // | |--------------------------------------------------------------| - // | | All infobars (infobar_container_) | + // | | All infobars (infobar_container_) * | // | |--------------------------------------------------------------| - // | | Bookmarks (bookmark_bar_view_) | + // | | Bookmarks (bookmark_bar_view_) * | // | |--------------------------------------------------------------| // | |Page content (contents_) || | // | |--------------------------------------|| Sidebar content | - // | || contents_container_ or ||| (sidebar_container_) | + // | || contents_container_ and/or ||| (sidebar_container_) | // | || preview_container_ ||| | // | || |(3) | // | Tabs (2)|| ||| | @@ -549,6 +544,11 @@ class BrowserView : public BrowserBubbleHost, // (2) - tabstrip_, position when side tabs are enabled // (3) - sidebar_split_ // (4) - contents_split_ + // + // * - The bookmark bar and info bar are swapped when on the new tab page. + // Additionally contents_ is positioned on top of the bookmark bar when + // the bookmark bar is detached. This is done to allow the + // preview_container_ to appear over the bookmark bar. // Tool/Info bars that we are currently showing. Used for layout. // active_bookmark_bar_ is either NULL, if the bookmark bar isn't showing, diff --git a/chrome/browser/views/frame/browser_view_layout.cc b/chrome/browser/views/frame/browser_view_layout.cc index 0abd70e..ae71abe 100644 --- a/chrome/browser/views/frame/browser_view_layout.cc +++ b/chrome/browser/views/frame/browser_view_layout.cc @@ -12,6 +12,7 @@ #include "chrome/browser/views/download_shelf_view.h" #include "chrome/browser/views/frame/browser_frame.h" #include "chrome/browser/views/frame/browser_view.h" +#include "chrome/browser/views/frame/contents_container.h" #include "chrome/browser/views/tabs/side_tab_strip.h" #include "chrome/browser/views/tabs/tab_strip.h" #include "chrome/browser/views/toolbar_view.h" @@ -206,15 +207,18 @@ void BrowserViewLayout::Uninstalled(views::View* host) {} void BrowserViewLayout::ViewAdded(views::View* host, views::View* view) { switch (view->GetID()) { case VIEW_ID_CONTENTS_SPLIT: { - contents_split_ = view; - if (SidebarManager::IsSidebarAllowed()) { - views::View* sidebar_split = contents_split_->GetChildViewAt(0); - contents_container_ = sidebar_split->GetChildViewAt(0); - } else { - contents_container_ = contents_split_->GetChildViewAt(0); - } + contents_split_ = view; + // TODO: this is fragile, fix. + if (SidebarManager::IsSidebarAllowed()) { + views::View* sidebar_split = contents_split_->GetChildViewAt(0); + contents_container_ = static_cast<ContentsContainer*>( + sidebar_split->GetChildViewAt(0)); + } else { + contents_container_ = static_cast<ContentsContainer*>( + contents_split_->GetChildViewAt(0)); } break; + } case VIEW_ID_INFO_BAR_CONTAINER: infobar_container_ = view; break; @@ -252,6 +256,10 @@ void BrowserViewLayout::Layout(views::View* host) { top = LayoutToolbar(top); top = LayoutBookmarkAndInfoBars(top); int bottom = LayoutDownloadShelf(browser_view_->height()); + int active_top_margin = GetTopMarginForActiveContent(); + top -= active_top_margin; + bottom += active_top_margin; + contents_container_->SetActiveTopMargin(active_top_margin); LayoutTabContents(top, bottom); // This must be done _after_ we lay out the TabContents since this // code calls back into us to find the bounding box the find bar @@ -276,6 +284,14 @@ gfx::Size BrowserViewLayout::GetPreferredSize(views::View* host) { ////////////////////////////////////////////////////////////////////////////// // BrowserViewLayout, private: +Browser* BrowserViewLayout::browser() { + return browser_view_->browser(); +} + +const Browser* BrowserViewLayout::browser() const { + return browser_view_->browser(); +} + int BrowserViewLayout::LayoutTabStrip() { if (!browser_view_->IsTabStripVisible()) { tabstrip_->SetVisible(false); @@ -357,13 +373,31 @@ int BrowserViewLayout::LayoutInfoBar(int top) { return top + height; } -// Layout the TabContents container, between the coordinates |top| and -// |bottom|. void BrowserViewLayout::LayoutTabContents(int top, int bottom) { contents_split_->SetBounds(vertical_layout_rect_.x(), top, vertical_layout_rect_.width(), bottom - top); } +int BrowserViewLayout::GetTopMarginForActiveContent() { + if (!active_bookmark_bar_ || !browser_view_->IsBookmarkBarVisible() || + !active_bookmark_bar_->IsDetached()) { + return 0; + } + + if (contents_split_->GetChildViewAt(1) && + contents_split_->GetChildViewAt(1)->IsVisible()) + return 0; + + if (SidebarManager::IsSidebarAllowed()) { + views::View* sidebar_split = contents_split_->GetChildViewAt(0); + if (sidebar_split->GetChildViewAt(1) && + sidebar_split->GetChildViewAt(1)->IsVisible()) + return 0; + } + + return active_bookmark_bar_->height(); +} + int BrowserViewLayout::LayoutDownloadShelf(int bottom) { // Re-layout the shelf either if it is visible or if it's close animation // is currently running. diff --git a/chrome/browser/views/frame/browser_view_layout.h b/chrome/browser/views/frame/browser_view_layout.h index 3175bdf..42652ef 100644 --- a/chrome/browser/views/frame/browser_view_layout.h +++ b/chrome/browser/views/frame/browser_view_layout.h @@ -6,9 +6,16 @@ #define CHROME_BROWSER_VIEWS_FRAME_BROWSER_VIEW_LAYOUT_H_ #pragma once -#include "chrome/browser/views/frame/browser_view.h" #include "views/layout_manager.h" +class BaseTabStrip; +class BookmarkBarView; +class Browser; +class BrowserView; +class ContentsContainer; +class DownloadShelfView; +class ToolbarView; + // The layout manager used in chrome browser. class BrowserViewLayout : public views::LayoutManager { public: @@ -40,12 +47,8 @@ class BrowserViewLayout : public views::LayoutManager { virtual gfx::Size GetPreferredSize(views::View* host); protected: - Browser* browser() { - return browser_view_->browser(); - } - const Browser* browser() const { - return browser_view_->browser(); - } + Browser* browser(); + const Browser* browser() const; // Layout the TabStrip, returns the coordinate of the bottom of the TabStrip, // for laying out subsequent controls. @@ -62,6 +65,11 @@ class BrowserViewLayout : public views::LayoutManager { // |bottom|. void LayoutTabContents(int top, int bottom); + // Returns the top margin to adjust the contents_container_ by. This is used + // to make the bookmark bar and contents_container_ overlap so that the + // preview contents hides the bookmark bar. + int GetTopMarginForActiveContent(); + // Layout the Download Shelf, returns the coordinate of the top of the // control, for laying out the previous control. int LayoutDownloadShelf(int bottom); @@ -81,7 +89,7 @@ class BrowserViewLayout : public views::LayoutManager { BaseTabStrip* tabstrip_; ToolbarView* toolbar_; views::View* contents_split_; - views::View* contents_container_; + ContentsContainer* contents_container_; views::View* infobar_container_; DownloadShelfView* download_shelf_; BookmarkBarView* active_bookmark_bar_; diff --git a/chrome/browser/views/frame/contents_container.cc b/chrome/browser/views/frame/contents_container.cc new file mode 100644 index 0000000..3d455ef --- /dev/null +++ b/chrome/browser/views/frame/contents_container.cc @@ -0,0 +1,194 @@ +// 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/frame/contents_container.h" + +#include "app/resource_bundle.h" +#include "chrome/browser/location_bar.h" +#include "chrome/browser/views/frame/browser_view.h" +#include "grit/theme_resources.h" +#include "views/controls/image_view.h" +#include "views/widget/root_view.h" + +#if defined(OS_WIN) +#include "views/widget/widget_win.h" +#elif defined(OS_LINUX) +#include "chrome/browser/gtk/gtk_util.h" +#include "views/window/window_gtk.h" +#endif + +#if defined(OS_WIN) + +class ContentsContainer::TearWindow : public views::WidgetWin { + public: + explicit TearWindow(ContentsContainer* contents_container) + : contents_container_(contents_container) { + set_window_style(WS_POPUP | WS_CLIPCHILDREN); + set_window_ex_style(WS_EX_TOOLWINDOW | WS_EX_LAYERED); + } + + virtual ~TearWindow() { + // On windows it's possible for us to be deleted before the + // ContentsContainer. If this happens make sure contents_container_ doesn't + // attempt to delete us too. + if (contents_container_) + contents_container_->TearWindowDestroyed(); + } + + void set_contents_container(ContentsContainer* contents_container) { + contents_container_ = contents_container; + } + + virtual LRESULT OnMouseActivate(HWND window, + UINT hit_test, + UINT mouse_message) { + // Don't activate the window when the user clicks it. + contents_container_->browser_view_->GetLocationBar()->Revert(); + return MA_NOACTIVATE; + } + + private: + ContentsContainer* contents_container_; + + DISALLOW_COPY_AND_ASSIGN(TearWindow); +}; + +#endif + +ContentsContainer::ContentsContainer(BrowserView* browser_view, + views::View* active) + : browser_view_(browser_view), + active_(active), + preview_(NULL), + preview_tab_contents_(NULL), + tear_window_(NULL), + active_top_margin_(0) { + AddChildView(active_); +} + +ContentsContainer::~ContentsContainer() { + DeleteTearWindow(); +} + +void ContentsContainer::MakePreviewContentsActiveContents() { + active_ = preview_; + preview_ = NULL; + DeleteTearWindow(); + Layout(); +} + +void ContentsContainer::SetPreview(views::View* preview, + TabContents* preview_tab_contents) { + if (preview == preview_) + return; + + if (preview_) { + RemoveChildView(preview_); + DeleteTearWindow(); + } + preview_ = preview; + preview_tab_contents_ = preview_tab_contents; + if (preview_) { + AddChildView(preview_); + CreateTearWindow(); + } + + Layout(); + + if (preview_) + tear_window_->Show(); // Show after we'ved positioned it in Layout. +} + +void ContentsContainer::SetActiveTopMargin(int margin) { + if (active_top_margin_ == margin) + return; + + active_top_margin_ = margin; + // Make sure we layout next time around. We need this in case our bounds + // haven't changed. + InvalidateLayout(); +} + +void ContentsContainer::Layout() { + // The active view always gets the full bounds. + active_->SetBounds(0, active_top_margin_, width(), + std::max(0, height() - active_top_margin_)); + + if (preview_) { + preview_->SetBounds(0, 0, width(), height()); + PositionTearWindow(); + } + + // Need to invoke views::View in case any views whose bounds didn't change + // still need a layout. + views::View::Layout(); +} + +void ContentsContainer::CreateTearWindow() { + DCHECK(preview_); + tear_window_ = CreateTearWindowImpl(); + + views::ImageView* image_view = new views::ImageView(); + image_view->SetImage(ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_MATCH_PREVIEW_TEAR)); + tear_window_->SetContentsView(image_view); +} + +#if defined(OS_WIN) + +ContentsContainer::TearWindow* ContentsContainer::CreateTearWindowImpl() { + TearWindow* widget = new TearWindow(this); + widget->Init(browser_view_->GetNativeHandle(), gfx::Rect()); + return widget; +} + +#elif defined(OS_LINUX) + +ContentsContainer::TearWindow* ContentsContainer::CreateTearWindowImpl() { + views::WidgetGtk* widget = new views::WidgetGtk(views::WidgetGtk::TYPE_POPUP); + widget->MakeTransparent(); + widget->Init(NULL, gfx::Rect()); + gtk_util::StackPopupWindow(widget->GetNativeView(), + GTK_WIDGET(browser_view_->GetNativeHandle())); + return widget; +} + +#endif + +void ContentsContainer::PositionTearWindow() { + if (!tear_window_) + return; + + gfx::Rect vis_bounds = GetVisibleBounds(); + + gfx::Size pref = tear_window_->GetRootView()->GetPreferredSize(); + // Constrain to the the visible bounds as we may be given a different size + // than is actually visible. + pref.SetSize(std::min(pref.width(), vis_bounds.width()), + std::min(pref.height(), vis_bounds.height())); + + gfx::Rect bounds(0, 0, pref.width(), pref.height()); + bounds.set_x(MirroredLeftPointForRect(bounds)); + + gfx::Point origin(bounds.origin()); + views::View::ConvertPointToScreen(this, &origin); + + tear_window_->SetBounds(gfx::Rect(origin, pref)); +} + +void ContentsContainer::DeleteTearWindow() { + if (!tear_window_) + return; + + tear_window_->Close(); +#if defined(OS_WIN) + tear_window_->set_contents_container(NULL); +#endif + // Close deletes the tear window. + tear_window_ = NULL; +} + +void ContentsContainer::TearWindowDestroyed() { + tear_window_ = NULL; +} diff --git a/chrome/browser/views/frame/contents_container.h b/chrome/browser/views/frame/contents_container.h new file mode 100644 index 0000000..533ad36 --- /dev/null +++ b/chrome/browser/views/frame/contents_container.h @@ -0,0 +1,82 @@ +// 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. + +#ifndef CHROME_BROWSER_VIEWS_FRAME_CONTENTS_CONTAINER_H_ +#define CHROME_BROWSER_VIEWS_FRAME_CONTENTS_CONTAINER_H_ +#pragma once + +#include "views/view.h" + +class BrowserView; +class TabContents; + +namespace views { +class Widget; +} + +// ContentsContainer is responsible for managing the TabContents views. +// ContentsContainer has up to two children: one for the currently active +// TabContents and one for the match preview TabContents. +class ContentsContainer : public views::View { + public: + ContentsContainer(BrowserView* browser_view, views::View* active); + virtual ~ContentsContainer(); + + // Makes the preview view the active view and nulls out the old active view. + // It's assumed the caller will delete or remove the old active view + // separately. + void MakePreviewContentsActiveContents(); + + // Sets the preview view. This does not delete the old. + void SetPreview(views::View* preview, TabContents* preview_tab_contents); + + TabContents* preview_tab_contents() const { return preview_tab_contents_; } + + // Sets the active top margin. + void SetActiveTopMargin(int margin); + + // View overrides: + virtual void Layout(); + + private: +#if defined(OS_WIN) + class TearWindow; +#else + typedef views::Widget TearWindow; +#endif + + // Creates and configures the tear window. + void CreateTearWindow(); + + // Creates and returns a new TearWindow. + TearWindow* CreateTearWindowImpl(); + + // Resets the bounds of the tear window. + void PositionTearWindow(); + + // Closes and deletes the tear window. + void DeleteTearWindow(); + + // Invoked when the tear window is destroyed. + void TearWindowDestroyed(); + + BrowserView* browser_view_; + + views::View* active_; + + views::View* preview_; + + TabContents* preview_tab_contents_; + + // Window used to show the page tear. + TearWindow* tear_window_; + + // The margin between the top and the active view. This is used to make the + // preview overlap the bookmark bar on the new tab page. + int active_top_margin_; + + DISALLOW_COPY_AND_ASSIGN(ContentsContainer); +}; + +#endif // CHROME_BROWSER_VIEWS_FRAME_CONTENTS_CONTAINER_H_ diff --git a/chrome/browser/views/location_bar/location_bar_view.cc b/chrome/browser/views/location_bar/location_bar_view.cc index 79bb6e8..e970485 100644 --- a/chrome/browser/views/location_bar/location_bar_view.cc +++ b/chrome/browser/views/location_bar/location_bar_view.cc @@ -16,10 +16,12 @@ #include "base/utf_string_conversions.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/alternate_nav_url_fetcher.h" +#include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/defaults.h" #include "chrome/browser/extensions/extension_browser_event_router.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/tab_contents/match_preview.h" @@ -98,7 +100,8 @@ LocationBarView::LocationBarView(Profile* profile, mode_(mode), show_focus_rect_(false), bubble_type_(FirstRun::MINIMAL_BUBBLE), - template_url_model_(NULL) { + template_url_model_(NULL), + update_match_preview_(true) { DCHECK(profile_); SetID(VIEW_ID_LOCATION_BAR); SetFocusable(true); @@ -714,13 +717,33 @@ void LocationBarView::OnMouseReleased(const views::MouseEvent& event, #endif void LocationBarView::OnAutocompleteWillClosePopup() { + if (!update_match_preview_) + return; + + MatchPreview* match_preview = delegate_->GetMatchPreview(); + if (match_preview) + match_preview->DestroyPreviewContents(); } void LocationBarView::OnAutocompleteLosingFocus( gfx::NativeView view_gaining_focus) { + SetSuggestedText(string16()); + + MatchPreview* match_preview = delegate_->GetMatchPreview(); + if (!match_preview) + return; + + if (!match_preview->is_active() || !match_preview->preview_contents()) + return; + + if (ShouldCommitMatchPreviewOnFocusLoss(view_gaining_focus)) + match_preview->CommitCurrentPreview(); + else + match_preview->DestroyPreviewContents(); } void LocationBarView::OnAutocompleteWillAccept() { + update_match_preview_ = false; } void LocationBarView::OnAutocompleteAccept( @@ -728,34 +751,39 @@ void LocationBarView::OnAutocompleteAccept( WindowOpenDisposition disposition, PageTransition::Type transition, const GURL& alternate_nav_url) { - if (!url.is_valid()) - return; - - location_input_ = UTF8ToWide(url.spec()); - disposition_ = disposition; - transition_ = transition; - - if (command_updater_) { - if (!alternate_nav_url.is_valid()) { - command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); - return; - } - - AlternateNavURLFetcher* fetcher = - new AlternateNavURLFetcher(alternate_nav_url); - // The AlternateNavURLFetcher will listen for the pending navigation - // notification that will be issued as a result of the "open URL." It - // will automatically install itself into that navigation controller. - command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); - if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) { - // I'm not sure this should be reachable, but I'm not also sure enough - // that it shouldn't to stick in a NOTREACHED(). In any case, this is - // harmless. - delete fetcher; - } else { - // The navigation controller will delete the fetcher. + // WARNING: don't add an early return here. The calls after the if must + // happen. + if (url.is_valid()) { + location_input_ = UTF8ToWide(url.spec()); + disposition_ = disposition; + transition_ = transition; + + if (command_updater_) { + if (!alternate_nav_url.is_valid()) { + command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); + } else { + AlternateNavURLFetcher* fetcher = + new AlternateNavURLFetcher(alternate_nav_url); + // The AlternateNavURLFetcher will listen for the pending navigation + // notification that will be issued as a result of the "open URL." It + // will automatically install itself into that navigation controller. + command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); + if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) { + // I'm not sure this should be reachable, but I'm not also sure enough + // that it shouldn't to stick in a NOTREACHED(). In any case, this is + // harmless. + delete fetcher; + } else { + // The navigation controller will delete the fetcher. + } + } } } + + if (delegate_->GetMatchPreview()) + delegate_->GetMatchPreview()->DestroyPreviewContents(); + + update_match_preview_ = true; } void LocationBarView::OnChanged() { @@ -765,13 +793,21 @@ void LocationBarView::OnChanged() { Layout(); SchedulePaint(); - if (MatchPreview::IsEnabled() && GetTabContents() && - !profile_->IsOffTheRecord()) { - PageTransition::Type transition_type; - GURL url = location_entry_->model()->user_input_in_progress() ? - location_entry_->model()->CurrentURL(&transition_type) : GURL(); - GetTabContents()->match_preview()->Update(url); + MatchPreview* match_preview = delegate_->GetMatchPreview(); + string16 suggested_text; + if (update_match_preview_ && match_preview && GetTabContents()) { + if (location_entry_->model()->user_input_in_progress() && + location_entry_->model()->popup_model()->IsOpen()) { + match_preview->Update(GetTabContents(), + location_entry_->model()->CurrentMatch(), + WideToUTF16(location_entry_->GetText()), + &suggested_text); + } else { + match_preview->DestroyPreviewContents(); + } } + + SetSuggestedText(suggested_text); } void LocationBarView::OnInputInProgress(bool in_progress) { @@ -1024,7 +1060,7 @@ void LocationBarView::ShowFirstRunBubble(FirstRun::BubbleType bubble_type) { ShowFirstRunBubbleInternal(bubble_type); } -void LocationBarView::SetSuggestedText(const std::wstring& text) { +void LocationBarView::SetSuggestedText(const string16& text) { if (!text.empty()) { if (!suggested_text_view_) { suggested_text_view_ = new views::Label(); @@ -1032,13 +1068,13 @@ void LocationBarView::SetSuggestedText(const std::wstring& text) { suggested_text_view_->SetColor( GetColor(ToolbarModel::NONE, LocationBarView::DEEMPHASIZED_TEXT)); - suggested_text_view_->SetText(text); + suggested_text_view_->SetText(UTF16ToWide(text)); suggested_text_view_->SetFont(location_entry_->GetFont()); AddChildView(suggested_text_view_); - } else if (suggested_text_view_->GetText() == text) { + } else if (suggested_text_view_->GetText() == UTF16ToWide(text)) { return; } else { - suggested_text_view_->SetText(text); + suggested_text_view_->SetText(UTF16ToWide(text)); } } else if (suggested_text_view_) { delete suggested_text_view_; @@ -1141,3 +1177,28 @@ void LocationBarView::OnTemplateURLModelChanged() { ShowFirstRunBubble(bubble_type_); } +bool LocationBarView::ShouldCommitMatchPreviewOnFocusLoss( + gfx::NativeView view_gaining_focus) { + // The MatchPreview is active. Destroy it if the user didn't click on the + // RenderWidgetHostView (or one of its children). +#if defined(OS_WIN) + MatchPreview* match_preview = delegate_->GetMatchPreview(); + if (!view_gaining_focus || + !match_preview->preview_contents()->GetRenderWidgetHostView()) { + return false; + } + RenderWidgetHostView* rwhv = + match_preview->preview_contents()->GetRenderWidgetHostView(); + gfx::NativeView rwhv_native_view = rwhv->GetNativeView(); + gfx::NativeView view_gaining_focus_ancestor = view_gaining_focus; + while (view_gaining_focus_ancestor && + view_gaining_focus_ancestor != rwhv_native_view) { + view_gaining_focus_ancestor = ::GetParent(view_gaining_focus_ancestor); + } + return view_gaining_focus_ancestor != NULL; +#else + // TODO: implement me. + NOTIMPLEMENTED(); + return false; +#endif +} diff --git a/chrome/browser/views/location_bar/location_bar_view.h b/chrome/browser/views/location_bar/location_bar_view.h index 120de8d..d8c7976 100644 --- a/chrome/browser/views/location_bar/location_bar_view.h +++ b/chrome/browser/views/location_bar/location_bar_view.h @@ -38,6 +38,7 @@ class ExtensionAction; class GURL; class KeywordHintView; class LocationIconView; +class MatchPreview; class PageActionWithBadgeView; class Profile; class SelectedKeywordView; @@ -72,6 +73,9 @@ class LocationBarView : public LocationBar, // Should return the current tab contents. virtual TabContents* GetTabContents() = 0; + // Returns the MatchPreview, or NULL if there isn't one. + virtual MatchPreview* GetMatchPreview() = 0; + // Called by the location bar view when the user starts typing in the edit. // This forces our security style to be UNKNOWN for the duration of the // editing. @@ -201,7 +205,7 @@ class LocationBarView : public LocationBar, // Overridden from LocationBar: virtual void ShowFirstRunBubble(FirstRun::BubbleType bubble_type); - virtual void SetSuggestedText(const std::wstring& text); + virtual void SetSuggestedText(const string16& text); virtual std::wstring GetInputString() const; virtual WindowOpenDisposition GetWindowOpenDisposition() const; virtual PageTransition::Type GetPageTransition() const; @@ -293,6 +297,11 @@ class LocationBarView : public LocationBar, // Helper to show the first run info bubble. void ShowFirstRunBubbleInternal(FirstRun::BubbleType bubble_type); + // Returns true if the CommitMatchPreview should be invoked on the + // MatchPreview as the result of a focus loss. If this returns false + // DestroyPreviewContents is invoked. + bool ShouldCommitMatchPreviewOnFocusLoss(gfx::NativeView view_gaining_focus); + // Current profile. Not owned by us. Profile* profile_; @@ -381,6 +390,12 @@ class LocationBarView : public LocationBar, scoped_ptr<AccessibleWidgetHelper> accessible_widget_helper_; #endif + // Should the match preview be updated? This is set to false in + // OnAutocompleteWillAccept and true in OnAutocompleteAccept. This is needed + // as prior to accepting an autocomplete suggestion the model is reverted + // which triggers resetting the match preview. + bool update_match_preview_; + DISALLOW_IMPLICIT_CONSTRUCTORS(LocationBarView); }; diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc index ccdc7e2..d1ad65b 100644 --- a/chrome/browser/views/toolbar_view.cc +++ b/chrome/browser/views/toolbar_view.cc @@ -270,6 +270,10 @@ TabContents* ToolbarView::GetTabContents() { return browser_->GetSelectedTabContents(); } +MatchPreview* ToolbarView::GetMatchPreview() { + return browser_->match_preview(); +} + void ToolbarView::OnInputInProgress(bool in_progress) { // The edit should make sure we're only notified when something changes. DCHECK(model_->input_in_progress() != in_progress); diff --git a/chrome/browser/views/toolbar_view.h b/chrome/browser/views/toolbar_view.h index 2bded31..d95111b 100644 --- a/chrome/browser/views/toolbar_view.h +++ b/chrome/browser/views/toolbar_view.h @@ -88,6 +88,7 @@ class ToolbarView : public AccessibleToolbarView, // Overridden from LocationBarView::Delegate: virtual TabContents* GetTabContents(); + virtual MatchPreview* GetMatchPreview(); virtual void OnInputInProgress(bool in_progress); // Overridden from AnimationDelegate: |