Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(535)

Side by Side Diff: chrome/browser/google_apis/base_requests.h

Issue 18316002: Move URLRequestContextGetter to RequestSender in c/b/google_apis. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/drive/gdata_wapi_service.cc ('k') | chrome/browser/google_apis/base_requests.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 // 4 //
5 // This file provides base classes used to issue HTTP requests for Google 5 // This file provides base classes used to issue HTTP requests for Google
6 // APIs. 6 // APIs.
7 7
8 #ifndef CHROME_BROWSER_GOOGLE_APIS_BASE_REQUESTS_H_ 8 #ifndef CHROME_BROWSER_GOOGLE_APIS_BASE_REQUESTS_H_
9 #define CHROME_BROWSER_GOOGLE_APIS_BASE_REQUESTS_H_ 9 #define CHROME_BROWSER_GOOGLE_APIS_BASE_REQUESTS_H_
10 10
11 #include <string> 11 #include <string>
12 #include <vector> 12 #include <vector>
13 13
14 #include "base/callback.h" 14 #include "base/callback.h"
15 #include "base/files/file_path.h" 15 #include "base/files/file_path.h"
16 #include "base/memory/weak_ptr.h" 16 #include "base/memory/weak_ptr.h"
17 #include "base/threading/thread_checker.h" 17 #include "base/threading/thread_checker.h"
18 #include "chrome/browser/google_apis/gdata_errorcode.h" 18 #include "chrome/browser/google_apis/gdata_errorcode.h"
19 #include "googleurl/src/gurl.h" 19 #include "googleurl/src/gurl.h"
20 #include "net/url_request/url_fetcher.h" 20 #include "net/url_request/url_fetcher.h"
21 #include "net/url_request/url_fetcher_delegate.h" 21 #include "net/url_request/url_fetcher_delegate.h"
22 22
23 namespace base { 23 namespace base {
24 class Value; 24 class Value;
25 } // namespace base 25 } // namespace base
26 26
27 namespace net {
28 class URLRequestContextGetter;
29 } // namespace net
30
31 namespace google_apis { 27 namespace google_apis {
32 28
33 class RequestSender; 29 class RequestSender;
34 30
35 // Callback used to pass parsed JSON from ParseJson(). If parsing error occurs, 31 // Callback used to pass parsed JSON from ParseJson(). If parsing error occurs,
36 // then the passed argument is null. 32 // then the passed argument is null.
37 typedef base::Callback<void(scoped_ptr<base::Value> value)> ParseJsonCallback; 33 typedef base::Callback<void(scoped_ptr<base::Value> value)> ParseJsonCallback;
38 34
39 // Callback used for DownloadFileRequest and ResumeUploadRequestBase. 35 // Callback used for DownloadFileRequest and ResumeUploadRequestBase.
40 typedef base::Callback<void(int64 progress, int64 total)> ProgressCallback; 36 typedef base::Callback<void(int64 progress, int64 total)> ProgressCallback;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
90 public net::URLFetcherDelegate { 86 public net::URLFetcherDelegate {
91 public: 87 public:
92 // AuthenticatedRequestInterface overrides. 88 // AuthenticatedRequestInterface overrides.
93 virtual void Start(const std::string& access_token, 89 virtual void Start(const std::string& access_token,
94 const std::string& custom_user_agent, 90 const std::string& custom_user_agent,
95 const ReAuthenticateCallback& callback) OVERRIDE; 91 const ReAuthenticateCallback& callback) OVERRIDE;
96 virtual base::WeakPtr<AuthenticatedRequestInterface> GetWeakPtr() OVERRIDE; 92 virtual base::WeakPtr<AuthenticatedRequestInterface> GetWeakPtr() OVERRIDE;
97 virtual void Cancel() OVERRIDE; 93 virtual void Cancel() OVERRIDE;
98 94
99 protected: 95 protected:
100 UrlFetchRequestBase(RequestSender* sender, 96 explicit UrlFetchRequestBase(RequestSender* sender);
101 net::URLRequestContextGetter* url_request_context_getter);
102 virtual ~UrlFetchRequestBase(); 97 virtual ~UrlFetchRequestBase();
103 98
104 // Gets URL for the request. 99 // Gets URL for the request.
105 virtual GURL GetURL() const = 0; 100 virtual GURL GetURL() const = 0;
106 101
107 // Returns the request type. A derived class should override this method 102 // Returns the request type. A derived class should override this method
108 // for a request type other than HTTP GET. 103 // for a request type other than HTTP GET.
109 virtual net::URLFetcher::RequestType GetRequestType() const; 104 virtual net::URLFetcher::RequestType GetRequestType() const;
110 105
111 // Returns the extra HTTP headers for the request. A derived class should 106 // Returns the extra HTTP headers for the request. A derived class should
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 output_file_path_ = output_file_path; 156 output_file_path_ = output_file_path;
162 } 157 }
163 158
164 private: 159 private:
165 // URLFetcherDelegate overrides. 160 // URLFetcherDelegate overrides.
166 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; 161 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
167 162
168 // AuthenticatedRequestInterface overrides. 163 // AuthenticatedRequestInterface overrides.
169 virtual void OnAuthFailed(GDataErrorCode code) OVERRIDE; 164 virtual void OnAuthFailed(GDataErrorCode code) OVERRIDE;
170 165
171 net::URLRequestContextGetter* url_request_context_getter_;
172 ReAuthenticateCallback re_authenticate_callback_; 166 ReAuthenticateCallback re_authenticate_callback_;
173 int re_authenticate_count_; 167 int re_authenticate_count_;
174 scoped_ptr<net::URLFetcher> url_fetcher_; 168 scoped_ptr<net::URLFetcher> url_fetcher_;
175 RequestSender* sender_; 169 RequestSender* sender_;
176 170
177 bool save_temp_file_; 171 bool save_temp_file_;
178 base::FilePath output_file_path_; 172 base::FilePath output_file_path_;
179 173
180 base::ThreadChecker thread_checker_; 174 base::ThreadChecker thread_checker_;
181 175
182 // Note: This should remain the last member so it'll be destroyed and 176 // Note: This should remain the last member so it'll be destroyed and
183 // invalidate its weak pointers before any other members are destroyed. 177 // invalidate its weak pointers before any other members are destroyed.
184 base::WeakPtrFactory<UrlFetchRequestBase> weak_ptr_factory_; 178 base::WeakPtrFactory<UrlFetchRequestBase> weak_ptr_factory_;
185 179
186 DISALLOW_COPY_AND_ASSIGN(UrlFetchRequestBase); 180 DISALLOW_COPY_AND_ASSIGN(UrlFetchRequestBase);
187 }; 181 };
188 182
189 //============================ EntryActionRequest ============================ 183 //============================ EntryActionRequest ============================
190 184
191 // Callback type for Delete/Move DocumentServiceInterface calls. 185 // Callback type for Delete/Move DocumentServiceInterface calls.
192 typedef base::Callback<void(GDataErrorCode error)> EntryActionCallback; 186 typedef base::Callback<void(GDataErrorCode error)> EntryActionCallback;
193 187
194 // This class performs a simple action over a given entry (document/file). 188 // This class performs a simple action over a given entry (document/file).
195 // It is meant to be used for requests that return no JSON blobs. 189 // It is meant to be used for requests that return no JSON blobs.
196 class EntryActionRequest : public UrlFetchRequestBase { 190 class EntryActionRequest : public UrlFetchRequestBase {
197 public: 191 public:
198 // |url_request_context_getter| is used to initialize URLFetcher. 192 // |url_request_context_getter| is used to initialize URLFetcher.
199 // |callback| must not be null. 193 // |callback| must not be null.
200 EntryActionRequest( 194 EntryActionRequest(RequestSender* sender,
201 RequestSender* runner, 195 const EntryActionCallback& callback);
202 net::URLRequestContextGetter* url_request_context_getter,
203 const EntryActionCallback& callback);
204 virtual ~EntryActionRequest(); 196 virtual ~EntryActionRequest();
205 197
206 protected: 198 protected:
207 // Overridden from UrlFetchRequestBase. 199 // Overridden from UrlFetchRequestBase.
208 virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE; 200 virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE;
209 virtual void RunCallbackOnPrematureFailure(GDataErrorCode code) OVERRIDE; 201 virtual void RunCallbackOnPrematureFailure(GDataErrorCode code) OVERRIDE;
210 202
211 private: 203 private:
212 const EntryActionCallback callback_; 204 const EntryActionCallback callback_;
213 205
214 DISALLOW_COPY_AND_ASSIGN(EntryActionRequest); 206 DISALLOW_COPY_AND_ASSIGN(EntryActionRequest);
215 }; 207 };
216 208
217 //============================== GetDataRequest ============================== 209 //============================== GetDataRequest ==============================
218 210
219 // Callback type for DocumentServiceInterface::GetResourceList. 211 // Callback type for DocumentServiceInterface::GetResourceList.
220 // Note: json_data argument should be passed using base::Passed(&json_data), not 212 // Note: json_data argument should be passed using base::Passed(&json_data), not
221 // json_data.Pass(). 213 // json_data.Pass().
222 typedef base::Callback<void(GDataErrorCode error, 214 typedef base::Callback<void(GDataErrorCode error,
223 scoped_ptr<base::Value> json_data)> GetDataCallback; 215 scoped_ptr<base::Value> json_data)> GetDataCallback;
224 216
225 // This class performs the request for fetching and converting the fetched 217 // This class performs the request for fetching and converting the fetched
226 // content into a base::Value. 218 // content into a base::Value.
227 class GetDataRequest : public UrlFetchRequestBase { 219 class GetDataRequest : public UrlFetchRequestBase {
228 public: 220 public:
229 // |callback| must not be null. 221 // |callback| must not be null.
230 GetDataRequest(RequestSender* runner, 222 GetDataRequest(RequestSender* sender, const GetDataCallback& callback);
231 net::URLRequestContextGetter* url_request_context_getter,
232 const GetDataCallback& callback);
233 virtual ~GetDataRequest(); 223 virtual ~GetDataRequest();
234 224
235 // Parses JSON response. 225 // Parses JSON response.
236 void ParseResponse(GDataErrorCode fetch_error_code, const std::string& data); 226 void ParseResponse(GDataErrorCode fetch_error_code, const std::string& data);
237 227
238 protected: 228 protected:
239 // UrlFetchRequestBase overrides. 229 // UrlFetchRequestBase overrides.
240 virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE; 230 virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE;
241 virtual void RunCallbackOnPrematureFailure( 231 virtual void RunCallbackOnPrematureFailure(
242 GDataErrorCode fetch_error_code) OVERRIDE; 232 GDataErrorCode fetch_error_code) OVERRIDE;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
278 // of the target file to the upload URL 268 // of the target file to the upload URL
279 // 3) If there is more data to upload, go to 2). 269 // 3) If there is more data to upload, go to 2).
280 // 270 //
281 class InitiateUploadRequestBase : public UrlFetchRequestBase { 271 class InitiateUploadRequestBase : public UrlFetchRequestBase {
282 protected: 272 protected:
283 // |callback| will be called with the upload URL, where upload data is 273 // |callback| will be called with the upload URL, where upload data is
284 // uploaded to with ResumeUploadRequestBase. 274 // uploaded to with ResumeUploadRequestBase.
285 // |callback| must not be null. 275 // |callback| must not be null.
286 // |content_type| and |content_length| should be the attributes of the 276 // |content_type| and |content_length| should be the attributes of the
287 // uploading file. 277 // uploading file.
288 InitiateUploadRequestBase( 278 InitiateUploadRequestBase(RequestSender* sender,
289 RequestSender* runner, 279 const InitiateUploadCallback& callback,
290 net::URLRequestContextGetter* url_request_context_getter, 280 const std::string& content_type,
291 const InitiateUploadCallback& callback, 281 int64 content_length);
292 const std::string& content_type,
293 int64 content_length);
294 virtual ~InitiateUploadRequestBase(); 282 virtual ~InitiateUploadRequestBase();
295 283
296 // UrlFetchRequestBase overrides. 284 // UrlFetchRequestBase overrides.
297 virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE; 285 virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE;
298 virtual void RunCallbackOnPrematureFailure(GDataErrorCode code) OVERRIDE; 286 virtual void RunCallbackOnPrematureFailure(GDataErrorCode code) OVERRIDE;
299 virtual std::vector<std::string> GetExtraRequestHeaders() const OVERRIDE; 287 virtual std::vector<std::string> GetExtraRequestHeaders() const OVERRIDE;
300 288
301 private: 289 private:
302 const InitiateUploadCallback callback_; 290 const InitiateUploadCallback callback_;
303 const std::string content_type_; 291 const std::string content_type_;
(...skipping 22 matching lines...) Expand all
326 int64 start_position_received; 314 int64 start_position_received;
327 int64 end_position_received; 315 int64 end_position_received;
328 }; 316 };
329 317
330 // Base class for a URL fetch request expecting the response containing the 318 // Base class for a URL fetch request expecting the response containing the
331 // current uploading range. This class processes the response containing 319 // current uploading range. This class processes the response containing
332 // "Range" header and invoke OnRangeRequestComplete. 320 // "Range" header and invoke OnRangeRequestComplete.
333 class UploadRangeRequestBase : public UrlFetchRequestBase { 321 class UploadRangeRequestBase : public UrlFetchRequestBase {
334 protected: 322 protected:
335 // |upload_location| is the URL of where to upload the file to. 323 // |upload_location| is the URL of where to upload the file to.
336 UploadRangeRequestBase( 324 UploadRangeRequestBase(RequestSender* sender, const GURL& upload_url);
337 RequestSender* runner,
338 net::URLRequestContextGetter* url_request_context_getter,
339 const GURL& upload_url);
340 virtual ~UploadRangeRequestBase(); 325 virtual ~UploadRangeRequestBase();
341 326
342 // UrlFetchRequestBase overrides. 327 // UrlFetchRequestBase overrides.
343 virtual GURL GetURL() const OVERRIDE; 328 virtual GURL GetURL() const OVERRIDE;
344 virtual net::URLFetcher::RequestType GetRequestType() const OVERRIDE; 329 virtual net::URLFetcher::RequestType GetRequestType() const OVERRIDE;
345 virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE; 330 virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE;
346 virtual void RunCallbackOnPrematureFailure(GDataErrorCode code) OVERRIDE; 331 virtual void RunCallbackOnPrematureFailure(GDataErrorCode code) OVERRIDE;
347 332
348 // This method will be called when the request is done, regardless of 333 // This method will be called when the request is done, regardless of
349 // whether it is succeeded or failed. 334 // whether it is succeeded or failed.
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
390 protected: 375 protected:
391 // |start_position| is the start of range of contents currently stored in 376 // |start_position| is the start of range of contents currently stored in
392 // |buf|. |end_position| is the end of range of contents currently stared in 377 // |buf|. |end_position| is the end of range of contents currently stared in
393 // |buf|. This is exclusive. For instance, if you are to upload the first 378 // |buf|. This is exclusive. For instance, if you are to upload the first
394 // 500 bytes of data, |start_position| is 0 and |end_position| is 500. 379 // 500 bytes of data, |start_position| is 0 and |end_position| is 500.
395 // |content_length| and |content_type| are the length and type of the 380 // |content_length| and |content_type| are the length and type of the
396 // file content to be uploaded respectively. 381 // file content to be uploaded respectively.
397 // |buf| holds current content to be uploaded. 382 // |buf| holds current content to be uploaded.
398 // See also UploadRangeRequestBase's comment for remaining parameters 383 // See also UploadRangeRequestBase's comment for remaining parameters
399 // meaning. 384 // meaning.
400 ResumeUploadRequestBase( 385 ResumeUploadRequestBase(RequestSender* sender,
401 RequestSender* runner, 386 const GURL& upload_location,
402 net::URLRequestContextGetter* url_request_context_getter, 387 int64 start_position,
403 const GURL& upload_location, 388 int64 end_position,
404 int64 start_position, 389 int64 content_length,
405 int64 end_position, 390 const std::string& content_type,
406 int64 content_length, 391 const base::FilePath& local_file_path);
407 const std::string& content_type,
408 const base::FilePath& local_file_path);
409 virtual ~ResumeUploadRequestBase(); 392 virtual ~ResumeUploadRequestBase();
410 393
411 // UrlFetchRequestBase overrides. 394 // UrlFetchRequestBase overrides.
412 virtual std::vector<std::string> GetExtraRequestHeaders() const OVERRIDE; 395 virtual std::vector<std::string> GetExtraRequestHeaders() const OVERRIDE;
413 virtual bool GetContentFile(base::FilePath* local_file_path, 396 virtual bool GetContentFile(base::FilePath* local_file_path,
414 int64* range_offset, 397 int64* range_offset,
415 int64* range_length, 398 int64* range_length,
416 std::string* upload_content_type) OVERRIDE; 399 std::string* upload_content_type) OVERRIDE;
417 400
418 private: 401 private:
(...skipping 15 matching lines...) Expand all
434 // - HTTP_RESUME_INCOMPLETE and the range of previously uploaded data, 417 // - HTTP_RESUME_INCOMPLETE and the range of previously uploaded data,
435 // if a file has been partially uploaded. |value| is not used. 418 // if a file has been partially uploaded. |value| is not used.
436 // - HTTP_SUCCESS or HTTP_CREATED (up to the upload mode) and |value| 419 // - HTTP_SUCCESS or HTTP_CREATED (up to the upload mode) and |value|
437 // for the uploaded data, if a file has been completely uploaded. 420 // for the uploaded data, if a file has been completely uploaded.
438 // See also UploadRangeRequestBase. 421 // See also UploadRangeRequestBase.
439 class GetUploadStatusRequestBase : public UploadRangeRequestBase { 422 class GetUploadStatusRequestBase : public UploadRangeRequestBase {
440 public: 423 public:
441 // |content_length| is the whole data size to be uploaded. 424 // |content_length| is the whole data size to be uploaded.
442 // See also UploadRangeRequestBase's constructor comment for other 425 // See also UploadRangeRequestBase's constructor comment for other
443 // parameters. 426 // parameters.
444 GetUploadStatusRequestBase( 427 GetUploadStatusRequestBase(RequestSender* sender,
445 RequestSender* runner, 428 const GURL& upload_url,
446 net::URLRequestContextGetter* url_request_context_getter, 429 int64 content_length);
447 const GURL& upload_url,
448 int64 content_length);
449 virtual ~GetUploadStatusRequestBase(); 430 virtual ~GetUploadStatusRequestBase();
450 431
451 protected: 432 protected:
452 // UrlFetchRequestBase overrides. 433 // UrlFetchRequestBase overrides.
453 virtual std::vector<std::string> GetExtraRequestHeaders() const OVERRIDE; 434 virtual std::vector<std::string> GetExtraRequestHeaders() const OVERRIDE;
454 435
455 private: 436 private:
456 const int64 content_length_; 437 const int64 content_length_;
457 438
458 DISALLOW_COPY_AND_ASSIGN(GetUploadStatusRequestBase); 439 DISALLOW_COPY_AND_ASSIGN(GetUploadStatusRequestBase);
(...skipping 24 matching lines...) Expand all
483 // progress_callback: 464 // progress_callback:
484 // This callback is called for periodically reporting the number of bytes 465 // This callback is called for periodically reporting the number of bytes
485 // downloaded so far. May be null. 466 // downloaded so far. May be null.
486 // 467 //
487 // download_url: 468 // download_url:
488 // Specifies the target file to download. 469 // Specifies the target file to download.
489 // 470 //
490 // output_file_path: 471 // output_file_path:
491 // Specifies the file path to save the downloaded file. 472 // Specifies the file path to save the downloaded file.
492 // 473 //
493 DownloadFileRequest( 474 DownloadFileRequest(RequestSender* sender,
494 RequestSender* runner, 475 const DownloadActionCallback& download_action_callback,
495 net::URLRequestContextGetter* url_request_context_getter, 476 const GetContentCallback& get_content_callback,
496 const DownloadActionCallback& download_action_callback, 477 const ProgressCallback& progress_callback,
497 const GetContentCallback& get_content_callback, 478 const GURL& download_url,
498 const ProgressCallback& progress_callback, 479 const base::FilePath& output_file_path);
499 const GURL& download_url,
500 const base::FilePath& output_file_path);
501 virtual ~DownloadFileRequest(); 480 virtual ~DownloadFileRequest();
502 481
503 protected: 482 protected:
504 // UrlFetchRequestBase overrides. 483 // UrlFetchRequestBase overrides.
505 virtual GURL GetURL() const OVERRIDE; 484 virtual GURL GetURL() const OVERRIDE;
506 virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE; 485 virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE;
507 virtual void RunCallbackOnPrematureFailure(GDataErrorCode code) OVERRIDE; 486 virtual void RunCallbackOnPrematureFailure(GDataErrorCode code) OVERRIDE;
508 487
509 // net::URLFetcherDelegate overrides. 488 // net::URLFetcherDelegate overrides.
510 virtual void OnURLFetchDownloadProgress(const net::URLFetcher* source, 489 virtual void OnURLFetchDownloadProgress(const net::URLFetcher* source,
511 int64 current, int64 total) OVERRIDE; 490 int64 current, int64 total) OVERRIDE;
512 virtual bool ShouldSendDownloadData() OVERRIDE; 491 virtual bool ShouldSendDownloadData() OVERRIDE;
513 virtual void OnURLFetchDownloadData( 492 virtual void OnURLFetchDownloadData(
514 const net::URLFetcher* source, 493 const net::URLFetcher* source,
515 scoped_ptr<std::string> download_data) OVERRIDE; 494 scoped_ptr<std::string> download_data) OVERRIDE;
516 495
517 private: 496 private:
518 const DownloadActionCallback download_action_callback_; 497 const DownloadActionCallback download_action_callback_;
519 const GetContentCallback get_content_callback_; 498 const GetContentCallback get_content_callback_;
520 const ProgressCallback progress_callback_; 499 const ProgressCallback progress_callback_;
521 const GURL download_url_; 500 const GURL download_url_;
522 501
523 DISALLOW_COPY_AND_ASSIGN(DownloadFileRequest); 502 DISALLOW_COPY_AND_ASSIGN(DownloadFileRequest);
524 }; 503 };
525 504
526 } // namespace google_apis 505 } // namespace google_apis
527 506
528 #endif // CHROME_BROWSER_GOOGLE_APIS_BASE_REQUESTS_H_ 507 #endif // CHROME_BROWSER_GOOGLE_APIS_BASE_REQUESTS_H_
OLDNEW
« no previous file with comments | « chrome/browser/drive/gdata_wapi_service.cc ('k') | chrome/browser/google_apis/base_requests.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698