root/chrome/renderer/searchbox/searchbox.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. AreMostVisitedItemsEqual
  2. GetInstantRestrictedIDFromPath
  3. GetRestrictedIDFromFaviconUrl
  4. GetRestrictedIDFromThumbnailUrl
  5. start_margin_
  6. LogEvent
  7. LogMostVisitedImpression
  8. LogMostVisitedNavigation
  9. CheckIsUserSignedInToChromeAs
  10. DeleteMostVisitedItem
  11. GenerateFaviconURLFromTransientURL
  12. GenerateThumbnailURLFromTransientURL
  13. GetMostVisitedItems
  14. GetMostVisitedItemWithID
  15. GetThemeBackgroundInfo
  16. Focus
  17. NavigateToURL
  18. Paste
  19. SetVoiceSearchSupported
  20. StartCapturingKeyStrokes
  21. StopCapturingKeyStrokes
  22. UndoAllMostVisitedDeletions
  23. UndoMostVisitedDeletion
  24. OnMessageReceived
  25. OnChromeIdentityCheckResult
  26. OnDetermineIfPageSupportsInstant
  27. OnFocusChanged
  28. OnMarginChange
  29. OnMostVisitedChanged
  30. OnPromoInformationReceived
  31. OnSetDisplayInstantResults
  32. OnSetInputInProgress
  33. OnSetSuggestionToPrefetch
  34. OnSubmit
  35. OnThemeChanged
  36. OnToggleVoiceSearch
  37. GetURLForMostVisitedItem
  38. Reset

// 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/renderer/searchbox/searchbox.h"

#include <string>

#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/favicon/favicon_types.h"
#include "chrome/common/favicon/favicon_url_parser.h"
#include "chrome/common/omnibox_focus_state.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h"
#include "chrome/renderer/searchbox/searchbox_extension.h"
#include "content/public/renderer/render_view.h"
#include "grit/renderer_resources.h"
#include "net/base/escape.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "ui/base/resource/resource_bundle.h"
#include "url/gurl.h"

namespace {

// The size of the InstantMostVisitedItem cache.
const size_t kMaxInstantMostVisitedItemCacheSize = 100;

// Returns true if items stored in |old_item_id_pairs| and |new_items| are
// equal.
bool AreMostVisitedItemsEqual(
    const std::vector<InstantMostVisitedItemIDPair>& old_item_id_pairs,
    const std::vector<InstantMostVisitedItem>& new_items) {
  if (old_item_id_pairs.size() != new_items.size())
    return false;

  for (size_t i = 0; i < new_items.size(); ++i) {
    if (new_items[i].url != old_item_id_pairs[i].second.url ||
        new_items[i].title != old_item_id_pairs[i].second.title) {
      return false;
    }
  }
  return true;
}

}  // namespace

namespace internal {  // for testing

// Parses |path| and fills in |id| with the InstantRestrictedID obtained from
// the |path|. |render_view_id| is the ID of the associated RenderView.
//
// |path| is a pair of |render_view_id| and |restricted_id|, and it is
// contained in Instant Extended URLs. A valid |path| is in the form:
// <render_view_id>/<restricted_id>
//
// If the |path| is valid, returns true and fills in |id| with restricted_id
// value. If the |path| is invalid, returns false and |id| is not set.
bool GetInstantRestrictedIDFromPath(int render_view_id,
                                    const std::string& path,
                                    InstantRestrictedID* id) {
  // Check that the path is of Most visited item ID form.
  std::vector<std::string> tokens;
  if (Tokenize(path, "/", &tokens) != 2)
    return false;

  int view_id = 0;
  if (!base::StringToInt(tokens[0], &view_id) || view_id != render_view_id)
    return false;
  return base::StringToInt(tokens[1], id);
}

bool GetRestrictedIDFromFaviconUrl(int render_view_id,
                                   const GURL& url,
                                   std::string* favicon_params,
                                   InstantRestrictedID* rid) {
  // Strip leading slash.
  std::string raw_path = url.path();
  DCHECK_GT(raw_path.length(), (size_t) 0);
  DCHECK_EQ(raw_path[0], '/');
  raw_path = raw_path.substr(1);

  chrome::ParsedFaviconPath parsed;
  if (!chrome::ParseFaviconPath(raw_path, chrome::FAVICON, &parsed))
    return false;

  // The part of the URL which details the favicon parameters should be returned
  // so the favicon URL can be reconstructed, by replacing the restricted_id
  // with the actual URL from which the favicon is being requested.
  *favicon_params = raw_path.substr(0, parsed.path_index);

  // The part of the favicon URL which is supposed to contain the URL from
  // which the favicon is being requested (i.e., the page's URL) actually
  // contains a pair in the format "<view_id>/<restricted_id>". If the page's
  // URL is not in the expected format then the execution must be stopped,
  // returning |true|, indicating that the favicon URL should be translated
  // without the page's URL part, to prevent search providers from spoofing
  // the user's browsing history. For example, the following favicon URL
  // "chrome-search://favicon/http://www.secretsite.com" it is not in the
  // expected format "chrome-search://favicon/<view_id>/<restricted_id>" so
  // the pages's URL part ("http://www.secretsite.com") should be removed
  // entirely from the translated URL otherwise the search engine would know
  // if the user has visited that page (by verifying whether the favicon URL
  // returns an image for a particular page's URL); the translated URL in this
  // case would be "chrome-search://favicon/" which would simply return the
  // default favicon.
  std::string id_part = raw_path.substr(parsed.path_index);
  InstantRestrictedID id;
  if (!GetInstantRestrictedIDFromPath(render_view_id, id_part, &id))
    return true;

  *rid = id;
  return true;
}

// Parses a thumbnail |url| and fills in |id| with the InstantRestrictedID
// obtained from the |url|. |render_view_id| is the ID of the associated
// RenderView.
//
// Valid |url| forms:
// chrome-search://thumb/<view_id>/<restricted_id>
//
// If the |url| is valid, returns true and fills in |id| with restricted_id
// value. If the |url| is invalid, returns false and |id| is not set.
bool GetRestrictedIDFromThumbnailUrl(int render_view_id,
                                     const GURL& url,
                                     InstantRestrictedID* id) {
  // Strip leading slash.
  std::string path = url.path();
  DCHECK_GT(path.length(), (size_t) 0);
  DCHECK_EQ(path[0], '/');
  path = path.substr(1);

  return GetInstantRestrictedIDFromPath(render_view_id, path, id);
}

}  // namespace internal

