Skip to content

Instantly share code, notes, and snippets.

@bartoszalksnin
Created April 15, 2017 18:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bartoszalksnin/a7652bedad431ea2badf6418d811d7be to your computer and use it in GitHub Desktop.
Save bartoszalksnin/a7652bedad431ea2badf6418d811d7be to your computer and use it in GitHub Desktop.
From fa8c1190b9e60c863014f6b4717ac0e6147e9c84 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C2=A8Bartosz?= <¨bartek@gopromo.pl¨>
Date: Sat, 15 Apr 2017 19:17:15 +0100
Subject: [PATCH] enable download for headless mode
---
headless/BUILD.gn | 2 +
.../lib/browser/headless_browser_context_impl.cc | 8 +-
.../lib/browser/headless_browser_context_impl.h | 2 +
.../browser/headless_browser_download_delegate.cc | 168 +++++++++++++++++++++
.../browser/headless_browser_download_delegate.h | 67 ++++++++
5 files changed, 246 insertions(+), 1 deletion(-)
create mode 100644 headless/lib/browser/headless_browser_download_delegate.cc
create mode 100644 headless/lib/browser/headless_browser_download_delegate.h
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 55afe1f..8f8c94f 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -210,6 +210,8 @@ static_library("headless_lib") {
"lib/browser/headless_browser_context_impl.h",
"lib/browser/headless_browser_context_options.cc",
"lib/browser/headless_browser_context_options.h",
+ "lib/browser/headless_browser_download_delegate.cc",
+ "lib/browser/headless_browser_download_delegate.h",
"lib/browser/headless_browser_impl.cc",
"lib/browser/headless_browser_impl.h",
"lib/browser/headless_browser_impl_mac.mm",
diff --git a/headless/lib/browser/headless_browser_context_impl.cc b/headless/lib/browser/headless_browser_context_impl.cc
index b691760..8155d58 100644
--- a/headless/lib/browser/headless_browser_context_impl.cc
+++ b/headless/lib/browser/headless_browser_context_impl.cc
@@ -178,7 +178,13 @@ content::ResourceContext* HeadlessBrowserContextImpl::GetResourceContext() {
content::DownloadManagerDelegate*
HeadlessBrowserContextImpl::GetDownloadManagerDelegate() {
- return nullptr;
+ if (!download_delegate_.get()) {
+ download_delegate_.reset(new HeadlessBrowserDownloadDelegate());
+ download_delegate_->SetDownloadManager(
+ content::BrowserContext::GetDownloadManager(this));
+ }
+
+ return download_delegate_.get();
}
content::BrowserPluginGuestManager*
diff --git a/headless/lib/browser/headless_browser_context_impl.h b/headless/lib/browser/headless_browser_context_impl.h
index 6882449..5fbc976 100644
--- a/headless/lib/browser/headless_browser_context_impl.h
+++ b/headless/lib/browser/headless_browser_context_impl.h
@@ -18,6 +18,7 @@
#include "headless/lib/browser/headless_url_request_context_getter.h"
#include "headless/public/headless_browser.h"
#include "headless/public/headless_browser_context.h"
+#include "headless/lib/browser/headless_browser_download_delegate.h"
namespace headless {
class HeadlessBrowserImpl;
@@ -93,6 +94,7 @@ class HeadlessBrowserContextImpl : public HeadlessBrowserContext,
HeadlessBrowserImpl* browser_; // Not owned.
std::unique_ptr<HeadlessBrowserContextOptions> context_options_;
std::unique_ptr<HeadlessResourceContext> resource_context_;
+ std::unique_ptr<HeadlessBrowserDownloadDelegate> download_delegate_;
base::FilePath path_;
std::unordered_map<std::string, std::unique_ptr<HeadlessWebContents>>
diff --git a/headless/lib/browser/headless_browser_download_delegate.cc b/headless/lib/browser/headless_browser_download_delegate.cc
new file mode 100644
index 0000000..dcdc24d
--- /dev/null
+++ b/headless/lib/browser/headless_browser_download_delegate.cc
@@ -0,0 +1,168 @@
+// Copyright 2013 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 "headless/lib/browser/headless_browser_download_delegate.h"
+
+#if defined(OS_WIN)
+#include <commdlg.h>
+#include <windows.h>
+#endif
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/filename_util.h"
+
+#if defined(OS_WIN)
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#endif
+
+namespace headless {
+
+HeadlessBrowserDownloadDelegate::HeadlessBrowserDownloadDelegate()
+ : download_manager_(NULL),
+ suppress_prompting_(false),
+ weak_ptr_factory_(this) {}
+
+HeadlessBrowserDownloadDelegate::~HeadlessBrowserDownloadDelegate() {
+ if (download_manager_) {
+ DCHECK_EQ(static_cast<content::DownloadManagerDelegate*>(this),
+ download_manager_->GetDelegate());
+ download_manager_->SetDelegate(NULL);
+ download_manager_ = NULL;
+ }
+}
+
+void HeadlessBrowserDownloadDelegate::SetDownloadManager(
+ content::DownloadManager* download_manager) {
+ download_manager_ = download_manager;
+}
+
+void HeadlessBrowserDownloadDelegate::Shutdown() {
+ // Revoke any pending callbacks. download_manager_ et. al. are no longer safe
+ // to access after this point.
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ download_manager_ = NULL;
+}
+
+bool HeadlessBrowserDownloadDelegate::DetermineDownloadTarget(
+ content::DownloadItem* download,
+ const content::DownloadTargetCallback& callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ // This assignment needs to be here because even at the call to
+ // SetDownloadManager, the system is not fully initialized.
+ if (default_download_path_.empty()) {
+ default_download_path_ =
+ download_manager_->GetBrowserContext()->GetPath().Append(
+ FILE_PATH_LITERAL("Downloads"));
+ }
+
+ if (!download->GetForcedFilePath().empty()) {
+ callback.Run(download->GetForcedFilePath(),
+ content::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+ content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+ download->GetForcedFilePath(),
+ content::DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_NONE);
+ return true;
+ }
+
+ FilenameDeterminedCallback filename_determined_callback =
+ base::Bind(&HeadlessBrowserDownloadDelegate::OnDownloadPathGenerated,
+ weak_ptr_factory_.GetWeakPtr(), download->GetId(), callback);
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::FILE, FROM_HERE,
+ base::Bind(&HeadlessBrowserDownloadDelegate::GenerateFilename,
+ download->GetURL(), download->GetContentDisposition(),
+ download->GetSuggestedFilename(), download->GetMimeType(),
+ default_download_path_, filename_determined_callback));
+ return true;
+}
+
+bool HeadlessBrowserDownloadDelegate::ShouldOpenDownload(
+ content::DownloadItem* item,
+ const content::DownloadOpenDelayedCallback& callback) {
+ return true;
+}
+
+void HeadlessBrowserDownloadDelegate::GetNextId(
+ const content::DownloadIdCallback& callback) {
+ static uint32_t next_id = content::DownloadItem::kInvalidId + 1;
+ callback.Run(next_id++);
+}
+
+// static
+void HeadlessBrowserDownloadDelegate::GenerateFilename(
+ const GURL& url,
+ const std::string& content_disposition,
+ const std::string& suggested_filename,
+ const std::string& mime_type,
+ const base::FilePath& suggested_directory,
+ const FilenameDeterminedCallback& callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
+ base::FilePath generated_name =
+ net::GenerateFileName(url, content_disposition, std::string(),
+ suggested_filename, mime_type, "download");
+
+ if (!base::PathExists(suggested_directory))
+ base::CreateDirectory(suggested_directory);
+
+ base::FilePath suggested_path(suggested_directory.Append(generated_name));
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+ base::Bind(callback, suggested_path));
+}
+
+void HeadlessBrowserDownloadDelegate::OnDownloadPathGenerated(
+ uint32_t download_id,
+ const content::DownloadTargetCallback& callback,
+ const base::FilePath& suggested_path) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ if (suppress_prompting_ || true) {
+ // Testing exit.
+ callback.Run(suggested_path,
+ content::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+ content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+ suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload")),
+ content::DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_NONE);
+ return;
+ }
+
+ ChooseDownloadPath(download_id, callback, suggested_path);
+}
+
+void HeadlessBrowserDownloadDelegate::ChooseDownloadPath(
+ uint32_t download_id,
+ const content::DownloadTargetCallback& callback,
+ const base::FilePath& suggested_path) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ content::DownloadItem* item = download_manager_->GetDownload(download_id);
+ if (!item || (item->GetState() != content::DownloadItem::IN_PROGRESS))
+ return;
+
+ base::FilePath result;
+
+ result = base::FilePath(suggested_path.value());
+
+ callback.Run(result, content::DownloadItem::TARGET_DISPOSITION_PROMPT,
+ content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, result,
+ content::DownloadInterruptReason::DOWNLOAD_INTERRUPT_REASON_NONE);
+}
+
+void HeadlessBrowserDownloadDelegate::SetDownloadBehaviorForTesting(
+ const base::FilePath& default_download_path) {
+ default_download_path_ = default_download_path;
+ suppress_prompting_ = true;
+}
+
+} // namespace content
diff --git a/headless/lib/browser/headless_browser_download_delegate.h b/headless/lib/browser/headless_browser_download_delegate.h
new file mode 100644
index 0000000..ccbea7a
--- /dev/null
+++ b/headless/lib/browser/headless_browser_download_delegate.h
@@ -0,0 +1,67 @@
+// Copyright 2013 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_HEADLESS_BROWSER_SHELL_DOWNLOAD_MANAGER_DELEGATE_H_
+#define CONTENT_HEADLESS_BROWSER_SHELL_DOWNLOAD_MANAGER_DELEGATE_H_
+
+#include <stdint.h>
+
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/download_manager_delegate.h"
+
+namespace headless {
+
+class DownloadManager;
+
+class HeadlessBrowserDownloadDelegate : public content::DownloadManagerDelegate {
+ public:
+ HeadlessBrowserDownloadDelegate();
+ ~HeadlessBrowserDownloadDelegate() override;
+
+ void SetDownloadManager(content::DownloadManager* manager);
+
+ void Shutdown() override;
+ bool DetermineDownloadTarget(content::DownloadItem* download,
+ const content::DownloadTargetCallback& callback) override;
+ bool ShouldOpenDownload(content::DownloadItem* item,
+ const content::DownloadOpenDelayedCallback& callback) override;
+ void GetNextId(const content::DownloadIdCallback& callback) override;
+
+ // Inhibits prompting and sets the default download path.
+ void SetDownloadBehaviorForTesting(
+ const base::FilePath& default_download_path);
+
+ private:
+ friend class base::RefCountedThreadSafe<HeadlessBrowserDownloadDelegate>;
+
+ typedef base::Callback<void(const base::FilePath&)>
+ FilenameDeterminedCallback;
+
+ static void GenerateFilename(const GURL& url,
+ const std::string& content_disposition,
+ const std::string& suggested_filename,
+ const std::string& mime_type,
+ const base::FilePath& suggested_directory,
+ const FilenameDeterminedCallback& callback);
+ void OnDownloadPathGenerated(uint32_t download_id,
+ const content::DownloadTargetCallback& callback,
+ const base::FilePath& suggested_path);
+ void ChooseDownloadPath(uint32_t download_id,
+ const content::DownloadTargetCallback& callback,
+ const base::FilePath& suggested_path);
+
+ content::DownloadManager* download_manager_;
+ base::FilePath default_download_path_;
+ bool suppress_prompting_;
+ base::WeakPtrFactory<HeadlessBrowserDownloadDelegate> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(HeadlessBrowserDownloadDelegate);
+};
+
+} // namespace content
+
+#endif // CONTENT_SHELL_BROWSER_SHELL_DOWNLOAD_MANAGER_DELEGATE_H_
--
2.7.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment