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/chromeos/offline/offline_load_page.h" 6 7 #include "base/i18n/rtl.h" 8 #include "base/metrics/histogram.h" 9 #include "base/string_piece.h" 10 #include "base/stringprintf.h" 11 #include "base/utf_string_conversions.h" 12 #include "base/values.h" 13 #include "chrome/browser/chromeos/cros/cros_library.h" 14 #include "chrome/browser/chromeos/cros/network_library.h" 15 #include "chrome/browser/extensions/extension_service.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/browser/tab_contents/tab_util.h" 18 #include "chrome/browser/ui/browser.h" 19 #include "chrome/browser/ui/browser_list.h" 20 #include "chrome/common/extensions/extension.h" 21 #include "chrome/common/jstemplate_builder.h" 22 #include "chrome/common/url_constants.h" 23 #include "content/browser/browser_thread.h" 24 #include "content/browser/tab_contents/navigation_controller.h" 25 #include "content/browser/tab_contents/navigation_entry.h" 26 #include "content/browser/tab_contents/tab_contents.h" 27 #include "content/common/notification_service.h" 28 #include "content/common/notification_type.h" 29 #include "grit/browser_resources.h" 30 #include "grit/generated_resources.h" 31 #include "ui/base/l10n/l10n_util.h" 32 #include "ui/base/resource/resource_bundle.h" 33 34 namespace { 35 36 // Maximum time to show a blank page. 37 const int kMaxBlankPeriod = 3000; 38 39 // A utility function to set the dictionary's value given by |resource_id|. 40 void SetString(DictionaryValue* strings, const char* name, int resource_id) { 41 strings->SetString(name, l10n_util::GetStringUTF16(resource_id)); 42 } 43 44 } // namespace 45 46 namespace chromeos { 47 48 // static 49 void OfflineLoadPage::Show(int process_host_id, int render_view_id, 50 const GURL& url, Delegate* delegate) { 51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 52 if (NetworkStateNotifier::is_connected()) { 53 // Check again in UI thread and proceed if it's connected. 54 delegate->OnBlockingPageComplete(true); 55 } else { 56 TabContents* tab_contents = 57 tab_util::GetTabContentsByID(process_host_id, render_view_id); 58 // There is a chance that the tab is closed after we decided to show 59 // offline and before we actually show the offline page. 60 if (!tab_contents) 61 return; 62 (new OfflineLoadPage(tab_contents, url, delegate))->Show(); 63 } 64 } 65 66 OfflineLoadPage::OfflineLoadPage(TabContents* tab_contents, 67 const GURL& url, 68 Delegate* delegate) 69 : InterstitialPage(tab_contents, true, url), 70 delegate_(delegate), 71 proceeded_(false), 72 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), 73 in_test_(false) { 74 registrar_.Add(this, NotificationType::NETWORK_STATE_CHANGED, 75 NotificationService::AllSources()); 76 } 77 78 std::string OfflineLoadPage::GetHTMLContents() { 79 DictionaryValue strings; 80 int64 time_to_wait = std::max( 81 static_cast<int64>(0), 82 kMaxBlankPeriod - 83 NetworkStateNotifier::GetOfflineDuration().InMilliseconds()); 84 // Set the timeout to show the page. 85 strings.SetInteger("time_to_wait", static_cast<int>(time_to_wait)); 86 // Button labels 87 SetString(&strings, "heading", IDS_OFFLINE_LOAD_HEADLINE); 88 SetString(&strings, "try_loading", IDS_OFFLINE_TRY_LOADING); 89 SetString(&strings, "network_settings", IDS_OFFLINE_NETWORK_SETTINGS); 90 91 // Activation 92 strings.SetBoolean("show_activation", ShowActivationMessage()); 93 94 bool rtl = base::i18n::IsRTL(); 95 strings.SetString("textdirection", rtl ? "rtl" : "ltr"); 96 97 string16 failed_url(ASCIIToUTF16(url().spec())); 98 if (rtl) 99 base::i18n::WrapStringWithLTRFormatting(&failed_url); 100 strings.SetString("url", failed_url); 101 102 // The offline page for app has icons and slightly different message. 103 Profile* profile = tab()->profile(); 104 DCHECK(profile); 105 const Extension* extension = NULL; 106 ExtensionService* extensions_service = profile->GetExtensionService(); 107 // Extension service does not exist in test. 108 if (extensions_service) 109 extension = extensions_service->GetExtensionByWebExtent(url()); 110 111 if (extension) 112 GetAppOfflineStrings(extension, failed_url, &strings); 113 else 114 GetNormalOfflineStrings(failed_url, &strings); 115 116 base::StringPiece html( 117 ResourceBundle::GetSharedInstance().GetRawDataResource( 118 IDR_OFFLINE_LOAD_HTML)); 119 return jstemplate_builder::GetI18nTemplateHtml(html, &strings); 120 } 121 122 void OfflineLoadPage::GetAppOfflineStrings( 123 const Extension* app, 124 const string16& failed_url, 125 DictionaryValue* strings) const { 126 strings->SetString("title", app->name()); 127 128 GURL icon_url = app->GetIconURL(Extension::EXTENSION_ICON_LARGE, 129 ExtensionIconSet::MATCH_EXACTLY); 130 if (icon_url.is_empty()) { 131 strings->SetString("display_icon", "none"); 132 strings->SetString("icon", string16()); 133 } else { 134 // Default icon is not accessible from interstitial page. 135 // TODO(oshima): Figure out how to use default icon. 136 strings->SetString("display_icon", "block"); 137 strings->SetString("icon", icon_url.spec()); 138 } 139 140 strings->SetString( 141 "msg", 142 l10n_util::GetStringFUTF16(IDS_APP_OFFLINE_LOAD_DESCRIPTION, 143 failed_url)); 144 } 145 146 void OfflineLoadPage::GetNormalOfflineStrings( 147 const string16& failed_url, DictionaryValue* strings) const { 148 strings->SetString("title", tab()->GetTitle()); 149 150 // No icon for normal web site. 151 strings->SetString("display_icon", "none"); 152 strings->SetString("icon", string16()); 153 154 strings->SetString( 155 "msg", 156 l10n_util::GetStringFUTF16(IDS_SITE_OFFLINE_LOAD_DESCRIPTION, 157 failed_url)); 158 } 159 160 void 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 == "proceed") { 168 Proceed(); 169 } else if (command == "dontproceed") { 170 DontProceed(); 171 } else if (command == "open_network_settings") { 172 Browser* browser = BrowserList::GetLastActive(); 173 DCHECK(browser); 174 browser->ShowOptionsTab(chrome::kInternetOptionsSubPage); 175 } else if (command == "open_activate_broadband") { 176 Browser* browser = BrowserList::GetLastActive(); 177 DCHECK(browser); 178 browser->OpenMobilePlanTabAndActivate(); 179 } else { 180 LOG(WARNING) << "Unknown command:" << cmd; 181 } 182 } 183 184 void OfflineLoadPage::Proceed() { 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 186 proceeded_ = true; 187 delegate_->OnBlockingPageComplete(true); 188 InterstitialPage::Proceed(); 189 } 190 191 void OfflineLoadPage::DontProceed() { 192 // Inogre if it's already proceeded. 193 if (proceeded_) 194 return; 195 delegate_->OnBlockingPageComplete(false); 196 InterstitialPage::DontProceed(); 197 } 198 199 void OfflineLoadPage::Observe(NotificationType type, 200 const NotificationSource& source, 201 const NotificationDetails& details) { 202 if (type.value == NotificationType::NETWORK_STATE_CHANGED) { 203 chromeos::NetworkStateDetails* state_details = 204 Details<chromeos::NetworkStateDetails>(details).ptr(); 205 DVLOG(1) << "NetworkStateChanaged notification received: state=" 206 << state_details->state(); 207 if (state_details->state() == 208 chromeos::NetworkStateDetails::CONNECTED) { 209 registrar_.Remove(this, NotificationType::NETWORK_STATE_CHANGED, 210 NotificationService::AllSources()); 211 Proceed(); 212 } 213 } else { 214 InterstitialPage::Observe(type, source, details); 215 } 216 } 217 218 bool OfflineLoadPage::ShowActivationMessage() { 219 CrosLibrary* cros = CrosLibrary::Get(); 220 if (!cros || !cros->GetNetworkLibrary()->cellular_available()) 221 return false; 222 223 const CellularNetworkVector& cell_networks = 224 cros->GetNetworkLibrary()->cellular_networks(); 225 for (size_t i = 0; i < cell_networks.size(); ++i) { 226 chromeos::ActivationState activation_state = 227 cell_networks[i]->activation_state(); 228 if (activation_state == ACTIVATION_STATE_ACTIVATED) 229 return false; 230 } 231 return true; 232 } 233 234 } // namespace chromeos 235