SearchBox::SearchBox(content::RenderView* render_view)
    : content::RenderViewObserver(render_view),
      content::RenderViewObserverTracker<SearchBox>(render_view),
    app_launcher_enabled_(false),
    is_focused_(false),
    is_input_in_progress_(false),
    is_key_capture_enabled_(false),
    display_instant_results_(false),
    most_visited_items_cache_(kMaxInstantMostVisitedItemCacheSize),
    query_(),
    start_margin_(0) {
}

SearchBox::~SearchBox() {
}

void SearchBox::LogEvent(NTPLoggingEventType event) {
  render_view()->Send(new ChromeViewHostMsg_LogEvent(
      render_view()->GetRoutingID(), render_view()->GetPageId(), event));
}

void SearchBox::LogMostVisitedImpression(int position,
                                         const base::string16& provider) {
  render_view()->Send(new ChromeViewHostMsg_LogMostVisitedImpression(
      render_view()->GetRoutingID(), render_view()->GetPageId(), position,
      provider));
}

void SearchBox::LogMostVisitedNavigation(int position,
                                         const base::string16& provider) {
  render_view()->Send(new ChromeViewHostMsg_LogMostVisitedNavigation(
      render_view()->GetRoutingID(), render_view()->GetPageId(), position,
      provider));
}

void SearchBox::CheckIsUserSignedInToChromeAs(const base::string16& identity) {
  render_view()->Send(new ChromeViewHostMsg_ChromeIdentityCheck(
      render_view()->GetRoutingID(), render_view()->GetPageId(), identity));
}

void SearchBox::DeleteMostVisitedItem(
    InstantRestrictedID most_visited_item_id) {
  render_view()->Send(new ChromeViewHostMsg_SearchBoxDeleteMostVisitedItem(
      render_view()->GetRoutingID(), render_view()->GetPageId(),
      GetURLForMostVisitedItem(most_visited_item_id)));
}

bool SearchBox::GenerateFaviconURLFromTransientURL(const GURL& transient_url,
                                                   GURL* url) const {
  std::string favicon_params;
  InstantRestrictedID rid = -1;
  bool success = internal::GetRestrictedIDFromFaviconUrl(
      render_view()->GetRoutingID(), transient_url, &favicon_params, &rid);
  if (!success)
    return false;

  InstantMostVisitedItem item;
  std::string item_url;
  if (rid != -1 && GetMostVisitedItemWithID(rid, &item))
    item_url = item.url.spec();

  *url = GURL(base::StringPrintf("chrome-search://favicon/%s%s",
                                 favicon_params.c_str(),
                                 item_url.c_str()));
  return true;
}

bool SearchBox::GenerateThumbnailURLFromTransientURL(const GURL& transient_url,
                                                     GURL* url) const {
  InstantRestrictedID rid = 0;
  if (!internal::GetRestrictedIDFromThumbnailUrl(render_view()->GetRoutingID(),
                                                 transient_url, &rid)) {
    return false;
  }

  GURL most_visited_item_url(GetURLForMostVisitedItem(rid));
  if (most_visited_item_url.is_empty())
    return false;
  *url = GURL(base::StringPrintf("chrome-search://thumb/%s",
                                 most_visited_item_url.spec().c_str()));
  return true;
}

