summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
blob: 7ebda485ad15dabd61e25fce9c850aa16a4cd25a (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
// Copyright (c) 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_engines/search_engine_tab_helper.h"

#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_fetcher.h"
#include "chrome/browser/search_engines/template_url_fetcher_factory.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/search_engines/template_url_fetcher_ui_callbacks.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/frame_navigate_params.h"

using content::NavigationController;
using content::NavigationEntry;
using content::WebContents;

DEFINE_WEB_CONTENTS_USER_DATA_KEY(SearchEngineTabHelper);

namespace {

// Returns true if the entry's transition type is FORM_SUBMIT.
bool IsFormSubmit(const NavigationEntry* entry) {
  return (content::PageTransitionStripQualifier(entry->GetTransitionType()) ==
          content::PAGE_TRANSITION_FORM_SUBMIT);
}

base::string16 GenerateKeywordFromNavigationEntry(
    const NavigationEntry* entry) {
  // Don't autogenerate keywords for pages that are the result of form
  // submissions.
  if (IsFormSubmit(entry))
    return base::string16();

  // We want to use the user typed URL if available since that represents what
  // the user typed to get here, and fall back on the regular URL if not.
  GURL url = entry->GetUserTypedURL();
  if (!url.is_valid()) {
    url = entry->GetURL();
    if (!url.is_valid())
      return base::string16();
  }

  // Don't autogenerate keywords for referrers that are anything other than HTTP
  // or have a path.
  //
  // If we relax the path constraint, we need to be sure to sanitize the path
  // elements and update AutocompletePopup to look for keywords using the path.
  // See http://b/issue?id=863583.
  if (!url.SchemeIs(content::kHttpScheme) || (url.path().length() > 1))
    return base::string16();

  return TemplateURLService::GenerateKeyword(url);
}

}  // namespace

SearchEngineTabHelper::~SearchEngineTabHelper() {
}

void SearchEngineTabHelper::DidNavigateMainFrame(
    const content::LoadCommittedDetails& /*details*/,
    const content::FrameNavigateParams& params) {
  GenerateKeywordIfNecessary(params);
}

bool SearchEngineTabHelper::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(SearchEngineTabHelper, message)
    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PageHasOSDD, OnPageHasOSDD)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()

  return handled;
}

SearchEngineTabHelper::SearchEngineTabHelper(WebContents* web_contents)
    : content::WebContentsObserver(web_contents) {
  DCHECK(web_contents);
}

void SearchEngineTabHelper::OnPageHasOSDD(
    int32 page_id,
    const GURL& doc_url,
    const search_provider::OSDDType& msg_provider_type) {
  // Checks to see if we should generate a keyword based on the OSDD, and if
  // necessary uses TemplateURLFetcher to download the OSDD and create a
  // keyword.

  // Make sure page_id is the current page and other basic checks.
  if (!doc_url.is_valid())
    return;
  Profile* profile =
      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
  if (!web_contents()->IsActiveEntry(page_id) ||
      !TemplateURLFetcherFactory::GetForProfile(profile) ||
      profile->IsOffTheRecord())
    return;

  TemplateURLFetcher::ProviderType provider_type =
      (msg_provider_type == search_provider::AUTODETECTED_PROVIDER) ?
          TemplateURLFetcher::AUTODETECTED_PROVIDER :
          TemplateURLFetcher::EXPLICIT_PROVIDER;

  // If the current page is a form submit, find the last page that was not a
  // form submit and use its url to generate the keyword from.
  const NavigationController& controller = web_contents()->GetController();
  const NavigationEntry* entry = controller.GetLastCommittedEntry();
  for (int index = controller.GetLastCommittedEntryIndex();
       (index > 0) && IsFormSubmit(entry);
       entry = controller.GetEntryAtIndex(index))
    --index;
  if (IsFormSubmit(entry))
    return;

  // Autogenerate a keyword for the autodetected case; in the other cases we'll
  // generate a keyword later after fetching the OSDD.
  base::string16 keyword;
  if (provider_type == TemplateURLFetcher::AUTODETECTED_PROVIDER) {
    keyword = GenerateKeywordFromNavigationEntry(entry);
    if (keyword.empty())
      return;
  }

  // Download the OpenSearch description document. If this is successful, a
  // new keyword will be created when done.
  TemplateURLFetcherFactory::GetForProfile(profile)->ScheduleDownload(
      keyword, doc_url, entry->GetFavicon().url, web_contents(),
      new TemplateURLFetcherUICallbacks(this, web_contents()), provider_type);
}

void SearchEngineTabHelper::GenerateKeywordIfNecessary(
    const content::FrameNavigateParams& params) {
  if (!params.searchable_form_url.is_valid())
    return;

  Profile* profile =
      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
  if (profile->IsOffTheRecord())
    return;

  const NavigationController& controller = web_contents()->GetController();
  int last_index = controller.GetLastCommittedEntryIndex();
  // When there was no previous page, the last index will be 0. This is
  // normally due to a form submit that opened in a new tab.
  // TODO(brettw) bug 916126: we should support keywords when form submits
  //              happen in new tabs.
  if (last_index <= 0)
    return;

  base::string16 keyword(GenerateKeywordFromNavigationEntry(
      controller.GetEntryAtIndex(last_index - 1)));
  if (keyword.empty())
    return;

  TemplateURLService* url_service =
      TemplateURLServiceFactory::GetForProfile(profile);
  if (!url_service)
    return;

  if (!url_service->loaded()) {
    url_service->Load();
    return;
  }

  TemplateURL* current_url;
  GURL url = params.searchable_form_url;
  if (!url_service->CanReplaceKeyword(keyword, url, &current_url))
    return;

  if (current_url) {
    if (current_url->originating_url().is_valid()) {
      // The existing keyword was generated from an OpenSearch description
      // document, don't regenerate.
      return;
    }
    url_service->Remove(current_url);
  }

  TemplateURLData data;
  data.short_name = keyword;
  data.SetKeyword(keyword);
  data.SetURL(url.spec());
  DCHECK(controller.GetLastCommittedEntry());
  const GURL& current_favicon =
      controller.GetLastCommittedEntry()->GetFavicon().url;
  // If the favicon url isn't valid, it means there really isn't a favicon, or
  // the favicon url wasn't obtained before the load started. This assumes the
  // latter.
  // TODO(sky): Need a way to set the favicon that doesn't involve generating
  // its url.
  data.favicon_url = current_favicon.is_valid() ?
      current_favicon : TemplateURL::GenerateFaviconURL(params.referrer.url);
  data.safe_for_autoreplace = true;
  data.input_encodings.push_back(params.searchable_form_encoding);
  url_service->Add(new TemplateURL(profile, data));
}