summaryrefslogtreecommitdiffstats
path: root/content/public/test/download_test_observer.h
blob: 6626ffe5ece6d5bac9e3c2e3342ef159e8f00a17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
// 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 <set>
#include <vector>

#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<bool(DownloadItem*)> 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<DownloadItem*> DownloadSet;

  // Maps states to the number of times they have been encountered
  typedef std::map<DownloadItem::DownloadState, size_t> 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<DownloadManager> 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<int32> 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<DownloadTestFlushObserver> {
 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<DownloadTestFlushObserver>;

  virtual ~DownloadTestFlushObserver();

 private:
  typedef std::set<DownloadItem*> 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<DownloadTestItemCreationObserver> {
 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>;

  ~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_