summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/search/search_tab_helper.cc
blob: 881b22ef3608c94cc2837ed942c1a4f14a9913ab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
// Copyright 2012 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/ui/search/search_tab_helper.h"

#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/search.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_type.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

DEFINE_WEB_CONTENTS_USER_DATA_KEY(SearchTabHelper);

namespace {

bool IsNTP(const content::WebContents* contents) {
  // We can't use WebContents::GetURL() because that uses the active entry,
  // whereas we want the visible entry.
  const content::NavigationEntry* entry =
      contents->GetController().GetVisibleEntry();
  if (entry && entry->GetVirtualURL() == GURL(chrome::kChromeUINewTabURL))
    return true;

  return chrome::IsInstantNTP(contents);
}

bool IsSearchResults(const content::WebContents* contents) {
  return !chrome::GetSearchTerms(contents).empty();
}

// TODO(kmadhusu): Move this helper from anonymous namespace to chrome
// namespace and remove InstantPage::IsLocal().
bool IsLocal(const content::WebContents* contents) {
  return contents &&
      (contents->GetURL() == GURL(chrome::kChromeSearchLocalNtpUrl) ||
       contents->GetURL() == GURL(chrome::kChromeSearchLocalGoogleNtpUrl));
}

}  // namespace

SearchTabHelper::SearchTabHelper(content::WebContents* web_contents)
    : WebContentsObserver(web_contents),
      is_search_enabled_(chrome::IsInstantExtendedAPIEnabled()),
      user_input_in_progress_(false),
      popup_is_open_(false),
      user_text_is_empty_(true),
      web_contents_(web_contents) {
  if (!is_search_enabled_)
    return;

  // Observe NOTIFICATION_NAV_ENTRY_COMMITTED events so we can reset state
  // associated with the WebContents  (such as mode, last known most visited
  // items, instant support state etc).
  registrar_.Add(
      this,
      content::NOTIFICATION_NAV_ENTRY_COMMITTED,
      content::Source<content::NavigationController>(
          &web_contents->GetController()));
}

SearchTabHelper::~SearchTabHelper() {
}

void SearchTabHelper::InitForPreloadedNTP() {
  UpdateMode(true, true);
}

void SearchTabHelper::OmniboxEditModelChanged(bool user_input_in_progress,
                                              bool cancelling,
                                              bool popup_is_open,
                                              bool user_text_is_empty) {
  if (!is_search_enabled_)
    return;

  user_input_in_progress_ = user_input_in_progress;
  popup_is_open_ = popup_is_open;
  user_text_is_empty_ = user_text_is_empty;
  if (!user_input_in_progress && !cancelling)
    return;

  UpdateMode(false, false);
}

void SearchTabHelper::NavigationEntryUpdated() {
  if (!is_search_enabled_)
    return;

  UpdateMode(false, false);
}

void SearchTabHelper::InstantSupportChanged(bool instant_support) {
  if (!is_search_enabled_)
    return;

  model_.SetInstantSupportState(instant_support ? INSTANT_SUPPORT_YES :
                                                  INSTANT_SUPPORT_NO);
}

bool SearchTabHelper::SupportsInstant() const {
  return model_.instant_support() == INSTANT_SUPPORT_YES;
}

void SearchTabHelper::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED, type);
  content::LoadCommittedDetails* load_details =
      content::Details<content::LoadCommittedDetails>(details).ptr();
  if (!load_details->is_main_frame)
    return;

  UpdateMode(true, false);

  // Already determined the instant support state for this page, do not reset
  // the instant support state.
  //
  // When we get a navigation entry committed event, there seem to be two ways
  // to tell whether the navigation was "in-page". Ideally, when
  // LoadCommittedDetails::is_in_page is true, we should have
  // LoadCommittedDetails::type to be NAVIGATION_TYPE_IN_PAGE. Unfortunately,
  // they are different in some cases. To workaround this bug, we are checking
  // (is_in_page || type == NAVIGATION_TYPE_IN_PAGE). Please refer to
  // crbug.com/251330 for more details.
  if (load_details->is_in_page ||
      load_details->type == content::NAVIGATION_TYPE_IN_PAGE) {
    return;
  }

  model_.SetInstantSupportState(INSTANT_SUPPORT_UNKNOWN);
  model_.SetVoiceSearchSupported(false);
}

