OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/tab_contents/match_preview.h" | 5 #include "chrome/browser/tab_contents/match_preview.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/utf_string_conversions.h" |
| 11 #include "chrome/browser/autocomplete/autocomplete.h" |
| 12 #include "chrome/browser/favicon_service.h" |
| 13 #include "chrome/browser/history/history_marshaling.h" |
| 14 #include "chrome/browser/profile.h" |
| 15 #include "chrome/browser/renderer_host/render_view_host.h" |
| 16 #include "chrome/browser/renderer_host/render_widget_host.h" |
| 17 #include "chrome/browser/renderer_host/render_widget_host_view.h" |
| 18 #include "chrome/browser/search_engines/template_url.h" |
| 19 #include "chrome/browser/search_engines/template_url_model.h" |
| 20 #include "chrome/browser/tab_contents/match_preview_delegate.h" |
10 #include "chrome/browser/tab_contents/navigation_controller.h" | 21 #include "chrome/browser/tab_contents/navigation_controller.h" |
11 #include "chrome/browser/tab_contents/navigation_entry.h" | 22 #include "chrome/browser/tab_contents/navigation_entry.h" |
12 #include "chrome/browser/tab_contents/tab_contents.h" | 23 #include "chrome/browser/tab_contents/tab_contents.h" |
13 #include "chrome/browser/tab_contents/tab_contents_delegate.h" | 24 #include "chrome/browser/tab_contents/tab_contents_delegate.h" |
| 25 #include "chrome/browser/tab_contents/tab_contents_view.h" |
14 #include "chrome/common/chrome_switches.h" | 26 #include "chrome/common/chrome_switches.h" |
| 27 #include "chrome/common/notification_observer.h" |
| 28 #include "chrome/common/notification_registrar.h" |
15 #include "chrome/common/notification_service.h" | 29 #include "chrome/common/notification_service.h" |
16 #include "chrome/common/page_transition_types.h" | 30 #include "chrome/common/page_transition_types.h" |
| 31 #include "chrome/common/render_messages.h" |
17 #include "chrome/common/renderer_preferences.h" | 32 #include "chrome/common/renderer_preferences.h" |
| 33 #include "gfx/codec/png_codec.h" |
18 #include "ipc/ipc_message.h" | 34 #include "ipc/ipc_message.h" |
19 | 35 |
| 36 namespace { |
| 37 |
| 38 const char kUserInputScript[] = |
| 39 "if (window.chrome.userInput) window.chrome.userInput(\"$1\");"; |
| 40 |
| 41 // Sends the user input script to |tab_contents|. |text| is the text the user |
| 42 // input into the omnibox. |
| 43 void SendUserInputScript(TabContents* tab_contents, |
| 44 const string16& text, |
| 45 bool done) { |
| 46 // TODO: support done. |
| 47 string16 escaped_text(text); |
| 48 ReplaceSubstringsAfterOffset(&escaped_text, 0L, ASCIIToUTF16("\""), |
| 49 ASCIIToUTF16("\\\"")); |
| 50 string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserInputScript), |
| 51 escaped_text, NULL); |
| 52 tab_contents->render_view_host()->ExecuteJavascriptInWebFrame( |
| 53 std::wstring(), |
| 54 UTF16ToWide(script)); |
| 55 } |
| 56 |
| 57 } // namespace |
| 58 |
| 59 // FrameLoadObserver is responsible for waiting for the TabContents to finish |
| 60 // loading and when done sending the necessary script down to the page. |
| 61 class MatchPreview::FrameLoadObserver : public NotificationObserver { |
| 62 public: |
| 63 FrameLoadObserver(MatchPreview* match_preview, const string16& text) |
| 64 : match_preview_(match_preview), |
| 65 tab_contents_(match_preview->preview_contents()), |
| 66 unique_id_(tab_contents_->controller().pending_entry()->unique_id()), |
| 67 text_(text), |
| 68 send_done_(false) { |
| 69 registrar_.Add(this, NotificationType::LOAD_COMPLETED_MAIN_FRAME, |
| 70 Source<TabContents>(tab_contents_)); |
| 71 registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, |
| 72 Source<TabContents>(tab_contents_)); |
| 73 } |
| 74 |
| 75 // Sets the text to send to the page. |
| 76 void set_text(const string16& text) { text_ = text; } |
| 77 |
| 78 // Invoked when the MatchPreview releases ownership of the TabContents and |
| 79 // the page hasn't finished loading. |
| 80 void DetachFromPreview() { |
| 81 match_preview_ = NULL; |
| 82 send_done_ = true; |
| 83 } |
| 84 |
| 85 // NotificationObserver: |
| 86 virtual void Observe(NotificationType type, |
| 87 const NotificationSource& source, |
| 88 const NotificationDetails& details) { |
| 89 switch (type.value) { |
| 90 case NotificationType::LOAD_COMPLETED_MAIN_FRAME: { |
| 91 int page_id = *(Details<int>(details).ptr()); |
| 92 NavigationEntry* active_entry = |
| 93 tab_contents_->controller().GetActiveEntry(); |
| 94 if (!active_entry || active_entry->page_id() != page_id || |
| 95 active_entry->unique_id() != unique_id_) { |
| 96 return; |
| 97 } |
| 98 |
| 99 SendUserInputScript(tab_contents_, text_, send_done_); |
| 100 |
| 101 if (match_preview_) |
| 102 match_preview_->PageFinishedLoading(); |
| 103 |
| 104 delete this; |
| 105 return; |
| 106 } |
| 107 |
| 108 case NotificationType::TAB_CONTENTS_DESTROYED: |
| 109 delete this; |
| 110 return; |
| 111 |
| 112 default: |
| 113 NOTREACHED(); |
| 114 break; |
| 115 } |
| 116 } |
| 117 |
| 118 private: |
| 119 // MatchPreview that created us. |
| 120 MatchPreview* match_preview_; |
| 121 |
| 122 // The TabContents we're listening for changes on. |
| 123 TabContents* tab_contents_; |
| 124 |
| 125 // unique_id of the NavigationEntry we're waiting on. |
| 126 const int unique_id_; |
| 127 |
| 128 // Text to send down to the page. |
| 129 string16 text_; |
| 130 |
| 131 // Passed to SendScript. |
| 132 bool send_done_; |
| 133 |
| 134 // Registers and unregisters us for notifications. |
| 135 NotificationRegistrar registrar_; |
| 136 |
| 137 DISALLOW_COPY_AND_ASSIGN(FrameLoadObserver); |
| 138 }; |
| 139 |
| 140 // PaintObserver implementation. When the RenderWidgetHost paints itself this |
| 141 // notifies MatchPreview, which makes the TabContents active. |
| 142 class MatchPreview::PaintObserverImpl : public RenderWidgetHost::PaintObserver { |
| 143 public: |
| 144 explicit PaintObserverImpl(MatchPreview* preview) |
| 145 : match_preview_(preview) { |
| 146 } |
| 147 |
| 148 virtual void RenderWidgetHostWillPaint(RenderWidgetHost* rwh) { |
| 149 } |
| 150 |
| 151 virtual void RenderWidgetHostDidPaint(RenderWidgetHost* rwh) { |
| 152 match_preview_->PreviewDidPaint(); |
| 153 rwh->set_paint_observer(NULL); |
| 154 // WARNING: we've been deleted. |
| 155 } |
| 156 |
| 157 private: |
| 158 MatchPreview* match_preview_; |
| 159 |
| 160 DISALLOW_COPY_AND_ASSIGN(PaintObserverImpl); |
| 161 }; |
| 162 |
20 class MatchPreview::TabContentsDelegateImpl : public TabContentsDelegate { | 163 class MatchPreview::TabContentsDelegateImpl : public TabContentsDelegate { |
21 public: | 164 public: |
22 explicit TabContentsDelegateImpl(MatchPreview* match_preview) | 165 explicit TabContentsDelegateImpl(MatchPreview* match_preview) |
23 : match_preview_(match_preview) { | 166 : match_preview_(match_preview), |
| 167 installed_paint_observer_(false), |
| 168 waiting_for_new_page_(true) { |
| 169 } |
| 170 |
| 171 // Invoked prior to loading a new URL. |
| 172 void PrepareForNewLoad() { |
| 173 waiting_for_new_page_ = true; |
| 174 add_page_vector_.clear(); |
| 175 } |
| 176 |
| 177 // Invoked when removed as the delegate. Gives a chance to do any necessary |
| 178 // cleanup. |
| 179 void Reset() { |
| 180 installed_paint_observer_ = false; |
| 181 } |
| 182 |
| 183 // Commits the currently buffered history. |
| 184 void CommitHistory() { |
| 185 TabContents* tab = match_preview_->preview_contents(); |
| 186 if (tab->profile()->IsOffTheRecord()) |
| 187 return; |
| 188 |
| 189 for (size_t i = 0; i < add_page_vector_.size(); ++i) |
| 190 tab->UpdateHistoryForNavigation(add_page_vector_[i].get()); |
| 191 |
| 192 NavigationEntry* active_entry = tab->controller().GetActiveEntry(); |
| 193 DCHECK(active_entry); |
| 194 tab->UpdateHistoryPageTitle(*active_entry); |
| 195 |
| 196 FaviconService* favicon_service = |
| 197 tab->profile()->GetFaviconService(Profile::EXPLICIT_ACCESS); |
| 198 |
| 199 if (favicon_service && active_entry->favicon().is_valid() && |
| 200 !active_entry->favicon().bitmap().empty()) { |
| 201 std::vector<unsigned char> image_data; |
| 202 gfx::PNGCodec::EncodeBGRASkBitmap(active_entry->favicon().bitmap(), false, |
| 203 &image_data); |
| 204 favicon_service->SetFavicon(active_entry->url(), |
| 205 active_entry->favicon().url(), |
| 206 image_data); |
| 207 } |
24 } | 208 } |
25 | 209 |
26 virtual void OpenURLFromTab(TabContents* source, | 210 virtual void OpenURLFromTab(TabContents* source, |
27 const GURL& url, const GURL& referrer, | 211 const GURL& url, const GURL& referrer, |
28 WindowOpenDisposition disposition, | 212 WindowOpenDisposition disposition, |
29 PageTransition::Type transition) {} | 213 PageTransition::Type transition) {} |
30 virtual void NavigationStateChanged(const TabContents* source, | 214 virtual void NavigationStateChanged(const TabContents* source, |
31 unsigned changed_flags) {} | 215 unsigned changed_flags) { |
| 216 if (!installed_paint_observer_ && source->controller().entry_count()) { |
| 217 // The load has been committed. Install an observer that waits for the |
| 218 // first paint then makes the preview active. We wait for the load to be |
| 219 // committed before waiting on paint as there is always an initial paint |
| 220 // when a new renderer is created from the resize so that if we showed the |
| 221 // preview after the first paint we would end up with a white rect. |
| 222 installed_paint_observer_ = true; |
| 223 source->GetRenderWidgetHostView()->GetRenderWidgetHost()-> |
| 224 set_paint_observer(new PaintObserverImpl(match_preview_)); |
| 225 } |
| 226 } |
32 virtual void AddNewContents(TabContents* source, | 227 virtual void AddNewContents(TabContents* source, |
33 TabContents* new_contents, | 228 TabContents* new_contents, |
34 WindowOpenDisposition disposition, | 229 WindowOpenDisposition disposition, |
35 const gfx::Rect& initial_pos, | 230 const gfx::Rect& initial_pos, |
36 bool user_gesture) {} | 231 bool user_gesture) {} |
37 virtual void ActivateContents(TabContents* contents) { | 232 virtual void ActivateContents(TabContents* contents) { |
38 match_preview_->CommitCurrentPreview(); | |
39 } | 233 } |
40 virtual void DeactivateContents(TabContents* contents) {} | 234 virtual void DeactivateContents(TabContents* contents) {} |
41 virtual void LoadingStateChanged(TabContents* source) {} | 235 virtual void LoadingStateChanged(TabContents* source) {} |
42 virtual void CloseContents(TabContents* source) {} | 236 virtual void CloseContents(TabContents* source) {} |
43 virtual void MoveContents(TabContents* source, const gfx::Rect& pos) {} | 237 virtual void MoveContents(TabContents* source, const gfx::Rect& pos) {} |
44 virtual void DetachContents(TabContents* source) {} | 238 virtual void DetachContents(TabContents* source) {} |
45 virtual bool IsPopup(const TabContents* source) const { | 239 virtual bool IsPopup(const TabContents* source) const { |
46 return false; | 240 return false; |
47 } | 241 } |
48 virtual TabContents* GetConstrainingContents(TabContents* source) { | 242 virtual TabContents* GetConstrainingContents(TabContents* source) { |
49 return NULL; | 243 return NULL; |
50 } | 244 } |
51 virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {} | 245 virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {} |
52 virtual void URLStarredChanged(TabContents* source, bool starred) {} | 246 virtual void URLStarredChanged(TabContents* source, bool starred) {} |
53 virtual void UpdateTargetURL(TabContents* source, const GURL& url) {} | 247 virtual void UpdateTargetURL(TabContents* source, const GURL& url) {} |
54 virtual void ContentsMouseEvent( | 248 virtual void ContentsMouseEvent( |
55 TabContents* source, const gfx::Point& location, bool motion) {} | 249 TabContents* source, const gfx::Point& location, bool motion) {} |
56 virtual void ContentsZoomChange(bool zoom_in) {} | 250 virtual void ContentsZoomChange(bool zoom_in) {} |
57 virtual void OnContentSettingsChange(TabContents* source) {} | 251 virtual void OnContentSettingsChange(TabContents* source) {} |
58 virtual bool IsApplication() const { return false; } | 252 virtual bool IsApplication() const { return false; } |
59 virtual void ConvertContentsToApplication(TabContents* source) {} | 253 virtual void ConvertContentsToApplication(TabContents* source) {} |
60 virtual bool CanReloadContents(TabContents* source) const { return true; } | 254 virtual bool CanReloadContents(TabContents* source) const { return true; } |
61 virtual gfx::Rect GetRootWindowResizerRect() const { | 255 virtual gfx::Rect GetRootWindowResizerRect() const { |
62 return match_preview_->host_->delegate() ? | 256 return gfx::Rect(); |
63 match_preview_->host_->delegate()->GetRootWindowResizerRect() : | |
64 gfx::Rect(); | |
65 } | 257 } |
66 virtual void ShowHtmlDialog(HtmlDialogUIDelegate* delegate, | 258 virtual void ShowHtmlDialog(HtmlDialogUIDelegate* delegate, |
67 gfx::NativeWindow parent_window) {} | 259 gfx::NativeWindow parent_window) {} |
68 virtual void BeforeUnloadFired(TabContents* tab, | 260 virtual void BeforeUnloadFired(TabContents* tab, |
69 bool proceed, | 261 bool proceed, |
70 bool* proceed_to_fire_unload) {} | 262 bool* proceed_to_fire_unload) {} |
71 virtual void ForwardMessageToExternalHost(const std::string& message, | 263 virtual void ForwardMessageToExternalHost(const std::string& message, |
72 const std::string& origin, | 264 const std::string& origin, |
73 const std::string& target) {} | 265 const std::string& target) {} |
74 virtual bool IsExternalTabContainer() const { return false; } | 266 virtual bool IsExternalTabContainer() const { return false; } |
75 virtual void SetFocusToLocationBar(bool select_all) {} | 267 virtual void SetFocusToLocationBar(bool select_all) {} |
76 virtual void RenderWidgetShowing() {} | 268 virtual void RenderWidgetShowing() {} |
77 virtual ExtensionFunctionDispatcher* CreateExtensionFunctionDispatcher( | 269 virtual ExtensionFunctionDispatcher* CreateExtensionFunctionDispatcher( |
78 RenderViewHost* render_view_host, | 270 RenderViewHost* render_view_host, |
79 const std::string& extension_id) { | 271 const std::string& extension_id) { |
80 return NULL; | 272 return NULL; |
81 } | 273 } |
82 virtual bool TakeFocus(bool reverse) { return false; } | 274 virtual bool TakeFocus(bool reverse) { return false; } |
83 virtual void SetTabContentBlocked(TabContents* contents, bool blocked) {} | 275 virtual void SetTabContentBlocked(TabContents* contents, bool blocked) {} |
84 virtual void TabContentsFocused(TabContents* tab_content) { | 276 virtual void TabContentsFocused(TabContents* tab_content) { |
85 match_preview_->CommitCurrentPreview(); | |
86 } | 277 } |
87 virtual int GetExtraRenderViewHeight() const { return 0; } | 278 virtual int GetExtraRenderViewHeight() const { return 0; } |
88 virtual bool CanDownload(int request_id) { return false; } | 279 virtual bool CanDownload(int request_id) { return false; } |
89 virtual void OnStartDownload(DownloadItem* download, TabContents* tab) {} | 280 virtual void OnStartDownload(DownloadItem* download, TabContents* tab) {} |
90 virtual bool HandleContextMenu(const ContextMenuParams& params) { | 281 virtual bool HandleContextMenu(const ContextMenuParams& params) { |
91 return false; | 282 return false; |
92 } | 283 } |
93 virtual bool ExecuteContextMenuCommand(int command) { | 284 virtual bool ExecuteContextMenuCommand(int command) { |
94 return false; | 285 return false; |
95 } | 286 } |
96 virtual void ConfirmAddSearchProvider(const TemplateURL* template_url, | 287 virtual void ConfirmAddSearchProvider(const TemplateURL* template_url, |
97 Profile* profile) {} | 288 Profile* profile) {} |
98 virtual void ShowPageInfo(Profile* profile, | 289 virtual void ShowPageInfo(Profile* profile, |
99 const GURL& url, | 290 const GURL& url, |
100 const NavigationEntry::SSLStatus& ssl, | 291 const NavigationEntry::SSLStatus& ssl, |
101 bool show_history) {} | 292 bool show_history) {} |
102 virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, | 293 virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, |
103 bool* is_keyboard_shortcut) { | 294 bool* is_keyboard_shortcut) { |
104 return false; | 295 return false; |
105 } | 296 } |
106 virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {} | 297 virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {} |
107 virtual void ShowRepostFormWarningDialog(TabContents* tab_contents) {} | 298 virtual void ShowRepostFormWarningDialog(TabContents* tab_contents) {} |
108 virtual void ShowContentSettingsWindow(ContentSettingsType content_type) {} | 299 virtual void ShowContentSettingsWindow(ContentSettingsType content_type) {} |
109 virtual void ShowCollectedCookiesDialog(TabContents* tab_contents) {} | 300 virtual void ShowCollectedCookiesDialog(TabContents* tab_contents) {} |
110 virtual bool OnGoToEntryOffset(int offset) { return false; } | 301 virtual bool OnGoToEntryOffset(int offset) { return false; } |
111 virtual bool ShouldAddNavigationToHistory( | 302 virtual bool ShouldAddNavigationsToHistory( |
112 const history::HistoryAddPageArgs& add_page_args, | 303 const history::HistoryAddPageArgs& add_page_args, |
113 NavigationType::Type navigation_type) { | 304 NavigationType::Type navigation_type) { |
| 305 if (waiting_for_new_page_ && navigation_type == NavigationType::NEW_PAGE) |
| 306 waiting_for_new_page_ = false; |
| 307 |
| 308 if (!waiting_for_new_page_) { |
| 309 add_page_vector_.push_back( |
| 310 scoped_refptr<history::HistoryAddPageArgs>(add_page_args.Clone())); |
| 311 } |
114 return false; | 312 return false; |
115 } | 313 } |
116 virtual void OnDidGetApplicationInfo(TabContents* tab_contents, | 314 virtual void OnDidGetApplicationInfo(TabContents* tab_contents, |
117 int32 page_id) {} | 315 int32 page_id) {} |
118 virtual gfx::NativeWindow GetFrameNativeWindow() { | 316 virtual gfx::NativeWindow GetFrameNativeWindow() { |
119 return match_preview_->host_->delegate() ? | 317 return NULL; |
120 match_preview_->host_->delegate()->GetFrameNativeWindow() : NULL; | |
121 } | 318 } |
122 virtual void TabContentsCreated(TabContents* new_contents) {} | 319 virtual void TabContentsCreated(TabContents* new_contents) {} |
123 virtual bool infobars_enabled() { return false; } | 320 virtual bool infobars_enabled() { return false; } |
124 virtual bool ShouldEnablePreferredSizeNotifications() { return false; } | 321 virtual bool ShouldEnablePreferredSizeNotifications() { return false; } |
125 virtual void UpdatePreferredSize(const gfx::Size& pref_size) {} | 322 virtual void UpdatePreferredSize(const gfx::Size& pref_size) {} |
126 virtual void ContentTypeChanged(TabContents* source) {} | 323 virtual void ContentTypeChanged(TabContents* source) {} |
127 | 324 |
| 325 virtual void OnSetSuggestResult(int32 page_id, const std::string& result) { |
| 326 TabContents* source = match_preview_->preview_contents(); |
| 327 // TODO: only allow for default search provider. |
| 328 if (source->controller().GetActiveEntry() && |
| 329 page_id == source->controller().GetActiveEntry()->page_id()) { |
| 330 match_preview_->SetCompleteSuggestedText(UTF8ToUTF16(result)); |
| 331 } |
| 332 } |
| 333 |
128 private: | 334 private: |
| 335 typedef std::vector<scoped_refptr<history::HistoryAddPageArgs> > |
| 336 AddPageVector; |
| 337 |
129 MatchPreview* match_preview_; | 338 MatchPreview* match_preview_; |
130 | 339 |
| 340 // Has the paint observer been installed? See comment in |
| 341 // NavigationStateChanged for details on this. |
| 342 bool installed_paint_observer_; |
| 343 |
| 344 // Used to cache data that needs to be added to history. Normally entries are |
| 345 // added to history as the user types, but for match preview we only want to |
| 346 // add the items to history if the user commits the match preview. So, we |
| 347 // cache them here and if committed then add the items to history. |
| 348 AddPageVector add_page_vector_; |
| 349 |
| 350 // Are we we waiting for a NavigationType of NEW_PAGE? If we're waiting for |
| 351 // NEW_PAGE navigation we don't add history items to add_page_vector_. |
| 352 bool waiting_for_new_page_; |
| 353 |
131 DISALLOW_COPY_AND_ASSIGN(TabContentsDelegateImpl); | 354 DISALLOW_COPY_AND_ASSIGN(TabContentsDelegateImpl); |
132 }; | 355 }; |
133 | 356 |
134 MatchPreview::MatchPreview(TabContents* host) : host_(host) { | |
135 delegate_.reset(new TabContentsDelegateImpl(this)); | |
136 } | |
137 | |
138 MatchPreview::~MatchPreview() { | |
139 // Delete the TabContents before the delegate as the TabContents holds a | |
140 // reference to the delegate. | |
141 preview_contents_.reset(NULL); | |
142 } | |
143 | |
144 // static | 357 // static |
145 bool MatchPreview::IsEnabled() { | 358 bool MatchPreview::IsEnabled() { |
146 static bool enabled = false; | 359 static bool enabled = false; |
147 static bool checked = false; | 360 static bool checked = false; |
148 if (!checked) { | 361 if (!checked) { |
149 checked = true; | 362 checked = true; |
150 enabled = CommandLine::ForCurrentProcess()->HasSwitch( | 363 enabled = CommandLine::ForCurrentProcess()->HasSwitch( |
151 switches::kEnableMatchPreview); | 364 switches::kEnableMatchPreview); |
152 } | 365 } |
153 return enabled; | 366 return enabled; |
154 } | 367 } |
155 | 368 |
156 void MatchPreview::Update(const GURL& url) { | 369 MatchPreview::MatchPreview(MatchPreviewDelegate* delegate) |
157 if (url_ == url) | 370 : delegate_(delegate), |
| 371 tab_contents_(NULL), |
| 372 is_active_(false), |
| 373 template_url_id_(0) { |
| 374 preview_tab_contents_delegate_.reset(new TabContentsDelegateImpl(this)); |
| 375 } |
| 376 |
| 377 MatchPreview::~MatchPreview() { |
| 378 // Delete the TabContents before the delegate as the TabContents holds a |
| 379 // reference to the delegate. |
| 380 preview_contents_.reset(NULL); |
| 381 } |
| 382 |
| 383 void MatchPreview::Update(TabContents* tab_contents, |
| 384 const AutocompleteMatch& match, |
| 385 const string16& user_text, |
| 386 string16* suggested_text) { |
| 387 if (tab_contents != tab_contents_) |
| 388 DestroyPreviewContents(); |
| 389 |
| 390 tab_contents_ = tab_contents; |
| 391 |
| 392 if (url_ == match.destination_url) |
158 return; | 393 return; |
159 | 394 |
160 url_ = url; | 395 url_ = match.destination_url; |
161 | 396 |
162 if (url_.is_empty() || !url_.is_valid()) { | 397 if (url_.is_empty() || !url_.is_valid()) { |
163 DestroyPreviewContents(); | 398 DestroyPreviewContents(); |
164 return; | 399 return; |
165 } | 400 } |
166 | 401 |
167 if (!preview_contents_.get()) { | 402 user_text_ = user_text; |
| 403 |
| 404 if (preview_contents_.get() == NULL) { |
168 preview_contents_.reset( | 405 preview_contents_.reset( |
169 new TabContents(host_->profile(), NULL, MSG_ROUTING_NONE, NULL, NULL)); | 406 new TabContents(tab_contents_->profile(), NULL, MSG_ROUTING_NONE, |
170 preview_contents_->set_delegate(delegate_.get()); | 407 NULL, NULL)); |
171 NotificationService::current()->Notify( | 408 // Propagate the max page id. That way if we end up merging the two |
172 NotificationType::MATCH_PREVIEW_TAB_CONTENTS_CREATED, | 409 // NavigationControllers (which happens if we commit) none of the page ids |
173 Source<TabContents>(host_), | 410 // will overlap. |
174 NotificationService::NoDetails()); | 411 int32 max_page_id = tab_contents_->GetMaxPageID(); |
| 412 if (max_page_id != -1) |
| 413 preview_contents_->controller().set_max_restored_page_id(max_page_id + 1); |
| 414 |
| 415 preview_contents_->set_delegate(preview_tab_contents_delegate_.get()); |
| 416 |
| 417 gfx::Rect tab_bounds; |
| 418 tab_contents_->view()->GetContainerBounds(&tab_bounds); |
| 419 preview_contents_->view()->SizeContents(tab_bounds.size()); |
| 420 |
| 421 preview_contents_->ShowContents(); |
| 422 } |
| 423 preview_tab_contents_delegate_->PrepareForNewLoad(); |
| 424 |
| 425 const TemplateURL* template_url = match.template_url; |
| 426 if (match.type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED || |
| 427 match.type == AutocompleteMatch::SEARCH_HISTORY || |
| 428 match.type == AutocompleteMatch::SEARCH_SUGGEST) { |
| 429 TemplateURLModel* model = tab_contents->profile()->GetTemplateURLModel(); |
| 430 template_url = model ? model->GetDefaultSearchProvider() : NULL; |
| 431 } |
| 432 TemplateURLID template_url_id = template_url ? template_url->id() : 0; |
| 433 |
| 434 if (template_url && template_url->supports_instant() && |
| 435 TemplateURL::SupportsReplacement(template_url)) { |
| 436 if (template_url_id == template_url_id_) { |
| 437 if (frame_load_observer_.get()) { |
| 438 // The page hasn't loaded yet. We'll send the script down when it does. |
| 439 frame_load_observer_->set_text(user_text_); |
| 440 return; |
| 441 } |
| 442 SendUserInputScript(preview_contents_.get(), user_text_, false); |
| 443 if (complete_suggested_text_.size() > user_text_.size() && |
| 444 !complete_suggested_text_.compare(0, user_text_.size(), user_text_)) { |
| 445 *suggested_text = complete_suggested_text_.substr(user_text_.size()); |
| 446 } |
| 447 } else { |
| 448 // TODO: should we use a different url for instant? |
| 449 GURL url = GURL(template_url->url()->ReplaceSearchTerms( |
| 450 *template_url, std::wstring(), |
| 451 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring())); |
| 452 // user_text_ is sent once the page finishes loading by FrameLoadObserver. |
| 453 preview_contents_->controller().LoadURL(url, GURL(), match.transition); |
| 454 frame_load_observer_.reset(new FrameLoadObserver(this, user_text_)); |
| 455 } |
| 456 } else { |
| 457 frame_load_observer_.reset(NULL); |
| 458 preview_contents_->controller().LoadURL(url_, GURL(), match.transition); |
175 } | 459 } |
176 | 460 |
177 // TODO: figure out transition type. | 461 template_url_id_ = template_url_id; |
178 preview_contents_->controller().LoadURL(url, GURL(), | |
179 PageTransition::GENERATED); | |
180 } | 462 } |
181 | 463 |
182 void MatchPreview::DestroyPreviewContents() { | 464 void MatchPreview::DestroyPreviewContents() { |
183 url_ = GURL(); | 465 delegate_->HideMatchPreview(); |
184 preview_contents_.reset(NULL); | 466 delete ReleasePreviewContents(false); |
185 } | 467 } |
186 | 468 |
187 void MatchPreview::CommitCurrentPreview() { | 469 void MatchPreview::CommitCurrentPreview() { |
188 DCHECK(preview_contents_.get()); | 470 DCHECK(preview_contents_.get()); |
189 if (host_->delegate()) | 471 delegate_->CommitMatchPreview(); |
190 host_->delegate()->CommitMatchPreview(host_); | |
191 } | 472 } |
192 | 473 |
193 TabContents* MatchPreview::ReleasePreviewContents() { | 474 TabContents* MatchPreview::ReleasePreviewContents(bool commit_history) { |
| 475 template_url_id_ = 0; |
194 url_ = GURL(); | 476 url_ = GURL(); |
| 477 user_text_.clear(); |
| 478 complete_suggested_text_.clear(); |
| 479 if (frame_load_observer_.get()) { |
| 480 frame_load_observer_->DetachFromPreview(); |
| 481 // FrameLoadObserver will delete itself either when the TabContents is |
| 482 // deleted, or when the page finishes loading. |
| 483 FrameLoadObserver* unused ALLOW_UNUSED = frame_load_observer_.release(); |
| 484 } |
| 485 if (preview_contents_.get()) { |
| 486 if (commit_history) |
| 487 preview_tab_contents_delegate_->CommitHistory(); |
| 488 // Destroy the paint observer. |
| 489 if (preview_contents_->GetRenderWidgetHostView()) { |
| 490 // RenderWidgetHostView may be null during shutdown. |
| 491 preview_contents_->GetRenderWidgetHostView()->GetRenderWidgetHost()-> |
| 492 set_paint_observer(NULL); |
| 493 } |
| 494 preview_contents_->set_delegate(NULL); |
| 495 preview_tab_contents_delegate_->Reset(); |
| 496 is_active_ = false; |
| 497 } |
195 return preview_contents_.release(); | 498 return preview_contents_.release(); |
196 } | 499 } |
| 500 |
| 501 void MatchPreview::SetCompleteSuggestedText( |
| 502 const string16& complete_suggested_text) { |
| 503 if (complete_suggested_text == complete_suggested_text_) |
| 504 return; |
| 505 |
| 506 if (user_text_.compare(0, user_text_.size(), complete_suggested_text, |
| 507 0, user_text_.size())) { |
| 508 // The user text no longer contains the suggested text, ignore it. |
| 509 complete_suggested_text_.clear(); |
| 510 delegate_->SetSuggestedText(string16()); |
| 511 return; |
| 512 } |
| 513 |
| 514 complete_suggested_text_ = complete_suggested_text; |
| 515 delegate_->SetSuggestedText( |
| 516 complete_suggested_text_.substr(user_text_.size())); |
| 517 } |
| 518 |
| 519 void MatchPreview::PreviewDidPaint() { |
| 520 DCHECK(!is_active_); |
| 521 is_active_ = true; |
| 522 delegate_->ShowMatchPreview(); |
| 523 } |
| 524 |
| 525 void MatchPreview::PageFinishedLoading() { |
| 526 // FrameLoadObserver deletes itself after this call. |
| 527 FrameLoadObserver* unused ALLOW_UNUSED = frame_load_observer_.release(); |
| 528 } |
OLD | NEW |