summaryrefslogtreecommitdiffstats
path: root/chrome/browser/download/download_request_limiter.h
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/download/download_request_limiter.h')
-rw-r--r--chrome/browser/download/download_request_limiter.h251
1 files changed, 251 insertions, 0 deletions
diff --git a/chrome/browser/download/download_request_limiter.h b/chrome/browser/download/download_request_limiter.h
new file mode 100644
index 0000000..f5b4cfa
--- /dev/null
+++ b/chrome/browser/download/download_request_limiter.h
@@ -0,0 +1,251 @@
+// 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_DOWNLOAD_DOWNLOAD_REQUEST_LIMITER_H_
+#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_LIMITER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+
+class DownloadRequestInfoBarDelegate;
+class NavigationController;
+class TabContents;
+
+// DownloadRequestLimiter is responsible for determining whether a download
+// should be allowed or not. It is designed to keep pages from downloading
+// multiple files without user interaction. DownloadRequestLimiter is invoked
+// from ResourceDispatcherHost any time a download begins
+// (CanDownloadOnIOThread). The request is processed on the UI thread, and the
+// request is notified (back on the IO thread) as to whether the download should
+// be allowed or denied.
+//
+// Invoking CanDownloadOnIOThread notifies the callback and may update the
+// download status. The following details the various states:
+// . Each NavigationController initially starts out allowing a download
+// (ALLOW_ONE_DOWNLOAD).
+// . The first time CanDownloadOnIOThread is invoked the download is allowed and
+// the state changes to PROMPT_BEFORE_DOWNLOAD.
+// . If the state is PROMPT_BEFORE_DOWNLOAD and the user clicks the mouse,
+// presses enter, the space bar or navigates to another page the state is
+// reset to ALLOW_ONE_DOWNLOAD.
+// . If a download is attempted and the state is PROMPT_BEFORE_DOWNLOAD the user
+// is prompted as to whether the download is allowed or disallowed. The users
+// choice stays until the user navigates to a different host. For example, if
+// the user allowed the download, multiple downloads are allowed without any
+// user intervention until the user navigates to a different host.
+class DownloadRequestLimiter
+ : public base::RefCountedThreadSafe<DownloadRequestLimiter> {
+ public:
+ // Download status for a particular page. See class description for details.
+ enum DownloadStatus {
+ ALLOW_ONE_DOWNLOAD,
+ PROMPT_BEFORE_DOWNLOAD,
+ ALLOW_ALL_DOWNLOADS,
+ DOWNLOADS_NOT_ALLOWED
+ };
+
+ // Max number of downloads before a "Prompt Before Download" Dialog is shown.
+ static const size_t kMaxDownloadsAtOnce = 50;
+
+ // The callback from CanDownloadOnIOThread. This is invoked on the io thread.
+ class Callback {
+ public:
+ virtual void ContinueDownload() = 0;
+ virtual void CancelDownload() = 0;
+ virtual int GetRequestId() = 0;
+
+ protected:
+ virtual ~Callback() {}
+ };
+
+ // TabDownloadState maintains the download state for a particular tab.
+ // TabDownloadState prompts the user with an infobar as necessary.
+ // TabDownloadState deletes itself (by invoking
+ // DownloadRequestLimiter::Remove) as necessary.
+ class TabDownloadState : public NotificationObserver {
+ public:
+ // Creates a new TabDownloadState. |controller| is the controller the
+ // TabDownloadState tracks the state of and is the host for any dialogs that
+ // are displayed. |originating_controller| is used to determine the host of
+ // the initial download. If |originating_controller| is null, |controller|
+ // is used. |originating_controller| is typically null, but differs from
+ // |controller| in the case of a constrained popup requesting the download.
+ TabDownloadState(DownloadRequestLimiter* host,
+ NavigationController* controller,
+ NavigationController* originating_controller);
+ virtual ~TabDownloadState();
+
+ // Status of the download.
+ void set_download_status(DownloadRequestLimiter::DownloadStatus status) {
+ status_ = status;
+ }
+ DownloadRequestLimiter::DownloadStatus download_status() const {
+ return status_;
+ }
+
+ // Number of "ALLOWED" downloads.
+ void increment_download_count() {
+ download_count_++;
+ }
+ size_t download_count() const {
+ return download_count_;
+ }
+
+ // Invoked when a user gesture occurs (mouse click, enter or space). This
+ // may result in invoking Remove on DownloadRequestLimiter.
+ void OnUserGesture();
+
+ // Asks the user if they really want to allow the download.
+ // See description above CanDownloadOnIOThread for details on lifetime of
+ // callback.
+ void PromptUserForDownload(TabContents* tab,
+ DownloadRequestLimiter::Callback* callback);
+
+ // Are we showing a prompt to the user?
+ bool is_showing_prompt() const { return (infobar_ != NULL); }
+
+ // NavigationController we're tracking.
+ NavigationController* controller() const { return controller_; }
+
+ // Invoked from DownloadRequestDialogDelegate. Notifies the delegates and
+ // changes the status appropriately. Virtual for testing.
+ virtual void Cancel();
+ virtual void Accept();
+
+ protected:
+ // Used for testing.
+ TabDownloadState()
+ : host_(NULL),
+ controller_(NULL),
+ status_(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD),
+ download_count_(0),
+ infobar_(NULL) {
+ }
+
+ private:
+ // NotificationObserver method.
+ void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Notifies the callbacks as to whether the download is allowed or not.
+ // Updates status_ appropriately.
+ void NotifyCallbacks(bool allow);
+
+ DownloadRequestLimiter* host_;
+
+ NavigationController* controller_;
+
+ // Host of the first page the download started on. This may be empty.
+ std::string initial_page_host_;
+
+ DownloadRequestLimiter::DownloadStatus status_;
+
+ size_t download_count_;
+
+ // Callbacks we need to notify. This is only non-empty if we're showing a
+ // dialog.
+ // See description above CanDownloadOnIOThread for details on lifetime of
+ // callbacks.
+ std::vector<DownloadRequestLimiter::Callback*> callbacks_;
+
+ // Used to remove observers installed on NavigationController.
+ NotificationRegistrar registrar_;
+
+ // Handles showing the infobar to the user, may be null.
+ DownloadRequestInfoBarDelegate* infobar_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabDownloadState);
+ };
+
+ DownloadRequestLimiter();
+
+ // Returns the download status for a page. This does not change the state in
+ // anyway.
+ DownloadStatus GetDownloadStatus(TabContents* tab);
+
+ // Updates the state of the page as necessary and notifies the callback.
+ // WARNING: both this call and the callback are invoked on the io thread.
+ //
+ // DownloadRequestLimiter does not retain/release the Callback. It is up to
+ // the caller to ensure the callback is valid until the request is complete.
+ void CanDownloadOnIOThread(int render_process_host_id,
+ int render_view_id,
+ Callback* callback);
+
+ // Invoked when the user presses the mouse, enter key or space bar. This may
+ // change the download status for the page. See the class description for
+ // details.
+ void OnUserGesture(TabContents* tab);
+
+ private:
+ friend class base::RefCountedThreadSafe<DownloadRequestLimiter>;
+ friend class DownloadRequestLimiterTest;
+ friend class TabDownloadState;
+
+ ~DownloadRequestLimiter();
+
+ // For unit tests. If non-null this is used instead of creating a dialog.
+ class TestingDelegate {
+ public:
+ virtual bool ShouldAllowDownload() = 0;
+
+ protected:
+ virtual ~TestingDelegate() {}
+ };
+ static void SetTestingDelegate(TestingDelegate* delegate);
+
+ // Gets the download state for the specified controller. If the
+ // TabDownloadState does not exist and |create| is true, one is created.
+ // See TabDownloadState's constructor description for details on the two
+ // controllers.
+ //
+ // The returned TabDownloadState is owned by the DownloadRequestLimiter and
+ // deleted when no longer needed (the Remove method is invoked).
+ TabDownloadState* GetDownloadState(
+ NavigationController* controller,
+ NavigationController* originating_controller,
+ bool create);
+
+ // CanDownloadOnIOThread invokes this on the UI thread. This determines the
+ // tab and invokes CanDownloadImpl.
+ void CanDownload(int render_process_host_id,
+ int render_view_id,
+ Callback* callback);
+
+ // Does the work of updating the download status on the UI thread and
+ // potentially prompting the user.
+ void CanDownloadImpl(TabContents* originating_tab,
+ Callback* callback);
+
+ // Invoked on the UI thread. Schedules a call to NotifyCallback on the io
+ // thread.
+ void ScheduleNotification(Callback* callback, bool allow);
+
+ // Notifies the callback. This *must* be invoked on the IO thread.
+ void NotifyCallback(Callback* callback, bool allow);
+
+ // Removes the specified TabDownloadState from the internal map and deletes
+ // it. This has the effect of resetting the status for the tab to
+ // ALLOW_ONE_DOWNLOAD.
+ void Remove(TabDownloadState* state);
+
+ // Maps from tab to download state. The download state for a tab only exists
+ // if the state is other than ALLOW_ONE_DOWNLOAD. Similarly once the state
+ // transitions from anything but ALLOW_ONE_DOWNLOAD back to ALLOW_ONE_DOWNLOAD
+ // the TabDownloadState is removed and deleted (by way of Remove).
+ typedef std::map<NavigationController*, TabDownloadState*> StateMap;
+ StateMap state_map_;
+
+ static TestingDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadRequestLimiter);
+};
+
+#endif // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_LIMITER_H_