void SearchBox::GetMostVisitedItems(
    std::vector<InstantMostVisitedItemIDPair>* items) const {
  return most_visited_items_cache_.GetCurrentItems(items);
}

bool SearchBox::GetMostVisitedItemWithID(
    InstantRestrictedID most_visited_item_id,
    InstantMostVisitedItem* item) const {
  return most_visited_items_cache_.GetItemWithRestrictedID(most_visited_item_id,
                                                           item);
}

const ThemeBackgroundInfo& SearchBox::GetThemeBackgroundInfo() {
  return theme_info_;
}

void SearchBox::Focus() {
  render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
      render_view()->GetRoutingID(), render_view()->GetPageId(),
      OMNIBOX_FOCUS_VISIBLE));
}

void SearchBox::NavigateToURL(const GURL& url,
                              WindowOpenDisposition disposition,
                              bool is_most_visited_item_url) {
  render_view()->Send(new ChromeViewHostMsg_SearchBoxNavigate(
      render_view()->GetRoutingID(), render_view()->GetPageId(), url,
      disposition, is_most_visited_item_url));
}

void SearchBox::Paste(const base::string16& text) {
  render_view()->Send(new ChromeViewHostMsg_PasteAndOpenDropdown(
      render_view()->GetRoutingID(), render_view()->GetPageId(), text));
}

void SearchBox::SetVoiceSearchSupported(bool supported) {
  render_view()->Send(new ChromeViewHostMsg_SetVoiceSearchSupported(
      render_view()->GetRoutingID(), render_view()->GetPageId(), supported));
}

void SearchBox::StartCapturingKeyStrokes() {
  render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
      render_view()->GetRoutingID(), render_view()->GetPageId(),
      OMNIBOX_FOCUS_INVISIBLE));
}

void SearchBox::StopCapturingKeyStrokes() {
  render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
      render_view()->GetRoutingID(), render_view()->GetPageId(),
      OMNIBOX_FOCUS_NONE));
}

void SearchBox::UndoAllMostVisitedDeletions() {
  render_view()->Send(
      new ChromeViewHostMsg_SearchBoxUndoAllMostVisitedDeletions(
      render_view()->GetRoutingID(), render_view()->GetPageId()));
}

void SearchBox::UndoMostVisitedDeletion(
    InstantRestrictedID most_visited_item_id) {
  render_view()->Send(new ChromeViewHostMsg_SearchBoxUndoMostVisitedDeletion(
      render_view()->GetRoutingID(), render_view()->GetPageId(),
      GetURLForMostVisitedItem(most_visited_item_id)));
}

bool SearchBox::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(SearchBox, message)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_ChromeIdentityCheckResult,
                        OnChromeIdentityCheckResult)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_DetermineIfPageSupportsInstant,
                        OnDetermineIfPageSupportsInstant)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxFocusChanged, OnFocusChanged)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxMarginChange, OnMarginChange)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxMostVisitedItemsChanged,
                        OnMostVisitedChanged)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxPromoInformation,
                        OnPromoInformationReceived)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetDisplayInstantResults,
                        OnSetDisplayInstantResults)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetInputInProgress,
                        OnSetInputInProgress)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetSuggestionToPrefetch,
                        OnSetSuggestionToPrefetch)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSubmit, OnSubmit)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxThemeChanged,
                        OnThemeChanged)
    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxToggleVoiceSearch,
                        OnToggleVoiceSearch)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void SearchBox::OnChromeIdentityCheckResult(const base::string16& identity,
                                            bool identity_match) {
  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
    extensions_v8::SearchBoxExtension::DispatchChromeIdentityCheckResult(
        render_view()->GetWebView()->mainFrame(), identity, identity_match);
  }
}

void SearchBox::OnDetermineIfPageSupportsInstant() {
  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
    bool result = extensions_v8::SearchBoxExtension::PageSupportsInstant(
        render_view()->GetWebView()->mainFrame());
    DVLOG(1) << render_view() << " PageSupportsInstant: " << result;
    render_view()->Send(new ChromeViewHostMsg_InstantSupportDetermined(
        render_view()->GetRoutingID(), render_view()->GetPageId(), result));
  }
}

