summaryrefslogtreecommitdiffstats
path: root/chrome/browser/tab_contents
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/tab_contents')
-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
10 files changed, 1397 insertions, 632 deletions
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_