summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-04 23:22:04 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-04 23:22:04 +0000
commita0b846623f3c7dbf6cbc6a36cd71135f5d218ca3 (patch)
tree20bf34a75e48029fd2149c0973bec9107c3891ef /chrome/browser
parent1f8d420b980bb1bb0d18d8486656751460cfcc2b (diff)
downloadchromium_src-a0b846623f3c7dbf6cbc6a36cd71135f5d218ca3.zip
chromium_src-a0b846623f3c7dbf6cbc6a36cd71135f5d218ca3.tar.gz
chromium_src-a0b846623f3c7dbf6cbc6a36cd71135f5d218ca3.tar.bz2
Changes instant around to support multiple loaders. This is
necessitated by needing to hold off on showing instant results until I know if the server really supports instant. This change is mostly refactoring to enable this. Most of what was MatchPreview has been moved into MatchPreviewLoader. LoaderManager is used to determine which MatchPreviewLoader to use as well as keeping around a cache of them. MatchPreview for the most part delegates to LoaderManager and MatchPreviewLoader now. I'll rename all these classes to instant (and move into its own directory) next. BUG=54833 TEST=none Review URL: http://codereview.chromium.org/3541008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61441 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/browser.cc8
-rw-r--r--chrome/browser/browser.h2
-rw-r--r--chrome/browser/browser_window.h2
-rw-r--r--chrome/browser/cocoa/browser_window_cocoa.h2
-rw-r--r--chrome/browser/cocoa/browser_window_cocoa.mm2
-rw-r--r--chrome/browser/gtk/browser_window_gtk.cc2
-rw-r--r--chrome/browser/gtk/browser_window_gtk.h2
-rw-r--r--chrome/browser/search_engines/template_url_id.h2
-rw-r--r--chrome/browser/tab_contents/loader_manager.cc123
-rw-r--r--chrome/browser/tab_contents/loader_manager.h97
-rw-r--r--chrome/browser/tab_contents/loader_manager_unittest.cc209
-rw-r--r--chrome/browser/tab_contents/match_preview.cc666
-rw-r--r--chrome/browser/tab_contents/match_preview.h74
-rw-r--r--chrome/browser/tab_contents/match_preview_commit_type.h22
-rw-r--r--chrome/browser/tab_contents/match_preview_delegate.h2
-rw-r--r--chrome/browser/tab_contents/match_preview_loader.cc659
-rw-r--r--chrome/browser/tab_contents/match_preview_loader.h139
-rw-r--r--chrome/browser/tab_contents/match_preview_loader_delegate.h38
-rw-r--r--chrome/browser/views/frame/browser_view.cc8
-rw-r--r--chrome/browser/views/frame/browser_view.h2
-rw-r--r--chrome/browser/views/location_bar/location_bar_view.cc11
21 files changed, 1419 insertions, 653 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index b5c1048..01e70c3 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -3274,9 +3274,9 @@ void Browser::OnStateChanged() {
///////////////////////////////////////////////////////////////////////////////
// Browser, MatchPreviewDelegate implementation:
-void Browser::ShowMatchPreview() {
+void Browser::ShowMatchPreview(TabContents* preview_contents) {
DCHECK(match_preview_->tab_contents() == GetSelectedTabContents());
- window_->ShowMatchPreview();
+ window_->ShowMatchPreview(preview_contents);
}
void Browser::HideMatchPreview() {
@@ -4149,13 +4149,13 @@ bool Browser::OpenMatchPreview(WindowOpenDisposition disposition) {
return false;
if (disposition == CURRENT_TAB) {
- match_preview()->CommitCurrentPreview(MatchPreview::COMMIT_PRESSED_ENTER);
+ match_preview()->CommitCurrentPreview(MATCH_PREVIEW_COMMIT_PRESSED_ENTER);
return true;
}
if (disposition == NEW_FOREGROUND_TAB || disposition == NEW_BACKGROUND_TAB) {
HideMatchPreview();
TabContents* preview_contents = match_preview()->ReleasePreviewContents(
- MatchPreview::COMMIT_PRESSED_ENTER);
+ MATCH_PREVIEW_COMMIT_PRESSED_ENTER);
preview_contents->controller().PruneAllButActive();
tab_handler_->GetTabStripModel()->AddTabContents(
preview_contents,
diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h
index b10f544..9b29b87 100644
--- a/chrome/browser/browser.h
+++ b/chrome/browser/browser.h
@@ -775,7 +775,7 @@ class Browser : public TabHandlerDelegate,
virtual void OnStateChanged();
// Overriden from MatchPreviewDelegate:
- virtual void ShowMatchPreview();
+ virtual void ShowMatchPreview(TabContents* preview_contents);
virtual void HideMatchPreview();
virtual void CommitMatchPreview(TabContents* preview_contents);
virtual void SetSuggestedText(const string16& text);
diff --git a/chrome/browser/browser_window.h b/chrome/browser/browser_window.h
index 4ff6d5e..aa109c5 100644
--- a/chrome/browser/browser_window.h
+++ b/chrome/browser/browser_window.h
@@ -313,7 +313,7 @@ class BrowserWindow {
#endif
// Invoked when the match preview's tab contents should be shown.
- virtual void ShowMatchPreview() = 0;
+ virtual void ShowMatchPreview(TabContents* preview_contents) = 0;
// Invoked when the match preview's tab contents should be hidden.
virtual void HideMatchPreview() = 0;
diff --git a/chrome/browser/cocoa/browser_window_cocoa.h b/chrome/browser/cocoa/browser_window_cocoa.h
index 2fe7b8f..a395397 100644
--- a/chrome/browser/cocoa/browser_window_cocoa.h
+++ b/chrome/browser/cocoa/browser_window_cocoa.h
@@ -106,7 +106,7 @@ class BrowserWindowCocoa : public BrowserWindow,
virtual void Paste();
virtual void ToggleTabStripMode();
virtual void OpenTabpose();
- virtual void ShowMatchPreview();
+ virtual void ShowMatchPreview(TabContents* preview_contents);
virtual void HideMatchPreview();
virtual gfx::Rect GetMatchPreviewBounds();
diff --git a/chrome/browser/cocoa/browser_window_cocoa.mm b/chrome/browser/cocoa/browser_window_cocoa.mm
index 168300e..0c8d4e9 100644
--- a/chrome/browser/cocoa/browser_window_cocoa.mm
+++ b/chrome/browser/cocoa/browser_window_cocoa.mm
@@ -564,7 +564,7 @@ void BrowserWindowCocoa::OpenTabpose() {
[controller_ openTabpose];
}
-void BrowserWindowCocoa::ShowMatchPreview() {
+void BrowserWindowCocoa::ShowMatchPreview(TabContents* preview_contents) {
// TODO: implement me
NOTIMPLEMENTED();
}
diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc
index 58b1db5..d3146fa 100644
--- a/chrome/browser/gtk/browser_window_gtk.cc
+++ b/chrome/browser/gtk/browser_window_gtk.cc
@@ -1133,7 +1133,7 @@ void BrowserWindowGtk::Paste() {
DoCutCopyPaste(this, &RenderViewHost::Paste, "paste-clipboard");
}
-void BrowserWindowGtk::ShowMatchPreview() {
+void BrowserWindowGtk::ShowMatchPreview(TabContents* preview_contents) {
// TODO: implement me
NOTIMPLEMENTED();
}
diff --git a/chrome/browser/gtk/browser_window_gtk.h b/chrome/browser/gtk/browser_window_gtk.h
index b032a96..b5da791 100644
--- a/chrome/browser/gtk/browser_window_gtk.h
+++ b/chrome/browser/gtk/browser_window_gtk.h
@@ -123,7 +123,7 @@ class BrowserWindowGtk : public BrowserWindow,
virtual void Copy();
virtual void Paste();
virtual void ToggleTabStripMode() {}
- virtual void ShowMatchPreview();
+ virtual void ShowMatchPreview(TabContents* preview_contents);
virtual void HideMatchPreview();
virtual gfx::Rect GetMatchPreviewBounds();
diff --git a/chrome/browser/search_engines/template_url_id.h b/chrome/browser/search_engines/template_url_id.h
index db607e2..1fb1455 100644
--- a/chrome/browser/search_engines/template_url_id.h
+++ b/chrome/browser/search_engines/template_url_id.h
@@ -6,6 +6,8 @@
#define CHROME_BROWSER_SEARCH_ENGINES_TEMPLATE_URL_ID_H_
#pragma once
+#include "base/basictypes.h"
+
typedef int64 TemplateURLID;
#endif // CHROME_BROWSER_SEARCH_ENGINES_TEMPLATE_URL_ID_H_
diff --git a/chrome/browser/tab_contents/loader_manager.cc b/chrome/browser/tab_contents/loader_manager.cc
new file mode 100644
index 0000000..962154f
--- /dev/null
+++ b/chrome/browser/tab_contents/loader_manager.cc
@@ -0,0 +1,123 @@
+// 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/tab_contents/loader_manager.h"
+
+#include "base/logging.h"
+#include "chrome/browser/tab_contents/match_preview_loader.h"
+#include "chrome/browser/tab_contents/match_preview_loader_delegate.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+
+LoaderManager::LoaderManager(MatchPreviewLoaderDelegate* loader_delegate)
+ : loader_delegate_(loader_delegate),
+ current_loader_(NULL),
+ pending_loader_(NULL) {
+}
+
+LoaderManager::~LoaderManager() {
+ for (Loaders::iterator i = instant_loaders_.begin();
+ i != instant_loaders_.end(); ++i) {
+ if (i->second == current_loader_)
+ current_loader_ = NULL;
+ if (i->second == pending_loader_)
+ pending_loader_ = NULL;
+ delete i->second;
+ }
+ instant_loaders_.clear();
+
+ if (current_loader_)
+ delete current_loader_;
+ if (pending_loader_)
+ delete pending_loader_;
+}
+
+MatchPreviewLoader* LoaderManager::UpdateLoader(
+ TemplateURLID instant_id,
+ scoped_ptr<MatchPreviewLoader>* old_loader) {
+ MatchPreviewLoader* old_current_loader = current_loader_;
+ MatchPreviewLoader* old_pending_loader = pending_loader_;
+
+ // Determine the new loader.
+ MatchPreviewLoader* loader = NULL;
+ if (instant_id) {
+ loader = GetInstantLoader(instant_id);
+ } else {
+ if (current_loader_ && !current_loader_->template_url_id())
+ loader = current_loader_;
+ else if (pending_loader_ && !pending_loader_->template_url_id())
+ loader = pending_loader_;
+ else
+ loader = CreateLoader(0);
+ }
+
+ if (loader->ready()) {
+ // The loader is ready, make it the current loader no matter what.
+ current_loader_ = loader;
+ pending_loader_ = NULL;
+ } else {
+ // The loader isn't ready make it the current only if the current isn't
+ // ready. If the current is ready, then stop the current and make the new
+ // loader pending.
+ if (!current_loader_ || !current_loader_->ready()) {
+ current_loader_ = loader;
+ DCHECK(!pending_loader_);
+ } else {
+ // preview_contents() may be null for tests.
+ if (!current_loader_->template_url_id() &&
+ current_loader_->preview_contents()) {
+ current_loader_->preview_contents()->Stop();
+ }
+ pending_loader_ = loader;
+ }
+ }
+
+ if (current_loader_ != old_current_loader && old_current_loader &&
+ !old_current_loader->template_url_id()) {
+ old_loader->reset(old_current_loader);
+ }
+ if (pending_loader_ != old_pending_loader && old_pending_loader &&
+ !old_pending_loader->template_url_id()) {
+ DCHECK(!old_loader->get());
+ old_loader->reset(old_pending_loader);
+ }
+
+ return active_loader();
+}
+
+void LoaderManager::MakePendingCurrent(
+ scoped_ptr<MatchPreviewLoader>* old_loader) {
+ DCHECK(current_loader_);
+ DCHECK(pending_loader_);
+
+ if (!current_loader_->template_url_id())
+ old_loader->reset(current_loader_);
+
+ current_loader_ = pending_loader_;
+ pending_loader_ = NULL;
+}
+
+MatchPreviewLoader* LoaderManager::ReleaseCurrentLoader() {
+ DCHECK(current_loader_);
+ MatchPreviewLoader* loader = current_loader_;
+ if (current_loader_->template_url_id()) {
+ Loaders::iterator i =
+ instant_loaders_.find(current_loader_->template_url_id());
+ DCHECK(i != instant_loaders_.end());
+ instant_loaders_.erase(i);
+ }
+ current_loader_ = NULL;
+ return loader;
+}
+
+MatchPreviewLoader* LoaderManager::CreateLoader(TemplateURLID id) {
+ MatchPreviewLoader* loader = new MatchPreviewLoader(loader_delegate_, id);
+ if (id)
+ instant_loaders_[id] = loader;
+ return loader;
+}
+
+MatchPreviewLoader* LoaderManager::GetInstantLoader(TemplateURLID id) {
+ Loaders::iterator i = instant_loaders_.find(id);
+ return i == instant_loaders_.end() ? CreateLoader(id) : i->second;
+}
diff --git a/chrome/browser/tab_contents/loader_manager.h b/chrome/browser/tab_contents/loader_manager.h
new file mode 100644
index 0000000..a72defc
--- /dev/null
+++ b/chrome/browser/tab_contents/loader_manager.h
@@ -0,0 +1,97 @@
+// 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_LOADER_MANAGER_H_
+#define CHROME_BROWSER_TAB_CONTENTS_LOADER_MANAGER_H_
+#pragma once
+
+#include <map>
+
+#include "base/scoped_ptr.h"
+#include "chrome/browser/search_engines/template_url_id.h"
+
+class MatchPreviewLoader;
+class MatchPreviewLoaderDelegate;
+
+// LoaderManager is responsible for maintaining the MatchPreviewLoaders for
+// MatchPreview. LoaderManager keeps track of one loader for loading non-instant
+// urls, and a loader per TemplateURLID for loading instant urls. A loader per
+// TemplateURLID is necessitated due to not knowing in advance if a site
+// really supports instant (for example, the user might have opted out even
+// though it's supported).
+//
+// Users of LoaderManager need only concern themselves with the current and
+// pending loaders. The current loader is the loader that if ready is shown by
+// MatchPreview. The pending loader is used if the current loader is ready and
+// update is invoked with a different id. In this case the current loader is
+// left as current (and it's preview contents stopped) and the newly created
+// loader is set to pending. Once the pending loader is ready
+// MakePendingCurrent should be invoked to make the pending the current loader.
+//
+// MatchPreviewLoader owns all the MatchPreviewLoaders returned. You can take
+// ownership of the current loader by invoking ReleaseCurrentLoader.
+class LoaderManager {
+ public:
+ explicit LoaderManager(MatchPreviewLoaderDelegate* loader_delegate);
+ ~LoaderManager();
+
+ // Updates the current loader. If the current loader is replaced and should be
+ // deleted it is set in |old_loader|. This is done to allow the caller to
+ // notify delegates before the old loader is destroyed. This returns the
+ // active MatchPreviewLoader that should be used.
+ MatchPreviewLoader* UpdateLoader(TemplateURLID instant_id,
+ scoped_ptr<MatchPreviewLoader>* old_loader);
+
+ // Makes the pending loader the current loader. If ownership of the old
+ // loader is to pass to the caller |old_loader| is set appropriately.
+ void MakePendingCurrent(scoped_ptr<MatchPreviewLoader>* old_loader);
+
+ // Returns the current loader and clears internal references to it. This
+ // should be used prior to destroying the LoaderManager when the owner of
+ // LoaderManager wants to take ownership of the loader.
+ MatchPreviewLoader* ReleaseCurrentLoader();
+
+ // Returns the current loader, may be null.
+ MatchPreviewLoader* current_loader() const { return current_loader_; }
+
+ // Returns the pending loader, may be null.
+ MatchPreviewLoader* pending_loader() const { return pending_loader_; }
+
+ // The active loader is the loader that should be used for new loads. It is
+ // either the pending loader or the current loader.
+ MatchPreviewLoader* active_loader() const {
+ return pending_loader_ ? pending_loader_ : current_loader_;
+ }
+
+ // Returns the number of instant loaders.
+ // This is exposed for tests.
+ size_t num_instant_loaders() const { return instant_loaders_.size(); }
+
+ private:
+ typedef std::map<TemplateURLID, MatchPreviewLoader*> Loaders;
+
+ // Creates a loader and if |id| is non-zero registers it in instant_loaders_.
+ MatchPreviewLoader* CreateLoader(TemplateURLID id);
+
+ // Returns the loader for loading instant results with the specified id. If
+ // there is no loader for the specified id a new one is created.
+ MatchPreviewLoader* GetInstantLoader(TemplateURLID id);
+
+ MatchPreviewLoaderDelegate* loader_delegate_;
+
+ // The current loader.
+ MatchPreviewLoader* current_loader_;
+
+ // Loader we want to use as soon as ready. This is only non-null if
+ // current_loader_ is ready and Update is invoked with a different template
+ // url id.
+ MatchPreviewLoader* pending_loader_;
+
+ // Maps for template url id to loader used for that template url id.
+ Loaders instant_loaders_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoaderManager);
+};
+
+#endif // CHROME_BROWSER_TAB_CONTENTS_LOADER_MANAGER_H_
diff --git a/chrome/browser/tab_contents/loader_manager_unittest.cc b/chrome/browser/tab_contents/loader_manager_unittest.cc
new file mode 100644
index 0000000..4c9c5de
--- /dev/null
+++ b/chrome/browser/tab_contents/loader_manager_unittest.cc
@@ -0,0 +1,209 @@
+// 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 "base/scoped_ptr.h"
+#include "chrome/browser/tab_contents/loader_manager.h"
+#include "chrome/browser/tab_contents/match_preview_loader.h"
+#include "chrome/browser/tab_contents/match_preview_loader_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MatchPreviewLoaderDelegateImpl : public MatchPreviewLoaderDelegate {
+ public:
+ MatchPreviewLoaderDelegateImpl() {}
+
+ virtual void ShowMatchPreviewLoader(MatchPreviewLoader* loader) {}
+
+ virtual void SetSuggestedTextFor(MatchPreviewLoader* loader,
+ const string16& text) {}
+
+ virtual gfx::Rect GetMatchPreviewBounds() {
+ return gfx::Rect();
+ }
+
+ virtual bool ShouldCommitPreviewOnMouseUp() {
+ return false;
+ }
+
+ virtual void CommitPreview(MatchPreviewLoader* loader) {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MatchPreviewLoaderDelegateImpl);
+};
+
+}
+
+class LoaderManagerTest : public testing::Test {
+ public:
+ LoaderManagerTest() {}
+
+ void MarkReady(MatchPreviewLoader* loader) {
+ loader->ready_ = true;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LoaderManagerTest);
+};
+
+// Makes sure UpdateLoader works when invoked once.
+TEST_F(LoaderManagerTest, Basic) {
+ MatchPreviewLoaderDelegateImpl delegate;
+ LoaderManager manager(&delegate);
+ scoped_ptr<MatchPreviewLoader> loader;
+ manager.UpdateLoader(0, &loader);
+ EXPECT_EQ(NULL, loader.get());
+ EXPECT_TRUE(manager.current_loader());
+ EXPECT_EQ(NULL, manager.pending_loader());
+ EXPECT_EQ(0, manager.current_loader()->template_url_id());
+}
+
+// Make sure invoking update twice for non-instant results keeps the same
+// loader.
+TEST_F(LoaderManagerTest, UpdateTwice) {
+ MatchPreviewLoaderDelegateImpl delegate;
+ LoaderManager manager(&delegate);
+ scoped_ptr<MatchPreviewLoader> loader;
+ manager.UpdateLoader(0, &loader);
+ MatchPreviewLoader* current_loader = manager.current_loader();
+ manager.UpdateLoader(0, &loader);
+ EXPECT_EQ(NULL, loader.get());
+ EXPECT_EQ(current_loader, manager.current_loader());
+ EXPECT_EQ(NULL, manager.pending_loader());
+}
+
+// Make sure invoking update twice for instant results keeps the same loader.
+TEST_F(LoaderManagerTest, UpdateInstantTwice) {
+ MatchPreviewLoaderDelegateImpl delegate;
+ LoaderManager manager(&delegate);
+ scoped_ptr<MatchPreviewLoader> loader;
+ manager.UpdateLoader(1, &loader);
+ MatchPreviewLoader* current_loader = manager.current_loader();
+ manager.UpdateLoader(1, &loader);
+ EXPECT_EQ(NULL, loader.get());
+ EXPECT_EQ(current_loader, manager.current_loader());
+ EXPECT_EQ(NULL, manager.pending_loader());
+ EXPECT_EQ(1u, manager.num_instant_loaders());
+}
+
+// Makes sure transitioning from non-instant to instant works.
+TEST_F(LoaderManagerTest, NonInstantToInstant) {
+ MatchPreviewLoaderDelegateImpl delegate;
+ LoaderManager manager(&delegate);
+ scoped_ptr<MatchPreviewLoader> loader;
+ manager.UpdateLoader(0, &loader);
+ MatchPreviewLoader* current_loader = manager.current_loader();
+ manager.UpdateLoader(1, &loader);
+ EXPECT_TRUE(loader.get() != NULL);
+ EXPECT_NE(current_loader, manager.current_loader());
+ EXPECT_EQ(NULL, manager.pending_loader());
+ EXPECT_EQ(1u, manager.num_instant_loaders());
+}
+
+// Makes sure instant loaders aren't deleted when invoking update with different
+// ids.
+TEST_F(LoaderManagerTest, DontDeleteInstantLoaders) {
+ MatchPreviewLoaderDelegateImpl delegate;
+ LoaderManager manager(&delegate);
+ scoped_ptr<MatchPreviewLoader> loader;
+ manager.UpdateLoader(1, &loader);
+ MatchPreviewLoader* current_loader = manager.current_loader();
+ manager.UpdateLoader(2, &loader);
+ EXPECT_EQ(NULL, loader.get());
+ EXPECT_NE(current_loader, manager.current_loader());
+ EXPECT_EQ(NULL, manager.pending_loader());
+ EXPECT_EQ(2u, manager.num_instant_loaders());
+}
+
+// Makes sure a new loader is created and assigned to secondary when
+// transitioning from a ready non-instant to instant.
+TEST_F(LoaderManagerTest, CreateSecondaryWhenReady) {
+ MatchPreviewLoaderDelegateImpl delegate;
+ LoaderManager manager(&delegate);
+ scoped_ptr<MatchPreviewLoader> loader;
+ manager.UpdateLoader(0, &loader);
+ MatchPreviewLoader* current_loader = manager.current_loader();
+ ASSERT_TRUE(current_loader);
+ MarkReady(current_loader);
+
+ manager.UpdateLoader(1, &loader);
+ EXPECT_EQ(NULL, loader.get());
+ EXPECT_EQ(current_loader, manager.current_loader());
+ EXPECT_TRUE(manager.pending_loader());
+ EXPECT_NE(current_loader, manager.pending_loader());
+ EXPECT_EQ(1u, manager.num_instant_loaders());
+
+ // Make the pending loader current.
+ MatchPreviewLoader* pending_loader = manager.pending_loader();
+ manager.MakePendingCurrent(&loader);
+ EXPECT_TRUE(loader.get());
+ EXPECT_EQ(pending_loader, manager.current_loader());
+ EXPECT_EQ(NULL, manager.pending_loader());
+ EXPECT_EQ(1u, manager.num_instant_loaders());
+}
+
+// Makes sure releasing an instant updates maps currectly.
+TEST_F(LoaderManagerTest, ReleaseInstant) {
+ MatchPreviewLoaderDelegateImpl delegate;
+ LoaderManager manager(&delegate);
+ scoped_ptr<MatchPreviewLoader> loader;
+ manager.UpdateLoader(1, &loader);
+ scoped_ptr<MatchPreviewLoader> current_loader(manager.ReleaseCurrentLoader());
+ EXPECT_TRUE(current_loader.get());
+ EXPECT_EQ(NULL, manager.current_loader());
+ EXPECT_EQ(0u, manager.num_instant_loaders());
+}
+
+// Tests transitioning from a non-instant ready loader to an instant ready
+// loader is immediate.
+TEST_F(LoaderManagerTest, NonInstantToInstantWhenReady) {
+ MatchPreviewLoaderDelegateImpl delegate;
+ LoaderManager manager(&delegate);
+ scoped_ptr<MatchPreviewLoader> loader;
+ manager.UpdateLoader(1, &loader);
+ ASSERT_TRUE(manager.current_loader());
+ EXPECT_EQ(1, manager.current_loader()->template_url_id());
+ MatchPreviewLoader* instant_loader = manager.current_loader();
+
+ manager.UpdateLoader(0, &loader);
+ MatchPreviewLoader* non_instant_loader = manager.current_loader();
+ ASSERT_TRUE(non_instant_loader);
+ MarkReady(non_instant_loader);
+ EXPECT_NE(non_instant_loader, instant_loader);
+
+ MarkReady(instant_loader);
+ manager.UpdateLoader(1, &loader);
+ EXPECT_EQ(non_instant_loader, loader.get());
+ EXPECT_EQ(instant_loader, manager.current_loader());
+ EXPECT_EQ(NULL, manager.pending_loader());
+ EXPECT_EQ(1u, manager.num_instant_loaders());
+}
+
+// Tests transitioning between 3 instant loaders, all ready.
+TEST_F(LoaderManagerTest, ThreeInstant) {
+ MatchPreviewLoaderDelegateImpl delegate;
+ LoaderManager manager(&delegate);
+ scoped_ptr<MatchPreviewLoader> loader;
+ manager.UpdateLoader(1, &loader);
+ ASSERT_TRUE(manager.current_loader());
+ EXPECT_EQ(1, manager.current_loader()->template_url_id());
+ MatchPreviewLoader* instant_loader1 = manager.current_loader();
+ MarkReady(instant_loader1);
+
+ manager.UpdateLoader(2, &loader);
+ MatchPreviewLoader* instant_loader2 = manager.pending_loader();
+ ASSERT_TRUE(instant_loader2);
+ EXPECT_EQ(2, instant_loader2->template_url_id());
+ EXPECT_NE(instant_loader1, instant_loader2);
+ EXPECT_EQ(instant_loader1, manager.current_loader());
+
+ manager.UpdateLoader(3, &loader);
+ MatchPreviewLoader* instant_loader3 = manager.pending_loader();
+ ASSERT_TRUE(instant_loader3);
+ EXPECT_EQ(3, instant_loader3->template_url_id());
+ EXPECT_NE(instant_loader1, instant_loader3);
+ EXPECT_NE(instant_loader2, instant_loader3);
+ EXPECT_EQ(instant_loader1, manager.current_loader());
+}
diff --git a/chrome/browser/tab_contents/match_preview.cc b/chrome/browser/tab_contents/match_preview.cc
index 7e8e63f..ef8baf2 100644
--- a/chrome/browser/tab_contents/match_preview.cc
+++ b/chrome/browser/tab_contents/match_preview.cc
@@ -4,463 +4,17 @@
#include "chrome/browser/tab_contents/match_preview.h"
-#include <algorithm>
-
#include "base/command_line.h"
-#include "base/string_number_conversions.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/loader_manager.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/match_preview_loader.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 {
-
-// Script sent as the user is typing and the provider supports instant.
-// Params:
-// . the text the user typed.
-// TODO: add support for the 2nd and 3rd params.
-const char kUserInputScript[] =
- "if (window.chrome.userInput) window.chrome.userInput(\"$1\", 0, 0);";
-
-// Script sent when the page is committed and the provider supports instant.
-// Params:
-// . the text the user typed.
-// . boolean indicating if the user pressed enter to accept the text.
-const char kUserDoneScript[] =
- "if (window.chrome.userWantsQuery) "
- "window.chrome.userWantsQuery(\"$1\", $2);";
-
-// Script sent when the bounds of the omnibox changes and the provider supports
-// instant. The params are the bounds relative to the origin of the preview
-// (x, y, width, height).
-const char kSetOmniboxBoundsScript[] =
- "if (window.chrome.setDropdownDimensions) "
- "window.chrome.setDropdownDimensions($1, $2, $3, $4);";
-
-// Escapes quotes in the |text| so that it be passed to JavaScript as a quoted
-// string.
-string16 EscapeUserText(const string16& text) {
- string16 escaped_text(text);
- ReplaceSubstringsAfterOffset(&escaped_text, 0L, ASCIIToUTF16("\""),
- ASCIIToUTF16("\\\""));
- return escaped_text;
-}
-
-// Sends the script for when the user commits the preview. |pressed_enter| is
-// true if the user pressed enter to commit.
-void SendDoneScript(TabContents* tab_contents,
- const string16& text,
- bool pressed_enter) {
- std::vector<string16> params;
- params.push_back(EscapeUserText(text));
- params.push_back(pressed_enter ? ASCIIToUTF16("true") :
- ASCIIToUTF16("false"));
- string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserDoneScript),
- params,
- NULL);
- tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
- std::wstring(),
- UTF16ToWide(script));
-}
-
-// 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) {
- string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserInputScript),
- EscapeUserText(text),
- NULL);
- tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
- std::wstring(),
- UTF16ToWide(script));
-}
-
-// Sends the script for setting the bounds of the omnibox to |tab_contents|.
-void SendOmniboxBoundsScript(TabContents* tab_contents,
- const gfx::Rect& bounds) {
- std::vector<string16> bounds_vector;
- bounds_vector.push_back(base::IntToString16(bounds.x()));
- bounds_vector.push_back(base::IntToString16(bounds.y()));
- bounds_vector.push_back(base::IntToString16(bounds.width()));
- bounds_vector.push_back(base::IntToString16(bounds.height()));
- string16 script = ReplaceStringPlaceholders(
- ASCIIToUTF16(kSetOmniboxBoundsScript),
- bounds_vector,
- 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),
- pressed_enter_(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(bool pressed_enter) {
- match_preview_ = NULL;
- pressed_enter_ = pressed_enter;
- }
-
- // 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;
- }
-
- if (match_preview_) {
- gfx::Rect bounds = match_preview_->GetOmniboxBoundsInTermsOfPreview();
- if (!bounds.IsEmpty())
- SendOmniboxBoundsScript(tab_contents_, bounds);
- }
-
- SendUserInputScript(tab_contents_, text_);
-
- if (match_preview_)
- match_preview_->PageFinishedLoading();
- else
- SendDoneScript(tab_contents_, text_, pressed_enter_);
-
- 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 SendDoneScript.
- bool pressed_enter_;
-
- // 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_->ShowPreview();
- 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),
- installed_paint_observer_(false),
- waiting_for_new_page_(true),
- is_mouse_down_from_activate_(false) {
- }
-
- // 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;
- is_mouse_down_from_activate_ = false;
- }
-
- bool is_mouse_down_from_activate() const {
- return is_mouse_down_from_activate_;
- }
-
- // 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,
- const GURL& url, const GURL& referrer,
- WindowOpenDisposition disposition,
- PageTransition::Type transition) {}
- virtual void NavigationStateChanged(const TabContents* source,
- 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) {
- }
- virtual void DeactivateContents(TabContents* contents) {}
- virtual void LoadingStateChanged(TabContents* source) {}
- virtual void CloseContents(TabContents* source) {}
- virtual void MoveContents(TabContents* source, const gfx::Rect& pos) {}
- virtual void DetachContents(TabContents* source) {}
- virtual bool IsPopup(const TabContents* source) const {
- return false;
- }
- virtual bool ShouldFocusConstrainedWindow(TabContents* source) {
- // Return false so that constrained windows are not initially focused. If
- // we did otherwise the preview would prematurely get committed when focus
- // goes to the constrained window.
- return false;
- }
- virtual void WillShowConstrainedWindow(TabContents* source) {
- if (!match_preview_->is_active()) {
- // A constrained window shown for an auth may not paint. Show the preview
- // contents.
- if (installed_paint_observer_) {
- source->GetRenderWidgetHostView()->GetRenderWidgetHost()->
- set_paint_observer(NULL);
- }
- installed_paint_observer_ = true;
- match_preview_->ShowPreview();
- }
- }
- virtual TabContents* GetConstrainingContents(TabContents* source) {
- return NULL;
- }
- virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {}
- virtual void URLStarredChanged(TabContents* source, bool starred) {}
- virtual void UpdateTargetURL(TabContents* source, const GURL& url) {}
- virtual void ContentsMouseEvent(
- TabContents* source, const gfx::Point& location, bool motion) {}
- virtual void ContentsZoomChange(bool zoom_in) {}
- virtual void OnContentSettingsChange(TabContents* source) {}
- virtual bool IsApplication() const { return false; }
- virtual void ConvertContentsToApplication(TabContents* source) {}
- virtual bool CanReloadContents(TabContents* source) const { return true; }
- virtual gfx::Rect GetRootWindowResizerRect() const {
- return gfx::Rect();
- }
- virtual void ShowHtmlDialog(HtmlDialogUIDelegate* delegate,
- gfx::NativeWindow parent_window) {}
- virtual void BeforeUnloadFired(TabContents* tab,
- bool proceed,
- bool* proceed_to_fire_unload) {}
- virtual void ForwardMessageToExternalHost(const std::string& message,
- const std::string& origin,
- const std::string& target) {}
- virtual bool IsExternalTabContainer() const { return false; }
- virtual void SetFocusToLocationBar(bool select_all) {}
- virtual bool ShouldFocusPageAfterCrash() { return false; }
- virtual void RenderWidgetShowing() {}
- virtual ExtensionFunctionDispatcher* CreateExtensionFunctionDispatcher(
- RenderViewHost* render_view_host,
- const std::string& extension_id) {
- return NULL;
- }
- virtual bool TakeFocus(bool reverse) { return false; }
- virtual void LostCapture() {
- CommitFromMouseReleaseIfNecessary();
- }
- virtual void SetTabContentBlocked(TabContents* contents, bool blocked) {}
- virtual void TabContentsFocused(TabContents* tab_content) {
- }
- virtual int GetExtraRenderViewHeight() const { return 0; }
- virtual bool CanDownload(int request_id) { return false; }
- virtual void OnStartDownload(DownloadItem* download, TabContents* tab) {}
- virtual bool HandleContextMenu(const ContextMenuParams& params) {
- return false;
- }
- virtual bool ExecuteContextMenuCommand(int command) {
- return false;
- }
- virtual void ConfirmAddSearchProvider(const TemplateURL* template_url,
- Profile* profile) {}
- virtual void ShowPageInfo(Profile* profile,
- const GURL& url,
- const NavigationEntry::SSLStatus& ssl,
- bool show_history) {}
- virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
- bool* is_keyboard_shortcut) {
- return false;
- }
- virtual void HandleMouseUp() {
- CommitFromMouseReleaseIfNecessary();
- }
- virtual void HandleMouseActivate() {
- is_mouse_down_from_activate_ = true;
- }
- virtual void ShowRepostFormWarningDialog(TabContents* tab_contents) {}
- virtual void ShowContentSettingsWindow(ContentSettingsType content_type) {}
- virtual void ShowCollectedCookiesDialog(TabContents* tab_contents) {}
- virtual bool OnGoToEntryOffset(int offset) { return false; }
- virtual bool ShouldAddNavigationToHistory(
- 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 NULL;
- }
- virtual void TabContentsCreated(TabContents* new_contents) {}
- virtual bool infobars_enabled() { return false; }
- virtual bool ShouldEnablePreferredSizeNotifications() { return false; }
- virtual void UpdatePreferredSize(const gfx::Size& pref_size) {}
-
- 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;
-
- void CommitFromMouseReleaseIfNecessary() {
- bool was_down = is_mouse_down_from_activate_;
- is_mouse_down_from_activate_ = false;
- if (was_down && match_preview_->commit_on_mouse_up_)
- match_preview_->CommitCurrentPreview(MatchPreview::COMMIT_FOCUS_LOST);
- }
-
- MatchPreview* match_preview_;
-
- // Has the paint observer been installed? See comment in
- // NavigationStateChanged for details on this.
- bool installed_paint_observer_;
-
- // 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_;
-
- // 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_;
-
- // Returns true if the mouse is down from an activate.
- bool is_mouse_down_from_activate_;
-
- DISALLOW_COPY_AND_ASSIGN(TabContentsDelegateImpl);
-};
+#include "chrome/common/url_constants.h"
// static
bool MatchPreview::IsEnabled() {
@@ -478,16 +32,11 @@ MatchPreview::MatchPreview(MatchPreviewDelegate* delegate)
: delegate_(delegate),
tab_contents_(NULL),
is_active_(false),
- template_url_id_(0),
commit_on_mouse_up_(false),
last_transition_type_(PageTransition::LINK) {
- 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,
@@ -497,42 +46,22 @@ void MatchPreview::Update(TabContents* tab_contents,
if (tab_contents != tab_contents_)
DestroyPreviewContents();
+ const GURL& url = match.destination_url;
+
tab_contents_ = tab_contents;
commit_on_mouse_up_ = false;
last_transition_type_ = match.transition;
- if (url_ == match.destination_url)
+ if (loader_manager_.get() && loader_manager_->active_loader()->url() == url)
return;
- url_ = match.destination_url;
-
- if (url_.is_empty() || !url_.is_valid()) {
+ if (url.is_empty() || !url.is_valid() || !ShouldShowPreviewFor(url)) {
DestroyPreviewContents();
return;
}
- user_text_ = user_text;
-
- if (preview_contents_.get() == NULL) {
- preview_contents_.reset(
- 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();
+ if (!loader_manager_.get())
+ loader_manager_.reset(new LoaderManager(this));
const TemplateURL* template_url = match.template_url;
if (match.type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED ||
@@ -541,63 +70,52 @@ void MatchPreview::Update(TabContents* tab_contents,
TemplateURLModel* model = tab_contents->profile()->GetTemplateURLModel();
template_url = model ? model->GetDefaultSearchProvider() : NULL;
}
+ // TODO(sky): remove the id check. It's only necessary because the search
+ // engine saved to prefs doesn't have an id. Jean-luc is fixing separately.
+ if (template_url && (!template_url->supports_instant() ||
+ !template_url->id() ||
+ !TemplateURL::SupportsReplacement(template_url))) {
+ template_url = 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 (is_waiting_for_load()) {
- // 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_);
- 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 {
- template_url_id_ = 0;
- frame_load_observer_.reset(NULL);
- preview_contents_->controller().LoadURL(url_, GURL(), match.transition);
- }
+ MatchPreviewLoader* old_loader = loader_manager_->current_loader();
+ scoped_ptr<MatchPreviewLoader> owned_loader;
+ MatchPreviewLoader* new_loader =
+ loader_manager_->UpdateLoader(template_url_id, &owned_loader);
- template_url_id_ = template_url_id;
+ new_loader->SetOmniboxBounds(omnibox_bounds_);
+ new_loader->Update(tab_contents, match, user_text, template_url,
+ suggested_text);
+ if (old_loader != new_loader && new_loader->ready())
+ delegate_->ShowMatchPreview(new_loader->preview_contents());
}
void MatchPreview::SetOmniboxBounds(const gfx::Rect& bounds) {
if (omnibox_bounds_ == bounds)
return;
- omnibox_bounds_ = bounds;
- if (preview_contents_.get() && is_showing_instant() &&
- !is_waiting_for_load()) {
- SendOmniboxBoundsScript(preview_contents_.get(),
- GetOmniboxBoundsInTermsOfPreview());
+ if (loader_manager_.get()) {
+ if (loader_manager_->current_loader())
+ loader_manager_->current_loader()->SetOmniboxBounds(bounds);
+ if (loader_manager_->pending_loader())
+ loader_manager_->pending_loader()->SetOmniboxBounds(bounds);
}
}
void MatchPreview::DestroyPreviewContents() {
- if (!preview_contents_.get()) {
+ if (!loader_manager_.get()) {
// We're not showing anything, nothing to do.
return;
}
delegate_->HideMatchPreview();
- delete ReleasePreviewContents(COMMIT_DESTROY);
+ delete ReleasePreviewContents(MATCH_PREVIEW_COMMIT_DESTROY);
}
-void MatchPreview::CommitCurrentPreview(CommitType type) {
- DCHECK(preview_contents_.get());
+void MatchPreview::CommitCurrentPreview(MatchPreviewCommitType type) {
+ DCHECK(loader_manager_.get());
+ DCHECK(loader_manager_->current_loader());
delegate_->CommitMatchPreview(ReleasePreviewContents(type));
}
@@ -606,81 +124,75 @@ void MatchPreview::SetCommitOnMouseUp() {
}
bool MatchPreview::IsMouseDownFromActivate() {
- return preview_tab_contents_delegate_->is_mouse_down_from_activate();
+ DCHECK(loader_manager_.get());
+ DCHECK(loader_manager_->current_loader());
+ return loader_manager_->current_loader()->IsMouseDownFromActivate();
}
-TabContents* MatchPreview::ReleasePreviewContents(CommitType type) {
- if (!preview_contents_.get())
+TabContents* MatchPreview::ReleasePreviewContents(MatchPreviewCommitType type) {
+ if (!loader_manager_.get())
return NULL;
- if (frame_load_observer_.get()) {
- frame_load_observer_->DetachFromPreview(type == COMMIT_PRESSED_ENTER);
- // FrameLoadObserver will delete itself either when the TabContents is
- // deleted, or when the page finishes loading.
- FrameLoadObserver* unused ALLOW_UNUSED = frame_load_observer_.release();
- } else if (type != COMMIT_DESTROY && is_showing_instant()) {
- SendDoneScript(preview_contents_.get(),
- user_text_,
- type == COMMIT_PRESSED_ENTER);
- }
- commit_on_mouse_up_ = false;
+ scoped_ptr<MatchPreviewLoader> loader(
+ loader_manager_->ReleaseCurrentLoader());
+ TabContents* tab = loader->ReleasePreviewContents(type);
+
+ is_active_ = false;
omnibox_bounds_ = gfx::Rect();
- template_url_id_ = 0;
- url_ = GURL();
- user_text_.clear();
- complete_suggested_text_.clear();
- if (preview_contents_.get()) {
- if (type != COMMIT_DESTROY)
- preview_tab_contents_delegate_->CommitHistory();
- // Destroy the paint observer.
- // RenderWidgetHostView may be null during shutdown.
- if (preview_contents_->GetRenderWidgetHostView()) {
- 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();
+ commit_on_mouse_up_ = false;
+ loader_manager_.reset(NULL);
+ return tab;
}
-void MatchPreview::SetCompleteSuggestedText(
- const string16& complete_suggested_text) {
- if (complete_suggested_text == complete_suggested_text_)
- return;
+TabContents* MatchPreview::GetPreviewContents() {
+ return loader_manager_.get() ?
+ loader_manager_->current_loader()->preview_contents() : NULL;
+}
- 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;
+bool MatchPreview::IsShowingInstant() {
+ return loader_manager_.get() &&
+ loader_manager_->current_loader()->is_showing_instant();
+}
+
+void MatchPreview::ShowMatchPreviewLoader(MatchPreviewLoader* loader) {
+ DCHECK(loader_manager_.get());
+ if (loader_manager_->current_loader() == loader) {
+ is_active_ = true;
+ delegate_->ShowMatchPreview(loader->preview_contents());
+ } else if (loader_manager_->pending_loader() == loader) {
+ scoped_ptr<MatchPreviewLoader> old_loader;
+ loader_manager_->MakePendingCurrent(&old_loader);
+ delegate_->ShowMatchPreview(loader->preview_contents());
+ } else {
+ NOTREACHED();
}
+}
- complete_suggested_text_ = complete_suggested_text;
- delegate_->SetSuggestedText(
- complete_suggested_text_.substr(user_text_.size()));
+void MatchPreview::SetSuggestedTextFor(MatchPreviewLoader* loader,
+ const string16& text) {
+ if (loader_manager_->current_loader() == loader)
+ delegate_->SetSuggestedText(text);
}
-void MatchPreview::ShowPreview() {
- DCHECK(!is_active_);
- is_active_ = true;
- delegate_->ShowMatchPreview();
+gfx::Rect MatchPreview::GetMatchPreviewBounds() {
+ return delegate_->GetMatchPreviewBounds();
}
-void MatchPreview::PageFinishedLoading() {
- // FrameLoadObserver deletes itself after this call.
- FrameLoadObserver* unused ALLOW_UNUSED = frame_load_observer_.release();
+bool MatchPreview::ShouldCommitPreviewOnMouseUp() {
+ return commit_on_mouse_up_;
}
-gfx::Rect MatchPreview::GetOmniboxBoundsInTermsOfPreview() {
- if (omnibox_bounds_.IsEmpty())
- return omnibox_bounds_;
+void MatchPreview::CommitPreview(MatchPreviewLoader* loader) {
+ if (loader_manager_.get() && loader_manager_->current_loader() == loader) {
+ CommitCurrentPreview(MATCH_PREVIEW_COMMIT_FOCUS_LOST);
+ } else {
+ // This can happen if the mouse was down, we swapped out the preview and
+ // the mouse was released. Generally this shouldn't happen, but if it does
+ // revert.
+ DestroyPreviewContents();
+ }
+}
- gfx::Rect preview_bounds(delegate_->GetMatchPreviewBounds());
- return gfx::Rect(omnibox_bounds_.x() - preview_bounds.x(),
- omnibox_bounds_.y() - preview_bounds.y(),
- omnibox_bounds_.width(),
- omnibox_bounds_.height());
+bool MatchPreview::ShouldShowPreviewFor(const GURL& url) {
+ return !url.SchemeIs(chrome::kJavaScriptScheme);
}
diff --git a/chrome/browser/tab_contents/match_preview.h b/chrome/browser/tab_contents/match_preview.h
index 5b59528f..7d2d7f0 100644
--- a/chrome/browser/tab_contents/match_preview.h
+++ b/chrome/browser/tab_contents/match_preview.h
@@ -9,13 +9,15 @@
#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/browser/tab_contents/match_preview_commit_type.h"
+#include "chrome/browser/tab_contents/match_preview_loader_delegate.h"
#include "chrome/common/page_transition_types.h"
#include "gfx/rect.h"
#include "googleurl/src/gurl.h"
struct AutocompleteMatch;
+class LoaderManager;
class MatchPreviewDelegate;
class TabContents;
@@ -27,20 +29,8 @@ class TabContents;
// 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 {
+class MatchPreview : public MatchPreviewLoaderDelegate {
public:
- enum CommitType {
- // The commit is the result of the user pressing enter.
- COMMIT_PRESSED_ENTER,
-
- // The commit is the result of focus being lost. This typically corresponds
- // to a mouse click event.
- COMMIT_FOCUS_LOST,
-
- // Used internally.
- COMMIT_DESTROY
- };
-
explicit MatchPreview(MatchPreviewDelegate* delegate);
~MatchPreview();
@@ -67,13 +57,12 @@ class MatchPreview {
// Invoked when the user does some gesture that should trigger making the
// current previewed page the permanent page.
- void CommitCurrentPreview(CommitType type);
+ void CommitCurrentPreview(MatchPreviewCommitType type);
// Sets MatchPreview so that when the mouse is released the preview is
// committed.
void SetCommitOnMouseUp();
- // Returns true if the preview will be committed on mouse up.
bool commit_on_mouse_up() const { return commit_on_mouse_up_; }
// Returns true if the mouse is down as the result of activating the preview
@@ -83,13 +72,13 @@ class MatchPreview {
// Releases the preview TabContents passing ownership to the caller. This is
// intended to be called when the preview TabContents is committed. This does
// not notify the delegate.
- TabContents* ReleasePreviewContents(CommitType type);
+ TabContents* ReleasePreviewContents(MatchPreviewCommitType type);
// TabContents the match is being shown for.
TabContents* tab_contents() const { return tab_contents_; }
// The preview TabContents; may be null.
- TabContents* preview_contents() const { return preview_contents_.get(); }
+ TabContents* GetPreviewContents();
// Returns true if the preview TabContents is active. In some situations this
// may return false yet preview_contents() returns non-NULL.
@@ -100,16 +89,18 @@ class MatchPreview {
return last_transition_type_;
}
- const GURL& url() const { return url_; }
-
// Are we showing instant results?
- bool is_showing_instant() const { return template_url_id_ != 0; }
+ bool IsShowingInstant();
- private:
- class FrameLoadObserver;
- class PaintObserverImpl;
- class TabContentsDelegateImpl;
+ // MatchPreviewLoaderDelegate
+ virtual void ShowMatchPreviewLoader(MatchPreviewLoader* loader);
+ virtual void SetSuggestedTextFor(MatchPreviewLoader* loader,
+ const string16& text);
+ virtual gfx::Rect GetMatchPreviewBounds();
+ virtual bool ShouldCommitPreviewOnMouseUp();
+ virtual void CommitPreview(MatchPreviewLoader* loader);
+ private:
// 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.
@@ -123,54 +114,29 @@ class MatchPreview {
// Invoked once the page has finished loading and the script has been sent.
void PageFinishedLoading();
- // Returns the bounds of the omnibox in terms of the preview tab contents.
- gfx::Rect GetOmniboxBoundsInTermsOfPreview();
-
- // Are we waiting for the preview page to finish loading?
- bool is_waiting_for_load() const {
- return frame_load_observer_.get() != NULL;
- }
+ // Returns true if we should show preview for |url|.
+ bool ShouldShowPreviewFor(const GURL& url);
MatchPreviewDelegate* delegate_;
// The TabContents last passed to |Update|.
TabContents* tab_contents_;
- // The url we're displaying.
- GURL url_;
-
- // 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> 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_;
-
// See description above setter.
gfx::Rect omnibox_bounds_;
- scoped_ptr<FrameLoadObserver> frame_load_observer_;
-
// See description above CommitOnMouseUp.
bool commit_on_mouse_up_;
// See description above getter.
PageTransition::Type last_transition_type_;
+ scoped_ptr<LoaderManager> loader_manager_;
+
DISALLOW_COPY_AND_ASSIGN(MatchPreview);
};
diff --git a/chrome/browser/tab_contents/match_preview_commit_type.h b/chrome/browser/tab_contents/match_preview_commit_type.h
new file mode 100644
index 0000000..a4bbd3f
--- /dev/null
+++ b/chrome/browser/tab_contents/match_preview_commit_type.h
@@ -0,0 +1,22 @@
+// 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_COMMIT_TYPE_H_
+#define CHROME_BROWSER_TAB_CONTENTS_MATCH_PREVIEW_COMMIT_TYPE_H_
+#pragma once
+
+// Enum describing the ways the MatchPreview can be committed.
+enum MatchPreviewCommitType {
+ // The commit is the result of the user pressing enter.
+ MATCH_PREVIEW_COMMIT_PRESSED_ENTER,
+
+ // The commit is the result of focus being lost. This typically corresponds
+ // to a mouse click event.
+ MATCH_PREVIEW_COMMIT_FOCUS_LOST,
+
+ // Used internally by MatchPreview.
+ MATCH_PREVIEW_COMMIT_DESTROY
+};
+
+#endif // CHROME_BROWSER_TAB_CONTENTS_MATCH_PREVIEW_COMMIT_TYPE_H_
diff --git a/chrome/browser/tab_contents/match_preview_delegate.h b/chrome/browser/tab_contents/match_preview_delegate.h
index 8506cef..fed4d89 100644
--- a/chrome/browser/tab_contents/match_preview_delegate.h
+++ b/chrome/browser/tab_contents/match_preview_delegate.h
@@ -19,7 +19,7 @@ class Rect;
class MatchPreviewDelegate {
public:
// Invoked when the preview TabContents should be shown.
- virtual void ShowMatchPreview() = 0;
+ virtual void ShowMatchPreview(TabContents* preview_contents) = 0;
// Invoked when the preview TabContents should be hidden.
virtual void HideMatchPreview() = 0;
diff --git a/chrome/browser/tab_contents/match_preview_loader.cc b/chrome/browser/tab_contents/match_preview_loader.cc
new file mode 100644
index 0000000..f7ff7ed
--- /dev/null
+++ b/chrome/browser/tab_contents/match_preview_loader.cc
@@ -0,0 +1,659 @@
+// 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/tab_contents/match_preview_loader.h"
+
+#include <algorithm>
+
+#include "base/command_line.h"
+#include "base/string_number_conversions.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/tab_contents/match_preview_loader_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/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 {
+
+// Script sent as the user is typing and the provider supports instant.
+// Params:
+// . the text the user typed.
+// TODO: add support for the 2nd and 3rd params.
+const char kUserInputScript[] =
+ "if (window.chrome.userInput) window.chrome.userInput(\"$1\", 0, 0);";
+
+// Script sent when the page is committed and the provider supports instant.
+// Params:
+// . the text the user typed.
+// . boolean indicating if the user pressed enter to accept the text.
+const char kUserDoneScript[] =
+ "if (window.chrome.userWantsQuery) "
+ "window.chrome.userWantsQuery(\"$1\", $2);";
+
+// Script sent when the bounds of the omnibox changes and the provider supports
+// instant. The params are the bounds relative to the origin of the preview
+// (x, y, width, height).
+const char kSetOmniboxBoundsScript[] =
+ "if (window.chrome.setDropdownDimensions) "
+ "window.chrome.setDropdownDimensions($1, $2, $3, $4);";
+
+// Escapes quotes in the |text| so that it be passed to JavaScript as a quoted
+// string.
+string16 EscapeUserText(const string16& text) {
+ string16 escaped_text(text);
+ ReplaceSubstringsAfterOffset(&escaped_text, 0L, ASCIIToUTF16("\""),
+ ASCIIToUTF16("\\\""));
+ return escaped_text;
+}
+
+// Sends the script for when the user commits the preview. |pressed_enter| is
+// true if the user pressed enter to commit.
+void SendDoneScript(TabContents* tab_contents,
+ const string16& text,
+ bool pressed_enter) {
+ std::vector<string16> params;
+ params.push_back(EscapeUserText(text));
+ params.push_back(pressed_enter ? ASCIIToUTF16("true") :
+ ASCIIToUTF16("false"));
+ string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserDoneScript),
+ params,
+ NULL);
+ tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
+ std::wstring(),
+ UTF16ToWide(script));
+}
+
+// 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) {
+ string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserInputScript),
+ EscapeUserText(text),
+ NULL);
+ tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
+ std::wstring(),
+ UTF16ToWide(script));
+}
+
+// Sends the script for setting the bounds of the omnibox to |tab_contents|.
+void SendOmniboxBoundsScript(TabContents* tab_contents,
+ const gfx::Rect& bounds) {
+ std::vector<string16> bounds_vector;
+ bounds_vector.push_back(base::IntToString16(bounds.x()));
+ bounds_vector.push_back(base::IntToString16(bounds.y()));
+ bounds_vector.push_back(base::IntToString16(bounds.width()));
+ bounds_vector.push_back(base::IntToString16(bounds.height()));
+ string16 script = ReplaceStringPlaceholders(
+ ASCIIToUTF16(kSetOmniboxBoundsScript),
+ bounds_vector,
+ 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 MatchPreviewLoader::FrameLoadObserver : public NotificationObserver {
+ public:
+ FrameLoadObserver(MatchPreviewLoader* 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),
+ pressed_enter_(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 MatchPreviewLoader releases ownership of the TabContents
+ // and the page hasn't finished loading.
+ void DetachFromPreview(bool pressed_enter) {
+ match_preview_ = NULL;
+ pressed_enter_ = pressed_enter;
+ }
+
+ // 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;
+ }
+
+ if (match_preview_) {
+ gfx::Rect bounds = match_preview_->GetOmniboxBoundsInTermsOfPreview();
+ if (!bounds.IsEmpty())
+ SendOmniboxBoundsScript(tab_contents_, bounds);
+ }
+
+ SendUserInputScript(tab_contents_, text_);
+
+ if (match_preview_)
+ match_preview_->PageFinishedLoading();
+ else
+ SendDoneScript(tab_contents_, text_, pressed_enter_);
+
+ delete this;
+ return;
+ }
+
+ case NotificationType::TAB_CONTENTS_DESTROYED:
+ delete this;
+ return;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ private:
+ // MatchPreviewLoader that created us.
+ MatchPreviewLoader* 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 SendDoneScript.
+ bool pressed_enter_;
+
+ // Registers and unregisters us for notifications.
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameLoadObserver);
+};
+
+// PaintObserver implementation. When the RenderWidgetHost paints itself this
+// notifies MatchPreviewLoader, which makes the TabContents active.
+class MatchPreviewLoader::PaintObserverImpl
+ : public RenderWidgetHost::PaintObserver {
+ public:
+ explicit PaintObserverImpl(MatchPreviewLoader* preview)
+ : match_preview_(preview) {
+ }
+
+ virtual void RenderWidgetHostWillPaint(RenderWidgetHost* rwh) {
+ }
+
+ virtual void RenderWidgetHostDidPaint(RenderWidgetHost* rwh) {
+ match_preview_->PreviewPainted();
+ rwh->set_paint_observer(NULL);
+ // WARNING: we've been deleted.
+ }
+
+ private:
+ MatchPreviewLoader* match_preview_;
+
+ DISALLOW_COPY_AND_ASSIGN(PaintObserverImpl);
+};
+
+class MatchPreviewLoader::TabContentsDelegateImpl : public TabContentsDelegate {
+ public:
+ explicit TabContentsDelegateImpl(MatchPreviewLoader* match_preview)
+ : match_preview_(match_preview),
+ installed_paint_observer_(false),
+ waiting_for_new_page_(true),
+ is_mouse_down_from_activate_(false) {
+ }
+
+ // 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;
+ is_mouse_down_from_activate_ = false;
+ }
+
+ bool is_mouse_down_from_activate() const {
+ return is_mouse_down_from_activate_;
+ }
+
+ // 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,
+ const GURL& url, const GURL& referrer,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition) {}
+ virtual void NavigationStateChanged(const TabContents* source,
+ 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) {
+ }
+ virtual void DeactivateContents(TabContents* contents) {}
+ virtual void LoadingStateChanged(TabContents* source) {}
+ virtual void CloseContents(TabContents* source) {}
+ virtual void MoveContents(TabContents* source, const gfx::Rect& pos) {}
+ virtual void DetachContents(TabContents* source) {}
+ virtual bool IsPopup(const TabContents* source) const {
+ return false;
+ }
+ virtual bool ShouldFocusConstrainedWindow(TabContents* source) {
+ // Return false so that constrained windows are not initially focused. If
+ // we did otherwise the preview would prematurely get committed when focus
+ // goes to the constrained window.
+ return false;
+ }
+ virtual void WillShowConstrainedWindow(TabContents* source) {
+ if (!match_preview_->ready()) {
+ // A constrained window shown for an auth may not paint. Show the preview
+ // contents.
+ if (installed_paint_observer_) {
+ source->GetRenderWidgetHostView()->GetRenderWidgetHost()->
+ set_paint_observer(NULL);
+ }
+ installed_paint_observer_ = true;
+ match_preview_->ShowPreview();
+ }
+ }
+ virtual TabContents* GetConstrainingContents(TabContents* source) {
+ return NULL;
+ }
+ virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {}
+ virtual void URLStarredChanged(TabContents* source, bool starred) {}
+ virtual void UpdateTargetURL(TabContents* source, const GURL& url) {}
+ virtual void ContentsMouseEvent(
+ TabContents* source, const gfx::Point& location, bool motion) {}
+ virtual void ContentsZoomChange(bool zoom_in) {}
+ virtual void OnContentSettingsChange(TabContents* source) {}
+ virtual bool IsApplication() const { return false; }
+ virtual void ConvertContentsToApplication(TabContents* source) {}
+ virtual bool CanReloadContents(TabContents* source) const { return true; }
+ virtual gfx::Rect GetRootWindowResizerRect() const {
+ return gfx::Rect();
+ }
+ virtual void ShowHtmlDialog(HtmlDialogUIDelegate* delegate,
+ gfx::NativeWindow parent_window) {}
+ virtual void BeforeUnloadFired(TabContents* tab,
+ bool proceed,
+ bool* proceed_to_fire_unload) {}
+ virtual void ForwardMessageToExternalHost(const std::string& message,
+ const std::string& origin,
+ const std::string& target) {}
+ virtual bool IsExternalTabContainer() const { return false; }
+ virtual void SetFocusToLocationBar(bool select_all) {}
+ virtual bool ShouldFocusPageAfterCrash() { return false; }
+ virtual void RenderWidgetShowing() {}
+ virtual ExtensionFunctionDispatcher* CreateExtensionFunctionDispatcher(
+ RenderViewHost* render_view_host,
+ const std::string& extension_id) {
+ return NULL;
+ }
+ virtual bool TakeFocus(bool reverse) { return false; }
+ virtual void LostCapture() {
+ CommitFromMouseReleaseIfNecessary();
+ }
+ virtual void SetTabContentBlocked(TabContents* contents, bool blocked) {}
+ virtual void TabContentsFocused(TabContents* tab_content) {
+ }
+ virtual int GetExtraRenderViewHeight() const { return 0; }
+ virtual bool CanDownload(int request_id) { return false; }
+ virtual void OnStartDownload(DownloadItem* download, TabContents* tab) {}
+ virtual bool HandleContextMenu(const ContextMenuParams& params) {
+ return false;
+ }
+ virtual bool ExecuteContextMenuCommand(int command) {
+ return false;
+ }
+ virtual void ConfirmAddSearchProvider(const TemplateURL* template_url,
+ Profile* profile) {}
+ virtual void ShowPageInfo(Profile* profile,
+ const GURL& url,
+ const NavigationEntry::SSLStatus& ssl,
+ bool show_history) {}
+ virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
+ bool* is_keyboard_shortcut) {
+ return false;
+ }
+ virtual void HandleMouseUp() {
+ CommitFromMouseReleaseIfNecessary();
+ }
+ virtual void HandleMouseActivate() {
+ is_mouse_down_from_activate_ = true;
+ }
+ virtual void ShowRepostFormWarningDialog(TabContents* tab_contents) {}
+ virtual void ShowContentSettingsWindow(ContentSettingsType content_type) {}
+ virtual void ShowCollectedCookiesDialog(TabContents* tab_contents) {}
+ virtual bool OnGoToEntryOffset(int offset) { return false; }
+ virtual bool ShouldAddNavigationToHistory(
+ 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 NULL;
+ }
+ virtual void TabContentsCreated(TabContents* new_contents) {}
+ virtual bool infobars_enabled() { return false; }
+ virtual bool ShouldEnablePreferredSizeNotifications() { return false; }
+ 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;
+
+ void CommitFromMouseReleaseIfNecessary() {
+ bool was_down = is_mouse_down_from_activate_;
+ is_mouse_down_from_activate_ = false;
+ if (was_down && match_preview_->ShouldCommitPreviewOnMouseUp())
+ match_preview_->CommitPreview();
+ }
+
+ MatchPreviewLoader* match_preview_;
+
+ // Has the paint observer been installed? See comment in
+ // NavigationStateChanged for details on this.
+ bool installed_paint_observer_;
+
+ // 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_;
+
+ // 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_;
+
+ // Returns true if the mouse is down from an activate.
+ bool is_mouse_down_from_activate_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabContentsDelegateImpl);
+};
+
+MatchPreviewLoader::MatchPreviewLoader(MatchPreviewLoaderDelegate* delegate,
+ TemplateURLID id)
+ : delegate_(delegate),
+ template_url_id_(id),
+ ready_(false) {
+ preview_tab_contents_delegate_.reset(new TabContentsDelegateImpl(this));
+}
+
+MatchPreviewLoader::~MatchPreviewLoader() {
+ // Delete the TabContents before the delegate as the TabContents holds a
+ // reference to the delegate.
+ preview_contents_.reset(NULL);
+}
+
+void MatchPreviewLoader::Update(TabContents* tab_contents,
+ const AutocompleteMatch& match,
+ const string16& user_text,
+ const TemplateURL* template_url,
+ string16* suggested_text) {
+ DCHECK(url_ != match.destination_url);
+
+ url_ = match.destination_url;
+
+ DCHECK(!url_.is_empty() && url_.is_valid());
+
+ user_text_ = user_text;
+
+ bool created_preview_contents;
+ if (preview_contents_.get() == NULL) {
+ preview_contents_.reset(
+ 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();
+ created_preview_contents = true;
+ } else {
+ created_preview_contents = false;
+ }
+ preview_tab_contents_delegate_->PrepareForNewLoad();
+
+ if (template_url) {
+ DCHECK(template_url_id_ == template_url->id());
+ if (!created_preview_contents) {
+ if (is_waiting_for_load()) {
+ // 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_);
+ 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 {
+ DCHECK(template_url_id_ == 0);
+ frame_load_observer_.reset(NULL);
+ preview_contents_->controller().LoadURL(url_, GURL(), match.transition);
+ }
+}
+
+void MatchPreviewLoader::SetOmniboxBounds(const gfx::Rect& bounds) {
+ if (omnibox_bounds_ == bounds)
+ return;
+
+ omnibox_bounds_ = bounds;
+ if (preview_contents_.get() && is_showing_instant() &&
+ !is_waiting_for_load()) {
+ SendOmniboxBoundsScript(preview_contents_.get(),
+ GetOmniboxBoundsInTermsOfPreview());
+ }
+}
+
+void MatchPreviewLoader::DestroyPreviewContents() {
+ if (!preview_contents_.get()) {
+ // We're not showing anything, nothing to do.
+ return;
+ }
+
+ delete ReleasePreviewContents(MATCH_PREVIEW_COMMIT_DESTROY);
+}
+
+bool MatchPreviewLoader::IsMouseDownFromActivate() {
+ return preview_tab_contents_delegate_->is_mouse_down_from_activate();
+}
+
+TabContents* MatchPreviewLoader::ReleasePreviewContents(
+ MatchPreviewCommitType type) {
+ if (!preview_contents_.get())
+ return NULL;
+
+ if (frame_load_observer_.get()) {
+ frame_load_observer_->DetachFromPreview(
+ type == MATCH_PREVIEW_COMMIT_PRESSED_ENTER);
+ // FrameLoadObserver will delete itself either when the TabContents is
+ // deleted, or when the page finishes loading.
+ FrameLoadObserver* unused ALLOW_UNUSED = frame_load_observer_.release();
+ } else if (type != MATCH_PREVIEW_COMMIT_DESTROY && is_showing_instant()) {
+ SendDoneScript(preview_contents_.get(),
+ user_text_,
+ type == MATCH_PREVIEW_COMMIT_PRESSED_ENTER);
+ }
+ omnibox_bounds_ = gfx::Rect();
+ url_ = GURL();
+ user_text_.clear();
+ complete_suggested_text_.clear();
+ if (preview_contents_.get()) {
+ if (type != MATCH_PREVIEW_COMMIT_DESTROY)
+ preview_tab_contents_delegate_->CommitHistory();
+ // Destroy the paint observer.
+ // RenderWidgetHostView may be null during shutdown.
+ if (preview_contents_->GetRenderWidgetHostView()) {
+ preview_contents_->GetRenderWidgetHostView()->GetRenderWidgetHost()->
+ set_paint_observer(NULL);
+ }
+ preview_contents_->set_delegate(NULL);
+ preview_tab_contents_delegate_->Reset();
+ ready_ = false;
+ }
+ return preview_contents_.release();
+}
+
+bool MatchPreviewLoader::ShouldCommitPreviewOnMouseUp() {
+ return delegate_->ShouldCommitPreviewOnMouseUp();
+}
+
+void MatchPreviewLoader::CommitPreview() {
+ delegate_->CommitPreview(this);
+}
+
+void MatchPreviewLoader::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_->SetSuggestedTextFor(this, string16());
+ return;
+ }
+
+ complete_suggested_text_ = complete_suggested_text;
+ delegate_->SetSuggestedTextFor(
+ this,
+ complete_suggested_text_.substr(user_text_.size()));
+}
+
+void MatchPreviewLoader::PreviewPainted() {
+ ShowPreview();
+}
+
+void MatchPreviewLoader::ShowPreview() {
+ if (!ready_) {
+ ready_ = true;
+ delegate_->ShowMatchPreviewLoader(this);
+ }
+}
+
+void MatchPreviewLoader::PageFinishedLoading() {
+ // FrameLoadObserver deletes itself after this call.
+ FrameLoadObserver* unused ALLOW_UNUSED = frame_load_observer_.release();
+}
+
+gfx::Rect MatchPreviewLoader::GetOmniboxBoundsInTermsOfPreview() {
+ if (omnibox_bounds_.IsEmpty())
+ return omnibox_bounds_;
+
+ gfx::Rect preview_bounds(delegate_->GetMatchPreviewBounds());
+ return gfx::Rect(omnibox_bounds_.x() - preview_bounds.x(),
+ omnibox_bounds_.y() - preview_bounds.y(),
+ omnibox_bounds_.width(),
+ omnibox_bounds_.height());
+}
diff --git a/chrome/browser/tab_contents/match_preview_loader.h b/chrome/browser/tab_contents/match_preview_loader.h
new file mode 100644
index 0000000..be96378
--- /dev/null
+++ b/chrome/browser/tab_contents/match_preview_loader.h
@@ -0,0 +1,139 @@
+// 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_LOADER_H_
+#define CHROME_BROWSER_TAB_CONTENTS_MATCH_PREVIEW_LOADER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/string16.h"
+#include "chrome/browser/search_engines/template_url_id.h"
+#include "chrome/browser/tab_contents/match_preview_commit_type.h"
+#include "chrome/common/page_transition_types.h"
+#include "gfx/rect.h"
+#include "googleurl/src/gurl.h"
+
+struct AutocompleteMatch;
+class LoaderManagerTest;
+class MatchPreviewLoaderDelegate;
+class TabContents;
+class TemplateURL;
+
+// MatchPreviewLoader does the loading of a particular URL for MatchPreview.
+// MatchPreviewLoader notifies its delegate, which is typically MatchPreview,
+// of all interesting events.
+class MatchPreviewLoader {
+ public:
+ MatchPreviewLoader(MatchPreviewLoaderDelegate* delegate, TemplateURLID id);
+ ~MatchPreviewLoader();
+
+ // Invoked to load a URL. |tab_contents| is the TabContents the preview is
+ // going to be shown on top of and potentially replace.
+ void Update(TabContents* tab_contents,
+ const AutocompleteMatch& match,
+ const string16& user_text,
+ const TemplateURL* template_url,
+ string16* suggested_text);
+
+ // Sets the bounds of the omnibox (in screen coordinates). The bounds are
+ // remembered until the preview is committed or destroyed. This is only used
+ // when showing results for a search provider that supports instant.
+ void SetOmniboxBounds(const gfx::Rect& bounds);
+
+ // Destroys the preview TabContents. Does nothing if the preview TabContents
+ // has not been created.
+ void DestroyPreviewContents();
+
+ // Returns true if the mouse is down as the result of activating the preview
+ // content.
+ bool IsMouseDownFromActivate();
+
+ // Releases the preview TabContents passing ownership to the caller. This is
+ // intended to be called when the preview TabContents is committed. This does
+ // not notify the delegate.
+ TabContents* ReleasePreviewContents(MatchPreviewCommitType type);
+
+ // Calls through to method of same name on delegate.
+ bool ShouldCommitPreviewOnMouseUp();
+ void CommitPreview();
+
+ // The preview TabContents; may be null.
+ TabContents* preview_contents() const { return preview_contents_.get(); }
+
+ // Returns true if the preview TabContents is ready to be shown.
+ bool ready() const { return ready_; }
+
+ const GURL& url() const { return url_; }
+
+ // Are we showing instant results?
+ bool is_showing_instant() const { return template_url_id_ != 0; }
+
+ // If we're showing instant this returns non-zero.
+ TemplateURLID template_url_id() const { return template_url_id_; }
+
+ private:
+ friend class LoaderManagerTest;
+ 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 page paints.
+ void PreviewPainted();
+
+ // Invoked to show the preview. This is invoked in two possible cases: when
+ // the renderer paints, or when an auth dialog is shown. This notifies the
+ // delegate the preview is ready to be shown.
+ void ShowPreview();
+
+ // Invoked once the page has finished loading and the script has been sent.
+ void PageFinishedLoading();
+
+ // Returns the bounds of the omnibox in terms of the preview tab contents.
+ gfx::Rect GetOmniboxBoundsInTermsOfPreview();
+
+ // Are we waiting for the preview page to finish loading?
+ bool is_waiting_for_load() const {
+ return frame_load_observer_.get() != NULL;
+ }
+
+ MatchPreviewLoaderDelegate* delegate_;
+
+ // 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.
+ const TemplateURLID template_url_id_;
+
+ // The url we're displaying.
+ GURL url_;
+
+ // 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> preview_tab_contents_delegate_;
+
+ // The preview TabContents; may be null.
+ scoped_ptr<TabContents> preview_contents_;
+
+ // Is the preview_contents ready to be shown?
+ bool ready_;
+
+ // The text the user typed in the omnibox.
+ string16 user_text_;
+
+ // The latest suggestion from the page.
+ string16 complete_suggested_text_;
+
+ // See description above setter.
+ gfx::Rect omnibox_bounds_;
+
+ scoped_ptr<FrameLoadObserver> frame_load_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(MatchPreviewLoader);
+};
+
+#endif // CHROME_BROWSER_TAB_CONTENTS_MATCH_PREVIEW_LOADER_H_
diff --git a/chrome/browser/tab_contents/match_preview_loader_delegate.h b/chrome/browser/tab_contents/match_preview_loader_delegate.h
new file mode 100644
index 0000000..3384b0c
--- /dev/null
+++ b/chrome/browser/tab_contents/match_preview_loader_delegate.h
@@ -0,0 +1,38 @@
+// 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_LOADER_DELEGATE_H_
+#define CHROME_BROWSER_TAB_CONTENTS_MATCH_PREVIEW_LOADER_DELEGATE_H_
+#pragma once
+
+#include "base/string16.h"
+
+namespace gfx {
+class Rect;
+}
+
+class MatchPreviewLoader;
+
+// MatchPreviewLoader's delegate. This interface is implemented by
+// MatchPreview.
+class MatchPreviewLoaderDelegate {
+ public:
+ // Invoked when the loader is ready to be shown.
+ virtual void ShowMatchPreviewLoader(MatchPreviewLoader* loader) = 0;
+
+ // Invoked when the renderer has suggested text.
+ virtual void SetSuggestedTextFor(MatchPreviewLoader* loader,
+ const string16& text) = 0;
+
+ // Returns the bounds of the match preview.
+ virtual gfx::Rect GetMatchPreviewBounds() = 0;
+
+ // Returns true if preview should be committed on mouse up.
+ virtual bool ShouldCommitPreviewOnMouseUp() = 0;
+
+ // Invoked when the preview should be committed.
+ virtual void CommitPreview(MatchPreviewLoader* loader) = 0;
+};
+
+#endif // CHROME_BROWSER_TAB_CONTENTS_MATCH_PREVIEW_LOADER_DELEGATE_H_
diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc
index a25aae0..a2bacef 100644
--- a/chrome/browser/views/frame/browser_view.cc
+++ b/chrome/browser/views/frame/browser_view.cc
@@ -1355,13 +1355,11 @@ void BrowserView::ToggleTabStripMode() {
frame_->TabStripDisplayModeChanged();
}
-void BrowserView::ShowMatchPreview() {
+void BrowserView::ShowMatchPreview(TabContents* preview_contents) {
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);
+ contents_->SetPreview(preview_container_, preview_contents);
+ preview_container_->ChangeTabContents(preview_contents);
}
void BrowserView::HideMatchPreview() {
diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h
index 08ab3ce..ec9111d 100644
--- a/chrome/browser/views/frame/browser_view.h
+++ b/chrome/browser/views/frame/browser_view.h
@@ -321,7 +321,7 @@ class BrowserView : public BrowserBubbleHost,
virtual void Copy();
virtual void Paste();
virtual void ToggleTabStripMode();
- virtual void ShowMatchPreview();
+ virtual void ShowMatchPreview(TabContents* preview_contents);
virtual void HideMatchPreview();
virtual gfx::Rect GetMatchPreviewBounds();
diff --git a/chrome/browser/views/location_bar/location_bar_view.cc b/chrome/browser/views/location_bar/location_bar_view.cc
index 474679b..5af0fe5 100644
--- a/chrome/browser/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/views/location_bar/location_bar_view.cc
@@ -733,12 +733,12 @@ void LocationBarView::OnAutocompleteLosingFocus(
if (!match_preview)
return;
- if (!match_preview->is_active() || !match_preview->preview_contents())
+ if (!match_preview->is_active() || !match_preview->GetPreviewContents())
return;
switch (GetCommitType(view_gaining_focus)) {
case COMMIT_MATCH_PREVIEW_IMMEDIATELY:
- match_preview->CommitCurrentPreview(MatchPreview::COMMIT_FOCUS_LOST);
+ match_preview->CommitCurrentPreview(MATCH_PREVIEW_COMMIT_FOCUS_LOST);
break;
case COMMIT_MATCH_PREVIEW_ON_MOUSE_UP:
match_preview->SetCommitOnMouseUp();
@@ -1208,18 +1208,19 @@ LocationBarView::MatchPreviewCommitType LocationBarView::GetCommitType(
#if defined(OS_WIN)
MatchPreview* match_preview = delegate_->GetMatchPreview();
RenderWidgetHostView* rwhv =
- match_preview->preview_contents()->GetRenderWidgetHostView();
+ match_preview->GetPreviewContents()->GetRenderWidgetHostView();
if (!view_gaining_focus || !rwhv)
return REVERT_MATCH_PREVIEW;
- gfx::NativeView tab_view = match_preview->preview_contents()->GetNativeView();
+ gfx::NativeView tab_view =
+ match_preview->GetPreviewContents()->GetNativeView();
if (rwhv->GetNativeView() == view_gaining_focus ||
tab_view == view_gaining_focus) {
// Focus is going to the renderer. Only commit the match preview if the
// mouse is down. If the mouse isn't down it means someone else moved focus
// and we shouldn't commit.
if (match_preview->IsMouseDownFromActivate()) {
- if (match_preview->is_showing_instant()) {
+ if (match_preview->IsShowingInstant()) {
// We're showing instant results. As instant results may shift when
// committing we commit on the mouse up. This way a slow click still
// works fine.