summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authoroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-15 03:40:23 +0000
committeroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-15 03:40:23 +0000
commit6692b0d7bca9e0e3327d371c312196f3af9ed7c6 (patch)
treee589215d311709b95cdbdbae330ae86c439b46a4 /chrome
parent2c77c96e4c474ff213c4cf2ed850970c48bbced0 (diff)
downloadchromium_src-6692b0d7bca9e0e3327d371c312196f3af9ed7c6.zip
chromium_src-6692b0d7bca9e0e3327d371c312196f3af9ed7c6.tar.gz
chromium_src-6692b0d7bca9e0e3327d371c312196f3af9ed7c6.tar.bz2
Show offline interstitial page when offline and reload when reconnected to network.
* Added OfflineResourceHandler that intercept the request and show interstitial page. * Added OfflineLoadPage that is shown when offline. This gets deleted when - User pressed "Load now" btton to proceed or - User pressed "Cancel" button to cancel loading - Network become available. The page first appears as blank page (a little darker than white for now. I'll update when mock is ready), and then options become available after 3 seconds maximum. * Added unit test for OfflineLoadPage class. * OfflineLoadService class to control when/if a load request should proceed regardless of network status. The current implementation is tentative and will proceed if if loading was requested in a given tab. I'll revisit this class to improve the logic in separate CL later. Known Issue: - thumbnail is not working yet. I'll working on this in separate Cl. - a tab shows URL instead of title string. I'll fix this in separate CL. - InitNavigationParams in offline_load_page_unittest is copied from safe_browsing_blocking_page_unittest. I'll move this to common place in separate CL. (hopefully before checking this in) BUG=chromium-os:3605 TEST=unit test: offline_load_page_unittest manual: disable wifi and ethernet, then login. chrome will show the offline page when tab is activated. Enable network (wifi or ethernet) and all tab will start loading again. Review URL: http://codereview.chromium.org/2931005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52433 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/generated_resources.grd16
-rw-r--r--chrome/browser/browser_resources.grd1
-rw-r--r--chrome/browser/chromeos/network_state_notifier.cc30
-rw-r--r--chrome/browser/chromeos/network_state_notifier.h19
-rw-r--r--chrome/browser/chromeos/network_state_notifier_browsertest.cc5
-rw-r--r--chrome/browser/chromeos/offline/offline_load_page.cc137
-rw-r--r--chrome/browser/chromeos/offline/offline_load_page.h69
-rw-r--r--chrome/browser/chromeos/offline/offline_load_page_unittest.cc139
-rw-r--r--chrome/browser/chromeos/offline/offline_load_service.cc104
-rw-r--r--chrome/browser/chromeos/offline/offline_load_service.h74
-rw-r--r--chrome/browser/renderer_host/offline_resource_handler.cc161
-rw-r--r--chrome/browser/renderer_host/offline_resource_handler.h74
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host.cc12
-rw-r--r--chrome/browser/renderer_host/resource_queue.cc4
-rw-r--r--chrome/browser/resources/offline_load.html107
-rw-r--r--chrome/browser/sessions/session_restore.cc69
-rw-r--r--chrome/chrome_browser.gypi8
-rw-r--r--chrome/chrome_tests.gypi1
18 files changed, 1008 insertions, 22 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 4825c04..c802f74 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6291,6 +6291,22 @@ Keep your key file in a safe place. You will need it to create new versions of y
Options
</message>
+ <!-- Offline page -->
+ <if expr="pp_ifdef('chromeos')">
+ <message name="IDS_OFFLINE_LOAD_HEADLINE" desc="Offline Page HTML headline">
+ Network is not available.
+ </message>
+ <message name="IDS_OFFLINE_LOAD_DESCRIPTION" desc="Offline Page HTML description">
+ Page will be loaded when network becomes available. Press 'Load Now' if you want to load now.
+ </message>
+ <message name="IDS_OFFLINE_LOAD_BUTTON">
+ Load Now
+ </message>
+ <message name="IDS_OFFLINE_BACK_BUTTON">
+ Cancel
+ </message>
+ </if>
+
<!-- SafeBrowsing -->
<message name="IDS_SAFE_BROWSING_MALWARE_TITLE" desc="SafeBrowsing Malware HTML title">
Malware Detected!
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index ccdbc04..7463d03 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -74,6 +74,7 @@ without changes to the corresponding grd file. eadee -->
<include name="IDR_HOST_REGISTRATION_PAGE_HTML" file="resources\host_registration_page.html" flattenhtml="true" type="BINDATA" />
<include name="IDR_MEDIAPLAYER_HTML" file="resources\mediaplayer.html" flattenhtml="true" type="BINDATA" />
<include name="IDR_MEDIAPLAYERPLAYLIST_HTML" file="resources\playlist.html" flattenhtml="true" type="BINDATA" />
+ <include name="IDR_OFFLINE_LOAD_HTML" file="resources\offline_load.html" flattenhtml="true" type="BINDATA" />
<include name="IDR_OS_CREDITS_HTML" file="resources\about_os_credits.html" flattenhtml="true" type="BINDATA" />
<include name="IDR_SLIDESHOW_HTML" file="resources\slideshow.html" flattenhtml="true" type="BINDATA" />
</if>
diff --git a/chrome/browser/chromeos/network_state_notifier.cc b/chrome/browser/chromeos/network_state_notifier.cc
index 2d13030..3ee696c 100644
--- a/chrome/browser/chromeos/network_state_notifier.cc
+++ b/chrome/browser/chromeos/network_state_notifier.cc
@@ -2,23 +2,38 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "chrome/browser/chromeos/network_state_notifier.h"
+
+#include "base/message_loop.h"
+#include "base/time.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
-#include "chrome/browser/chromeos/network_state_notifier.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
-#include "base/message_loop.h"
-
namespace chromeos {
+using base::Time;
+using base::TimeDelta;
+
+// static
NetworkStateNotifier* NetworkStateNotifier::Get() {
return Singleton<NetworkStateNotifier>::get();
}
+// static
+TimeDelta NetworkStateNotifier::GetOfflineDuration() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ // TODO(oshima): make this instance method so that
+ // we can mock this for ui_tests.
+ // http://crbug.com/4825 .
+ return base::Time::Now() - Get()->offline_start_time_;
+}
+
NetworkStateNotifier::NetworkStateNotifier()
- : task_factory_(this) {
- state_ = RetrieveState();
+ : ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
+ state_(RetrieveState()),
+ offline_start_time_(Time::Now()) {
}
void NetworkStateNotifier::NetworkChanged(NetworkLibrary* cros) {
@@ -37,6 +52,11 @@ void NetworkStateNotifier::UpdateNetworkState(
NetworkStateDetails::State new_state) {
DLOG(INFO) << "UpdateNetworkState: new="
<< new_state << ", old=" << state_;
+ if (state_ == NetworkStateDetails::CONNECTED &&
+ new_state != NetworkStateDetails::CONNECTED) {
+ offline_start_time_ = Time::Now();
+ }
+
state_ = new_state;
NetworkStateDetails details(state_);
NotificationService::current()->Notify(
diff --git a/chrome/browser/chromeos/network_state_notifier.h b/chrome/browser/chromeos/network_state_notifier.h
index a48d60e..a64f264 100644
--- a/chrome/browser/chromeos/network_state_notifier.h
+++ b/chrome/browser/chromeos/network_state_notifier.h
@@ -9,6 +9,7 @@
#include "base/singleton.h"
#include "base/task.h"
+#include "base/time.h"
namespace chromeos {
@@ -48,15 +49,19 @@ class NetworkStateNotifier : public NetworkLibrary::Observer {
// Returns the singleton instance of the network state notifier;
static NetworkStateNotifier* Get();
- // NetworkLibrary::Observer implementation.
- virtual void NetworkChanged(NetworkLibrary* cros);
- virtual void NetworkTraffic(NetworkLibrary* cros, int traffic_type) {}
+ // The duration of being in offline. The value is undefined when
+ // when network is connected.
+ static base::TimeDelta GetOfflineDuration();
// Returns true if the network is connected.
static bool is_connected() {
return Get()->state_ == NetworkStateDetails::CONNECTED;
}
+ // NetworkLibrary::Observer implementation.
+ virtual void NetworkChanged(NetworkLibrary* cros);
+ virtual void NetworkTraffic(NetworkLibrary* cros, int traffic_type) {}
+
private:
friend struct DefaultSingletonTraits<NetworkStateNotifier>;
@@ -70,14 +75,18 @@ class NetworkStateNotifier : public NetworkLibrary::Observer {
// This should be invoked in UI thread.
void UpdateNetworkState(NetworkStateDetails::State new_state);
+ // A factory to post a task in UI thread.
+ ScopedRunnableMethodFactory<NetworkStateNotifier> task_factory_;
+
// The current network state.
NetworkStateDetails::State state_;
- ScopedRunnableMethodFactory<NetworkStateNotifier> task_factory_;
+ // The start time of offline.
+ base::Time offline_start_time_;
DISALLOW_COPY_AND_ASSIGN(NetworkStateNotifier);
};
-} // chromeos
+} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_NETWORK_STATE_NOTIFIER_H_
diff --git a/chrome/browser/chromeos/network_state_notifier_browsertest.cc b/chrome/browser/chromeos/network_state_notifier_browsertest.cc
index bf2019c..b917413 100644
--- a/chrome/browser/chromeos/network_state_notifier_browsertest.cc
+++ b/chrome/browser/chromeos/network_state_notifier_browsertest.cc
@@ -30,6 +30,8 @@ class NetworkStateNotifierTest : public CrosInProcessBrowserTest,
InitStatusAreaMocks();
SetStatusAreaMocksExpectations();
// Initialize network state notifier.
+ ASSERT_TRUE(CrosLibrary::Get()->EnsureLoaded());
+ ASSERT_TRUE(mock_network_library_);
EXPECT_CALL(*mock_network_library_, Connected())
.Times(1)
.WillRepeatedly((Return(true)))
@@ -71,7 +73,6 @@ IN_PROC_BROWSER_TEST_F(NetworkStateNotifierTest, TestConnected) {
.WillRepeatedly((Return(true)))
.RetiresOnSaturation();
NetworkStateNotifier* notifier = NetworkStateNotifier::Get();
- DCHECK(CrosLibrary::Get()->EnsureLoaded());
notifier->NetworkChanged(mock_network_library_);
WaitForNotification();
EXPECT_EQ(chromeos::NetworkStateDetails::CONNECTED, state_);
@@ -90,7 +91,6 @@ IN_PROC_BROWSER_TEST_F(NetworkStateNotifierTest, TestConnecting) {
.WillOnce((Return(true)))
.RetiresOnSaturation();
NetworkStateNotifier* notifier = NetworkStateNotifier::Get();
- DCHECK(CrosLibrary::Get()->EnsureLoaded());
notifier->NetworkChanged(mock_network_library_);
WaitForNotification();
EXPECT_EQ(chromeos::NetworkStateDetails::CONNECTING, state_);
@@ -109,7 +109,6 @@ IN_PROC_BROWSER_TEST_F(NetworkStateNotifierTest, TestDisconnected) {
.WillOnce((Return(false)))
.RetiresOnSaturation();
NetworkStateNotifier* notifier = NetworkStateNotifier::Get();
- DCHECK(CrosLibrary::Get()->EnsureLoaded());
notifier->NetworkChanged(mock_network_library_);
WaitForNotification();
EXPECT_EQ(chromeos::NetworkStateDetails::DISCONNECTED, state_);
diff --git a/chrome/browser/chromeos/offline/offline_load_page.cc b/chrome/browser/chromeos/offline/offline_load_page.cc
new file mode 100644
index 0000000..0dc98264
--- /dev/null
+++ b/chrome/browser/chromeos/offline/offline_load_page.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 2010 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/browser/chromeos/offline/offline_load_page.h"
+
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "base/histogram.h"
+#include "base/i18n/rtl.h"
+#include "base/string_piece.h"
+#include "base/values.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_util.h"
+#include "chrome/common/jstemplate_builder.h"
+#include "chrome/common/notification_type.h"
+#include "grit/browser_resources.h"
+#include "grit/generated_resources.h"
+
+namespace {
+
+// Maximum time to show a blank page.
+const int kMaxBlankPeriod = 3000;
+
+// A utility function to set the dictionary's value given by |resource_id|.
+void SetString(DictionaryValue* strings, const wchar_t* name, int resource_id) {
+ strings->SetString(name, l10n_util::GetString(resource_id));
+}
+
+} // namespace
+
+namespace chromeos {
+
+// static
+void OfflineLoadPage::Show(int process_host_id, int render_view_id,
+ const GURL& url, Delegate* delegate) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ if (NetworkStateNotifier::is_connected()) {
+ // Check again in UI thread and proceed if it's connected.
+ delegate->OnBlockingPageComplete(true);
+ } else {
+ TabContents* tab_contents =
+ tab_util::GetTabContentsByID(process_host_id, render_view_id);
+ DCHECK(tab_contents);
+ (new OfflineLoadPage(tab_contents, url, delegate))->Show();
+ }
+}
+
+OfflineLoadPage::OfflineLoadPage(TabContents* tab_contents,
+ const GURL& url,
+ Delegate* delegate)
+ : InterstitialPage(tab_contents, true, url),
+ delegate_(delegate) {
+ registrar_.Add(this, NotificationType::NETWORK_STATE_CHANGED,
+ NotificationService::AllSources());
+}
+
+std::string OfflineLoadPage::GetHTMLContents() {
+ DictionaryValue strings;
+ SetString(&strings, L"headLine", IDS_OFFLINE_LOAD_HEADLINE);
+ SetString(&strings, L"description", IDS_OFFLINE_LOAD_DESCRIPTION);
+ SetString(&strings, L"load_button", IDS_OFFLINE_LOAD_BUTTON);
+ SetString(&strings, L"back_button", IDS_OFFLINE_BACK_BUTTON);
+
+ // TODO(oshima): tab()->GetTitle() always return url. This has to be
+ // a cached title.
+ strings.SetString(L"title", UTF16ToWide(tab()->GetTitle()));
+ strings.SetString(L"textdirection", base::i18n::IsRTL() ? L"rtl" : L"ltr");
+ strings.SetString(L"display_go_back",
+ tab()->controller().CanGoBack() ? L"inline" : L"none");
+ int64 time_to_wait = std::max(
+ static_cast<int64>(0),
+ kMaxBlankPeriod -
+ NetworkStateNotifier::GetOfflineDuration().InMilliseconds());
+ strings.SetString(L"on_load",
+ StringPrintf(L"startTimer(%ld)", time_to_wait));
+
+ // TODO(oshima): thumbnail is not working yet. fix this.
+ const std::string url = "chrome://thumb/" + GetURL().spec();
+ strings.SetString(L"thumbnailUrl", "url(" + url + ")");
+
+ base::StringPiece html(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_OFFLINE_LOAD_HTML));
+ return jstemplate_builder::GetI18nTemplateHtml(html, &strings);
+}
+
+void OfflineLoadPage::CommandReceived(const std::string& cmd) {
+ std::string command(cmd);
+ // The Jasonified response has quotes, remove them.
+ if (command.length() > 1 && command[0] == '"') {
+ command = command.substr(1, command.length() - 2);
+ }
+ // TODO(oshima): record action for metrics.
+ if (command == "proceed") {
+ Proceed();
+ } else if (command == "dontproceed") {
+ DontProceed();
+ } else {
+ LOG(WARNING) << "Unknown command:" << cmd;
+ }
+}
+
+void OfflineLoadPage::Proceed() {
+ delegate_->OnBlockingPageComplete(true);
+ InterstitialPage::Proceed();
+}
+
+void OfflineLoadPage::DontProceed() {
+ delegate_->OnBlockingPageComplete(false);
+ InterstitialPage::DontProceed();
+}
+
+void OfflineLoadPage::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type.value == NotificationType::NETWORK_STATE_CHANGED) {
+ chromeos::NetworkStateDetails* state_details =
+ Details<chromeos::NetworkStateDetails>(details).ptr();
+ DLOG(INFO) << "NetworkStateChanaged notification received: state="
+ << state_details->state();
+ if (state_details->state() ==
+ chromeos::NetworkStateDetails::CONNECTED) {
+ registrar_.Remove(this, NotificationType::NETWORK_STATE_CHANGED,
+ NotificationService::AllSources());
+ Proceed();
+ }
+ } else {
+ InterstitialPage::Observe(type, source, details);
+ }
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/offline/offline_load_page.h b/chrome/browser/chromeos/offline/offline_load_page.h
new file mode 100644
index 0000000..3363853
--- /dev/null
+++ b/chrome/browser/chromeos/offline/offline_load_page.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_OFFLINE_OFFLINE_LOAD_PAGE_H_
+#define CHROME_BROWSER_CHROMEOS_OFFLINE_OFFLINE_LOAD_PAGE_H_
+
+#include <string>
+
+#include "chrome/browser/chromeos/network_state_notifier.h"
+#include "chrome/browser/tab_contents/interstitial_page.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_service.h"
+
+class TabContents;
+
+namespace chromeos {
+
+// OfflineLoadPage class shows the interstitial page that is shown
+// when no network is available and hides when some network (either
+// one of wifi, 3g or ethernet) becomes available.
+// It deletes itself when the interstitial page is closed.
+class OfflineLoadPage : public InterstitialPage {
+ public:
+ // A delegate class that is called when the interstitinal page
+ // is closed.
+ class Delegate {
+ public:
+ Delegate() {}
+ virtual ~Delegate() {}
+ // Called when a user selected to proceed or not to proceed
+ // with loading.
+ virtual void OnBlockingPageComplete(bool proceed) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Delegate);
+ };
+ static void Show(int process_host_id, int render_view_id,
+ const GURL& url, Delegate* delegate);
+ // Import show here so that overloading works.
+ using InterstitialPage::Show;
+
+ protected:
+ // Create a offline load page for the |tab_contents|.
+ OfflineLoadPage(TabContents* tab_contents, const GURL& url,
+ Delegate* delegate);
+ virtual ~OfflineLoadPage() {}
+
+ private:
+ // InterstitialPage implementation.
+ virtual std::string GetHTMLContents();
+ virtual void CommandReceived(const std::string& command);
+ virtual void Proceed();
+ virtual void DontProceed();
+
+ // Overrides InterstitialPage's Observe.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ Delegate* delegate_;
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(OfflineLoadPage);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_OFFLINE_OFFLINE_LOAD_PAGE_H_
diff --git a/chrome/browser/chromeos/offline/offline_load_page_unittest.cc b/chrome/browser/chromeos/offline/offline_load_page_unittest.cc
new file mode 100644
index 0000000..a619243
--- /dev/null
+++ b/chrome/browser/chromeos/offline/offline_load_page_unittest.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2010 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/browser/renderer_host/test/test_render_view_host.h"
+
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/chromeos/offline/offline_load_page.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/common/render_messages.h"
+
+static const char* kURL1 = "http://www.google.com/";
+static const char* kURL2 = "http://www.gmail.com/";
+
+namespace {
+
+// An OfflineLoadPage class that does not create windows.
+class TestOfflineLoadPage : public chromeos::OfflineLoadPage {
+ public:
+ TestOfflineLoadPage(TabContents* tab_contents,
+ const GURL& url,
+ Delegate* delegate)
+ : chromeos::OfflineLoadPage(tab_contents, url, delegate) {
+ }
+
+ // Overriden from InterstitialPage. Don't create a view.
+ virtual TabContentsView* CreateTabContentsView() {
+ return NULL;
+ }
+};
+
+} // namespace
+
+namespace chromeos {
+
+class OfflineLoadPageTest : public RenderViewHostTestHarness,
+ public OfflineLoadPage::Delegate {
+ public:
+ // The decision the user made.
+ enum UserResponse {
+ PENDING,
+ OK,
+ CANCEL
+ };
+
+ OfflineLoadPageTest()
+ : ui_thread_(ChromeThread::UI, MessageLoop::current()),
+ io_thread_(ChromeThread::IO, MessageLoop::current()) {
+ }
+
+ virtual void SetUp() {
+ RenderViewHostTestHarness::SetUp();
+ user_response_ = PENDING;
+ }
+
+ // OfflineLoadPage::Delegate implementation.
+ virtual void OnBlockingPageComplete(bool proceed) {
+ if (proceed)
+ user_response_ = OK;
+ else
+ user_response_ = CANCEL;
+ }
+
+ void Navigate(const char* url, int page_id) {
+ ViewHostMsg_FrameNavigate_Params params;
+ InitNavigateParams(&params, page_id, GURL(url), PageTransition::TYPED);
+ contents()->TestDidNavigate(contents_->render_view_host(), params);
+ }
+
+ void ShowInterstitial(const char* url) {
+ (new TestOfflineLoadPage(contents(), GURL(url), this))->Show();
+ }
+
+ // Returns the OfflineLoadPage currently showing or NULL if none is
+ // showing.
+ InterstitialPage* GetOfflineLoadPage() {
+ return InterstitialPage::GetInterstitialPage(contents());
+ }
+
+ UserResponse user_response() const { return user_response_; }
+
+ private:
+ UserResponse user_response_;
+ ChromeThread ui_thread_;
+ ChromeThread io_thread_;
+};
+
+
+TEST_F(OfflineLoadPageTest, OfflinePaeProceed) {
+ // Start a load.
+ Navigate(kURL1, 1);
+ // Load next page.
+ controller().LoadURL(GURL(kURL2), GURL(), PageTransition::TYPED);
+
+ // Simulate the load causing an offline browsing interstitial page
+ // to be shown.
+ ShowInterstitial(kURL2);
+ InterstitialPage* interstitial = GetOfflineLoadPage();
+ ASSERT_TRUE(interstitial);
+ MessageLoop::current()->RunAllPending();
+
+ // Simulate the user clicking "proceed".
+ interstitial->Proceed();
+
+ EXPECT_EQ(OK, user_response());
+
+ // The URL remains to be URL2.
+ EXPECT_EQ(kURL2, contents()->GetURL().spec());
+
+ // Ccommit navigation and the interstitial page is gone.
+ Navigate(kURL2, 2);
+ EXPECT_FALSE(GetOfflineLoadPage());
+}
+
+// Tests showing an offline page and not proceeding.
+TEST_F(OfflineLoadPageTest, OfflinePageDontProceed) {
+ // Start a load.
+ Navigate(kURL1, 1);
+ controller().LoadURL(GURL(kURL2), GURL(), PageTransition::TYPED);
+
+ // Simulate the load causing an offline interstitial page to be shown.
+ ShowInterstitial(kURL2);
+ InterstitialPage* interstitial = GetOfflineLoadPage();
+ ASSERT_TRUE(interstitial);
+ MessageLoop::current()->RunAllPending();
+
+ // Simulate the user clicking "don't proceed".
+ interstitial->DontProceed();
+
+ // The interstitial should be gone.
+ EXPECT_EQ(CANCEL, user_response());
+ EXPECT_FALSE(GetOfflineLoadPage());
+ // We did not proceed, the pending entry should be gone.
+ EXPECT_FALSE(controller().pending_entry());
+ // the URL is set back to kURL1.
+ EXPECT_EQ(kURL1, contents()->GetURL().spec());
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/offline/offline_load_service.cc b/chrome/browser/chromeos/offline/offline_load_service.cc
new file mode 100644
index 0000000..63602f5
--- /dev/null
+++ b/chrome/browser/chromeos/offline/offline_load_service.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2010 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/browser/chromeos/offline/offline_load_service.h"
+
+#include "base/singleton.h"
+#include "base/ref_counted.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_util.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+
+namespace chromeos {
+
+// A utility class that serves a singleton instance of OfflineLoadService.
+// OfflineLoadSerivce itself cannot be a singleton as it implements
+// RefCount interface.
+class OfflineLoadServiceSingleton {
+ public:
+ chromeos::OfflineLoadService* offline_load_service() {
+ return offline_load_service_.get();
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<OfflineLoadServiceSingleton>;
+ OfflineLoadServiceSingleton()
+ : offline_load_service_(new chromeos::OfflineLoadService()) {}
+ virtual ~OfflineLoadServiceSingleton() {}
+
+ scoped_refptr<chromeos::OfflineLoadService> offline_load_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(OfflineLoadServiceSingleton);
+};
+
+// static
+OfflineLoadService* OfflineLoadService::Get() {
+ return Singleton<OfflineLoadServiceSingleton>::get()->offline_load_service();
+}
+
+void OfflineLoadService::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ if (type.value == NotificationType::TAB_CLOSED) {
+ registrar_.Remove(this, NotificationType::TAB_CLOSED, source);
+ NavigationController* tab = Source<NavigationController>(source).ptr();
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(this,
+ &OfflineLoadService::RemoveTabContents,
+ tab->tab_contents()));
+ }
+}
+
+bool OfflineLoadService::ShouldProceed(int process_host_id,
+ int render_view_id,
+ const GURL& url) const {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ TabContents* tab_contents = tab_util::GetTabContentsByID(
+ process_host_id, render_view_id);
+ DCHECK(tab_contents);
+ bool proceed = tabs_.find(tab_contents) != tabs_.end();
+ DLOG(INFO) << "ShouldProceed:" << proceed << ", url=" << url.spec()
+ << ", tab_contents=" << tab_contents;
+ return proceed;
+}
+
+void OfflineLoadService::Proceeded(int process_host_id,
+ int render_view_id,
+ const GURL& url) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ TabContents* tab_contents = tab_util::GetTabContentsByID(
+ process_host_id, render_view_id);
+ DCHECK(tab_contents);
+ if (tabs_.find(tab_contents) == tabs_.end()) {
+ DLOG(INFO) << "Proceeded: url=" << url.spec()
+ << ", tab_contents=" << tab_contents;
+ tabs_.insert(tab_contents);
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this,
+ &OfflineLoadService::RegisterNotification,
+ &tab_contents->controller()));
+ } else {
+ DLOG(WARNING) << "Proceeded: ignoring duplicate";
+ }
+}
+
+void OfflineLoadService::RemoveTabContents(TabContents* tab_contents) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ tabs_.erase(tabs_.find(tab_contents));
+}
+
+void OfflineLoadService::RegisterNotification(
+ NavigationController* navigation_controller) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ registrar_.Add(this, NotificationType::TAB_CLOSED,
+ Source<NavigationController>(
+ navigation_controller));
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/offline/offline_load_service.h b/chrome/browser/chromeos/offline/offline_load_service.h
new file mode 100644
index 0000000..119475e
--- /dev/null
+++ b/chrome/browser/chromeos/offline/offline_load_service.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_OFFLINE_OFFLINE_LOAD_SERVICE_H_
+#define CHROME_BROWSER_CHROMEOS_OFFLINE_OFFLINE_LOAD_SERVICE_H_
+
+#include <tr1/unordered_set>
+
+#include "base/ref_counted.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/notification_registrar.h"
+
+class GURL;
+class NavigationController;
+class TabContents;
+template<class A> class scoped_ptr;
+
+namespace chromeos {
+
+// OfflineLoadService decides whether or not the given url in the tab
+// should be loaded when the network is not available. The current
+// implementation simply memorize the tab that loads offline page.
+// TODO(oshima): Improve the logic so that we can automatically load
+// if there is valid cache content and/or it supports offline mode.
+class OfflineLoadService
+ : public NotificationObserver,
+ public base::RefCountedThreadSafe<OfflineLoadService> {
+ public:
+ // Returns the singleton instance of the offline load service.
+ static OfflineLoadService* Get();
+
+ // Returns true if the tab should proceed with loading the page even if
+ // it's offline.
+ bool ShouldProceed(int process_id, int render_view_id,
+ const GURL& url) const;
+
+ // Record that the user pressed "procced" button for
+ // the tab_contents.
+ void Proceeded(int proceed_id, int render_view_id, const GURL& url);
+
+ // NotificationObserver implementation.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ friend class base::RefCountedThreadSafe<OfflineLoadService>;
+ friend class scoped_ptr<OfflineLoadService>;
+ friend class OfflineLoadServiceSingleton;
+
+ OfflineLoadService() {}
+ virtual ~OfflineLoadService() {}
+
+ // A method invoked in UI thread to remove |tab_contents| from |tabs_|.
+ void RemoveTabContents(TabContents* tab_contents);
+
+ // A method invoked in UI thread to register TAB_CLOSED
+ // notification.
+ void RegisterNotification(
+ NavigationController* navigation_controller);
+
+ NotificationRegistrar registrar_;
+
+ // Set of tabs that should proceed
+ std::tr1::unordered_set<const TabContents*> tabs_;
+
+ DISALLOW_COPY_AND_ASSIGN(OfflineLoadService);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_OFFLINE_OFFLINE_LOAD_SERVICE_H_
diff --git a/chrome/browser/renderer_host/offline_resource_handler.cc b/chrome/browser/renderer_host/offline_resource_handler.cc
new file mode 100644
index 0000000..c270d14
--- /dev/null
+++ b/chrome/browser/renderer_host/offline_resource_handler.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2010 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/browser/renderer_host/offline_resource_handler.h"
+
+#include <vector>
+
+#include "base/histogram.h"
+#include "base/logging.h"
+#include "base/singleton.h"
+#include "base/string_util.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/chromeos/network_state_notifier.h"
+#include "chrome/browser/chromeos/offline/offline_load_page.h"
+#include "chrome/browser/chromeos/offline/offline_load_service.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h"
+#include "chrome/common/url_constants.h"
+
+OfflineResourceHandler::OfflineResourceHandler(
+ ResourceHandler* handler,
+ int host_id,
+ int route_id,
+ ResourceDispatcherHost* rdh,
+ URLRequest* request)
+ : next_handler_(handler),
+ process_host_id_(host_id),
+ render_view_id_(route_id),
+ rdh_(rdh),
+ request_(request),
+ deferred_request_id_(-1) {
+}
+
+bool OfflineResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return next_handler_->OnUploadProgress(request_id, position, size);
+}
+
+bool OfflineResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& new_url,
+ ResourceResponse* response,
+ bool* defer) {
+ return next_handler_->OnRequestRedirected(
+ request_id, new_url, response, defer);
+}
+
+bool OfflineResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response) {
+ return next_handler_->OnResponseStarted(request_id, response);
+}
+
+bool OfflineResourceHandler::OnResponseCompleted(
+ int request_id,
+ const URLRequestStatus& status,
+ const std::string& security_info) {
+ return next_handler_->OnResponseCompleted(request_id, status, security_info);
+}
+
+void OfflineResourceHandler::OnRequestClosed() {
+ next_handler_->OnRequestClosed();
+}
+
+bool OfflineResourceHandler::OnWillStart(int request_id,
+ const GURL& url,
+ bool* defer) {
+ if (ShouldShowOfflinePage(url)) {
+ deferred_request_id_ = request_id;
+ deferred_url_ = url;
+ DLOG(INFO) << "WillStart: this=" << this << ", request id=" << request_id;
+ AddRef(); // Balanced with OnBlockingPageComplete
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &OfflineResourceHandler::ShowOfflinePage));
+ *defer = true;
+ return true;
+ }
+ return next_handler_->OnWillStart(request_id, url, defer);
+}
+
+// We'll let the original event handler provide a buffer, and reuse it for
+// subsequent reads until we're done buffering.
+bool OfflineResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
+ int* buf_size, int min_size) {
+ return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
+}
+
+bool OfflineResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
+ return next_handler_->OnReadCompleted(request_id, bytes_read);
+}
+
+void OfflineResourceHandler::OnBlockingPageComplete(bool proceed) {
+ if (deferred_request_id_ < 0) {
+ LOG(WARNING) << "OnBlockingPageComplete called after completion: "
+ << " this=" << this << ", request_id="
+ << deferred_request_id_;
+ NOTREACHED();
+ return;
+ }
+ if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) {
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(this,
+ &OfflineResourceHandler::OnBlockingPageComplete,
+ proceed));
+ return;
+ }
+ if (proceed) {
+ Resume();
+ } else {
+ int request_id = deferred_request_id_;
+ ClearRequestInfo();
+ rdh_->CancelRequest(process_host_id_, request_id, false);
+ }
+ Release(); // Balanced with OnWillStart
+}
+
+void OfflineResourceHandler::ClearRequestInfo() {
+ deferred_url_ = GURL();
+ deferred_request_id_ = -1;
+}
+
+bool OfflineResourceHandler::IsRemote(const GURL& url) const {
+ return url.SchemeIs(chrome::kFtpScheme) ||
+ url.SchemeIs(chrome::kHttpScheme) ||
+ url.SchemeIs(chrome::kHttpsScheme);
+}
+
+bool OfflineResourceHandler::ShouldShowOfflinePage(const GURL& url) const {
+ // Only check main frame. If the network is disconnected while
+ // loading other resources, we'll simply show broken link/images.
+ return IsRemote(url) &&
+ !chromeos::NetworkStateNotifier::is_connected() &&
+ ResourceDispatcherHost::InfoForRequest(request_)->resource_type()
+ == ResourceType::MAIN_FRAME &&
+ !chromeos::OfflineLoadService::Get()->ShouldProceed(
+ process_host_id_, render_view_id_, url);
+}
+
+void OfflineResourceHandler::Resume() {
+ const GURL url = deferred_url_;
+ int request_id = deferred_request_id_;
+ ClearRequestInfo();
+
+ chromeos::OfflineLoadService::Get()->Proceeded(
+ process_host_id_, render_view_id_, url);
+
+ DCHECK_NE(request_id, -1);
+ bool defer = false;
+ DLOG(INFO) << "Resume load: this=" << this
+ << ", request id=" << request_id;
+ next_handler_->OnWillStart(request_id, url, &defer);
+ if (!defer)
+ rdh_->StartDeferredRequest(process_host_id_, request_id);
+}
+
+void OfflineResourceHandler::ShowOfflinePage() {
+ chromeos::OfflineLoadPage::Show(
+ process_host_id_, render_view_id_, deferred_url_, this);
+}
diff --git a/chrome/browser/renderer_host/offline_resource_handler.h b/chrome/browser/renderer_host/offline_resource_handler.h
new file mode 100644
index 0000000..567e3a6
--- /dev/null
+++ b/chrome/browser/renderer_host/offline_resource_handler.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_OFFLINE_RESOURCE_HANDLER_H_
+#define CHROME_BROWSER_RENDERER_HOST_OFFLINE_RESOURCE_HANDLER_H_
+
+#include <string>
+
+#include "base/ref_counted.h"
+#include "chrome/browser/chromeos/offline/offline_load_page.h"
+#include "chrome/browser/renderer_host/resource_handler.h"
+
+class MessageLoop;
+class ResourceDispatcherHost;
+class URLRequest;
+
+// Used to show an offline interstitial page when the network is not available.
+class OfflineResourceHandler : public ResourceHandler,
+ public chromeos::OfflineLoadPage::Delegate {
+ public:
+ OfflineResourceHandler(ResourceHandler* handler,
+ int host_id,
+ int render_view_id,
+ ResourceDispatcherHost* rdh,
+ URLRequest* request);
+ ~OfflineResourceHandler() {}
+
+ // ResourceHandler implementation:
+ virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size);
+ virtual bool OnRequestRedirected(int request_id, const GURL& new_url,
+ ResourceResponse* response, bool* defer);
+ virtual bool OnResponseStarted(int request_id, ResourceResponse* response);
+ virtual bool OnWillStart(int request_id, const GURL& url, bool* defer);
+ virtual bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size,
+ int min_size);
+ virtual bool OnReadCompleted(int request_id, int* bytes_read);
+ virtual bool OnResponseCompleted(int request_id,
+ const URLRequestStatus& status,
+ const std::string& security_info);
+ virtual void OnRequestClosed();
+
+ // chromeos::OfflineLoadPage::Delegate
+ virtual void OnBlockingPageComplete(bool proceed);
+
+ private:
+ // Erease the state assocaited with a deferred load request.
+ void ClearRequestInfo();
+ bool IsRemote(const GURL& url) const;
+
+ // Resume the deferred load request.
+ void Resume();
+
+ // Tells if chrome should show the offline page.
+ bool ShouldShowOfflinePage(const GURL& url) const;
+
+ // Shows the offline interstitinal page in UI thread.
+ void ShowOfflinePage();
+
+ scoped_refptr<ResourceHandler> next_handler_;
+
+ int process_host_id_;
+ int render_view_id_;
+ ResourceDispatcherHost* rdh_;
+ URLRequest* request_;
+
+ // The state for deferred load quest.
+ int deferred_request_id_;
+ GURL deferred_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(OfflineResourceHandler);
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_OFFLINE_RESOURCE_HANDLER_H_
diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc
index 3f093ae..130997ac 100644
--- a/chrome/browser/renderer_host/resource_dispatcher_host.cc
+++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc
@@ -64,6 +64,11 @@
#include "webkit/appcache/appcache_interceptor.h"
#include "webkit/appcache/appcache_interfaces.h"
+// TODO(oshima): Enable this for other platforms.
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/renderer_host/offline_resource_handler.h"
+#endif
+
// Uncomment to enable logging of request traffic.
// #define LOG_RESOURCE_DISPATCHER_REQUESTS
@@ -447,6 +452,13 @@ void ResourceDispatcherHost::BeginRequest(
request_data.resource_type);
}
+#if defined(OS_CHROMEOS)
+ // We check offline first, then check safe browsing so that we still can block
+ // unsafe site after we remove offline page.
+ handler =
+ new OfflineResourceHandler(handler, child_id, route_id, this, request);
+#endif
+
// Make extra info and read footer (contains request ID).
ResourceDispatcherHostRequestInfo* extra_info =
new ResourceDispatcherHostRequestInfo(
diff --git a/chrome/browser/renderer_host/resource_queue.cc b/chrome/browser/renderer_host/resource_queue.cc
index dca6bd2..73e96a2 100644
--- a/chrome/browser/renderer_host/resource_queue.cc
+++ b/chrome/browser/renderer_host/resource_queue.cc
@@ -45,7 +45,9 @@ void ResourceQueue::AddRequest(
GlobalRequestID request_id(request_info.child_id(),
request_info.request_id());
- DCHECK(!ContainsKey(requests_, request_id));
+ DCHECK(!ContainsKey(requests_, request_id))
+ << "child_id:" << request_info.child_id()
+ << ", request_id:" << request_info.request_id();
requests_[request_id] = request;
DelegateSet interested_delegates;
diff --git a/chrome/browser/resources/offline_load.html b/chrome/browser/resources/offline_load.html
new file mode 100644
index 0000000..b2042a3
--- /dev/null
+++ b/chrome/browser/resources/offline_load.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<html id="template_root" i18n-values="dir:textdirection">
+<head>
+<title i18n-content="title"></title>
+<style>
+body {
+ background-color:#E0E0E0;
+ font-family:Helvetica,Arial,sans-serif;
+ margin:0px;
+ visibility: hidden;
+}
+.background {
+ position:absolute;
+ width:100%;
+ height:100%;
+}
+.cell {
+ padding:40px;
+}
+.box {
+ width:80%;
+ background-color:white;
+ color:black;
+ font-size:10pt;
+ line-height:16pt;
+ text-align:left;
+ padding:20px;
+ position:relative;
+ -webkit-box-shadow:3px 3px 8px #200;
+ border-radius:5px;
+}
+html[dir='rtl'] .box {
+ text-align:right;
+}
+
+.icon {
+ position:absolute;
+}
+.title {
+ margin:0px 87px 0px;
+ font-size:18pt;
+ line-height: 140%;
+ margin-bottom:6pt;
+ font-weight:bold;
+ color:#660000;
+}
+.main {
+ margin:0px 90px 0px;
+}
+.submission {
+ margin:15px 5px 15px 0px;
+ padding:0px;
+}
+input {
+ margin:0px;
+}
+
+.thumbnail {
+ background-repeat: no-repeat;
+ background-position: center center;
+ width:400px;
+ height:250px;
+ padding:20px;
+ position:relative;
+ -webkit-box-shadow:3px 3px 8px #200;
+ border-radius:5px;
+ visibility: hidden;
+}
+
+}
+</style>
+
+<script>
+function sendCommand(cmd) {
+ window.domAutomationController.setAutomationId(1);
+ window.domAutomationController.send(cmd);
+}
+// Show the offline page.
+function showPage() {
+ document.body.style.visibility = 'visible';
+}
+// Start the timer to show the page.
+function startTimer(time) {
+ // wait 2.5 seconds before showing 'load now', 'go back' button.
+ setTimeout('showPage()', time);
+}
+</script>
+</head>
+<body oncontextmenu="return false;" i18n-values="onload:on_load">
+<table width="100%" cellspacing="0" cellpadding="0">
+ <td class="cell" valign="middle" align="center">
+ <div class="box" id="box">
+ <div class="title" i18n-content="headLine"></div>
+ <div class="main" i18n-values=".innerHTML:description"></div>
+ <div class="main">
+ <form class="submission">
+ <input type="button" name="continue_button" i18n-values="value:load_button" onclick="sendCommand('proceed')"><br>
+ <input type="button" name="back_button" i18n-values="value:back_button;.style.display:display_go_back" onclick="sendCommand('dontproceed')">
+ </form>
+ </div>
+ </div>
+ <div id="thumbnail" class="thumbnail" i18n-values=".style.background-image:thumbnailUrl">
+ </div>
+ </td>
+</table>
+</body>
+</html>
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index 9a28c71..6ec5a66 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -26,6 +26,10 @@
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/network_state_notifier.h"
+#endif
+
// Are we in the process of restoring?
static bool restoring = false;
@@ -127,17 +131,29 @@ void TabLoader::ScheduleLoad(NavigationController* controller) {
}
void TabLoader::StartLoading() {
+#if defined(OS_CHROMEOS)
+ if (chromeos::NetworkStateNotifier::is_connected()) {
+ loading_ = true;
+ LoadNextTab();
+ } else {
+ // Start listening to network state notification now.
+ registrar_.Add(this, NotificationType::NETWORK_STATE_CHANGED,
+ NotificationService::AllSources());
+ }
+#else
loading_ = true;
LoadNextTab();
+#endif
}
void TabLoader::LoadNextTab() {
if (!tabs_to_load_.empty()) {
NavigationController* tab = tabs_to_load_.front();
+ DCHECK(tab);
tabs_loading_.insert(tab);
tabs_to_load_.pop_front();
tab->LoadIfNecessary();
- if (tab && tab->tab_contents()) {
+ if (tab->tab_contents()) {
int tab_index;
Browser* browser = Browser::GetBrowserForController(tab, &tab_index);
if (browser && browser->selected_index() != tab_index) {
@@ -169,13 +185,50 @@ void TabLoader::LoadNextTab() {
void TabLoader::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
- DCHECK(type == NotificationType::TAB_CLOSED ||
- type == NotificationType::LOAD_STOP);
- NavigationController* tab = Source<NavigationController>(source).ptr();
- RemoveTab(tab);
- if (loading_) {
- LoadNextTab();
- // WARNING: if there are no more tabs to load, we have been deleted.
+ switch (type.value) {
+#if defined(OS_CHROMEOS)
+ case NotificationType::NETWORK_STATE_CHANGED: {
+ chromeos::NetworkStateDetails* state_details =
+ Details<chromeos::NetworkStateDetails>(details).ptr();
+ switch (state_details->state()) {
+ case chromeos::NetworkStateDetails::CONNECTED:
+ if (!loading_) {
+ loading_ = true;
+ LoadNextTab();
+ }
+ // start loading
+ break;
+ case chromeos::NetworkStateDetails::CONNECTING:
+ // keep it going
+ break;
+ case chromeos::NetworkStateDetails::DISCONNECTED:
+ // disconnected while loading. set loaing_ false so
+ // that it stops trying to load next tab.
+ loading_ = false;
+ break;
+ default:
+ NOTREACHED() << "Unknown nework state notification:"
+ << state_details->state();
+ }
+ break;
+ }
+#endif
+ case NotificationType::TAB_CLOSED:
+ case NotificationType::LOAD_STOP: {
+ NavigationController* tab = Source<NavigationController>(source).ptr();
+ RemoveTab(tab);
+ if (loading_) {
+ LoadNextTab();
+ // WARNING: if there are no more tabs to load, we have been deleted.
+ } else if (tabs_to_load_.empty()) {
+ tabs_loading_.clear();
+ delete this;
+ return;
+ }
+ break;
+ }
+ default:
+ NOTREACHED() << "Unknown notification received:" << type.value;
}
}
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 441d14f..c96158d 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -517,6 +517,10 @@
'browser/chromeos/network_state_notifier.h',
'browser/chromeos/network_message_observer.cc',
'browser/chromeos/network_message_observer.h',
+ 'browser/chromeos/offline/offline_load_page.cc',
+ 'browser/chromeos/offline/offline_load_page.h',
+ 'browser/chromeos/offline/offline_load_service.cc',
+ 'browser/chromeos/offline/offline_load_service.h',
'browser/chromeos/options/internet_page_view.cc',
'browser/chromeos/options/internet_page_view.h',
'browser/chromeos/options/ip_config_view.cc',
@@ -2058,6 +2062,8 @@
'browser/renderer_host/gtk_im_context_wrapper.h',
'browser/renderer_host/gtk_key_bindings_handler.cc',
'browser/renderer_host/gtk_key_bindings_handler.h',
+ 'browser/renderer_host/offline_resource_handler.cc',
+ 'browser/renderer_host/offline_resource_handler.h',
'browser/renderer_host/render_process_host.cc',
'browser/renderer_host/render_process_host.h',
'browser/renderer_host/render_sandbox_host_linux.h',
@@ -2800,6 +2806,8 @@
['exclude', 'browser/dom_ui/filebrowse_ui.cc'],
['exclude', 'browser/dom_ui/mediaplayer_ui.cc'],
['exclude', 'browser/dom_ui/slideshow_ui.cc'],
+ ['exclude', 'browser/renderer_host/offline_resource_handler.cc'],
+ ['exclude', 'browser/renderer_host/offline_resource_handler.h'],
],
}],
['chromeos==1', {
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index ea886ca..c36ad23 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -613,6 +613,7 @@
'browser/chromeos/login/google_authenticator_unittest.cc',
'browser/chromeos/login/mock_auth_response_handler.cc',
'browser/chromeos/notifications/desktop_notifications_unittest.cc',
+ 'browser/chromeos/offline/offline_load_page_unittest.cc',
'browser/chromeos/options/language_config_model_unittest.cc',
'browser/chromeos/pipe_reader_unittest.cc',
'browser/chromeos/status/language_menu_button_unittest.cc',