// Copyright (c) 2012 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 CONTENT_TEST_DOWNLOAD_TEST_OBSERVER_H_ #define CONTENT_TEST_DOWNLOAD_TEST_OBSERVER_H_ #include #include #include "base/basictypes.h" #include "base/callback_forward.h" #include "base/memory/ref_counted.h" #include "content/public/browser/download_item.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/download_url_parameters.h" #include "net/base/net_errors.h" namespace content { // Detects an arbitrary change on a download item. // TODO: Rewrite other observers to use this (or be replaced by it). class DownloadUpdatedObserver : public DownloadItem::Observer { public: typedef base::Callback EventFilter; // The filter passed may be called multiple times, even after it // returns true. DownloadUpdatedObserver(DownloadItem* item, EventFilter filter); virtual ~DownloadUpdatedObserver(); // Returns when either the event has been seen (at least once since // object construction) or the item is destroyed. Return value indicates // if the wait ended because the item was seen (true) or the object // destroyed (false). bool WaitForEvent(); private: // DownloadItem::Observer virtual void OnDownloadUpdated(DownloadItem* item) OVERRIDE; virtual void OnDownloadDestroyed(DownloadItem* item) OVERRIDE; DownloadItem* item_; EventFilter filter_; bool waiting_; bool event_seen_; DISALLOW_COPY_AND_ASSIGN(DownloadUpdatedObserver); }; // Detects changes to the downloads after construction. // Finishes when one of the following happens: // - A specified number of downloads change to a terminal state (defined // in derived classes). // - Specific events, such as a select file dialog. // Callers may either probe for the finished state, or wait on it. // // TODO(rdsmith): Detect manager going down, remove pointer to // DownloadManager, transition to finished. (For right now we // just use a scoped_refptr<> to keep it around, but that may cause // timeouts on waiting if a DownloadManager::Shutdown() occurs which // cancels our in-progress downloads.) class DownloadTestObserver : public DownloadManager::Observer, public DownloadItem::Observer { public: // Action an observer should take if a dangerous download is encountered. enum DangerousDownloadAction { ON_DANGEROUS_DOWNLOAD_ACCEPT, // Accept the download ON_DANGEROUS_DOWNLOAD_DENY, // Deny the download ON_DANGEROUS_DOWNLOAD_FAIL, // Fail if a dangerous download is seen ON_DANGEROUS_DOWNLOAD_IGNORE // Make it the callers problem. }; // Create an object that will be considered finished when |wait_count| // download items have entered a terminal state. DownloadTestObserver(DownloadManager* download_manager, size_t wait_count, DangerousDownloadAction dangerous_download_action); virtual ~DownloadTestObserver(); // Wait for the requested number of downloads to enter a terminal state. void WaitForFinished(); // Return true if everything's happened that we're configured for. bool IsFinished() const; // DownloadItem::Observer virtual void OnDownloadUpdated(DownloadItem* download) OVERRIDE; virtual void OnDownloadDestroyed(DownloadItem* download) OVERRIDE; // DownloadManager::Observer virtual void OnDownloadCreated( DownloadManager* manager, DownloadItem* item) OVERRIDE; size_t NumDangerousDownloadsSeen() const; size_t NumDownloadsSeenInState(DownloadItem::DownloadState state) const; protected: // Only to be called by derived classes' constructors. virtual void Init(); // Called to see if a download item is in a final state. virtual bool IsDownloadInFinalState(DownloadItem* download) = 0; private: typedef std::set DownloadSet; // Maps states to the number of times they have been encountered typedef std::map StateMap; // Called when we know that a download item is in a final state. // Note that this is not the same as it first transitioning in to the // final state; multiple notifications may occur once the item is in // that state. So we keep our own track of transitions into final. void DownloadInFinalState(DownloadItem* download); void SignalIfFinished(); // The observed download manager. scoped_refptr download_manager_; // The set of DownloadItem's that have transitioned to their finished state // since construction of this object. When the size of this array // reaches wait_count_, we're done. DownloadSet finished_downloads_; // The set of DownloadItem's we are currently observing. Generally there // won't be any overlap with the above; once we see the final state // on a DownloadItem, we'll stop observing it. DownloadSet downloads_observed_; // The map of states to the number of times they have been observed since // we started looking. // Recorded at the time downloads_observed_ is recorded, but cleared in the // constructor to exclude pre-existing states. StateMap states_observed_; // The number of downloads to wait on completing. size_t wait_count_; // The number of downloads entered in final state in Init(). We use // |finished_downloads_| to track the incoming transitions to final state we // should ignore, and to track the number of final state transitions that // occurred between construction and return from wait. But some downloads may // be in our final state (and thus be entered into |finished_downloads_|) when // we construct this class. We don't want to count those in our transition to // finished. int finished_downloads_at_construction_; // Whether an internal message loop has been started and must be quit upon // all downloads completing. bool waiting_; // Action to take if a dangerous download is encountered. DangerousDownloadAction dangerous_download_action_; // Holds the download ids which were dangerous. std::set dangerous_downloads_seen_; DISALLOW_COPY_AND_ASSIGN(DownloadTestObserver); }; class DownloadTestObserverTerminal : public DownloadTestObserver { public: // Create an object that will be considered finished when |wait_count| // download items have entered a terminal state (any but IN_PROGRESS). // If |finish_on_select_file| is true, the object will also be // considered finished if the DownloadManager raises a // SelectFileDialogDisplayed() notification. DownloadTestObserverTerminal( DownloadManager* download_manager, size_t wait_count, DangerousDownloadAction dangerous_download_action); virtual ~DownloadTestObserverTerminal(); private: virtual bool IsDownloadInFinalState(DownloadItem* download) OVERRIDE; DISALLOW_COPY_AND_ASSIGN(DownloadTestObserverTerminal); }; // Detects changes to the downloads after construction. // Finishes when a specified number of downloads change to the // IN_PROGRESS state, or a Select File Dialog has appeared. // Dangerous downloads are accepted. // Callers may either probe for the finished state, or wait on it. class DownloadTestObserverInProgress : public DownloadTestObserver { public: // Create an object that will be considered finished when |wait_count| // download items have entered state |IN_PROGRESS|. // If |finish_on_select_file| is true, the object will also be // considered finished if the DownloadManager raises a // SelectFileDialogDisplayed() notification. DownloadTestObserverInProgress( DownloadManager* download_manager, size_t wait_count); virtual ~DownloadTestObserverInProgress(); private: virtual bool IsDownloadInFinalState(DownloadItem* download) OVERRIDE; DISALLOW_COPY_AND_ASSIGN(DownloadTestObserverInProgress); }; // The WaitForFlush() method on this class returns after: // * There are no IN_PROGRESS download items remaining on the // DownloadManager. // * There have been two round trip messages through the file and // IO threads. // This almost certainly means that a Download cancel has propagated through // the system. class DownloadTestFlushObserver : public DownloadManager::Observer, public DownloadItem::Observer, public base::RefCountedThreadSafe { public: explicit DownloadTestFlushObserver(DownloadManager* download_manager); void WaitForFlush(); // DownloadsManager observer methods. virtual void OnDownloadCreated( DownloadManager* manager, DownloadItem* item) OVERRIDE; // DownloadItem observer methods. virtual void OnDownloadUpdated(DownloadItem* download) OVERRIDE; virtual void OnDownloadDestroyed(DownloadItem* download) OVERRIDE; protected: friend class base::RefCountedThreadSafe; virtual ~DownloadTestFlushObserver(); private: typedef std::set DownloadSet; // If we're waiting for that flush point, check the number // of downloads in the IN_PROGRESS state and take appropriate // action. If requested, also observes all downloads while iterating. void CheckDownloadsInProgress(bool observe_downloads); void PingFileThread(int cycle); void PingIOThread(int cycle); DownloadManager* download_manager_; DownloadSet downloads_observed_; bool waiting_for_zero_inprogress_; DISALLOW_COPY_AND_ASSIGN(DownloadTestFlushObserver); }; // Waits for a callback indicating that the DownloadItem is about to be created, // or that an error occurred and it won't be created. class DownloadTestItemCreationObserver : public base::RefCountedThreadSafe { public: DownloadTestItemCreationObserver(); void WaitForDownloadItemCreation(); int download_id() const { return download_id_; } net::Error error() const { return error_; } bool started() const { return called_back_count_ > 0; } bool succeeded() const { return started() && (error_ == net::OK); } const DownloadUrlParameters::OnStartedCallback callback(); private: friend class base::RefCountedThreadSafe; ~DownloadTestItemCreationObserver(); void DownloadItemCreationCallback(DownloadItem* item, net::Error error); // The download creation information we received. int download_id_; net::Error error_; // Count of callbacks. size_t called_back_count_; // We are in the message loop. bool waiting_; DISALLOW_COPY_AND_ASSIGN(DownloadTestItemCreationObserver); }; } // namespace content` #endif // CONTENT_TEST_DOWNLOAD_TEST_OBSERVER_H_