help_handler.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright (c) 2012 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/help/help_handler.h"
6
7#include <string>
8
9#include "base/basictypes.h"
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/command_line.h"
13#include "base/strings/string16.h"
14#include "base/strings/utf_string_conversions.h"
15#include "base/values.h"
16#include "chrome/browser/browser_process.h"
17#include "chrome/browser/google/google_util.h"
18#include "chrome/browser/policy/browser_policy_connector.h"
19#include "chrome/browser/ui/browser.h"
20#include "chrome/browser/ui/browser_commands.h"
21#include "chrome/browser/ui/browser_finder.h"
22#include "chrome/browser/ui/chrome_pages.h"
23#include "chrome/browser/ui/send_feedback_experiment.h"
24#include "chrome/common/chrome_notification_types.h"
25#include "chrome/common/chrome_version_info.h"
26#include "chrome/common/url_constants.h"
27#include "content/public/browser/browser_thread.h"
28#include "content/public/browser/notification_service.h"
29#include "content/public/browser/web_ui.h"
30#include "content/public/browser/web_ui_data_source.h"
31#include "content/public/common/content_client.h"
32#include "grit/chromium_strings.h"
33#include "grit/generated_resources.h"
34#include "grit/google_chrome_strings.h"
35#include "ui/base/l10n/l10n_util.h"
36#include "ui/base/resource/resource_bundle.h"
37#include "v8/include/v8.h"
38#include "webkit/common/user_agent/user_agent_util.h"
39#include "webkit/glue/webkit_glue.h"
40
41#if defined(OS_CHROMEOS)
42#include "base/files/file_util_proxy.h"
43#include "base/i18n/time_formatting.h"
44#include "base/prefs/pref_service.h"
45#include "base/sys_info.h"
46#include "chrome/browser/chromeos/login/user_manager.h"
47#include "chrome/browser/chromeos/settings/cros_settings.h"
48#include "chrome/browser/profiles/profile.h"
49#include "chrome/browser/ui/webui/help/help_utils_chromeos.h"
50#include "content/public/browser/browser_thread.h"
51#endif
52
53using base::ListValue;
54using content::BrowserThread;
55
56namespace {
57
58const char kResourceReportIssue[] = "reportAnIssue";
59
60// Returns the browser version as a string.
61string16 BuildBrowserVersionString() {
62  chrome::VersionInfo version_info;
63  DCHECK(version_info.is_valid());
64
65  std::string browser_version = version_info.Version();
66  std::string version_modifier =
67      chrome::VersionInfo::GetVersionStringModifier();
68  if (!version_modifier.empty())
69    browser_version += " " + version_modifier;
70
71#if !defined(GOOGLE_CHROME_BUILD)
72  browser_version += " (";
73  browser_version += version_info.LastChange();
74  browser_version += ")";
75#endif
76
77  return UTF8ToUTF16(browser_version);
78}
79
80#if defined(OS_CHROMEOS)
81
82// Returns message that informs user that for update it's better to
83// connect to a network of one of the allowed types.
84string16 GetAllowedConnectionTypesMessage() {
85  if (help_utils_chromeos::IsUpdateOverCellularAllowed()) {
86    return l10n_util::GetStringUTF16(IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED);
87  } else {
88    return l10n_util::GetStringUTF16(
89        IDS_UPGRADE_NETWORK_LIST_CELLULAR_DISALLOWED);
90  }
91}
92
93bool CanChangeReleaseChannel() {
94  // On non managed machines we have local owner who is the only one to change
95  // anything.
96  if (chromeos::UserManager::Get()->IsCurrentUserOwner())
97    return true;
98  // On a managed machine we delegate this setting to the users of the same
99  // domain only if the policy value is "domain".
100  if (g_browser_process->browser_policy_connector()->IsEnterpriseManaged()) {
101    bool value = false;
102    if (!chromeos::CrosSettings::Get()->GetBoolean(
103            chromeos::kReleaseChannelDelegated, &value) || !value)
104      return false;
105    // Get the currently logged in user and strip the domain part only.
106    std::string domain = "";
107    std::string user = chromeos::UserManager::Get()->GetLoggedInUser()->email();
108    size_t at_pos = user.find('@');
109    if (at_pos != std::string::npos && at_pos + 1 < user.length())
110      domain = user.substr(user.find('@') + 1);
111    return domain == g_browser_process->browser_policy_connector()->
112        GetEnterpriseDomain();
113  }
114  return false;
115}
116
117// Pointer to a |StringValue| holding the date of the build date to Chromium
118// OS. Because this value is obtained by reading a file, it is cached here to
119// prevent the need to read from the file system multiple times unnecessarily.
120Value* g_build_date_string = NULL;
121
122#endif  // defined(OS_CHROMEOS)
123
124}  // namespace
125
126HelpHandler::HelpHandler()
127    : version_updater_(VersionUpdater::Create()),
128      weak_factory_(this) {
129}
130
131HelpHandler::~HelpHandler() {
132}
133
134void HelpHandler::GetLocalizedValues(content::WebUIDataSource* source) {
135  struct L10nResources {
136    const char* name;
137    int ids;
138  };
139
140  static L10nResources resources[] = {
141    { "helpTitle", IDS_HELP_TITLE },
142    { "aboutTitle", IDS_ABOUT_TAB_TITLE },
143#if defined(OS_CHROMEOS)
144    { "aboutProductTitle", IDS_PRODUCT_OS_NAME },
145#else
146    { "aboutProductTitle", IDS_PRODUCT_NAME },
147#endif
148    { "aboutProductDescription", IDS_ABOUT_PRODUCT_DESCRIPTION },
149    { "relaunch", IDS_RELAUNCH_BUTTON },
150    { "productName", IDS_PRODUCT_NAME },
151    { "productCopyright", IDS_ABOUT_VERSION_COPYRIGHT },
152    { "updateCheckStarted", IDS_UPGRADE_CHECK_STARTED },
153    { "upToDate", IDS_UPGRADE_UP_TO_DATE },
154    { "updating", IDS_UPGRADE_UPDATING },
155    { "updateAlmostDone", IDS_UPGRADE_SUCCESSFUL_RELAUNCH },
156    { "getHelpWithChrome", IDS_GET_HELP_USING_CHROME },
157    { kResourceReportIssue, IDS_REPORT_AN_ISSUE },
158#if defined(OS_CHROMEOS)
159    { "platform", IDS_PLATFORM_LABEL },
160    { "firmware", IDS_ABOUT_PAGE_FIRMWARE },
161    { "showMoreInfo", IDS_SHOW_MORE_INFO },
162    { "hideMoreInfo", IDS_HIDE_MORE_INFO },
163    { "channel", IDS_ABOUT_PAGE_CHANNEL },
164    { "stable", IDS_ABOUT_PAGE_CHANNEL_STABLE },
165    { "beta", IDS_ABOUT_PAGE_CHANNEL_BETA },
166    { "dev", IDS_ABOUT_PAGE_CHANNEL_DEVELOPMENT },
167    { "channel-changed", IDS_ABOUT_PAGE_CHANNEL_CHANGED },
168    { "webkit", IDS_WEBKIT },
169    { "userAgent", IDS_ABOUT_VERSION_USER_AGENT },
170    { "commandLine", IDS_ABOUT_VERSION_COMMAND_LINE },
171    { "buildDate", IDS_ABOUT_VERSION_BUILD_DATE },
172#endif
173#if defined(OS_MACOSX)
174    { "promote", IDS_ABOUT_CHROME_PROMOTE_UPDATER },
175    { "learnMore", IDS_LEARN_MORE },
176#endif
177  };
178
179  if (chrome::UseAlternateSendFeedbackText()) {
180    // Field trial to substitute "Report an Issue" with "Send Feedback".
181    // (crbug.com/169339)
182    std::string report_issue_key(kResourceReportIssue);
183    for (size_t i = 0; i < ARRAYSIZE_UNSAFE(resources); ++i) {
184      if (report_issue_key == resources[i].name)
185        resources[i].ids = IDS_SEND_FEEDBACK;
186    }
187  }
188
189  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(resources); ++i) {
190    source->AddString(resources[i].name,
191                      l10n_util::GetStringUTF16(resources[i].ids));
192  }
193
194  source->AddString(
195      "browserVersion",
196      l10n_util::GetStringFUTF16(IDS_ABOUT_PRODUCT_VERSION,
197                                 BuildBrowserVersionString()));
198
199  string16 license = l10n_util::GetStringFUTF16(
200      IDS_ABOUT_VERSION_LICENSE,
201      ASCIIToUTF16(chrome::kChromiumProjectURL),
202      ASCIIToUTF16(chrome::kChromeUICreditsURL));
203  source->AddString("productLicense", license);
204
205#if defined(OS_CHROMEOS)
206  string16 os_license = l10n_util::GetStringFUTF16(
207      IDS_ABOUT_CROS_VERSION_LICENSE,
208      ASCIIToUTF16(chrome::kChromeUIOSCreditsURL));
209  source->AddString("productOsLicense", os_license);
210#endif
211
212  string16 tos = l10n_util::GetStringFUTF16(
213      IDS_ABOUT_TERMS_OF_SERVICE, UTF8ToUTF16(chrome::kChromeUITermsURL));
214  source->AddString("productTOS", tos);
215
216  source->AddString("webkitVersion", webkit_glue::GetWebKitVersion());
217
218  source->AddString("jsEngine", "V8");
219  source->AddString("jsEngineVersion", v8::V8::GetVersion());
220
221  source->AddString("userAgentInfo", content::GetUserAgent(GURL()));
222
223  CommandLine::StringType command_line =
224      CommandLine::ForCurrentProcess()->GetCommandLineString();
225  source->AddString("commandLineInfo", command_line);
226}
227
228void HelpHandler::RegisterMessages() {
229  registrar_.Add(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED,
230                 content::NotificationService::AllSources());
231
232  web_ui()->RegisterMessageCallback("onPageLoaded",
233      base::Bind(&HelpHandler::OnPageLoaded, base::Unretained(this)));
234  web_ui()->RegisterMessageCallback("relaunchNow",
235      base::Bind(&HelpHandler::RelaunchNow, base::Unretained(this)));
236  web_ui()->RegisterMessageCallback("openFeedbackDialog",
237      base::Bind(&HelpHandler::OpenFeedbackDialog, base::Unretained(this)));
238  web_ui()->RegisterMessageCallback("openHelpPage",
239      base::Bind(&HelpHandler::OpenHelpPage, base::Unretained(this)));
240#if defined(OS_CHROMEOS)
241  web_ui()->RegisterMessageCallback("setReleaseTrack",
242      base::Bind(&HelpHandler::SetReleaseTrack, base::Unretained(this)));
243#endif
244#if defined(OS_MACOSX)
245  web_ui()->RegisterMessageCallback("promoteUpdater",
246      base::Bind(&HelpHandler::PromoteUpdater, base::Unretained(this)));
247#endif
248}
249
250void HelpHandler::Observe(int type, const content::NotificationSource& source,
251                          const content::NotificationDetails& details) {
252  switch (type) {
253    case chrome::NOTIFICATION_UPGRADE_RECOMMENDED: {
254      // A version update is installed and ready to go. Refresh the UI so the
255      // correct state will be shown.
256      version_updater_->CheckForUpdate(
257          base::Bind(&HelpHandler::SetUpdateStatus, base::Unretained(this))
258#if defined(OS_MACOSX)
259          , base::Bind(&HelpHandler::SetPromotionState, base::Unretained(this))
260#endif
261          );
262      break;
263    }
264    default:
265      NOTREACHED();
266  }
267}
268
269void HelpHandler::OnPageLoaded(const ListValue* args) {
270#if defined(OS_CHROMEOS)
271  // Version information is loaded from a callback
272  loader_.GetVersion(
273      chromeos::VersionLoader::VERSION_FULL,
274      base::Bind(&HelpHandler::OnOSVersion, base::Unretained(this)),
275      &tracker_);
276  loader_.GetFirmware(
277      base::Bind(&HelpHandler::OnOSFirmware, base::Unretained(this)),
278      &tracker_);
279
280  web_ui()->CallJavascriptFunction(
281      "help.HelpPage.updateEnableReleaseChannel",
282      base::FundamentalValue(CanChangeReleaseChannel()));
283
284  if (g_build_date_string == NULL) {
285    // If |g_build_date_string| is |NULL|, the date has not yet been assigned.
286    // Get the date of the last lsb-release file modification.
287    base::FileUtilProxy::GetFileInfo(
288        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
289        base::SysInfo::GetLsbReleaseFilePath(),
290        base::Bind(&HelpHandler::ProcessLsbFileInfo,
291                   weak_factory_.GetWeakPtr()));
292  } else {
293    web_ui()->CallJavascriptFunction("help.HelpPage.setBuildDate",
294                                     *g_build_date_string);
295  }
296#endif  // defined(OS_CHROMEOS)
297
298  version_updater_->CheckForUpdate(
299      base::Bind(&HelpHandler::SetUpdateStatus, base::Unretained(this))
300#if defined(OS_MACOSX)
301      , base::Bind(&HelpHandler::SetPromotionState, base::Unretained(this))
302#endif
303      );
304
305#if defined(OS_CHROMEOS)
306  version_updater_->GetReleaseChannel(
307      base::Bind(&HelpHandler::OnReleaseChannel, base::Unretained(this)));
308#endif
309}
310
311#if defined(OS_MACOSX)
312void HelpHandler::PromoteUpdater(const ListValue* args) {
313  version_updater_->PromoteUpdater();
314}
315#endif
316
317void HelpHandler::RelaunchNow(const ListValue* args) {
318  DCHECK(args->empty());
319  version_updater_->RelaunchBrowser();
320}
321
322void HelpHandler::OpenFeedbackDialog(const ListValue* args) {
323  DCHECK(args->empty());
324  Browser* browser = chrome::FindBrowserWithWebContents(
325      web_ui()->GetWebContents());
326  chrome::OpenFeedbackDialog(browser);
327}
328
329void HelpHandler::OpenHelpPage(const base::ListValue* args) {
330  DCHECK(args->empty());
331  Browser* browser = chrome::FindBrowserWithWebContents(
332      web_ui()->GetWebContents());
333  chrome::ShowHelp(browser, chrome::HELP_SOURCE_WEBUI);
334}
335
336#if defined(OS_CHROMEOS)
337
338void HelpHandler::SetReleaseTrack(const ListValue* args) {
339  if (!CanChangeReleaseChannel()) {
340    LOG(WARNING) << "Non-owner tried to change release track.";
341    return;
342  }
343
344  const std::string channel = UTF16ToUTF8(ExtractStringValue(args));
345  version_updater_->SetReleaseChannel(channel);
346  // On enterprise machines we can only use SetReleaseChannel to store the
347  // user choice in the lsb-release file but we can not modify the policy blob.
348  // Therefore we only call SetString if the device is locally owned and the
349  // currently logged in user is the owner.
350  if (chromeos::UserManager::Get()->IsCurrentUserOwner()) {
351    chromeos::CrosSettings::Get()->SetString(chromeos::kReleaseChannel,
352                                             channel);
353    // Check for update after switching release channel.
354    version_updater_->CheckForUpdate(base::Bind(&HelpHandler::SetUpdateStatus,
355                                                base::Unretained(this)));
356  }
357}
358
359#endif  // defined(OS_CHROMEOS)
360
361void HelpHandler::SetUpdateStatus(VersionUpdater::Status status,
362                                  int progress, const string16& message) {
363  // Only UPDATING state should have progress set.
364  DCHECK(status == VersionUpdater::UPDATING || progress == 0);
365
366  std::string status_str;
367  switch (status) {
368  case VersionUpdater::CHECKING:
369    status_str = "checking";
370    break;
371  case VersionUpdater::UPDATING:
372    status_str = "updating";
373    break;
374  case VersionUpdater::NEARLY_UPDATED:
375    status_str = "nearly_updated";
376    break;
377  case VersionUpdater::UPDATED:
378    status_str = "updated";
379    break;
380  case VersionUpdater::FAILED:
381  case VersionUpdater::FAILED_OFFLINE:
382  case VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED:
383    status_str = "failed";
384    break;
385  case VersionUpdater::DISABLED:
386    status_str = "disabled";
387    break;
388  }
389
390  web_ui()->CallJavascriptFunction("help.HelpPage.setUpdateStatus",
391                                   base::StringValue(status_str),
392                                   base::StringValue(message));
393
394  if (status == VersionUpdater::UPDATING) {
395    web_ui()->CallJavascriptFunction("help.HelpPage.setProgress",
396                                     base::FundamentalValue(progress));
397  }
398
399#if defined(OS_CHROMEOS)
400  if (status == VersionUpdater::FAILED_OFFLINE ||
401      status == VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED) {
402    string16 types_msg = GetAllowedConnectionTypesMessage();
403    if (!types_msg.empty()) {
404      web_ui()->CallJavascriptFunction(
405          "help.HelpPage.setAndShowAllowedConnectionTypesMsg",
406          base::StringValue(types_msg));
407    } else {
408      web_ui()->CallJavascriptFunction(
409          "help.HelpPage.showAllowedConnectionTypesMsg",
410          base::FundamentalValue(false));
411    }
412  } else {
413    web_ui()->CallJavascriptFunction(
414        "help.HelpPage.showAllowedConnectionTypesMsg",
415        base::FundamentalValue(false));
416  }
417#endif  // defined(OS_CHROMEOS)
418}
419
420#if defined(OS_MACOSX)
421void HelpHandler::SetPromotionState(VersionUpdater::PromotionState state) {
422  std::string state_str;
423  switch (state) {
424  case VersionUpdater::PROMOTE_HIDDEN:
425    state_str = "hidden";
426    break;
427  case VersionUpdater::PROMOTE_ENABLED:
428    state_str = "enabled";
429    break;
430  case VersionUpdater::PROMOTE_DISABLED:
431    state_str = "disabled";
432    break;
433  }
434
435  web_ui()->CallJavascriptFunction("help.HelpPage.setPromotionState",
436                                   base::StringValue(state_str));
437}
438#endif  // defined(OS_MACOSX)
439
440#if defined(OS_CHROMEOS)
441void HelpHandler::OnOSVersion(const std::string& version) {
442  web_ui()->CallJavascriptFunction("help.HelpPage.setOSVersion",
443                                   base::StringValue(version));
444}
445
446void HelpHandler::OnOSFirmware(const std::string& firmware) {
447  web_ui()->CallJavascriptFunction("help.HelpPage.setOSFirmware",
448                                   base::StringValue(firmware));
449}
450
451void HelpHandler::OnReleaseChannel(const std::string& channel) {
452  web_ui()->CallJavascriptFunction(
453      "help.HelpPage.updateSelectedChannel", base::StringValue(channel));
454}
455
456void HelpHandler::ProcessLsbFileInfo(
457    base::PlatformFileError error, const base::PlatformFileInfo& file_info) {
458  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
459
460  // If |g_build_date_string| is not |NULL|, then the file's information has
461  // already been retrieved by another tab.
462  if (g_build_date_string == NULL) {
463    base::Time time;
464    if (error == base::PLATFORM_FILE_OK) {
465      // Retrieves the time at which the Chrome OS build was created.
466      // Each time a new build is created, /etc/lsb-release is modified with the
467      // new version numbers of the release.
468      time = file_info.last_modified;
469    } else {
470      // If the time of the build cannot be retrieved, return and do not
471      // display the "Build Date" section.
472      return;
473    }
474
475    // Note that this string will be internationalized.
476    string16 build_date = base::TimeFormatFriendlyDate(time);
477    g_build_date_string = Value::CreateStringValue(build_date);
478  }
479
480  web_ui()->CallJavascriptFunction("help.HelpPage.setBuildDate",
481                                   *g_build_date_string);
482}
483#endif // defined(OS_CHROMEOS)
484