[email protected] | f0dc7523 | 2012-01-05 01:07:00 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | fd2b9ce | 2010-08-11 04:03:57 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 6b723f8 | 2010-10-05 20:14:27 | [diff] [blame] | 5 | #include "chrome/browser/instant/instant_controller.h" |
[email protected] | fd2b9ce | 2010-08-11 04:03:57 | [diff] [blame] | 6 | |
[email protected] | fd2b9ce | 2010-08-11 04:03:57 | [diff] [blame] | 7 | #include "base/command_line.h" |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 8 | #include "base/i18n/case_conversion.h" |
[email protected] | 7e03e81 | 2010-11-15 23:01:01 | [diff] [blame] | 9 | #include "base/metrics/histogram.h" |
[email protected] | 941b260c | 2012-08-23 23:00:47 | [diff] [blame] | 10 | #include "base/utf_string_conversions.h" |
| 11 | #include "chrome/browser/autocomplete/autocomplete_provider.h" |
[email protected] | f3d2b31 | 2012-08-23 22:27:59 | [diff] [blame] | 12 | #include "chrome/browser/favicon/favicon_service_factory.h" |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 13 | #include "chrome/browser/history/history.h" |
| 14 | #include "chrome/browser/history/history_service_factory.h" |
| 15 | #include "chrome/browser/history/history_tab_helper.h" |
[email protected] | 37c917b | 2012-06-28 15:53:59 | [diff] [blame] | 16 | #include "chrome/browser/instant/instant_controller_delegate.h" |
[email protected] | 6b723f8 | 2010-10-05 20:14:27 | [diff] [blame] | 17 | #include "chrome/browser/instant/instant_loader.h" |
[email protected] | ba6680f | 2010-11-01 20:35:08 | [diff] [blame] | 18 | #include "chrome/browser/platform_util.h" |
[email protected] | 018cbb2 | 2010-10-11 22:32:09 | [diff] [blame] | 19 | #include "chrome/browser/prefs/pref_service.h" |
[email protected] | 8e5c89a | 2011-06-07 18:13:33 | [diff] [blame] | 20 | #include "chrome/browser/search_engines/template_url_service.h" |
| 21 | #include "chrome/browser/search_engines/template_url_service_factory.h" |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 22 | #include "chrome/browser/ui/search/search.h" |
[email protected] | a7eef8f | 2012-06-08 22:19:39 | [diff] [blame] | 23 | #include "chrome/browser/ui/tab_contents/tab_contents.h" |
[email protected] | 43211582 | 2011-07-10 15:52:27 | [diff] [blame] | 24 | #include "chrome/common/chrome_notification_types.h" |
[email protected] | fd2b9ce | 2010-08-11 04:03:57 | [diff] [blame] | 25 | #include "chrome/common/chrome_switches.h" |
[email protected] | 018cbb2 | 2010-10-11 22:32:09 | [diff] [blame] | 26 | #include "chrome/common/pref_names.h" |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 27 | #include "content/public/browser/favicon_status.h" |
| 28 | #include "content/public/browser/navigation_entry.h" |
[email protected] | ad50def5 | 2011-10-19 23:17:07 | [diff] [blame] | 29 | #include "content/public/browser/notification_service.h" |
[email protected] | 5626b089 | 2012-02-20 14:46:58 | [diff] [blame] | 30 | #include "content/public/browser/render_widget_host_view.h" |
[email protected] | ef9572e | 2012-01-04 22:14:12 | [diff] [blame] | 31 | #include "content/public/browser/web_contents.h" |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 32 | #include "ui/gfx/codec/png_codec.h" |
[email protected] | fd2b9ce | 2010-08-11 04:03:57 | [diff] [blame] | 33 | |
[email protected] | 6eb8ea9 | 2011-08-22 21:01:41 | [diff] [blame] | 34 | #if defined(TOOLKIT_VIEWS) |
[email protected] | c13be0d | 2011-11-22 02:09:58 | [diff] [blame] | 35 | #include "ui/views/widget/widget.h" |
[email protected] | 6eb8ea9 | 2011-08-22 21:01:41 | [diff] [blame] | 36 | #endif |
| 37 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 38 | namespace { |
| 39 | |
| 40 | enum PreviewUsageType { |
| 41 | PREVIEW_CREATED = 0, |
| 42 | PREVIEW_DELETED, |
| 43 | PREVIEW_LOADED, |
| 44 | PREVIEW_SHOWED, |
| 45 | PREVIEW_COMMITTED, |
| 46 | PREVIEW_NUM_TYPES, |
| 47 | }; |
| 48 | |
| 49 | // An artificial delay (in milliseconds) we introduce before telling the Instant |
| 50 | // page about the new omnibox bounds, in cases where the bounds shrink. This is |
| 51 | // to avoid the page jumping up/down very fast in response to bounds changes. |
| 52 | const int kUpdateBoundsDelayMS = 1000; |
| 53 | |
| 54 | // The maximum number of times we'll load a non-Instant-supporting search engine |
| 55 | // before we give up and blacklist it for the rest of the browsing session. |
| 56 | const int kMaxInstantSupportFailures = 10; |
| 57 | |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 58 | // If an Instant page has not been used in these many milliseconds, it is |
| 59 | // reloaded so that the page does not become stale. |
| 60 | const int kStaleLoaderTimeoutMS = 3 * 3600 * 1000; |
| 61 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 62 | std::string ModeToString(InstantController::Mode mode) { |
| 63 | switch (mode) { |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 64 | case InstantController::EXTENDED: return "_Extended"; |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 65 | case InstantController::INSTANT: return "_Instant"; |
| 66 | case InstantController::SUGGEST: return "_Suggest"; |
| 67 | case InstantController::HIDDEN: return "_Hidden"; |
| 68 | case InstantController::SILENT: return "_Silent"; |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 69 | case InstantController::DISABLED: return "_Disabled"; |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | NOTREACHED(); |
| 73 | return std::string(); |
| 74 | } |
| 75 | |
| 76 | void AddPreviewUsageForHistogram(InstantController::Mode mode, |
| 77 | PreviewUsageType usage) { |
| 78 | DCHECK(0 <= usage && usage < PREVIEW_NUM_TYPES) << usage; |
| 79 | base::Histogram* histogram = base::LinearHistogram::FactoryGet( |
| 80 | "Instant.Previews" + ModeToString(mode), 1, PREVIEW_NUM_TYPES, |
| 81 | PREVIEW_NUM_TYPES + 1, base::Histogram::kUmaTargetedHistogramFlag); |
| 82 | histogram->Add(usage); |
| 83 | } |
| 84 | |
| 85 | void AddSessionStorageHistogram(InstantController::Mode mode, |
| 86 | const TabContents* tab1, |
| 87 | const TabContents* tab2) { |
| 88 | base::Histogram* histogram = base::BooleanHistogram::FactoryGet( |
| 89 | "Instant.SessionStorageNamespace" + ModeToString(mode), |
| 90 | base::Histogram::kUmaTargetedHistogramFlag); |
[email protected] | d1198fd | 2012-08-13 22:50:19 | [diff] [blame] | 91 | const content::SessionStorageNamespaceMap& session_storage_map1 = |
| 92 | tab1->web_contents()->GetController().GetSessionStorageNamespaceMap(); |
| 93 | const content::SessionStorageNamespaceMap& session_storage_map2 = |
| 94 | tab2->web_contents()->GetController().GetSessionStorageNamespaceMap(); |
| 95 | bool is_session_storage_the_same = |
| 96 | session_storage_map1.size() == session_storage_map2.size(); |
| 97 | if (is_session_storage_the_same) { |
| 98 | // The size is the same, so let's check that all entries match. |
| 99 | for (content::SessionStorageNamespaceMap::const_iterator |
| 100 | it1 = session_storage_map1.begin(), |
| 101 | it2 = session_storage_map2.begin(); |
| 102 | it1 != session_storage_map1.end() && |
| 103 | it2 != session_storage_map2.end(); |
| 104 | ++it1, ++it2) { |
| 105 | if (it1->first != it2->first || it1->second != it2->second) { |
| 106 | is_session_storage_the_same = false; |
| 107 | break; |
| 108 | } |
| 109 | } |
| 110 | } |
| 111 | histogram->AddBoolean(is_session_storage_the_same); |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 112 | } |
| 113 | |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 114 | InstantController::Mode GetModeForProfile(Profile* profile) { |
| 115 | if (!profile || profile->IsOffTheRecord() || !profile->GetPrefs() || |
| 116 | !profile->GetPrefs()->GetBoolean(prefs::kInstantEnabled)) |
| 117 | return InstantController::DISABLED; |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 118 | |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 119 | return chrome::search::IsInstantExtendedAPIEnabled(profile) ? |
| 120 | InstantController::EXTENDED : InstantController::INSTANT; |
[email protected] | 03bb953d | 2010-09-14 21:38:30 | [diff] [blame] | 121 | } |
| 122 | |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 123 | } // namespace |
| 124 | |
[email protected] | 6b723f8 | 2010-10-05 20:14:27 | [diff] [blame] | 125 | InstantController::~InstantController() { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 126 | if (GetPreviewContents()) |
| 127 | AddPreviewUsageForHistogram(mode_, PREVIEW_DELETED); |
[email protected] | 03bb953d | 2010-09-14 21:38:30 | [diff] [blame] | 128 | } |
| 129 | |
[email protected] | 7e03e81 | 2010-11-15 23:01:01 | [diff] [blame] | 130 | // static |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 131 | InstantController* InstantController::CreateInstant( |
| 132 | Profile* profile, |
| 133 | InstantControllerDelegate* delegate) { |
| 134 | const Mode mode = GetModeForProfile(profile); |
| 135 | return mode == DISABLED ? NULL : new InstantController(delegate, mode); |
| 136 | } |
| 137 | |
| 138 | // static |
| 139 | bool InstantController::IsExtendedAPIEnabled(Profile* profile) { |
| 140 | return GetModeForProfile(profile) == EXTENDED; |
| 141 | } |
| 142 | |
| 143 | // static |
| 144 | bool InstantController::IsInstantEnabled(Profile* profile) { |
| 145 | const Mode mode = GetModeForProfile(profile); |
| 146 | return mode == EXTENDED || mode == INSTANT; |
| 147 | } |
| 148 | |
| 149 | // static |
| 150 | bool InstantController::IsSuggestEnabled(Profile* profile) { |
| 151 | const Mode mode = GetModeForProfile(profile); |
| 152 | return mode == EXTENDED || mode == INSTANT || mode == SUGGEST; |
| 153 | } |
| 154 | |
| 155 | // static |
[email protected] | 7e03e81 | 2010-11-15 23:01:01 | [diff] [blame] | 156 | void InstantController::RegisterUserPrefs(PrefService* prefs) { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 157 | prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, false, |
[email protected] | 18c2ffac | 2011-09-16 21:07:29 | [diff] [blame] | 158 | PrefService::SYNCABLE_PREF); |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 159 | prefs->RegisterBooleanPref(prefs::kInstantEnabled, false, |
[email protected] | 18c2ffac | 2011-09-16 21:07:29 | [diff] [blame] | 160 | PrefService::SYNCABLE_PREF); |
[email protected] | 7ab402e6 | 2012-06-20 19:49:18 | [diff] [blame] | 161 | |
| 162 | // TODO(jamescook): Move this to search controller. |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 163 | prefs->RegisterDoublePref(prefs::kInstantAnimationScaleFactor, 1.0, |
[email protected] | 7ab402e6 | 2012-06-20 19:49:18 | [diff] [blame] | 164 | PrefService::UNSYNCABLE_PREF); |
[email protected] | 7e03e81 | 2010-11-15 23:01:01 | [diff] [blame] | 165 | } |
| 166 | |
[email protected] | 05cf2fa | 2012-05-29 20:36:06 | [diff] [blame] | 167 | bool InstantController::Update(const AutocompleteMatch& match, |
[email protected] | 6b723f8 | 2010-10-05 20:14:27 | [diff] [blame] | 168 | const string16& user_text, |
[email protected] | cd22332 | 2012-08-26 19:54:42 | [diff] [blame] | 169 | const string16& full_text, |
| 170 | bool verbatim) { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 171 | const TabContents* active_tab = delegate_->GetActiveTabContents(); |
[email protected] | bdf1d86 | 2010-11-24 02:46:11 | [diff] [blame] | 172 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 173 | // We could get here with no active tab if the Browser is closing. |
| 174 | if (!active_tab) { |
| 175 | Hide(); |
| 176 | return false; |
| 177 | } |
| 178 | |
| 179 | std::string instant_url; |
| 180 | Profile* profile = active_tab->profile(); |
| 181 | |
| 182 | // If the match's TemplateURL isn't valid, it is likely not a query. |
| 183 | if (!GetInstantURL(match.GetTemplateURL(profile), &instant_url)) { |
| 184 | Hide(); |
| 185 | return false; |
| 186 | } |
| 187 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 188 | if (full_text.empty()) { |
| 189 | Hide(); |
| 190 | return false; |
| 191 | } |
| 192 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 193 | ResetLoader(instant_url, active_tab); |
| 194 | last_active_tab_ = active_tab; |
| 195 | |
| 196 | // Track the non-Instant search URL for this query. |
| 197 | url_for_history_ = match.destination_url; |
[email protected] | 97b6c4f | 2010-09-27 19:31:26 | [diff] [blame] | 198 | last_transition_type_ = match.transition; |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 199 | |
[email protected] | cd22332 | 2012-08-26 19:54:42 | [diff] [blame] | 200 | // In EXTENDED mode, we send only |user_text| as the query text. In all other |
| 201 | // modes, we use the entire |full_text|. |
| 202 | const string16& query_text = mode_ == EXTENDED ? user_text : full_text; |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 203 | string16 last_query_text = mode_ == EXTENDED ? |
| 204 | last_user_text_ : last_full_text_; |
[email protected] | 64f09b8 | 2011-10-13 16:17:20 | [diff] [blame] | 205 | last_user_text_ = user_text; |
[email protected] | cd22332 | 2012-08-26 19:54:42 | [diff] [blame] | 206 | last_full_text_ = full_text; |
[email protected] | 64f09b8 | 2011-10-13 16:17:20 | [diff] [blame] | 207 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 208 | // Don't send an update to the loader if the query text hasn't changed. |
[email protected] | cd22332 | 2012-08-26 19:54:42 | [diff] [blame] | 209 | if (query_text == last_query_text && verbatim == last_verbatim_) { |
| 210 | // Reuse the last suggestion, as it's still valid. |
| 211 | delegate_->SetSuggestedText(last_suggestion_.text, |
| 212 | last_suggestion_.behavior); |
[email protected] | fd2b9ce | 2010-08-11 04:03:57 | [diff] [blame] | 213 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 214 | // We need to call Show() here because of this: |
| 215 | // 1. User has typed a query (say Q). Instant overlay is showing results. |
| 216 | // 2. User arrows-down to a URL entry or erases all omnibox text. Both of |
| 217 | // these cause the overlay to Hide(). |
| 218 | // 3. User arrows-up to Q or types Q again. The last text we processed is |
| 219 | // still Q, so we don't Update() the loader, but we do need to Show(). |
[email protected] | 941b260c | 2012-08-23 23:00:47 | [diff] [blame] | 220 | if (loader_processed_last_update_ && |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 221 | (mode_ == INSTANT || mode_ == EXTENDED)) |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 222 | Show(); |
[email protected] | 64f09b8 | 2011-10-13 16:17:20 | [diff] [blame] | 223 | return true; |
| 224 | } |
[email protected] | 36d5e559 | 2010-11-15 20:45:59 | [diff] [blame] | 225 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 226 | last_verbatim_ = verbatim; |
| 227 | loader_processed_last_update_ = false; |
[email protected] | 0e7cb68 | 2012-08-15 04:04:38 | [diff] [blame] | 228 | last_suggestion_ = InstantSuggestion(); |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 229 | |
| 230 | if (mode_ != SILENT) { |
[email protected] | cd22332 | 2012-08-26 19:54:42 | [diff] [blame] | 231 | loader_->Update(query_text, verbatim); |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 232 | |
| 233 | content::NotificationService::current()->Notify( |
| 234 | chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED, |
| 235 | content::Source<InstantController>(this), |
| 236 | content::NotificationService::NoDetails()); |
| 237 | } |
| 238 | |
[email protected] | cd22332 | 2012-08-26 19:54:42 | [diff] [blame] | 239 | // We don't have suggestions yet, but need to reset any existing "gray text". |
| 240 | delegate_->SetSuggestedText(string16(), INSTANT_COMPLETE_NOW); |
| 241 | |
[email protected] | 65d68da | 2011-09-08 03:19:33 | [diff] [blame] | 242 | return true; |
[email protected] | fd2b9ce | 2010-08-11 04:03:57 | [diff] [blame] | 243 | } |
| 244 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 245 | // TODO(tonyg): This method only fires when the omnibox bounds change. It also |
| 246 | // needs to fire when the preview bounds change (e.g.: open/close info bar). |
[email protected] | 6b723f8 | 2010-10-05 20:14:27 | [diff] [blame] | 247 | void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) { |
[email protected] | 941b260c | 2012-08-23 23:00:47 | [diff] [blame] | 248 | if (omnibox_bounds_ == bounds || (mode_ != INSTANT && mode_ != EXTENDED)) |
[email protected] | 46fe8e9 | 2010-09-22 03:32:47 | [diff] [blame] | 249 | return; |
| 250 | |
[email protected] | 6e6b59f4 | 2010-12-13 20:20:23 | [diff] [blame] | 251 | omnibox_bounds_ = bounds; |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 252 | if (omnibox_bounds_.height() > last_omnibox_bounds_.height()) { |
| 253 | update_bounds_timer_.Stop(); |
| 254 | SendBoundsToPage(); |
| 255 | } else if (!update_bounds_timer_.IsRunning()) { |
| 256 | update_bounds_timer_.Start(FROM_HERE, |
| 257 | base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), this, |
| 258 | &InstantController::SendBoundsToPage); |
| 259 | } |
[email protected] | 46fe8e9 | 2010-09-22 03:32:47 | [diff] [blame] | 260 | } |
| 261 | |
[email protected] | 941b260c | 2012-08-23 23:00:47 | [diff] [blame] | 262 | void InstantController::HandleAutocompleteResults( |
| 263 | const std::vector<AutocompleteProvider*>& providers) { |
| 264 | if (mode_ != EXTENDED || !GetPreviewContents()) |
| 265 | return; |
| 266 | |
| 267 | std::vector<InstantAutocompleteResult> results; |
| 268 | for (ACProviders::const_iterator provider = providers.begin(); |
| 269 | provider != providers.end(); ++provider) { |
| 270 | for (ACMatches::const_iterator match = (*provider)->matches().begin(); |
| 271 | match != (*provider)->matches().end(); ++match) { |
| 272 | InstantAutocompleteResult result; |
| 273 | result.provider = UTF8ToUTF16((*provider)->name()); |
| 274 | result.is_search = |
| 275 | match->type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED || |
| 276 | match->type == AutocompleteMatch::SEARCH_HISTORY || |
| 277 | match->type == AutocompleteMatch::SEARCH_SUGGEST || |
| 278 | match->type == AutocompleteMatch::SEARCH_OTHER_ENGINE; |
| 279 | result.contents = match->description; |
| 280 | result.destination_url = match->destination_url; |
| 281 | result.relevance = match->relevance; |
| 282 | results.push_back(result); |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | loader_->SendAutocompleteResults(results); |
| 287 | } |
| 288 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 289 | TabContents* InstantController::GetPreviewContents() const { |
| 290 | return loader_.get() ? loader_->preview_contents() : NULL; |
[email protected] | fd2b9ce | 2010-08-11 04:03:57 | [diff] [blame] | 291 | } |
| 292 | |
[email protected] | 3e48128 | 2011-10-15 15:39:50 | [diff] [blame] | 293 | void InstantController::Hide() { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 294 | last_active_tab_ = NULL; |
| 295 | if (is_showing_) { |
| 296 | is_showing_ = false; |
[email protected] | 2573b8d | 2011-03-01 16:20:36 | [diff] [blame] | 297 | delegate_->HideInstant(); |
| 298 | } |
[email protected] | 7cce9f2 | 2011-02-28 22:02:47 | [diff] [blame] | 299 | } |
| 300 | |
[email protected] | eadbf953 | 2011-11-03 23:52:16 | [diff] [blame] | 301 | bool InstantController::IsCurrent() const { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 302 | DCHECK(IsOutOfDate() || GetPreviewContents()); |
| 303 | return !IsOutOfDate() && GetPreviewContents() && loader_->supports_instant(); |
[email protected] | 64f09b8 | 2011-10-13 16:17:20 | [diff] [blame] | 304 | } |
| 305 | |
[email protected] | a7eef8f | 2012-06-08 22:19:39 | [diff] [blame] | 306 | TabContents* InstantController::CommitCurrentPreview(InstantCommitType type) { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 307 | const TabContents* active_tab = delegate_->GetActiveTabContents(); |
| 308 | TabContents* preview = ReleasePreviewContents(type); |
| 309 | AddSessionStorageHistogram(mode_, active_tab, preview); |
[email protected] | 05cf2fa | 2012-05-29 20:36:06 | [diff] [blame] | 310 | preview->web_contents()->GetController().CopyStateFromAndPrune( |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 311 | &active_tab->web_contents()->GetController()); |
[email protected] | 05cf2fa | 2012-05-29 20:36:06 | [diff] [blame] | 312 | delegate_->CommitInstant(preview); |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 313 | |
| 314 | // Try to create another loader immediately so that it is ready for the next |
| 315 | // user interaction. |
| 316 | CreateDefaultLoader(); |
| 317 | |
[email protected] | 05cf2fa | 2012-05-29 20:36:06 | [diff] [blame] | 318 | return preview; |
| 319 | } |
| 320 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 321 | TabContents* InstantController::ReleasePreviewContents(InstantCommitType type) { |
| 322 | TabContents* preview = loader_->ReleasePreviewContents(type, last_full_text_); |
| 323 | |
| 324 | // If the preview page has navigated since the last Update(), we need to add |
| 325 | // the navigation to history ourselves. Else, the page will navigate after |
| 326 | // commit, and it will be added to history in the usual manner. |
| 327 | scoped_refptr<history::HistoryAddPageArgs> last_navigation = |
| 328 | loader_->last_navigation(); |
| 329 | if (last_navigation != NULL) { |
| 330 | content::NavigationEntry* entry = |
| 331 | preview->web_contents()->GetController().GetActiveEntry(); |
| 332 | DCHECK_EQ(last_navigation->url, entry->GetURL()); |
| 333 | |
| 334 | // Add the page to history. |
| 335 | preview->history_tab_helper()->UpdateHistoryForNavigation(last_navigation); |
| 336 | |
| 337 | // Update the page title. |
| 338 | preview->history_tab_helper()->UpdateHistoryPageTitle(*entry); |
| 339 | |
| 340 | // Update the favicon. |
[email protected] | f3d2b31 | 2012-08-23 22:27:59 | [diff] [blame] | 341 | FaviconService* favicon_service = FaviconServiceFactory::GetForProfile( |
| 342 | preview->profile(), Profile::EXPLICIT_ACCESS); |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 343 | if (favicon_service && entry->GetFavicon().valid && |
| 344 | entry->GetFavicon().image.IsEmpty()) { |
| 345 | std::vector<unsigned char> image_data; |
| 346 | // TODO: Add all variants once the history service supports it. |
| 347 | gfx::PNGCodec::EncodeBGRASkBitmap( |
| 348 | entry->GetFavicon().image.AsBitmap(), false, &image_data); |
| 349 | favicon_service->SetFavicon(entry->GetURL(), |
| 350 | entry->GetFavicon().url, |
| 351 | image_data, |
| 352 | history::FAVICON); |
| 353 | } |
[email protected] | 05cf2fa | 2012-05-29 20:36:06 | [diff] [blame] | 354 | } |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 355 | |
| 356 | // Add a fake history entry with a non-Instant search URL, so that search |
| 357 | // terms extraction (for autocomplete history matches) works. |
| 358 | HistoryService* history = HistoryServiceFactory::GetForProfile( |
| 359 | preview->profile(), Profile::EXPLICIT_ACCESS); |
| 360 | if (history) { |
| 361 | history->AddPage(url_for_history_, NULL, 0, GURL(), last_transition_type_, |
| 362 | history::RedirectList(), history::SOURCE_BROWSED, false); |
| 363 | } |
| 364 | |
| 365 | AddPreviewUsageForHistogram(mode_, PREVIEW_COMMITTED); |
| 366 | |
| 367 | // We may have gotten here from CommitInstant(), which means the loader may |
| 368 | // still be on the stack. So, schedule a destruction for later. |
| 369 | MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release()); |
| 370 | |
| 371 | // This call is here to hide the preview and reset view state. It won't |
| 372 | // actually delete |loader_| because it was just released to DeleteSoon(). |
| 373 | DeleteLoader(); |
| 374 | |
| 375 | return preview; |
[email protected] | fd2b9ce | 2010-08-11 04:03:57 | [diff] [blame] | 376 | } |
| 377 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 378 | void InstantController::OnAutocompleteLostFocus( |
| 379 | gfx::NativeView view_gaining_focus) { |
| 380 | DCHECK(!is_showing_ || GetPreviewContents()); |
[email protected] | 484ae591 | 2010-09-29 19:16:14 | [diff] [blame] | 381 | |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 382 | // If there is no preview, nothing to do. |
| 383 | if (!GetPreviewContents()) |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 384 | return; |
[email protected] | 484ae591 | 2010-09-29 19:16:14 | [diff] [blame] | 385 | |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 386 | // If the preview is not showing, only need to check for loader staleness. |
| 387 | if (!is_showing_) { |
| 388 | MaybeOnStaleLoader(); |
| 389 | return; |
| 390 | } |
| 391 | |
[email protected] | 20ac3c3 | 2011-03-06 17:59:19 | [diff] [blame] | 392 | #if defined(OS_MACOSX) |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 393 | if (!loader_->IsPointerDownFromActivate()) { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 394 | Hide(); |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 395 | MaybeOnStaleLoader(); |
| 396 | } |
[email protected] | 20ac3c3 | 2011-03-06 17:59:19 | [diff] [blame] | 397 | #else |
[email protected] | 5626b089 | 2012-02-20 14:46:58 | [diff] [blame] | 398 | content::RenderWidgetHostView* rwhv = |
[email protected] | ef9572e | 2012-01-04 22:14:12 | [diff] [blame] | 399 | GetPreviewContents()->web_contents()->GetRenderWidgetHostView(); |
[email protected] | 5a85751b | 2010-11-17 01:33:27 | [diff] [blame] | 400 | if (!view_gaining_focus || !rwhv) { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 401 | Hide(); |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 402 | MaybeOnStaleLoader(); |
[email protected] | 5a85751b | 2010-11-17 01:33:27 | [diff] [blame] | 403 | return; |
| 404 | } |
[email protected] | ba6680f | 2010-11-01 20:35:08 | [diff] [blame] | 405 | |
[email protected] | 6eb8ea9 | 2011-08-22 21:01:41 | [diff] [blame] | 406 | #if defined(TOOLKIT_VIEWS) |
| 407 | // For views the top level widget is always focused. If the focus change |
| 408 | // originated in views determine the child Widget from the view that is being |
| 409 | // focused. |
[email protected] | 09c6943 | 2012-03-16 16:23:28 | [diff] [blame] | 410 | views::Widget* widget = |
| 411 | views::Widget::GetWidgetForNativeView(view_gaining_focus); |
| 412 | if (widget) { |
| 413 | views::FocusManager* focus_manager = widget->GetFocusManager(); |
| 414 | if (focus_manager && focus_manager->is_changing_focus() && |
| 415 | focus_manager->GetFocusedView() && |
| 416 | focus_manager->GetFocusedView()->GetWidget()) { |
| 417 | view_gaining_focus = |
| 418 | focus_manager->GetFocusedView()->GetWidget()->GetNativeView(); |
[email protected] | 6eb8ea9 | 2011-08-22 21:01:41 | [diff] [blame] | 419 | } |
| 420 | } |
| 421 | #endif |
| 422 | |
[email protected] | 3c9e187 | 2010-11-18 16:17:49 | [diff] [blame] | 423 | gfx::NativeView tab_view = |
[email protected] | ef9572e | 2012-01-04 22:14:12 | [diff] [blame] | 424 | GetPreviewContents()->web_contents()->GetNativeView(); |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 425 | |
[email protected] | ba6680f | 2010-11-01 20:35:08 | [diff] [blame] | 426 | // Focus is going to the renderer. |
| 427 | if (rwhv->GetNativeView() == view_gaining_focus || |
| 428 | tab_view == view_gaining_focus) { |
[email protected] | ba6680f | 2010-11-01 20:35:08 | [diff] [blame] | 429 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 430 | // If the mouse is not down, focus is not going to the renderer. Someone |
| 431 | // else moved focus and we shouldn't commit. |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 432 | if (!loader_->IsPointerDownFromActivate()) { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 433 | Hide(); |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 434 | MaybeOnStaleLoader(); |
| 435 | } |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 436 | |
[email protected] | 5a85751b | 2010-11-17 01:33:27 | [diff] [blame] | 437 | return; |
[email protected] | ba6680f | 2010-11-01 20:35:08 | [diff] [blame] | 438 | } |
| 439 | |
| 440 | // Walk up the view hierarchy. If the view gaining focus is a subview of the |
[email protected] | 0932b30c | 2012-04-17 13:25:10 | [diff] [blame] | 441 | // WebContents view (such as a windowed plugin or http auth dialog), we want |
[email protected] | ba6680f | 2010-11-01 20:35:08 | [diff] [blame] | 442 | // to keep the preview contents. Otherwise, focus has gone somewhere else, |
| 443 | // such as the JS inspector, and we want to cancel the preview. |
| 444 | gfx::NativeView view_gaining_focus_ancestor = view_gaining_focus; |
| 445 | while (view_gaining_focus_ancestor && |
| 446 | view_gaining_focus_ancestor != tab_view) { |
| 447 | view_gaining_focus_ancestor = |
| 448 | platform_util::GetParent(view_gaining_focus_ancestor); |
| 449 | } |
| 450 | |
[email protected] | 5a85751b | 2010-11-17 01:33:27 | [diff] [blame] | 451 | if (view_gaining_focus_ancestor) { |
| 452 | CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST); |
| 453 | return; |
| 454 | } |
[email protected] | ba6680f | 2010-11-01 20:35:08 | [diff] [blame] | 455 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 456 | Hide(); |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 457 | MaybeOnStaleLoader(); |
[email protected] | 20ac3c3 | 2011-03-06 17:59:19 | [diff] [blame] | 458 | #endif |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 459 | } |
[email protected] | ba6680f | 2010-11-01 20:35:08 | [diff] [blame] | 460 | |
[email protected] | 05cf2fa | 2012-05-29 20:36:06 | [diff] [blame] | 461 | void InstantController::OnAutocompleteGotFocus() { |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 462 | CreateDefaultLoader(); |
[email protected] | f2557bd | 2011-06-01 02:33:07 | [diff] [blame] | 463 | } |
| 464 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 465 | bool InstantController::commit_on_pointer_release() const { |
| 466 | return GetPreviewContents() && loader_->IsPointerDownFromActivate(); |
[email protected] | a0b8466 | 2010-10-04 23:22:04 | [diff] [blame] | 467 | } |
| 468 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 469 | void InstantController::SetSuggestions( |
[email protected] | 33b8b8e | 2011-03-15 14:51:55 | [diff] [blame] | 470 | InstantLoader* loader, |
[email protected] | 0e7cb68 | 2012-08-15 04:04:38 | [diff] [blame] | 471 | const std::vector<InstantSuggestion>& suggestions) { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 472 | DCHECK_EQ(loader_.get(), loader); |
| 473 | if (loader_ != loader || IsOutOfDate() || mode_ == SILENT || mode_ == HIDDEN) |
[email protected] | d4a2a6e24 | 2012-05-04 04:34:06 | [diff] [blame] | 474 | return; |
| 475 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 476 | loader_processed_last_update_ = true; |
[email protected] | 03bb953d | 2010-09-14 21:38:30 | [diff] [blame] | 477 | |
[email protected] | 0e7cb68 | 2012-08-15 04:04:38 | [diff] [blame] | 478 | InstantSuggestion suggestion; |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 479 | if (!suggestions.empty()) |
| 480 | suggestion = suggestions[0]; |
[email protected] | a0b8466 | 2010-10-04 23:22:04 | [diff] [blame] | 481 | |
[email protected] | 0e7cb68 | 2012-08-15 04:04:38 | [diff] [blame] | 482 | string16 suggestion_lower = base::i18n::ToLower(suggestion.text); |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 483 | string16 user_text_lower = base::i18n::ToLower(last_user_text_); |
| 484 | if (user_text_lower.size() >= suggestion_lower.size() || |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 485 | suggestion_lower.compare(0, user_text_lower.size(), user_text_lower)) |
[email protected] | 0e7cb68 | 2012-08-15 04:04:38 | [diff] [blame] | 486 | suggestion.text.clear(); |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 487 | else |
[email protected] | 0e7cb68 | 2012-08-15 04:04:38 | [diff] [blame] | 488 | suggestion.text.erase(0, last_user_text_.size()); |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 489 | |
| 490 | last_suggestion_ = suggestion; |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 491 | if (!last_verbatim_) |
[email protected] | 0e7cb68 | 2012-08-15 04:04:38 | [diff] [blame] | 492 | delegate_->SetSuggestedText(suggestion.text, suggestion.behavior); |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 493 | |
| 494 | if (mode_ != SUGGEST) |
| 495 | Show(); |
[email protected] | a0b8466 | 2010-10-04 23:22:04 | [diff] [blame] | 496 | } |
| 497 | |
[email protected] | 6b723f8 | 2010-10-05 20:14:27 | [diff] [blame] | 498 | void InstantController::CommitInstantLoader(InstantLoader* loader) { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 499 | DCHECK_EQ(loader_.get(), loader); |
| 500 | DCHECK(is_showing_ && !IsOutOfDate()) << is_showing_; |
| 501 | if (loader_ != loader || !is_showing_ || IsOutOfDate()) |
| 502 | return; |
| 503 | |
| 504 | CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST); |
| 505 | } |
| 506 | |
| 507 | void InstantController::InstantLoaderPreviewLoaded(InstantLoader* loader) { |
| 508 | DCHECK_EQ(loader_.get(), loader); |
| 509 | AddPreviewUsageForHistogram(mode_, PREVIEW_LOADED); |
| 510 | } |
| 511 | |
| 512 | void InstantController::InstantSupportDetermined(InstantLoader* loader, |
| 513 | bool supports_instant) { |
| 514 | DCHECK_EQ(loader_.get(), loader); |
| 515 | if (supports_instant) { |
| 516 | blacklisted_urls_.erase(loader->instant_url()); |
[email protected] | a0b8466 | 2010-10-04 23:22:04 | [diff] [blame] | 517 | } else { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 518 | ++blacklisted_urls_[loader->instant_url()]; |
| 519 | if (loader_ == loader) { |
| 520 | if (GetPreviewContents()) |
| 521 | AddPreviewUsageForHistogram(mode_, PREVIEW_DELETED); |
| 522 | |
| 523 | // Because of the state of the stack, we can't destroy the loader now. |
| 524 | MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release()); |
| 525 | DeleteLoader(); |
| 526 | } |
[email protected] | 03bb953d | 2010-09-14 21:38:30 | [diff] [blame] | 527 | } |
[email protected] | 03bb953d | 2010-09-14 21:38:30 | [diff] [blame] | 528 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 529 | content::Details<const bool> details(&supports_instant); |
| 530 | content::NotificationService::current()->Notify( |
| 531 | chrome::NOTIFICATION_INSTANT_SUPPORT_DETERMINED, |
| 532 | content::NotificationService::AllSources(), |
| 533 | details); |
[email protected] | 2573b8d | 2011-03-01 16:20:36 | [diff] [blame] | 534 | } |
| 535 | |
[email protected] | 00d6421 | 2011-05-25 18:18:28 | [diff] [blame] | 536 | void InstantController::SwappedTabContents(InstantLoader* loader) { |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 537 | DCHECK_EQ(loader_.get(), loader); |
| 538 | if (loader_ == loader && is_showing_) |
| 539 | delegate_->ShowInstant(); |
[email protected] | 00d6421 | 2011-05-25 18:18:28 | [diff] [blame] | 540 | } |
| 541 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 542 | void InstantController::InstantLoaderContentsFocused(InstantLoader* loader) { |
| 543 | DCHECK_EQ(loader_.get(), loader); |
| 544 | DCHECK(is_showing_ && !IsOutOfDate()) << is_showing_; |
[email protected] | 09c6943 | 2012-03-16 16:23:28 | [diff] [blame] | 545 | #if defined(USE_AURA) |
| 546 | // On aura the omnibox only receives a focus lost if we initiate the focus |
| 547 | // change. This does that. |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 548 | if (is_showing_ && !IsOutOfDate()) |
[email protected] | 09c6943 | 2012-03-16 16:23:28 | [diff] [blame] | 549 | delegate_->InstantPreviewFocused(); |
| 550 | #endif |
| 551 | } |
| 552 | |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 553 | InstantController::InstantController(InstantControllerDelegate* delegate, |
| 554 | Mode mode) |
| 555 | : delegate_(delegate), |
| 556 | mode_(mode), |
| 557 | last_active_tab_(NULL), |
| 558 | last_verbatim_(false), |
| 559 | last_transition_type_(content::PAGE_TRANSITION_LINK), |
| 560 | is_showing_(false), |
| 561 | loader_processed_last_update_(false) { |
| 562 | } |
| 563 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 564 | void InstantController::ResetLoader(const std::string& instant_url, |
| 565 | const TabContents* active_tab) { |
| 566 | if (GetPreviewContents() && loader_->instant_url() != instant_url) |
| 567 | DeleteLoader(); |
[email protected] | 2573b8d | 2011-03-01 16:20:36 | [diff] [blame] | 568 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 569 | if (!GetPreviewContents()) { |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 570 | DCHECK(!loader_.get()); |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 571 | loader_.reset(new InstantLoader(this, instant_url, active_tab)); |
| 572 | loader_->Init(); |
| 573 | AddPreviewUsageForHistogram(mode_, PREVIEW_CREATED); |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 574 | |
| 575 | // Reset the loader timer. |
| 576 | stale_loader_timer_.Stop(); |
| 577 | stale_loader_timer_.Start( |
| 578 | FROM_HERE, |
| 579 | base::TimeDelta::FromMilliseconds(kStaleLoaderTimeoutMS), this, |
| 580 | &InstantController::OnStaleLoader); |
[email protected] | f38adeeb | 2010-12-08 01:08:11 | [diff] [blame] | 581 | } |
| 582 | } |
| 583 | |
[email protected] | 08a9f97 | 2012-08-27 23:23:15 | [diff] [blame] | 584 | void InstantController::CreateDefaultLoader() { |
| 585 | const TabContents* active_tab = delegate_->GetActiveTabContents(); |
| 586 | |
| 587 | // We could get here with no active tab if the Browser is closing. |
| 588 | if (!active_tab) |
| 589 | return; |
| 590 | |
| 591 | const TemplateURL* template_url = |
| 592 | TemplateURLServiceFactory::GetForProfile(active_tab->profile())-> |
| 593 | GetDefaultSearchProvider(); |
| 594 | std::string instant_url; |
| 595 | if (!GetInstantURL(template_url, &instant_url)) |
| 596 | return; |
| 597 | |
| 598 | ResetLoader(instant_url, active_tab); |
| 599 | } |
| 600 | |
| 601 | void InstantController::OnStaleLoader() { |
| 602 | // If the loader is showing, do not delete it. It will get deleted the next |
| 603 | // time the autocomplete loses focus. |
| 604 | if (is_showing_) |
| 605 | return; |
| 606 | |
| 607 | DeleteLoader(); |
| 608 | CreateDefaultLoader(); |
| 609 | } |
| 610 | |
| 611 | void InstantController::MaybeOnStaleLoader() { |
| 612 | if (!stale_loader_timer_.IsRunning()) |
| 613 | OnStaleLoader(); |
| 614 | } |
| 615 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 616 | void InstantController::DeleteLoader() { |
| 617 | Hide(); |
| 618 | last_full_text_.clear(); |
| 619 | last_user_text_.clear(); |
| 620 | last_verbatim_ = false; |
[email protected] | 0e7cb68 | 2012-08-15 04:04:38 | [diff] [blame] | 621 | last_suggestion_ = InstantSuggestion(); |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 622 | last_transition_type_ = content::PAGE_TRANSITION_LINK; |
| 623 | last_omnibox_bounds_ = gfx::Rect(); |
| 624 | url_for_history_ = GURL(); |
| 625 | if (GetPreviewContents()) |
| 626 | AddPreviewUsageForHistogram(mode_, PREVIEW_DELETED); |
| 627 | loader_.reset(); |
[email protected] | 0a38747 | 2010-10-07 00:18:20 | [diff] [blame] | 628 | } |
| 629 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 630 | void InstantController::Show() { |
| 631 | if (!is_showing_) { |
| 632 | is_showing_ = true; |
| 633 | delegate_->ShowInstant(); |
| 634 | AddPreviewUsageForHistogram(mode_, PREVIEW_SHOWED); |
[email protected] | f38adeeb | 2010-12-08 01:08:11 | [diff] [blame] | 635 | } |
| 636 | } |
| 637 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 638 | void InstantController::SendBoundsToPage() { |
| 639 | if (last_omnibox_bounds_ == omnibox_bounds_ || IsOutOfDate() || |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 640 | !GetPreviewContents() || loader_->IsPointerDownFromActivate()) |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 641 | return; |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 642 | |
| 643 | last_omnibox_bounds_ = omnibox_bounds_; |
| 644 | gfx::Rect preview_bounds = delegate_->GetInstantBounds(); |
| 645 | gfx::Rect intersection = omnibox_bounds_.Intersect(preview_bounds); |
| 646 | |
| 647 | // Translate into window coordinates. |
| 648 | if (!intersection.IsEmpty()) { |
| 649 | intersection.Offset(-preview_bounds.origin().x(), |
| 650 | -preview_bounds.origin().y()); |
| 651 | } |
| 652 | |
| 653 | // In the current Chrome UI, these must always be true so they sanity check |
| 654 | // the above operations. In a future UI, these may be removed or adjusted. |
| 655 | // There is no point in sanity-checking |intersection.y()| because the omnibox |
| 656 | // can be placed anywhere vertically relative to the preview (for example, in |
| 657 | // Mac fullscreen mode, the omnibox is fully enclosed by the preview bounds). |
| 658 | DCHECK_LE(0, intersection.x()); |
| 659 | DCHECK_LE(0, intersection.width()); |
| 660 | DCHECK_LE(0, intersection.height()); |
| 661 | |
| 662 | loader_->SetOmniboxBounds(intersection); |
| 663 | } |
| 664 | |
| 665 | bool InstantController::GetInstantURL(const TemplateURL* template_url, |
| 666 | std::string* instant_url) const { |
| 667 | CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| 668 | if (command_line->HasSwitch(switches::kInstantURL)) { |
| 669 | *instant_url = command_line->GetSwitchValueASCII(switches::kInstantURL); |
| 670 | return true; |
| 671 | } |
| 672 | |
| 673 | if (!template_url) |
| 674 | return false; |
| 675 | |
| 676 | const TemplateURLRef& instant_url_ref = template_url->instant_url_ref(); |
[email protected] | e01af8b5 | 2012-08-30 20:25:16 | [diff] [blame] | 677 | if (!instant_url_ref.IsValid()) |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 678 | return false; |
| 679 | |
[email protected] | e01af8b5 | 2012-08-30 20:25:16 | [diff] [blame] | 680 | // Even if the URL template doesn't have search terms, it may have other |
| 681 | // components (such as {google:baseURL}) that need to be replaced. |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 682 | *instant_url = instant_url_ref.ReplaceSearchTerms( |
| 683 | TemplateURLRef::SearchTermsArgs(string16())); |
| 684 | |
[email protected] | 941b260c | 2012-08-23 23:00:47 | [diff] [blame] | 685 | // Extended mode should always use HTTPS. TODO(sreeram): This section can be |
| 686 | // removed if TemplateURLs supported "https://{google:host}/..." instead of |
| 687 | // only supporting "{google:baseURL}...". |
| 688 | if (mode_ == EXTENDED) { |
| 689 | GURL url_obj(*instant_url); |
| 690 | if (!url_obj.is_valid()) |
| 691 | return false; |
| 692 | |
| 693 | if (!url_obj.SchemeIsSecure()) { |
| 694 | const std::string new_scheme = "https"; |
| 695 | const std::string new_port = "443"; |
| 696 | GURL::Replacements secure; |
| 697 | secure.SetSchemeStr(new_scheme); |
| 698 | secure.SetPortStr(new_port); |
| 699 | url_obj = url_obj.ReplaceComponents(secure); |
| 700 | |
| 701 | if (!url_obj.is_valid()) |
| 702 | return false; |
| 703 | |
| 704 | *instant_url = url_obj.spec(); |
| 705 | } |
| 706 | } |
| 707 | |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 708 | std::map<std::string, int>::const_iterator iter = |
| 709 | blacklisted_urls_.find(*instant_url); |
| 710 | if (iter != blacklisted_urls_.end() && |
[email protected] | b67d0a4 | 2012-09-04 20:57:35 | [diff] [blame^] | 711 | iter->second > kMaxInstantSupportFailures) |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 712 | return false; |
[email protected] | c55e3b8 | 2012-08-09 15:27:05 | [diff] [blame] | 713 | |
| 714 | return true; |
| 715 | } |
| 716 | |
| 717 | bool InstantController::IsOutOfDate() const { |
| 718 | return !last_active_tab_ || |
| 719 | last_active_tab_ != delegate_->GetActiveTabContents(); |
[email protected] | f38adeeb | 2010-12-08 01:08:11 | [diff] [blame] | 720 | } |