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/chromeos/offline/offline_load_page.h"
6
7#include "ash/shell.h"
8#include "ash/shell_delegate.h"
9#include "ash/system/tray/system_tray_delegate.h"
10#include "base/i18n/rtl.h"
11#include "base/metrics/histogram.h"
12#include "base/strings/string_piece.h"
13#include "base/strings/stringprintf.h"
14#include "base/strings/utf_string_conversions.h"
15#include "base/values.h"
16#include "chrome/browser/chrome_notification_types.h"
17#include "chrome/browser/extensions/extension_service.h"
18#include "chrome/browser/extensions/extension_system.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/renderer_preferences_util.h"
21#include "chrome/browser/tab_contents/tab_util.h"
22#include "chrome/common/extensions/extension.h"
23#include "chrome/common/extensions/extension_constants.h"
24#include "chrome/common/extensions/extension_icon_set.h"
25#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
26#include "chrome/common/url_constants.h"
27#include "content/public/browser/browser_thread.h"
28#include "content/public/browser/interstitial_page.h"
29#include "content/public/browser/notification_types.h"
30#include "content/public/browser/web_contents.h"
31#include "grit/browser_resources.h"
32#include "grit/chromium_strings.h"
33#include "grit/generated_resources.h"
34#include "grit/google_chrome_strings.h"
35#include "grit/theme_resources.h"
36#include "net/base/escape.h"
37#include "ui/base/l10n/l10n_util.h"
38#include "ui/base/resource/resource_bundle.h"
39#include "ui/webui/jstemplate_builder.h"
40#include "ui/webui/web_ui_util.h"
41
42using content::BrowserThread;
43using content::InterstitialPage;
44using content::WebContents;
45
46namespace {
47
48// Maximum time to show a blank page.
49const int kMaxBlankPeriod = 3000;
50
51// A utility function to set the dictionary's value given by |resource_id|.
52void SetString(DictionaryValue* strings, const char* name, int resource_id) {
53  strings->SetString(name, l10n_util::GetStringUTF16(resource_id));
54}
55
56}  // namespace
57
58namespace chromeos {
59
60OfflineLoadPage::OfflineLoadPage(WebContents* web_contents,
61                                 const GURL& url,
62                                 const CompletionCallback& callback)
63    : callback_(callback),
64      proceeded_(false),
65      web_contents_(web_contents),
66      url_(url) {
67  net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
68  interstitial_page_ = InterstitialPage::Create(web_contents, true, url, this);
69}
70
71OfflineLoadPage::~OfflineLoadPage() {
72  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
73  net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
74}
75
76void OfflineLoadPage::Show() {
77  interstitial_page_->Show();
78}
79
80std::string OfflineLoadPage::GetHTMLContents() {
81  DictionaryValue strings;
82  int64 time_to_wait = kMaxBlankPeriod;
83  // Set the timeout to show the page.
84  strings.SetInteger("time_to_wait", static_cast<int>(time_to_wait));
85
86  // Button labels
87  SetString(&strings, "msg", IDS_OFFLINE_LOAD_DESCRIPTION);
88  SetString(&strings, "network_settings", IDS_OFFLINE_NETWORK_SETTINGS);
89  SetString(&strings, "product_name", IDS_SHORT_PRODUCT_NAME_LOWER);
90
91  // Get the Chromium/Chrome icon, we can't access the icon via chrome://theme
92  // on the webpage since the interstitial page isn't a webui and doesn't have
93  // access to chrome:// URL's.
94  strings.SetString("icon",
95                    webui::GetBitmapDataUrlFromResource(IDR_PRODUCT_LOGO_32));
96
97  webui::SetFontAndTextDirection(&strings);
98  string16 failed_url(ASCIIToUTF16(url_.spec()));
99  if (base::i18n::IsRTL())
100    base::i18n::WrapStringWithLTRFormatting(&failed_url);
101  strings.SetString("url", failed_url);
102
103  // The offline page for app has icons and slightly different message.
104  Profile* profile = Profile::FromBrowserContext(
105      web_contents_->GetBrowserContext());
106  DCHECK(profile);
107  const extensions::Extension* extension = NULL;
108  ExtensionService* extensions_service =
109      extensions::ExtensionSystem::Get(profile)->extension_service();
110  // Extension service does not exist in test.
111  if (extensions_service)
112    extension = extensions_service->extensions()->GetHostedAppByURL(url_);
113
114  if (extension)
115    GetAppOfflineStrings(extension, &strings);
116  else
117    GetNormalOfflineStrings(&strings);
118
119  base::StringPiece html(
120      ResourceBundle::GetSharedInstance().GetRawDataResource(
121          IDR_OFFLINE_LOAD_HTML));
122  return webui::GetI18nTemplateHtml(html, &strings);
123}
124
125 void OfflineLoadPage::OverrideRendererPrefs(
126      content::RendererPreferences* prefs) {
127  Profile* profile = Profile::FromBrowserContext(
128      web_contents_->GetBrowserContext());
129  renderer_preferences_util::UpdateFromSystemSettings(prefs, profile);
130}
131
132void OfflineLoadPage::OnProceed() {
133  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
134  proceeded_ = true;
135  NotifyBlockingPageComplete(true);
136}
137
138void OfflineLoadPage::OnDontProceed() {
139  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
140  // Ignore if it's already proceeded.
141  if (proceeded_)
142    return;
143  NotifyBlockingPageComplete(false);
144}
145
146void OfflineLoadPage::GetAppOfflineStrings(
147    const extensions::Extension* app,
148    DictionaryValue* strings) const {
149  strings->SetString("title", app->name());
150  strings->SetString(
151      "heading", l10n_util::GetStringUTF16(IDS_APP_OFFLINE_LOAD_HEADLINE));
152}
153
154void OfflineLoadPage::GetNormalOfflineStrings(DictionaryValue* strings) const {
155  strings->SetString("title", web_contents_->GetTitle());
156  strings->SetString(
157      "heading", l10n_util::GetStringUTF16(IDS_SITE_OFFLINE_LOAD_HEADLINE));
158}
159
160void OfflineLoadPage::CommandReceived(const std::string& cmd) {
161  std::string command(cmd);
162  // The Jasonified response has quotes, remove them.
163  if (command.length() > 1 && command[0] == '"') {
164    command = command.substr(1, command.length() - 2);
165  }
166  // TODO(oshima): record action for metrics.
167  if (command == "open_network_settings") {
168    ash::Shell::GetInstance()->system_tray_delegate()->ShowNetworkSettings("");
169  } else {
170    LOG(WARNING) << "Unknown command:" << cmd;
171  }
172}
173
174void OfflineLoadPage::NotifyBlockingPageComplete(bool proceed) {
175  BrowserThread::PostTask(
176      BrowserThread::IO, FROM_HERE, base::Bind(callback_, proceed));
177}
178
179void OfflineLoadPage::OnConnectionTypeChanged(
180    net::NetworkChangeNotifier::ConnectionType type) {
181  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
182  const bool online = type != net::NetworkChangeNotifier::CONNECTION_NONE;
183  DVLOG(1) << "ConnectionTypeObserver notification received: state="
184           << (online ? "online" : "offline");
185  if (online) {
186    net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
187    interstitial_page_->Proceed();
188  }
189}
190
191}  // namespace chromeos
192