1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/webui/new_tab_page_sync_handler.h"
6
7#include <vector>
8
9#include "base/callback.h"
10#include "base/string_split.h"
11#include "base/string_util.h"
12#include "base/utf_string_conversions.h"
13#include "base/values.h"
14#include "chrome/browser/net/chrome_url_request_context.h"
15#include "chrome/browser/profiles/profile.h"
16#include "content/browser/renderer_host/render_view_host.h"
17#include "grit/generated_resources.h"
18#include "net/base/cookie_monster.h"
19#include "net/url_request/url_request_context.h"
20#include "ui/base/l10n/l10n_util.h"
21
22// Default URL for the sync web interface.
23//
24// TODO(idana): when we figure out how we are going to allow third parties to
25// plug in their own sync engine, we should allow this value to be
26// configurable.
27static const char kSyncDefaultViewOnlineUrl[] = "http://docs.google.com";
28
29// TODO(idana): the following code was originally copied from
30// toolbar_importer.h/cc and it needs to be moved to a common Google Accounts
31// utility.
32
33// A simple pair of fields that identify a set of Google cookies, used to
34// filter from a larger set.
35struct GoogleCookieFilter {
36  // The generalized, fully qualified URL of pages where
37  // cookies with id |cookie_id| are obtained / accessed.
38  const char* url;
39  // The id of the cookie this filter is selecting,
40  // with name/value delimiter (i.e '=').
41  const char* cookie_id;
42};
43
44// Filters to select Google GAIA cookies.
45static const GoogleCookieFilter kGAIACookieFilters[] = {
46  { "http://.google.com/",       "SID=" },     // Gmail.
47  // Add filters here for other interesting cookies that should result in
48  // showing the promotions (e.g ASIDAS for dasher accounts).
49};
50
51bool IsGoogleGAIACookieInstalled() {
52  for (size_t i = 0; i < arraysize(kGAIACookieFilters); ++i) {
53    // Since we are running on the UI thread don't call GetURLRequestContext().
54    net::CookieStore* store =
55        Profile::GetDefaultRequestContext()->DONTUSEME_GetCookieStore();
56    GURL url(kGAIACookieFilters[i].url);
57    net::CookieOptions options;
58    options.set_include_httponly();  // The SID cookie might be httponly.
59    std::string cookies = store->GetCookiesWithOptions(url, options);
60    std::vector<std::string> cookie_list;
61    base::SplitString(cookies, ';', &cookie_list);
62    for (std::vector<std::string>::iterator current = cookie_list.begin();
63         current != cookie_list.end();
64         ++current) {
65      size_t position =
66          current->find(kGAIACookieFilters[i].cookie_id);
67      if (0 == position)
68        return true;
69    }
70  }
71  return false;
72}
73
74NewTabPageSyncHandler::NewTabPageSyncHandler() : sync_service_(NULL),
75  waiting_for_initial_page_load_(true) {
76}
77
78NewTabPageSyncHandler::~NewTabPageSyncHandler() {
79  if (sync_service_)
80    sync_service_->RemoveObserver(this);
81}
82
83// static
84NewTabPageSyncHandler::MessageType
85    NewTabPageSyncHandler::FromSyncStatusMessageType(
86        sync_ui_util::MessageType type) {
87  switch (type) {
88    case sync_ui_util::SYNC_ERROR:
89      return SYNC_ERROR;
90    case sync_ui_util::SYNC_PROMO:
91      return SYNC_PROMO;
92    case sync_ui_util::PRE_SYNCED:
93    case sync_ui_util::SYNCED:
94    default:
95      return HIDE;
96  }
97}
98
99WebUIMessageHandler* NewTabPageSyncHandler::Attach(WebUI* web_ui) {
100  sync_service_ = web_ui->GetProfile()->GetProfileSyncService();
101  DCHECK(sync_service_);  // This shouldn't get called by an incognito NTP.
102  DCHECK(!sync_service_->IsManaged());  // And neither if sync is managed.
103  sync_service_->AddObserver(this);
104  return WebUIMessageHandler::Attach(web_ui);
105}
106
107void NewTabPageSyncHandler::RegisterMessages() {
108  web_ui_->RegisterMessageCallback("GetSyncMessage",
109      NewCallback(this, &NewTabPageSyncHandler::HandleGetSyncMessage));
110  web_ui_->RegisterMessageCallback("SyncLinkClicked",
111      NewCallback(this, &NewTabPageSyncHandler::HandleSyncLinkClicked));
112}
113
114void NewTabPageSyncHandler::HandleGetSyncMessage(const ListValue* args) {
115  waiting_for_initial_page_load_ = false;
116  BuildAndSendSyncStatus();
117}
118
119void NewTabPageSyncHandler::HideSyncStatusSection() {
120  SendSyncMessageToPage(HIDE, std::string(), std::string());
121}
122
123void NewTabPageSyncHandler::BuildAndSendSyncStatus() {
124  DCHECK(!waiting_for_initial_page_load_);
125
126  // Hide the sync status section if sync is managed or disabled entirely.
127  if (!sync_service_ || sync_service_->IsManaged()) {
128    HideSyncStatusSection();
129    return;
130  }
131
132  // Don't show sync status if setup is not complete.
133  if (!sync_service_->HasSyncSetupCompleted()) {
134    return;
135  }
136
137  // Once sync has been enabled, the supported "sync statuses" for the NNTP
138  // from the user's perspective are:
139  //
140  // "Sync error", when we can't authenticate or establish a connection with
141  //               the sync server (appropriate information appended to
142  //               message).
143  string16 status_msg;
144  string16 link_text;
145  sync_ui_util::MessageType type =
146      sync_ui_util::GetStatusLabelsForNewTabPage(sync_service_,
147                                                 &status_msg,
148                                                 &link_text);
149  SendSyncMessageToPage(FromSyncStatusMessageType(type),
150                        UTF16ToUTF8(status_msg), UTF16ToUTF8(link_text));
151}
152
153void NewTabPageSyncHandler::HandleSyncLinkClicked(const ListValue* args) {
154  DCHECK(!waiting_for_initial_page_load_);
155  DCHECK(sync_service_);
156  if (!sync_service_->IsSyncEnabled())
157    return;
158  if (sync_service_->HasSyncSetupCompleted()) {
159    sync_service_->ShowErrorUI(NULL);
160    DictionaryValue value;
161    value.SetString("syncEnabledMessage",
162                    l10n_util::GetStringFUTF16(IDS_SYNC_NTP_SYNCED_TO,
163                        sync_service_->GetAuthenticatedUsername()));
164    web_ui_->CallJavascriptFunction("syncAlreadyEnabled", value);
165  } else {
166    // User clicked the 'Start now' link to begin syncing.
167    ProfileSyncService::SyncEvent(ProfileSyncService::START_FROM_NTP);
168    sync_service_->ShowLoginDialog(NULL);
169  }
170}
171
172void NewTabPageSyncHandler::OnStateChanged() {
173  // Don't do anything if the page has not yet loaded.
174  if (waiting_for_initial_page_load_)
175    return;
176  BuildAndSendSyncStatus();
177}
178
179void NewTabPageSyncHandler::SendSyncMessageToPage(
180    MessageType type, std::string msg,
181    std::string linktext) {
182  DictionaryValue value;
183  std::string user;
184  std::string title;
185  std::string linkurl;
186
187  // If there is nothing to show, we should hide the sync section altogether.
188  if (type == HIDE || (msg.empty() && linktext.empty())) {
189    value.SetBoolean("syncsectionisvisible", false);
190  } else {
191    if (type == SYNC_ERROR)
192      title = l10n_util::GetStringUTF8(IDS_SYNC_NTP_SYNC_SECTION_ERROR_TITLE);
193    else if (type == SYNC_PROMO)
194      title = l10n_util::GetStringUTF8(IDS_SYNC_NTP_SYNC_SECTION_PROMO_TITLE);
195    else
196      NOTREACHED();
197
198    value.SetBoolean("syncsectionisvisible", true);
199    value.SetString("msg", msg);
200    value.SetString("title", title);
201    if (linktext.empty()) {
202      value.SetBoolean("linkisvisible", false);
203    } else {
204      value.SetBoolean("linkisvisible", true);
205      value.SetString("linktext", linktext);
206
207      // The only time we set the URL is when the user is synced and we need to
208      // show a link to a web interface (e.g. http://docs.google.com). When we
209      // set that URL, HandleSyncLinkClicked won't be called when the user
210      // clicks on the link.
211      if (linkurl.empty()) {
212        value.SetBoolean("linkurlisset", false);
213      } else {
214        value.SetBoolean("linkurlisset", true);
215        value.SetString("linkurl", linkurl);
216      }
217    }
218  }
219  web_ui_->CallJavascriptFunction("syncMessageChanged", value);
220}
221