void SearchTabHelper::DidNavigateMainFrame(
    const content::LoadCommittedDetails& details,
    const content::FrameNavigateParams& params) {
  // Always set the title on the new tab page to be the one from our UI
  // resources.  Normally, we set the title when we begin a NTP load, but it
  // can get reset in several places (like when you press Reload). This check
  // ensures that the title is properly set to the string defined by the Chrome
  // UI language (rather than the server language) in all cases.
  //
  // We only override the title when it's nonempty to allow the page to set the
  // title if it really wants. An empty title means to use the default. There's
  // also a race condition between this code and the page's SetTitle call which
  // this rule avoids.
  content::NavigationEntry* entry =
      web_contents_->GetController().GetActiveEntry();
  if (entry && entry->GetTitle().empty() &&
      (entry->GetVirtualURL() == GURL(chrome::kChromeUINewTabURL) ||
       chrome::NavEntryIsInstantNTP(web_contents_, entry))) {
    entry->SetTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
  }
}

bool SearchTabHelper::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(SearchTabHelper, message)
    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SearchBoxShowBars,
                        OnSearchBoxShowBars)
    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SearchBoxHideBars,
                        OnSearchBoxHideBars)
    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_InstantSupportDetermined,
                        OnInstantSupportDetermined)
    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SetVoiceSearchSupported,
                        OnSetVoiceSearchSupported)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void SearchTabHelper::DidFinishLoad(
    int64 /* frame_id */,
    const GURL&  /* validated_url */,
    bool is_main_frame,
    content::RenderViewHost* /* render_view_host */) {
  if (is_main_frame)
    DetermineIfPageSupportsInstant();
}

void SearchTabHelper::UpdateMode(bool update_origin, bool is_preloaded_ntp) {
  SearchMode::Type type = SearchMode::MODE_DEFAULT;
  SearchMode::Origin origin = SearchMode::ORIGIN_DEFAULT;
  if (IsNTP(web_contents_) || is_preloaded_ntp) {
    type = SearchMode::MODE_NTP;
    origin = SearchMode::ORIGIN_NTP;
  } else if (IsSearchResults(web_contents_)) {
    type = SearchMode::MODE_SEARCH_RESULTS;
    origin = SearchMode::ORIGIN_SEARCH;
  }
  if (!update_origin)
    origin = model_.mode().origin;
  if (user_input_in_progress_)
    type = SearchMode::MODE_SEARCH_SUGGESTIONS;

  if (type == SearchMode::MODE_NTP && origin == SearchMode::ORIGIN_NTP &&
      !popup_is_open_ && !user_text_is_empty_) {
    // We're switching back (|popup_is_open_| is false) to an NTP (type and
    // mode are |NTP|) with suggestions (|user_text_is_empty_| is false), don't
    // modify visibility of top bars or voice search support.  This specific
    // omnibox state is set when OmniboxEditModelChanged() is called from
    // OmniboxEditModel::SetInputInProgress() which is called from
    // OmniboxEditModel::Revert().
    model_.SetState(SearchModel::State(SearchMode(type, origin),
                                       model_.state().top_bars_visible,
                                       model_.instant_support(),
                                       model_.state().voice_search_supported));
  } else {
    model_.SetMode(SearchMode(type, origin));
  }
}

void SearchTabHelper::DetermineIfPageSupportsInstant() {
  Profile* profile =
      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
  if (!chrome::ShouldAssignURLToInstantRenderer(web_contents_->GetURL(),
                                                profile)) {
    // The page is not in the Instant process. This page does not support
    // instant. If we send an IPC message to a page that is not in the Instant
    // process, it will never receive it and will never respond. Therefore,
    // return immediately.
    InstantSupportChanged(false);
  } else if (IsLocal(web_contents_)) {
    // Local pages always support Instant.
    InstantSupportChanged(true);
  } else {
    Send(new ChromeViewMsg_DetermineIfPageSupportsInstant(routing_id()));
  }
}

void SearchTabHelper::OnInstantSupportDetermined(int page_id,
                                                 bool instant_support) {
  if (!web_contents()->IsActiveEntry(page_id))
    return;

  InstantSupportChanged(instant_support);
}

void SearchTabHelper::OnSearchBoxShowBars(int page_id) {
  if (web_contents()->IsActiveEntry(page_id))
    model_.SetTopBarsVisible(true);
}

void SearchTabHelper::OnSearchBoxHideBars(int page_id) {
  if (web_contents()->IsActiveEntry(page_id)) {
    model_.SetTopBarsVisible(false);
    Send(new ChromeViewMsg_SearchBoxBarsHidden(routing_id()));
  }
}

void SearchTabHelper::OnSetVoiceSearchSupported(int page_id, bool supported) {
  if (web_contents()->IsActiveEntry(page_id))
    model_.SetVoiceSearchSupported(supported);
}