void SearchBox::OnFocusChanged(OmniboxFocusState new_focus_state,
                               OmniboxFocusChangeReason reason) {
  bool key_capture_enabled = new_focus_state == OMNIBOX_FOCUS_INVISIBLE;
  if (key_capture_enabled != is_key_capture_enabled_) {
    // Tell the page if the key capture mode changed unless the focus state
    // changed because of TYPING. This is because in that case, the browser
    // hasn't really stopped capturing key strokes.
    //
    // (More practically, if we don't do this check, the page would receive
    // onkeycapturechange before the corresponding onchange, and the page would
    // have no way of telling whether the keycapturechange happened because of
    // some actual user action or just because they started typing.)
    if (reason != OMNIBOX_FOCUS_CHANGE_TYPING &&
        render_view()->GetWebView() &&
        render_view()->GetWebView()->mainFrame()) {
      is_key_capture_enabled_ = key_capture_enabled;
      DVLOG(1) << render_view() << " OnKeyCaptureChange";
      extensions_v8::SearchBoxExtension::DispatchKeyCaptureChange(
          render_view()->GetWebView()->mainFrame());
    }
  }
  bool is_focused = new_focus_state == OMNIBOX_FOCUS_VISIBLE;
  if (is_focused != is_focused_) {
    is_focused_ = is_focused;
    DVLOG(1) << render_view() << " OnFocusChange";
    if (render_view()->GetWebView() &&
        render_view()->GetWebView()->mainFrame()) {
      extensions_v8::SearchBoxExtension::DispatchFocusChange(
          render_view()->GetWebView()->mainFrame());
    }
  }
}

void SearchBox::OnMarginChange(int margin) {
  start_margin_ = margin;
  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
    extensions_v8::SearchBoxExtension::DispatchMarginChange(
        render_view()->GetWebView()->mainFrame());
  }
}

void SearchBox::OnMostVisitedChanged(
    const std::vector<InstantMostVisitedItem>& items) {
  std::vector<InstantMostVisitedItemIDPair> last_known_items;
  GetMostVisitedItems(&last_known_items);

  if (AreMostVisitedItemsEqual(last_known_items, items))
    return;  // Do not send duplicate onmostvisitedchange events.

  most_visited_items_cache_.AddItems(items);
  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
    extensions_v8::SearchBoxExtension::DispatchMostVisitedChanged(
        render_view()->GetWebView()->mainFrame());
  }
}

void SearchBox::OnPromoInformationReceived(bool is_app_launcher_enabled) {
  app_launcher_enabled_ = is_app_launcher_enabled;
}

void SearchBox::OnSetDisplayInstantResults(bool display_instant_results) {
  display_instant_results_ = display_instant_results;
}

void SearchBox::OnSetInputInProgress(bool is_input_in_progress) {
  if (is_input_in_progress_ != is_input_in_progress) {
    is_input_in_progress_ = is_input_in_progress;
    DVLOG(1) << render_view() << " OnSetInputInProgress";
    if (render_view()->GetWebView() &&
        render_view()->GetWebView()->mainFrame()) {
      if (is_input_in_progress_) {
        extensions_v8::SearchBoxExtension::DispatchInputStart(
            render_view()->GetWebView()->mainFrame());
      } else {
        extensions_v8::SearchBoxExtension::DispatchInputCancel(
            render_view()->GetWebView()->mainFrame());
      }
    }
  }
}

void SearchBox::OnSetSuggestionToPrefetch(const InstantSuggestion& suggestion) {
  suggestion_ = suggestion;
  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
    DVLOG(1) << render_view() << " OnSetSuggestionToPrefetch";
    extensions_v8::SearchBoxExtension::DispatchSuggestionChange(
        render_view()->GetWebView()->mainFrame());
  }
}

void SearchBox::OnSubmit(const base::string16& query) {
  query_ = query;
  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
    DVLOG(1) << render_view() << " OnSubmit";
    extensions_v8::SearchBoxExtension::DispatchSubmit(
        render_view()->GetWebView()->mainFrame());
  }
  if (!query.empty())
    Reset();
}

void SearchBox::OnThemeChanged(const ThemeBackgroundInfo& theme_info) {
  // Do not send duplicate notifications.
  if (theme_info_ == theme_info)
    return;

  theme_info_ = theme_info;
  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
    extensions_v8::SearchBoxExtension::DispatchThemeChange(
        render_view()->GetWebView()->mainFrame());
  }
}

void SearchBox::OnToggleVoiceSearch() {
  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
    extensions_v8::SearchBoxExtension::DispatchToggleVoiceSearch(
        render_view()->GetWebView()->mainFrame());
  }
}

GURL SearchBox::GetURLForMostVisitedItem(InstantRestrictedID item_id) const {
  InstantMostVisitedItem item;
  return GetMostVisitedItemWithID(item_id, &item) ? item.url : GURL();
}

void SearchBox::Reset() {
  query_.clear();
  suggestion_ = InstantSuggestion();
  start_margin_ = 0;
  is_focused_ = false;
  is_key_capture_enabled_ = false;
  theme_info_ = ThemeBackgroundInfo();
}

/* [<][>][^][v][top][bottom][index][help] */