diff --git a/base/feature_list.cc b/base/feature_list.cc index 989631a387..f0d0bc4278 100644 --- a/base/feature_list.cc +++ b/base/feature_list.cc @@ -242,6 +242,14 @@ void FeatureList::InitializeFromCommandLine( OVERRIDE_ENABLE_FEATURE); initialized_from_command_line_ = true; + + + RegisterOverride("TabHoverCards", + FeatureList::OVERRIDE_DISABLE_FEATURE, nullptr); + RegisterOverride("ReaderMode", + FeatureList::OVERRIDE_ENABLE_FEATURE, nullptr); + + } void FeatureList::InitializeFromSharedMemory( diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 5c3e34a9a8..5d015f8f92 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn @@ -1687,11 +1687,13 @@ config("chromium_code") { "-Wimplicit-fallthrough", ] +# # TODO(thakis): Enable this more often, https://crbug.com/346399 # use_libfuzzer: https://crbug.com/1063180 - if (!is_nacl && !use_libfuzzer) { - cflags += [ "-Wunreachable-code" ] - } +# if (!is_nacl && !use_libfuzzer) { +# cflags += [ "-Wunreachable-code" ] +# } +# # Thread safety analysis is broken under nacl: https://crbug.com/982423. if (!is_nacl) { diff --git a/cc/animation/scroll_offset_animation_curve.cc b/cc/animation/scroll_offset_animation_curve.cc index 5a645b4bad..074d991003 100644 --- a/cc/animation/scroll_offset_animation_curve.cc +++ b/cc/animation/scroll_offset_animation_curve.cc @@ -20,10 +20,12 @@ const double kDurationDivisor = 60.0; // 0.7 seconds limit for long-distance programmatic scrolls const double kDeltaBasedMaxDuration = 0.7 * kDurationDivisor; -const double kInverseDeltaRampStartPx = 120.0; -const double kInverseDeltaRampEndPx = 480.0; -const double kInverseDeltaMinDuration = 6.0; -const double kInverseDeltaMaxDuration = 12.0; + +const double kInverseDeltaRampStartPx = 680.0; +const double kInverseDeltaRampEndPx = 700.0; +const double kInverseDeltaMinDuration = 10.0; +const double kInverseDeltaMaxDuration = 30.0; + const double kInverseDeltaSlope = (kInverseDeltaMinDuration - kInverseDeltaMaxDuration) / @@ -143,6 +145,11 @@ ScrollOffsetAnimationCurve::ScrollOffsetAnimationCurve( DCHECK_EQ((animation_type == AnimationType::kEaseInOut || animation_type == AnimationType::kImpulse), duration_behavior.has_value()); + + + animation_type = AnimationType::kEaseInOut; + + switch (animation_type) { case AnimationType::kEaseInOut: timing_function_ = CubicBezierTimingFunction::CreatePreset( @@ -228,6 +235,11 @@ base::TimeDelta ScrollOffsetAnimationCurve::SegmentDuration( const gfx::Vector2dF& delta, base::TimeDelta delayed_by, base::Optional velocity) { + + + animation_type_ = AnimationType::kEaseInOut; + + switch (animation_type_) { case AnimationType::kEaseInOut: DCHECK(duration_behavior_.has_value()); diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h index 09f6502cf3..0919efc10a 100644 --- a/chrome/app/chrome_command_ids.h +++ b/chrome/app/chrome_command_ids.h @@ -141,6 +141,13 @@ #define IDC_GROUP_TARGET_TAB 35052 #define IDC_DUPLICATE_TARGET_TAB 35053 + +#define IDC_PAGE_BLOCK_JAVASCRIPT 35080 +#define IDC_PAGE_DEFAULT_JAVASCRIPT 35081 +#define IDC_PAGE_ALLOW_JAVASCRIPT 35082 +#define IDC_PAGE_DISTILL 35083 + + // Clipboard commands #define IDC_CUT 36000 #define IDC_COPY 36001 diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 05d7f6d362..bfcbb3ff49 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -3512,6 +3512,14 @@ static_library("browser") { "diagnostics/recon_diagnostics.h", "diagnostics/sqlite_diagnostics.cc", "diagnostics/sqlite_diagnostics.h", + +# + "download/download_options_item_model.cc", + "download/download_options_item_model.h", + "download/download_options_shelf.cc", + "download/download_options_shelf.h", +# + "download/default_download_dir_policy_handler.cc", "download/default_download_dir_policy_handler.h", "download/download_commands.cc", diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc index 9f3e66afa9..e5c2a083bb 100644 --- a/chrome/browser/download/chrome_download_manager_delegate.cc +++ b/chrome/browser/download/chrome_download_manager_delegate.cc @@ -478,6 +478,8 @@ void ChromeDownloadManagerDelegate::ReturnNextId( ++next_download_id_; } + +/* bool ChromeDownloadManagerDelegate::DetermineDownloadTarget( DownloadItem* download, content::DownloadTargetCallback* callback) { @@ -503,6 +505,52 @@ bool ChromeDownloadManagerDelegate::DetermineDownloadTarget( std::move(target_determined_callback)); return true; } +*/ + + + +bool ChromeDownloadManagerDelegate::DetermineDownloadTarget( + DownloadItem* download, + content::DownloadTargetCallback& callback) { + content::WebContents* web_contents = + content::DownloadItemUtils::GetWebContents(download); + Browser* browser = web_contents ? chrome::FindBrowserWithWebContents(web_contents) : NULL; + if (browser) { + if (download->GetTargetDisposition() != DownloadItem::TARGET_DISPOSITION_PROMPT && download->GetOperation() == 0) { + browser->OnDownloadOptions(download); + } else { + SetupDownload(download->GetId(), 1, callback); + } + } else { + SetupDownload(download->GetId(), 1, callback); + } + return true; +} + + + +void ChromeDownloadManagerDelegate::SetupDownload( + int32_t download_id, + int32_t download_option, + const content::DownloadTargetCallback& callback) { + + DownloadItem* download = + download_manager_->GetDownload(download_id); + + download->SetOperation(download_option); + + DownloadTargetDeterminer::CompletionCallback target_determined_callback = + base::Bind(&ChromeDownloadManagerDelegate::OnDownloadTargetDetermined, + weak_ptr_factory_.GetWeakPtr(), + download->GetId(), + std::move(callback)); + DownloadTargetDeterminer::Start( + download, + GetPlatformDownloadPath(profile_, download, PLATFORM_TARGET_PATH), + kDefaultPlatformConflictAction, download_prefs_.get(), this, + std::move(target_determined_callback)); +} + bool ChromeDownloadManagerDelegate::ShouldAutomaticallyOpenFile( const GURL& url, diff --git a/chrome/browser/download/chrome_download_manager_delegate.h b/chrome/browser/download/chrome_download_manager_delegate.h index 3535714026..bba10d7f82 100644 --- a/chrome/browser/download/chrome_download_manager_delegate.h +++ b/chrome/browser/download/chrome_download_manager_delegate.h @@ -85,9 +85,19 @@ class ChromeDownloadManagerDelegate // content::DownloadManagerDelegate void Shutdown() override; void GetNextId(content::DownloadIdCallback callback) override; + + + void SetupDownload( + int32_t download_id, + int32_t download_option, + const content::DownloadTargetCallback& callback) override; + + bool DetermineDownloadTarget( download::DownloadItem* item, - content::DownloadTargetCallback* callback) override; + + content::DownloadTargetCallback& callback) override; + bool ShouldAutomaticallyOpenFile(const GURL& url, const base::FilePath& path) override; bool ShouldAutomaticallyOpenFileByPolicy(const GURL& url, diff --git a/chrome/browser/download/download_commands.h b/chrome/browser/download/download_commands.h index 41528e6832..4013f04577 100644 --- a/chrome/browser/download/download_commands.h +++ b/chrome/browser/download/download_commands.h @@ -20,6 +20,11 @@ class DownloadUIModel; class DownloadCommands { public: enum Command { + + + HIDE_DOWNLOAD_ITEM_VIEW, + + SHOW_IN_FOLDER = 1, // Open a folder view window with the item selected. OPEN_WHEN_COMPLETE, // Open the download when it's finished. ALWAYS_OPEN_TYPE, // Default this file extension to always open. diff --git a/chrome/browser/download/download_item_model.cc b/chrome/browser/download/download_item_model.cc index d5c18e3dd7..bce93279c7 100644 --- a/chrome/browser/download/download_item_model.cc +++ b/chrome/browser/download/download_item_model.cc @@ -526,6 +526,12 @@ bool DownloadItemModel::IsCommandEnabled( const DownloadCommands* download_commands, DownloadCommands::Command command) const { switch (command) { + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + return true; + + case DownloadCommands::SHOW_IN_FOLDER: return download_->CanShowInFolder(); case DownloadCommands::OPEN_WHEN_COMPLETE: @@ -567,6 +573,13 @@ bool DownloadItemModel::IsCommandChecked( const DownloadCommands* download_commands, DownloadCommands::Command command) const { switch (command) { + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + download_->SetHidden(true); + break; + + case DownloadCommands::OPEN_WHEN_COMPLETE: return download_->GetOpenWhenComplete() || download_crx_util::IsExtensionDownload(*download_); @@ -602,6 +615,13 @@ bool DownloadItemModel::IsCommandChecked( void DownloadItemModel::ExecuteCommand(DownloadCommands* download_commands, DownloadCommands::Command command) { switch (command) { + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + download_->SetHidden(true); + break; + + case DownloadCommands::SHOW_IN_FOLDER: download_->ShowDownloadInShell(); break; diff --git a/chrome/browser/download/download_options_item_model.cc b/chrome/browser/download/download_options_item_model.cc new file mode 100644 index 0000000000..2e797d8b72 --- /dev/null +++ b/chrome/browser/download/download_options_item_model.cc @@ -0,0 +1,801 @@ +// 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. + + + +#include "chrome/browser/download/download_options_item_model.h" + +#include "base/bind.h" +#include "base/i18n/number_formatting.h" +#include "base/i18n/rtl.h" +#include "base/memory/ptr_util.h" +#include "base/metrics/field_trial.h" +#include "base/strings/string16.h" +#include "base/strings/sys_string_conversions.h" +#include "base/supports_user_data.h" +#include "base/time/time.h" +#include "build/build_config.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/download/chrome_download_manager_delegate.h" +#include "chrome/browser/download/download_commands.h" +#include "chrome/browser/download/download_core_service.h" +#include "chrome/browser/download/download_core_service_factory.h" +#include "chrome/browser/download/download_crx_util.h" +#include "chrome/browser/download/download_history.h" +#include "chrome/browser/download/download_prefs.h" +#include "chrome/browser/download/download_stats.h" +#include "chrome/browser/download/offline_item_utils.h" +#include "chrome/browser/enterprise/connectors/common.h" +#include "chrome/browser/enterprise/connectors/connectors_manager.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/safe_browsing/download_protection/deep_scanning_request.h" +#include "chrome/browser/safe_browsing/download_protection/download_feedback_service.h" +#include "chrome/grit/chromium_strings.h" +#include "chrome/grit/generated_resources.h" +#include "components/download/public/common/download_danger_type.h" +#include "components/download/public/common/download_interrupt_reasons.h" +#include "components/download/public/common/download_item.h" +#include "components/safe_browsing/buildflags.h" +#include "components/safe_browsing/core/file_type_policies.h" +#include "components/safe_browsing/core/proto/download_file_types.pb.h" +#include "content/public/browser/download_item_utils.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/l10n/time_format.h" +#include "ui/base/text/bytes_formatting.h" + +#if !defined(OS_ANDROID) +#include "chrome/browser/ui/browser.h" +#endif + +using base::TimeDelta; +using download::DownloadItem; +using MixedContentStatus = download::DownloadItem::MixedContentStatus; +using safe_browsing::DownloadFileType; + +namespace { + +// Per DownloadItem data used by DownloadOptionsItemModel. The model doesn't keep any +// state since there could be multiple models associated with a single +// DownloadItem, and the lifetime of the model is shorter than the DownloadItem. +class DownloadOptionsItemModelData : public base::SupportsUserData::Data { + public: + ~DownloadOptionsItemModelData() override {} + + // Get the DownloadOptionsItemModelData object for |download|. Returns NULL if + // there's no model data. + static const DownloadOptionsItemModelData* Get(const DownloadItem* download); + + // Get the DownloadOptionsItemModelData object for |download|. Creates a model data + // object if not found. Always returns a non-NULL pointer, unless OOM. + static DownloadOptionsItemModelData* GetOrCreate(DownloadItem* download); + + // Whether the download should be displayed in the download shelf. True by + // default. + bool should_show_in_shelf_; + + // Whether the UI has been notified about this download. + bool was_ui_notified_; + + // Whether the download should be opened in the browser vs. the system handler + // for the file type. + bool should_prefer_opening_in_browser_; + + // Danger level of the file determined based on the file type and whether + // there was a user action associated with the download. + DownloadFileType::DangerLevel danger_level_; + + // Whether the download is currently being revived. + bool is_being_revived_; + + private: + DownloadOptionsItemModelData(); + + static const char kKey[]; +}; + +// static +const char DownloadOptionsItemModelData::kKey[] = "DownloadOptionsItemModelData key"; + +// static +const DownloadOptionsItemModelData* DownloadOptionsItemModelData::Get( + const DownloadItem* download) { + return static_cast(download->GetUserData(kKey)); +} + +// static +DownloadOptionsItemModelData* DownloadOptionsItemModelData::GetOrCreate( + DownloadItem* download) { + DownloadOptionsItemModelData* data = + static_cast(download->GetUserData(kKey)); + if (data == NULL) { + data = new DownloadOptionsItemModelData(); + data->should_show_in_shelf_ = !download->IsTransient(); + download->SetUserData(kKey, base::WrapUnique(data)); + } + return data; +} + +DownloadOptionsItemModelData::DownloadOptionsItemModelData() + : should_show_in_shelf_(true), + was_ui_notified_(false), + should_prefer_opening_in_browser_(false), + danger_level_(DownloadFileType::NOT_DANGEROUS), + is_being_revived_(false) {} + +} // namespace + +// ----------------------------------------------------------------------------- +// DownloadOptionsItemModel + +// static +DownloadUIModel::DownloadUIModelPtr DownloadOptionsItemModel::Wrap( + download::DownloadItem* download) { + DownloadUIModel::DownloadUIModelPtr model( + new DownloadOptionsItemModel(download), + base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get())); + return model; +} + +DownloadOptionsItemModel::DownloadOptionsItemModel(DownloadItem* download) + : download_(download) { + download_->AddObserver(this); +} + +DownloadOptionsItemModel::~DownloadOptionsItemModel() { + if (download_) + download_->RemoveObserver(this); +} + +ContentId DownloadOptionsItemModel::GetContentId() const { + bool off_the_record = content::DownloadItemUtils::GetBrowserContext(download_) + ->IsOffTheRecord(); + return ContentId(OfflineItemUtils::GetDownloadNamespacePrefix(off_the_record), + download_->GetGuid()); +} + +Profile* DownloadOptionsItemModel::profile() const { + return Profile::FromBrowserContext( + content::DownloadItemUtils::GetBrowserContext(download_)); +} + +base::string16 DownloadOptionsItemModel::GetTabProgressStatusText() const { + int64_t total = GetTotalBytes(); + int64_t size = download_->GetReceivedBytes(); + base::string16 received_size = ui::FormatBytes(size); + base::string16 amount = received_size; + + // Adjust both strings for the locale direction since we don't yet know which + // string we'll end up using for constructing the final progress string. + base::i18n::AdjustStringForLocaleDirection(&amount); + + if (total) { + base::string16 total_text = ui::FormatBytes(total); + base::i18n::AdjustStringForLocaleDirection(&total_text); + + base::i18n::AdjustStringForLocaleDirection(&received_size); + amount = l10n_util::GetStringFUTF16( + IDS_DOWNLOAD_TAB_PROGRESS_SIZE, received_size, total_text); + } else { + amount.assign(received_size); + } + int64_t current_speed = download_->CurrentSpeed(); + base::string16 speed_text = ui::FormatSpeed(current_speed); + base::i18n::AdjustStringForLocaleDirection(&speed_text); + + base::TimeDelta remaining; + base::string16 time_remaining; + if (download_->IsPaused()) { + time_remaining = l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED); + } else if (download_->TimeRemaining(&remaining)) { + time_remaining = ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING, + ui::TimeFormat::LENGTH_SHORT, + remaining); + } + + if (time_remaining.empty()) { + base::i18n::AdjustStringForLocaleDirection(&amount); + return l10n_util::GetStringFUTF16( + IDS_DOWNLOAD_TAB_PROGRESS_STATUS_TIME_UNKNOWN, speed_text, amount); + } + return l10n_util::GetStringFUTF16( + IDS_DOWNLOAD_TAB_PROGRESS_STATUS, speed_text, amount, time_remaining); +} + + +int64_t DownloadOptionsItemModel::GetCompletedBytes() const { + return download_->GetReceivedBytes(); +} + +int64_t DownloadOptionsItemModel::GetTotalBytes() const { + return download_->AllDataSaved() ? download_->GetReceivedBytes() : + download_->GetTotalBytes(); +} + +// TODO(asanka,rdsmith): Once 'open' moves exclusively to the +// ChromeDownloadManagerDelegate, we should calculate the percentage here +// instead of calling into the DownloadItem. +int DownloadOptionsItemModel::PercentComplete() const { + return download_->PercentComplete(); +} + +bool DownloadOptionsItemModel::IsDangerous() const { + return download_->IsDangerous(); +} + +bool DownloadOptionsItemModel::MightBeMalicious() const { + return IsDangerous() && (download_->GetDangerType() != + download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE); +} + +// If you change this definition of malicious, also update +// DownloadManagerImpl::NonMaliciousInProgressCount. +bool DownloadOptionsItemModel::IsMalicious() const { + if (!MightBeMalicious()) + return false; + switch (download_->GetDangerType()) { + case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: + case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: + case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: + case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: + case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS: + return true; + + case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: + case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: + case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: + case download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY: + case download::DOWNLOAD_DANGER_TYPE_MAX: + case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: + // We shouldn't get any of these due to the MightBeMalicious() test above. + NOTREACHED(); + FALLTHROUGH; + case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: + case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING: + case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED: + case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE: + case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING: + case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK: + case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE: + case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING: + case download::DOWNLOAD_DANGER_TYPE_BLOCKED_UNSUPPORTED_FILETYPE: + return false; + } + NOTREACHED(); + return false; +} + +bool DownloadOptionsItemModel::IsMixedContent() const { + return download_->IsMixedContent(); +} + +bool DownloadOptionsItemModel::ShouldAllowDownloadFeedback() const { +#if BUILDFLAG(FULL_SAFE_BROWSING) + if (!IsDangerous()) + return false; + return safe_browsing::DownloadFeedbackService::IsEnabledForDownload( + *download_); +#else + return false; +#endif +} + +bool DownloadOptionsItemModel::ShouldRemoveFromShelfWhenComplete() const { + switch (download_->GetState()) { + case DownloadItem::IN_PROGRESS: + // If the download is dangerous or malicious, we should display a warning + // on the shelf until the user accepts the download. + if (IsDangerous()) + return false; + + // If the download is a trusted extension, temporary, or will be opened + // automatically, then it should be removed from the shelf on completion. + // TODO(crbug.com/1077929): The logic for deciding opening behavior should + // be in a central location. + return (download_crx_util::IsTrustedExtensionDownload(profile(), + *download_) || + download_->IsTemporary() || download_->GetOpenWhenComplete() || + download_->ShouldOpenFileBasedOnExtension()); + + case DownloadItem::COMPLETE: + // If the download completed, then rely on GetAutoOpened() to check for + // opening behavior. This should accurately reflect whether the download + // was successfully opened. Extensions, for example, may fail to open. + return download_->GetAutoOpened() || download_->IsTemporary(); + + case DownloadItem::CANCELLED: + case DownloadItem::INTERRUPTED: + // Interrupted or cancelled downloads should remain on the shelf. + return false; + + case DownloadItem::MAX_DOWNLOAD_STATE: + NOTREACHED(); + } + + NOTREACHED(); + return false; +} + +bool DownloadOptionsItemModel::ShouldShowDownloadStartedAnimation() const { + return !download_->IsSavePackageDownload() && + !download_crx_util::IsTrustedExtensionDownload(profile(), *download_); +} + +bool DownloadOptionsItemModel::ShouldShowInShelf() const { + const DownloadOptionsItemModelData* data = DownloadOptionsItemModelData::Get(download_); + if (data) + return data->should_show_in_shelf_; + + return !download_->IsTransient(); +} + +void DownloadOptionsItemModel::SetShouldShowInShelf(bool should_show) { + DownloadOptionsItemModelData* data = DownloadOptionsItemModelData::GetOrCreate(download_); + data->should_show_in_shelf_ = should_show; +} + +bool DownloadOptionsItemModel::ShouldNotifyUI() const { + if (download_->IsTransient()) + return false; + + // The browser is only interested in new active downloads. History downloads + // that are completed or interrupted are not displayed on the shelf. The + // downloads page independently listens for new downloads when it is active. + // Note that the UI will be notified of downloads even if they are not meant + // to be displayed on the shelf (i.e. ShouldShowInShelf() returns false). This + // is because: * The shelf isn't the only UI. E.g. on Android, the UI is the + // system + // DownloadManager. + // * There are other UI activities that need to be performed. E.g. if the + // download was initiated from a new tab, then that tab should be closed. + return download_->GetDownloadCreationType() != + download::DownloadItem::DownloadCreationType:: + TYPE_HISTORY_IMPORT || + download_->GetState() == download::DownloadItem::IN_PROGRESS; +} + +bool DownloadOptionsItemModel::WasUINotified() const { + const DownloadOptionsItemModelData* data = DownloadOptionsItemModelData::Get(download_); + return data && data->was_ui_notified_; +} + +void DownloadOptionsItemModel::SetWasUINotified(bool was_ui_notified) { + DownloadOptionsItemModelData* data = DownloadOptionsItemModelData::GetOrCreate(download_); + data->was_ui_notified_ = was_ui_notified; +} + +bool DownloadOptionsItemModel::ShouldPreferOpeningInBrowser() const { + const DownloadOptionsItemModelData* data = DownloadOptionsItemModelData::Get(download_); + return data && data->should_prefer_opening_in_browser_; +} + +void DownloadOptionsItemModel::SetShouldPreferOpeningInBrowser(bool preference) { + DownloadOptionsItemModelData* data = DownloadOptionsItemModelData::GetOrCreate(download_); + data->should_prefer_opening_in_browser_ = preference; +} + +DownloadFileType::DangerLevel DownloadOptionsItemModel::GetDangerLevel() const { + const DownloadOptionsItemModelData* data = DownloadOptionsItemModelData::Get(download_); + return data ? data->danger_level_ : DownloadFileType::NOT_DANGEROUS; +} + +void DownloadOptionsItemModel::SetDangerLevel( + DownloadFileType::DangerLevel danger_level) { + DownloadOptionsItemModelData* data = DownloadOptionsItemModelData::GetOrCreate(download_); + data->danger_level_ = danger_level; +} + +download::DownloadItem::MixedContentStatus +DownloadOptionsItemModel::GetMixedContentStatus() const { + return download_->GetMixedContentStatus(); +} + +bool DownloadOptionsItemModel::IsBeingRevived() const { + const DownloadOptionsItemModelData* data = DownloadOptionsItemModelData::Get(download_); + return data && data->is_being_revived_; +} + +void DownloadOptionsItemModel::SetIsBeingRevived(bool is_being_revived) { + DownloadOptionsItemModelData* data = DownloadOptionsItemModelData::GetOrCreate(download_); + data->is_being_revived_ = is_being_revived; +} + +download::DownloadItem* DownloadOptionsItemModel::download() { + return download_; +} + +base::FilePath DownloadOptionsItemModel::GetFileNameToReportUser() const { + return download_->GetFileNameToReportUser(); +} + +base::FilePath DownloadOptionsItemModel::GetTargetFilePath() const { + return download_->GetTargetFilePath(); +} + +void DownloadOptionsItemModel::OpenDownload() { + download_->OpenDownload(); +} + +download::DownloadItem::DownloadState DownloadOptionsItemModel::GetState() const { + return download_->GetState(); +} + +bool DownloadOptionsItemModel::IsPaused() const { + return download_->IsPaused(); +} + +download::DownloadDangerType DownloadOptionsItemModel::GetDangerType() const { + return download_->GetDangerType(); +} + +bool DownloadOptionsItemModel::GetOpenWhenComplete() const { + return download_->GetOpenWhenComplete(); +} + +bool DownloadOptionsItemModel::IsOpenWhenCompleteByPolicy() const { + return download_->ShouldOpenFileByPolicyBasedOnExtension(); +} + +bool DownloadOptionsItemModel::TimeRemaining(base::TimeDelta* remaining) const { + return download_->TimeRemaining(remaining); +} + +bool DownloadOptionsItemModel::GetOpened() const { + return download_->GetOpened(); +} + +void DownloadOptionsItemModel::SetOpened(bool opened) { + download_->SetOpened(opened); +} + +bool DownloadOptionsItemModel::IsDone() const { + return download_->IsDone(); +} + +void DownloadOptionsItemModel::Pause() { + download_->Pause(); +} + +void DownloadOptionsItemModel::Resume() { + download_->Resume(true /* has_user_gesture */); +} + +void DownloadOptionsItemModel::Cancel(bool user_cancel) { + download_->Cancel(user_cancel); +} + +void DownloadOptionsItemModel::Remove() { + download_->Remove(); +} + +void DownloadOptionsItemModel::SetOpenWhenComplete(bool open) { + download_->SetOpenWhenComplete(open); +} + +base::FilePath DownloadOptionsItemModel::GetFullPath() const { + return download_->GetFullPath(); +} + +bool DownloadOptionsItemModel::CanResume() const { + return download_->CanResume(); +} + +bool DownloadOptionsItemModel::AllDataSaved() const { + return download_->AllDataSaved(); +} + +bool DownloadOptionsItemModel::GetFileExternallyRemoved() const { + return download_->GetFileExternallyRemoved(); +} + +GURL DownloadOptionsItemModel::GetURL() const { + return download_->GetURL(); +} + +void DownloadOptionsItemModel::OnDownloadUpdated(DownloadItem* download) { + for (auto& obs : observers_) + obs.OnDownloadUpdated(); +} + +void DownloadOptionsItemModel::OnDownloadOpened(DownloadItem* download) { + for (auto& obs : observers_) + obs.OnDownloadOpened(); +} + +void DownloadOptionsItemModel::OnDownloadDestroyed(DownloadItem* download) { + for (auto& obs : observers_) + obs.OnDownloadDestroyed(); + download_ = nullptr; +} + +void DownloadOptionsItemModel::OpenUsingPlatformHandler() { + DownloadCoreService* download_core_service = + DownloadCoreServiceFactory::GetForBrowserContext( + content::DownloadItemUtils::GetBrowserContext(download_)); + if (!download_core_service) + return; + + ChromeDownloadManagerDelegate* delegate = + download_core_service->GetDownloadManagerDelegate(); + if (!delegate) + return; + delegate->OpenDownloadUsingPlatformHandler(download_); + RecordDownloadOpenMethod(DOWNLOAD_OPEN_METHOD_USER_PLATFORM); +} + +#if !defined(OS_ANDROID) +bool DownloadOptionsItemModel::IsCommandEnabled( + const DownloadCommands* download_commands, + DownloadCommands::Command command) const { + switch (command) { + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + return true; + + + case DownloadCommands::SHOW_IN_FOLDER: + return download_->CanShowInFolder(); + case DownloadCommands::OPEN_WHEN_COMPLETE: + return download_->CanOpenDownload() && + !download_crx_util::IsExtensionDownload(*download_); + case DownloadCommands::PLATFORM_OPEN: + return download_->CanOpenDownload() && + !download_crx_util::IsExtensionDownload(*download_); + case DownloadCommands::ALWAYS_OPEN_TYPE: + // For temporary downloads, the target filename might be a temporary + // filename. Don't base an "Always open" decision based on it. Also + // exclude extensions. + return download_->CanOpenDownload() && + safe_browsing::FileTypePolicies::GetInstance() + ->IsAllowedToOpenAutomatically( + download_->GetTargetFilePath()) && + !download_crx_util::IsExtensionDownload(*download_); + case DownloadCommands::PAUSE: + return !download_->IsSavePackageDownload() && + DownloadUIModel::IsCommandEnabled(download_commands, command); + case DownloadCommands::CANCEL: + case DownloadCommands::RESUME: + case DownloadCommands::COPY_TO_CLIPBOARD: + case DownloadCommands::ANNOTATE: + case DownloadCommands::DISCARD: + case DownloadCommands::KEEP: + case DownloadCommands::LEARN_MORE_SCANNING: + case DownloadCommands::LEARN_MORE_INTERRUPTED: + case DownloadCommands::LEARN_MORE_MIXED_CONTENT: + case DownloadCommands::DEEP_SCAN: + case DownloadCommands::BYPASS_DEEP_SCANNING: + return DownloadUIModel::IsCommandEnabled(download_commands, command); + } + NOTREACHED(); + return false; +} + +bool DownloadOptionsItemModel::IsCommandChecked( + const DownloadCommands* download_commands, + DownloadCommands::Command command) const { + switch (command) { + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + download_->SetHidden(true); + break; + + + case DownloadCommands::OPEN_WHEN_COMPLETE: + return download_->GetOpenWhenComplete() || + download_crx_util::IsExtensionDownload(*download_); + case DownloadCommands::ALWAYS_OPEN_TYPE: +#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \ + defined(OS_MAC) + if (download_commands->CanOpenPdfInSystemViewer()) { + DownloadPrefs* prefs = DownloadPrefs::FromBrowserContext(profile()); + return prefs->ShouldOpenPdfInSystemReader(); + } +#endif + return download_->ShouldOpenFileBasedOnExtension(); + case DownloadCommands::PAUSE: + case DownloadCommands::RESUME: + return IsPaused(); + case DownloadCommands::SHOW_IN_FOLDER: + case DownloadCommands::PLATFORM_OPEN: + case DownloadCommands::CANCEL: + case DownloadCommands::DISCARD: + case DownloadCommands::KEEP: + case DownloadCommands::LEARN_MORE_SCANNING: + case DownloadCommands::LEARN_MORE_INTERRUPTED: + case DownloadCommands::LEARN_MORE_MIXED_CONTENT: + case DownloadCommands::COPY_TO_CLIPBOARD: + case DownloadCommands::ANNOTATE: + case DownloadCommands::DEEP_SCAN: + case DownloadCommands::BYPASS_DEEP_SCANNING: + return false; + } + return false; +} + +void DownloadOptionsItemModel::ExecuteCommand(DownloadCommands* download_commands, + DownloadCommands::Command command) { + switch (command) { + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + download_->SetHidden(true); + break; + + + case DownloadCommands::SHOW_IN_FOLDER: + download_->ShowDownloadInShell(); + break; + case DownloadCommands::OPEN_WHEN_COMPLETE: + download_->OpenDownload(); + break; + case DownloadCommands::ALWAYS_OPEN_TYPE: { + bool is_checked = IsCommandChecked(download_commands, + DownloadCommands::ALWAYS_OPEN_TYPE); + DownloadPrefs* prefs = DownloadPrefs::FromBrowserContext(profile()); +#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \ + defined(OS_MAC) + if (download_commands->CanOpenPdfInSystemViewer()) { + prefs->SetShouldOpenPdfInSystemReader(!is_checked); + SetShouldPreferOpeningInBrowser(is_checked); + break; + } +#endif + base::FilePath path = download_->GetTargetFilePath(); + if (is_checked) + prefs->DisableAutoOpenByUserBasedOnExtension(path); + else + prefs->EnableAutoOpenByUserBasedOnExtension(path); + break; + } + case DownloadCommands::BYPASS_DEEP_SCANNING: +#if BUILDFLAG(FULL_SAFE_BROWSING) + CompleteSafeBrowsingScan(); +#endif + FALLTHROUGH; + case DownloadCommands::KEEP: + if (IsMixedContent()) { + download_->ValidateMixedContentDownload(); + break; + } + DCHECK(IsDangerous()); +// Only sends uncommon download accept report if : +// 1. FULL_SAFE_BROWSING is enabled, and +// 2. Download verdict is uncommon, and +// 3. Download URL is not empty, and +// 4. User is not in incognito mode. +#if BUILDFLAG(FULL_SAFE_BROWSING) + if (GetDangerType() == download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT && + !GetURL().is_empty() && !profile()->IsOffTheRecord()) { + safe_browsing::SafeBrowsingService* sb_service = + g_browser_process->safe_browsing_service(); + // Compiles the uncommon download warning report. + safe_browsing::ClientSafeBrowsingReportRequest report; + report.set_type(safe_browsing::ClientSafeBrowsingReportRequest:: + DANGEROUS_DOWNLOAD_WARNING); + report.set_download_verdict( + safe_browsing::ClientDownloadResponse::UNCOMMON); + report.set_url(GetURL().spec()); + report.set_did_proceed(true); + std::string token = + safe_browsing::DownloadProtectionService::GetDownloadPingToken( + download_); + if (!token.empty()) + report.set_token(token); + std::string serialized_report; + if (report.SerializeToString(&serialized_report)) { + sb_service->SendSerializedDownloadReport(profile(), + serialized_report); + } else { + DCHECK(false) + << "Unable to serialize the uncommon download warning report."; + } + } +#endif + download_->ValidateDangerousDownload(); + break; + case DownloadCommands::LEARN_MORE_SCANNING: { +#if BUILDFLAG(FULL_SAFE_BROWSING) + using safe_browsing::DownloadProtectionService; + + safe_browsing::SafeBrowsingService* sb_service = + g_browser_process->safe_browsing_service(); + DownloadProtectionService* protection_service = + (sb_service ? sb_service->download_protection_service() : nullptr); + if (protection_service) + protection_service->ShowDetailsForDownload( + download_, download_commands->GetBrowser()); +#else + // Should only be getting invoked if we are using safe browsing. + NOTREACHED(); +#endif + break; + } + case DownloadCommands::PLATFORM_OPEN: + case DownloadCommands::CANCEL: + case DownloadCommands::DISCARD: + case DownloadCommands::LEARN_MORE_INTERRUPTED: + case DownloadCommands::LEARN_MORE_MIXED_CONTENT: + case DownloadCommands::PAUSE: + case DownloadCommands::RESUME: + case DownloadCommands::COPY_TO_CLIPBOARD: + case DownloadCommands::ANNOTATE: + DownloadUIModel::ExecuteCommand(download_commands, command); + break; + case DownloadCommands::DEEP_SCAN: + safe_browsing::SafeBrowsingService* sb_service = + g_browser_process->safe_browsing_service(); + if (!sb_service) + break; + safe_browsing::DownloadProtectionService* protection_service = + sb_service->download_protection_service(); + if (!protection_service) + break; + DownloadCoreService* download_core_service = + DownloadCoreServiceFactory::GetForBrowserContext( + content::DownloadItemUtils::GetBrowserContext(download_)); + DCHECK(download_core_service); + ChromeDownloadManagerDelegate* delegate = + download_core_service->GetDownloadManagerDelegate(); + DCHECK(delegate); + enterprise_connectors::AnalysisSettings settings; + settings.tags = {"malware"}; + protection_service->UploadForDeepScanning( + download_, + base::BindRepeating( + &ChromeDownloadManagerDelegate::CheckClientDownloadDone, + delegate->GetWeakPtr(), download_->GetId()), + safe_browsing::DeepScanningRequest::DeepScanTrigger:: + TRIGGER_APP_PROMPT, + std::move(settings)); + break; + } +} +#endif + +offline_items_collection::FailState DownloadOptionsItemModel::GetLastFailState() + const { + return OfflineItemUtils::ConvertDownloadInterruptReasonToFailState( + download_->GetLastReason()); +} + +std::string DownloadOptionsItemModel::GetMimeType() const { + return download_->GetMimeType(); +} + +bool DownloadOptionsItemModel::IsExtensionDownload() const { + return download_crx_util::IsExtensionDownload(*download_); +} + +#if BUILDFLAG(FULL_SAFE_BROWSING) +void DownloadOptionsItemModel::CompleteSafeBrowsingScan() { + ChromeDownloadManagerDelegate::SafeBrowsingState* state = + static_cast( + download_->GetUserData( + &ChromeDownloadManagerDelegate::SafeBrowsingState:: + kSafeBrowsingUserDataKey)); + state->CompleteDownload(); +} +#endif + +bool DownloadOptionsItemModel::ShouldShowDropdown() const { + // We don't show the dropdown for dangerous file types or for files + // blocked by enterprise policy. + if (IsDangerous() && GetState() != DownloadItem::CANCELLED && + !MightBeMalicious()) { + return false; + } + + if (GetDangerType() == + download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK || + GetDangerType() == + download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED || + GetDangerType() == download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE || + GetDangerType() == + download::DOWNLOAD_DANGER_TYPE_BLOCKED_UNSUPPORTED_FILETYPE) { + return false; + } + + return true; +} diff --git a/chrome/browser/download/download_options_item_model.h b/chrome/browser/download/download_options_item_model.h new file mode 100644 index 0000000000..6f1a579234 --- /dev/null +++ b/chrome/browser/download/download_options_item_model.h @@ -0,0 +1,121 @@ +// 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 CHROME_BROWSER_DOWNLOAD_DOWNLOAD_OPTIONS_ITEM_MODEL_H_ +#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_OPTIONS_ITEM_MODEL_H_ + +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "chrome/browser/download/download_ui_model.h" +#include "components/download/public/common/download_item.h" +#include "components/safe_browsing/buildflags.h" +#include "components/safe_browsing/core/proto/download_file_types.pb.h" + +// Implementation of DownloadUIModel that wrappers around a |DownloadItem*|. As +// such, the caller is expected to ensure that the |download| passed into the +// constructor outlives this |DownloadOptionsItemModel|. In addition, multiple +// DownloadOptionsItemModel objects could be wrapping the same DownloadItem. +class DownloadOptionsItemModel : public DownloadUIModel, + public download::DownloadItem::Observer { + public: + static DownloadUIModelPtr Wrap(download::DownloadItem* download); + + // Constructs a DownloadOptionsItemModel. The caller must ensure that |download| + // outlives this object. + explicit DownloadOptionsItemModel(download::DownloadItem* download); + ~DownloadOptionsItemModel() override; + + // DownloadUIModel implementation. + ContentId GetContentId() const override; + Profile* profile() const override; + base::string16 GetTabProgressStatusText() const override; + int64_t GetCompletedBytes() const override; + int64_t GetTotalBytes() const override; + int PercentComplete() const override; + bool IsDangerous() const override; + bool MightBeMalicious() const override; + bool IsMalicious() const override; + bool IsMixedContent() const override; + bool ShouldAllowDownloadFeedback() const override; + bool ShouldRemoveFromShelfWhenComplete() const override; + bool ShouldShowDownloadStartedAnimation() const override; + bool ShouldShowInShelf() const override; + void SetShouldShowInShelf(bool should_show) override; + bool ShouldNotifyUI() const override; + bool WasUINotified() const override; + void SetWasUINotified(bool should_notify) override; + bool ShouldPreferOpeningInBrowser() const override; + void SetShouldPreferOpeningInBrowser(bool preference) override; + safe_browsing::DownloadFileType::DangerLevel GetDangerLevel() const override; + void SetDangerLevel( + safe_browsing::DownloadFileType::DangerLevel danger_level) override; + download::DownloadItem::MixedContentStatus GetMixedContentStatus() + const override; + void OpenUsingPlatformHandler() override; + bool IsBeingRevived() const override; + void SetIsBeingRevived(bool is_being_revived) override; + download::DownloadItem* download() override; + base::FilePath GetFileNameToReportUser() const override; + base::FilePath GetTargetFilePath() const override; + void OpenDownload() override; + download::DownloadItem::DownloadState GetState() const override; + bool IsPaused() const override; + download::DownloadDangerType GetDangerType() const override; + bool GetOpenWhenComplete() const override; + bool IsOpenWhenCompleteByPolicy() const override; + bool TimeRemaining(base::TimeDelta* remaining) const override; + bool GetOpened() const override; + void SetOpened(bool opened) override; + bool IsDone() const override; + void Pause() override; + void Cancel(bool user_cancel) override; + void Resume() override; + void Remove() override; + void SetOpenWhenComplete(bool open) override; + base::FilePath GetFullPath() const override; + bool CanResume() const override; + bool AllDataSaved() const override; + bool GetFileExternallyRemoved() const override; + GURL GetURL() const override; + offline_items_collection::FailState GetLastFailState() const override; + +#if !defined(OS_ANDROID) + bool IsCommandEnabled(const DownloadCommands* download_commands, + DownloadCommands::Command command) const override; + bool IsCommandChecked(const DownloadCommands* download_commands, + DownloadCommands::Command command) const override; + void ExecuteCommand(DownloadCommands* download_commands, + DownloadCommands::Command command) override; +#endif + +#if BUILDFLAG(FULL_SAFE_BROWSING) + void CompleteSafeBrowsingScan() override; +#endif + + bool ShouldShowDropdown() const override; + + // download::DownloadItem::Observer implementation. + void OnDownloadUpdated(download::DownloadItem* download) override; + void OnDownloadOpened(download::DownloadItem* download) override; + void OnDownloadDestroyed(download::DownloadItem* download) override; + + private: + // DownloadUIModel implementation. + std::string GetMimeType() const override; + bool IsExtensionDownload() const override; + + // The DownloadItem that this model represents. Note that DownloadOptionsItemModel + // itself shouldn't maintain any state since there can be more than one + // DownloadOptionsItemModel in use with the same DownloadItem. + download::DownloadItem* download_; + + DISALLOW_COPY_AND_ASSIGN(DownloadOptionsItemModel); +}; + +#endif // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_OPTIONS_ITEM_MODEL_H_ diff --git a/chrome/browser/download/download_options_shelf.cc b/chrome/browser/download/download_options_shelf.cc new file mode 100644 index 0000000000..2c13e5bf8a --- /dev/null +++ b/chrome/browser/download/download_options_shelf.cc @@ -0,0 +1,162 @@ +// 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. + + + +#include "chrome/browser/download/download_options_shelf.h" + +#include + +#include "base/bind.h" +#include "base/location.h" +#include "base/optional.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "chrome/browser/download/download_core_service.h" +#include "chrome/browser/download/download_core_service_factory.h" +#include "chrome/browser/download/download_options_item_model.h" +#include "chrome/browser/download/download_started_animation.h" +#include "chrome/browser/download/offline_item_model.h" +#include "chrome/browser/download/offline_item_model_manager.h" +#include "chrome/browser/download/offline_item_model_manager_factory.h" +#include "chrome/browser/download/offline_item_utils.h" +#include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h" +#include "chrome/browser/platform_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_key.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "components/download/public/common/download_item.h" +#include "components/offline_items_collection/core/offline_content_aggregator.h" +#include "components/offline_items_collection/core/offline_item.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/download_manager.h" +#include "content/public/browser/web_contents.h" +#include "ui/gfx/animation/animation.h" + + +using download::DownloadItem; +#include "content/public/browser/download_item_utils.h" + + +DownloadOptionsShelf::DownloadOptionsShelf(Browser* browser, Profile* profile) + : browser_(browser), profile_(profile) {} + +DownloadOptionsShelf::~DownloadOptionsShelf() = default; + +void DownloadOptionsShelf::AddDownload(DownloadItem* download) { + DownloadUIModel::DownloadUIModelPtr model = DownloadOptionsItemModel::Wrap(download); + DCHECK(model); + if (model->ShouldRemoveFromShelfWhenComplete()) { + // If we are going to remove the download from the shelf upon completion, + // wait a few seconds to see if it completes quickly. If it's a small + // download, then the user won't have time to interact with it. + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::BindOnce(&DownloadOptionsShelf::ShowDownloadById, + weak_ptr_factory_.GetWeakPtr(), model->GetContentId()), + GetTransientDownloadShowDelay()); + } else { + ShowDownload(std::move(model)); + } +} + +void DownloadOptionsShelf::Open() { + if (is_hidden_) + should_show_on_unhide_ = true; + else + DoOpen(); +} + +void DownloadOptionsShelf::Close() { + if (is_hidden_) + should_show_on_unhide_ = false; + else + DoClose(); +} + +void DownloadOptionsShelf::Hide() { + if (is_hidden_) + return; + is_hidden_ = true; + if (IsShowing()) { + should_show_on_unhide_ = true; + DoHide(); + } +} + +void DownloadOptionsShelf::Unhide() { + if (!is_hidden_) + return; + is_hidden_ = false; + if (should_show_on_unhide_) { + should_show_on_unhide_ = false; + DoUnhide(); + } +} + +base::TimeDelta DownloadOptionsShelf::GetTransientDownloadShowDelay() const { + return base::TimeDelta::FromSeconds(0); +} + +void DownloadOptionsShelf::ShowDownload(DownloadUIModel::DownloadUIModelPtr download) { + if (download->GetState() == download::DownloadItem::COMPLETE && + download->ShouldRemoveFromShelfWhenComplete()) + return; + + if (!DownloadCoreServiceFactory::GetForBrowserContext(download->profile()) + ->IsShelfEnabled()) + return; + + Unhide(); + Open(); + + const bool should_show_download_started_animation = + download->ShouldShowDownloadStartedAnimation(); + DoShowDownload(std::move(download)); + + // Show the download started animation if: + // - Download started animation is enabled for this download. It is disabled + // for "Save As" downloads and extension installs, for example. + // - Rich animations are enabled. + // - The browser has an active visible WebContents. (browser isn't minimized, + // or running under a test etc.) + if (!should_show_download_started_animation || + !gfx::Animation::ShouldRenderRichAnimation() || !browser_) + return; + content::WebContents* const shelf_tab = + browser_->tab_strip_model()->GetActiveWebContents(); + if (shelf_tab && platform_util::IsVisible(shelf_tab->GetNativeView())) + DownloadStartedAnimation::Show(shelf_tab); +} + +void DownloadOptionsShelf::ShowDownloadById( + const offline_items_collection::ContentId& id) { + if (OfflineItemUtils::IsDownload(id)) { + auto* const manager = + content::BrowserContext::GetDownloadManager(profile()); + if (manager) { + auto* const download = manager->GetDownloadByGuid(id.id); + if (download) + ShowDownload(DownloadOptionsItemModel::Wrap(download)); + } + } else { + auto* const aggregator = + OfflineContentAggregatorFactory::GetForKey(profile()->GetProfileKey()); + if (aggregator) { + aggregator->GetItemById( + id, base::BindOnce(&DownloadOptionsShelf::OnGetDownloadDoneForOfflineItem, + weak_ptr_factory_.GetWeakPtr())); + } + } +} + +void DownloadOptionsShelf::OnGetDownloadDoneForOfflineItem( + const base::Optional& item) { + if (item.has_value()) { + auto* const manager = + OfflineItemModelManagerFactory::GetForBrowserContext(profile()); + ShowDownload(OfflineItemModel::Wrap(manager, item.value())); + } +} diff --git a/chrome/browser/download/download_options_shelf.h b/chrome/browser/download/download_options_shelf.h new file mode 100644 index 0000000000..05a9bf4557 --- /dev/null +++ b/chrome/browser/download/download_options_shelf.h @@ -0,0 +1,108 @@ +// 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 CHROME_BROWSER_DOWNLOAD_DOWNLOAD_OPTIONS_SHELF_H_ +#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_OPTIONS_SHELF_H_ + +#include "base/memory/weak_ptr.h" +#include "chrome/browser/download/download_ui_model.h" + +class Browser; +class Profile; + +namespace base { +template +class Optional; +class TimeDelta; +} // namespace base + +namespace base { +template +class Optional; +} // namespace base + +namespace offline_items_collection { +struct ContentId; +struct OfflineItem; +} // namespace offline_items_collection + +// This is an abstract base class for platform specific download shelf +// implementations. +class DownloadOptionsShelf { + public: + DownloadOptionsShelf(Browser* browser, Profile* profile); + DownloadOptionsShelf(const DownloadOptionsShelf&) = delete; + DownloadOptionsShelf& operator=(const DownloadOptionsShelf&) = delete; + virtual ~DownloadOptionsShelf(); + + // The browser view needs to know when we are going away to properly return + // the resize corner size to WebKit so that we don't draw on top of it. + // This returns the showing state of our animation which is set to true at + // the beginning Show and false at the beginning of a Hide. + virtual bool IsShowing() const = 0; + + // Returns whether the download shelf is showing the close animation. + virtual bool IsClosing() const = 0; + + // A new download has started. Add it to our shelf and show the download + // started animation. + // + // Some downloads are removed from the shelf on completion (See + // DownloadOptionsItemModel::ShouldRemoveFromShelfWhenComplete()). These transient + // downloads are added to the shelf after a delay. If the download completes + // before the delay duration, it will not be added to the shelf at all. + void AddDownload(download::DownloadItem* download); + + // Opens the shelf. + void Open(); + + // Closes the shelf. + void Close(); + + // Closes the shelf and prevents it from reopening until Unhide() is called. + void Hide(); + + // Allows the shelf to open after a previous call to Hide(). Opens the shelf + // if, had Hide() not been called, it would currently be open. + void Unhide(); + + Browser* browser() { return browser_; } + bool is_hidden() const { return is_hidden_; } + + protected: + virtual void DoShowDownload(DownloadUIModel::DownloadUIModelPtr download) = 0; + virtual void DoOpen() = 0; + virtual void DoClose() = 0; + virtual void DoHide() = 0; + virtual void DoUnhide() = 0; + + // Time delay to wait before adding a transient download to the shelf. + // Protected virtual for testing. + virtual base::TimeDelta GetTransientDownloadShowDelay() const; + + Profile* profile() { return profile_; } + + private: + // Shows the download on the shelf immediately. Also displays the download + // started animation if necessary. + void ShowDownload(DownloadUIModel::DownloadUIModelPtr download); + + // Similar to ShowDownload() but refers to the download using an ID. + void ShowDownloadById(const offline_items_collection::ContentId& id); + + // Callback used by ShowDownloadById() to trigger ShowDownload() once |item| + // has been fetched. + void OnGetDownloadDoneForOfflineItem( + const base::Optional& item); + + Browser* const browser_; + Profile* const profile_; + bool should_show_on_unhide_ = false; + bool is_hidden_ = false; + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +#endif // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_OPTIONS_SHELF_H_ diff --git a/chrome/browser/download/download_shelf.cc b/chrome/browser/download/download_shelf.cc index 8759efe4fb..6b6e44c7d0 100644 --- a/chrome/browser/download/download_shelf.cc +++ b/chrome/browser/download/download_shelf.cc @@ -89,7 +89,9 @@ void DownloadShelf::Unhide() { } base::TimeDelta DownloadShelf::GetTransientDownloadShowDelay() const { - return base::TimeDelta::FromSeconds(2); + + return base::TimeDelta::FromSeconds(0); + } void DownloadShelf::ShowDownload(DownloadUIModel::DownloadUIModelPtr download) { diff --git a/chrome/browser/download/download_shelf_context_menu.cc b/chrome/browser/download/download_shelf_context_menu.cc index 8ef746366c..8383cfe536 100644 --- a/chrome/browser/download/download_shelf_context_menu.cc +++ b/chrome/browser/download/download_shelf_context_menu.cc @@ -126,6 +126,11 @@ base::string16 DownloadShelfContextMenu::GetLabelForCommandId( case DownloadCommands::RESUME: id = IDS_DOWNLOAD_MENU_RESUME_ITEM; break; + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + id = IDS_DOWNLOAD_MENU_SHOW; + break; + case DownloadCommands::SHOW_IN_FOLDER: id = IDS_DOWNLOAD_MENU_SHOW; break; @@ -222,6 +227,11 @@ ui::SimpleMenuModel* DownloadShelfContextMenu::GetInProgressMenuModel( in_progress_download_menu_model_->AddItem( DownloadCommands::SHOW_IN_FOLDER, GetLabelForCommandId(DownloadCommands::SHOW_IN_FOLDER)); + + + in_progress_download_menu_model_->AddItem( + DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW, l10n_util::GetStringUTF16(IDS_EXTENSIONS_HIDE_DETAILS)); + } in_progress_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); @@ -288,6 +298,12 @@ ui::SimpleMenuModel* DownloadShelfContextMenu::GetFinishedMenuModel( finished_download_menu_model_->AddItem( DownloadCommands::SHOW_IN_FOLDER, GetLabelForCommandId(DownloadCommands::SHOW_IN_FOLDER)); + + + finished_download_menu_model_->AddItem( + DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW, l10n_util::GetStringUTF16(IDS_EXTENSIONS_HIDE_DETAILS)); + + finished_download_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); } diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc index f094105ac7..2158381297 100644 --- a/chrome/browser/download/download_target_determiner.cc +++ b/chrome/browser/download/download_target_determiner.cc @@ -243,16 +243,37 @@ DownloadTargetDeterminer::Result confirmation_reason_ = NeedsConfirmation(generated_filename); base::FilePath target_directory; if (confirmation_reason_ != DownloadConfirmationReason::NONE) { - DCHECK(!download_prefs_->IsDownloadPathManaged()); - // If the user is going to be prompted and the user has been prompted - // before, then always prefer the last directory that the user selected. - target_directory = download_prefs_->SaveFilePath(); + + + if (download_->GetOperation() == 2) { + confirmation_reason_ = DownloadConfirmationReason::NONE; + target_directory = download_prefs_->DownloadPath(); + download_->SetOpenWhenComplete(true); + target_directory = GetProfile()->GetCurrentCacheDownloadsPath(); + } else { + if (download_->GetOperation() == 1) { + DCHECK(!download_prefs_->IsDownloadPathManaged()); + // If the user is going to be prompted and the user has been prompted + // before, then always prefer the last directory that the user selected. + target_directory = download_prefs_->SaveFilePath(); RecordDownloadPathGeneration( DownloadPathGenerationEvent::USE_LAST_PROMPT_DIRECTORY, false); + } + } + + } else { target_directory = download_prefs_->DownloadPath(); RecordDownloadPathGeneration( DownloadPathGenerationEvent::USE_DEFAULTL_DOWNLOAD_DIRECTORY, false); + + if (download_->GetOperation() == 2) { + confirmation_reason_ = DownloadConfirmationReason::NONE; + download_->SetOpenWhenComplete(true); + target_directory = GetProfile()->GetCurrentCacheDownloadsPath(); + } + + } virtual_path_ = target_directory.Append(generated_filename); should_notify_extensions_ = true; diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc index dbf4534f6e..125562cf60 100644 --- a/chrome/browser/download/download_ui_model.cc +++ b/chrome/browser/download/download_ui_model.cc @@ -200,7 +200,9 @@ base::string16 DownloadUIModel::GetInterruptReasonText() const { return FailStateMessage(GetLastFailState()); } -base::string16 DownloadUIModel::GetStatusText() const { + +base::string16 DownloadUIModel::GetStatusText() { + switch (GetState()) { case DownloadItem::IN_PROGRESS: return GetInProgressStatusString(); @@ -511,6 +513,12 @@ bool DownloadUIModel::IsCommandEnabled( const DownloadCommands* download_commands, DownloadCommands::Command command) const { switch (command) { + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + return true; + + case DownloadCommands::SHOW_IN_FOLDER: case DownloadCommands::OPEN_WHEN_COMPLETE: case DownloadCommands::PLATFORM_OPEN: @@ -553,6 +561,11 @@ bool DownloadUIModel::IsCommandChecked( case DownloadCommands::PAUSE: case DownloadCommands::RESUME: return IsPaused(); + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + + case DownloadCommands::SHOW_IN_FOLDER: case DownloadCommands::PLATFORM_OPEN: case DownloadCommands::CANCEL: @@ -573,6 +586,13 @@ bool DownloadUIModel::IsCommandChecked( void DownloadUIModel::ExecuteCommand(DownloadCommands* download_commands, DownloadCommands::Command command) { switch (command) { + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + download()->SetHidden(true); + break; + + case DownloadCommands::SHOW_IN_FOLDER: case DownloadCommands::OPEN_WHEN_COMPLETE: case DownloadCommands::ALWAYS_OPEN_TYPE: @@ -583,6 +603,11 @@ void DownloadUIModel::ExecuteCommand(DownloadCommands* download_commands, break; case DownloadCommands::CANCEL: Cancel(true /* Cancelled by user */); + + + download()->SetCancelled(true); + + break; case DownloadCommands::DISCARD: Remove(); @@ -636,7 +661,10 @@ bool DownloadUIModel::IsExtensionDownload() const { return false; } -base::string16 DownloadUIModel::GetInProgressStatusString() const { + +base::string16 DownloadUIModel::GetInProgressStatusString() { + + DCHECK_EQ(DownloadItem::IN_PROGRESS, GetState()); TimeDelta time_remaining; @@ -659,6 +687,8 @@ base::string16 DownloadUIModel::GetInProgressStatusString() const { l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED)); } + +/* // A download scheduled to be opened when complete: "Opening in 10 secs" if (GetOpenWhenComplete()) { if (!time_remaining_known) @@ -677,6 +707,20 @@ base::string16 DownloadUIModel::GetInProgressStatusString() const { ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING, ui::TimeFormat::LENGTH_SHORT, time_remaining)); } +*/ + + + + int64_t speed = download()->CurrentSpeed(); + base::string16 time_remaining_plus_speed = ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING, ui::TimeFormat::LENGTH_SHORT, time_remaining) + base::UTF8ToUTF16(", ") + ui::FormatSpeed(speed); + if (time_remaining_known) { + return l10n_util::GetStringFUTF16( + IDS_DOWNLOAD_STATUS_IN_PROGRESS, size_ratio, + time_remaining_plus_speed); + } + if (GetCompletedBytes() > 0) + return size_ratio + base::UTF8ToUTF16(", ") + ui::FormatSpeed(speed); + // In progress download with no known time left and non-zero completed bytes: // "100/120 MB" or "100 MB" diff --git a/chrome/browser/download/download_ui_model.h b/chrome/browser/download/download_ui_model.h index 039511f565..efa71f00ea 100644 --- a/chrome/browser/download/download_ui_model.h +++ b/chrome/browser/download/download_ui_model.h @@ -64,7 +64,10 @@ class DownloadUIModel { base::string16 GetInterruptReasonText() const; // Returns a short one-line status string for the download. - base::string16 GetStatusText() const; + + + base::string16 GetStatusText(); + // Returns a string suitable for use as a tooltip. For a regular download, the // tooltip is the filename. For an interrupted download, the string states the @@ -323,7 +326,10 @@ class DownloadUIModel { private: // Returns a string indicating the status of an in-progress download. - base::string16 GetInProgressStatusString() const; + + + base::string16 GetInProgressStatusString(); + DISALLOW_COPY_AND_ASSIGN(DownloadUIModel); }; diff --git a/chrome/browser/download/offline_item_model.cc b/chrome/browser/download/offline_item_model.cc index 196c686fb2..56da41dddd 100644 --- a/chrome/browser/download/offline_item_model.cc +++ b/chrome/browser/download/offline_item_model.cc @@ -256,6 +256,13 @@ bool OfflineItemModel::IsCommandEnabled( const DownloadCommands* download_commands, DownloadCommands::Command command) const { switch (command) { + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + NOTIMPLEMENTED(); + return false; + + case DownloadCommands::SHOW_IN_FOLDER: case DownloadCommands::OPEN_WHEN_COMPLETE: case DownloadCommands::PLATFORM_OPEN: @@ -284,6 +291,13 @@ bool OfflineItemModel::IsCommandChecked( const DownloadCommands* download_commands, DownloadCommands::Command command) const { switch (command) { + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + NOTIMPLEMENTED(); + return false; + + case DownloadCommands::OPEN_WHEN_COMPLETE: case DownloadCommands::ALWAYS_OPEN_TYPE: NOTIMPLEMENTED(); @@ -311,6 +325,13 @@ bool OfflineItemModel::IsCommandChecked( void OfflineItemModel::ExecuteCommand(DownloadCommands* download_commands, DownloadCommands::Command command) { switch (command) { + + + case DownloadCommands::HIDE_DOWNLOAD_ITEM_VIEW: + NOTIMPLEMENTED(); + return; + + case DownloadCommands::SHOW_IN_FOLDER: case DownloadCommands::OPEN_WHEN_COMPLETE: case DownloadCommands::ALWAYS_OPEN_TYPE: diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc index 6ab9165f56..55ac3928cf 100644 --- a/chrome/browser/password_manager/chrome_password_manager_client.cc +++ b/chrome/browser/password_manager/chrome_password_manager_client.cc @@ -277,6 +277,10 @@ bool ChromePasswordManagerClient::IsFillingEnabled(const GURL& url) const { logger.LogBoolean(Logger::STRING_SSL_ERRORS_PRESENT, ssl_errors); } + + return IsPasswordManagementEnabledForCurrentPage(url); + + return !ssl_errors && IsPasswordManagementEnabledForCurrentPage(url); } diff --git a/chrome/browser/platform_util_win.cc b/chrome/browser/platform_util_win.cc index 7142fbee35..6a8713346f 100644 --- a/chrome/browser/platform_util_win.cc +++ b/chrome/browser/platform_util_win.cc @@ -67,11 +67,11 @@ void ShowItemInFolderOnWorkerThread(const base::FilePath& full_path) { return; const ITEMIDLIST* highlight[] = {file_item}; - - // Skip opening the folder during browser tests, to avoid leaving an open - // file explorer window behind. - if (!platform_util::internal::AreShellOperationsAllowed()) - return; + + + ShellExecute(NULL, NULL, dir.value().c_str(), NULL, NULL, SW_SHOW); + return; + hr = SHOpenFolderAndSelectItems(dir_item, base::size(highlight), highlight, 0); diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc index 6a9d6e1521..9edf3d796a 100644 --- a/chrome/browser/profiles/off_the_record_profile_impl.cc +++ b/chrome/browser/profiles/off_the_record_profile_impl.cc @@ -261,6 +261,12 @@ std::string OffTheRecordProfileImpl::GetProfileUserName() const { return std::string(); } + +base::FilePath OffTheRecordProfileImpl::GetCurrentCacheDownloadsPath() { + return profile_->GetCurrentCacheDownloadsPath(); +} + + base::FilePath OffTheRecordProfileImpl::GetPath() { return profile_->GetPath(); } diff --git a/chrome/browser/profiles/off_the_record_profile_impl.h b/chrome/browser/profiles/off_the_record_profile_impl.h index a58514afe7..068a1d7e2f 100644 --- a/chrome/browser/profiles/off_the_record_profile_impl.h +++ b/chrome/browser/profiles/off_the_record_profile_impl.h @@ -94,6 +94,10 @@ class OffTheRecordProfileImpl : public Profile { GURL GetHomePage() override; void SetCreationTimeForTesting(base::Time creation_time) override; + + + base::FilePath GetCurrentCacheDownloadsPath() override; + // content::BrowserContext implementation: base::FilePath GetPath() override; diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 9a3c5813ca..c5c0ec2cea 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -731,6 +731,10 @@ void ProfileImpl::DoFinalInit() { } #endif // BUILDFLAG(ENABLE_BACKGROUND_MODE) + + base_cache_path_ = base_cache_path; + + #if BUILDFLAG(ENABLE_PLUGINS) ChromePluginServiceFilter::GetInstance()->RegisterProfile(this); #endif @@ -752,6 +756,10 @@ void ProfileImpl::DoFinalInit() { } #endif + + base::CreateDirectory(GetCurrentCacheDownloadsPath()); + + #if !BUILDFLAG(IS_CHROMEOS_ASH) // Listen for bookmark model load, to bootstrap the sync service. // On CrOS sync service will be initialized after sign in. @@ -808,9 +816,25 @@ void ProfileImpl::set_last_selected_directory(const base::FilePath& path) { GetPrefs()->SetFilePath(prefs::kSelectFileLastDirectory, path); } + +base::FilePath ProfileImpl::GetCurrentCacheDownloadsPath() { + base::FilePath cache_downloads_path; +#if defined(OS_WIN) + cache_downloads_path = base_cache_path_.Append(base::FilePath(base::UTF8ToUTF16("Cache\\Downloads"))); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + cache_downloads_path = base_cache_path_.Append(base::FilePath("Cache/Downloads")); +#endif + return cache_downloads_path; +} + + ProfileImpl::~ProfileImpl() { MaybeSendDestroyedNotification(); + + base::DeleteFile(GetCurrentCacheDownloadsPath()); + + bool prefs_loaded = prefs_->GetInitializationStatus() != PrefService::INITIALIZATION_STATUS_WAITING; diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h index d21cd79384..d9604a77ec 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h @@ -74,6 +74,10 @@ class ProfileImpl : public Profile { static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + + base::FilePath GetCurrentCacheDownloadsPath() override; + + // content::BrowserContext implementation: #if !defined(OS_ANDROID) std::unique_ptr CreateZoomLevelDelegate( @@ -233,6 +237,10 @@ class ProfileImpl : public Profile { base::FilePath path_; + + base::FilePath base_cache_path_; + + base::Time path_creation_time_; // Task runner used for file access in the profile path. diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc index b88a6eabbf..442c98fb56 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc @@ -170,6 +170,15 @@ #include "ui/strings/grit/ui_strings.h" #include "url/origin.h" + +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "components/content_settings/core/common/content_settings_pattern.h" +#include "components/content_settings/core/common/content_settings_utils.h" +#include "components/dom_distiller/core/url_constants.h" +#include "chrome/browser/dom_distiller/tab_utils.h" + + #if BUILDFLAG(USE_RENDERER_SPELLCHECKER) #include "chrome/browser/renderer_context_menu/spelling_options_submenu_observer.h" #endif @@ -1509,6 +1518,39 @@ void RenderViewContextMenu::AppendPageItems() { menu_model_.AddItemWithStringId(IDC_BACK, IDS_CONTENT_CONTEXT_BACK); menu_model_.AddItemWithStringId(IDC_FORWARD, IDS_CONTENT_CONTEXT_FORWARD); menu_model_.AddItemWithStringId(IDC_RELOAD, IDS_CONTENT_CONTEXT_RELOAD); + + + if (params_.page_url.is_valid() + && params_.page_url.host() != chrome::kChromeUINewTabHost + && params_.page_url.host() != chrome::kChromeSearchLocalNtpHost + && !params_.page_url.SchemeIs(content::kChromeUIScheme) + && !params_.page_url.SchemeIs(content::kChromeDevToolsScheme) + && !params_.page_url.SchemeIs(content::kViewSourceScheme) + && !params_.page_url.SchemeIs(dom_distiller::kDomDistillerScheme) + && !params_.page_url.SchemeIs(content_settings::kExtensionScheme)) { + HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(GetProfile()); + content_settings::SettingInfo info; + std::unique_ptr value; + value = map->GetWebsiteSetting( + params_.page_url.GetOrigin(), params_.page_url.GetOrigin(), ContentSettingsType::JAVASCRIPT, + &info); + ContentSetting setting = content_settings::ValueToContentSetting(value.get()); + menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); + if (setting != CONTENT_SETTING_BLOCK) { + menu_model_.AddItem(IDC_PAGE_BLOCK_JAVASCRIPT, + base::UTF8ToUTF16("Block Javascript")); + } + if (setting != CONTENT_SETTING_ALLOW) { + menu_model_.AddItem(IDC_PAGE_ALLOW_JAVASCRIPT, + base::UTF8ToUTF16("Allow Javascript")); + } + menu_model_.AddItem(IDC_PAGE_DEFAULT_JAVASCRIPT, + base::UTF8ToUTF16("Default Javascript")); + menu_model_.AddItem(IDC_PAGE_DISTILL, + base::UTF8ToUTF16("Read Mode")); + } + + menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); menu_model_.AddItemWithStringId(IDC_SAVE_PAGE, IDS_CONTENT_CONTEXT_SAVEPAGEAS); @@ -2000,6 +2042,14 @@ bool RenderViewContextMenu::IsCommandIdEnabled(int id) const { return IsReloadEnabled(); case IDC_VIEW_SOURCE: + + + case IDC_PAGE_BLOCK_JAVASCRIPT: + case IDC_PAGE_DEFAULT_JAVASCRIPT: + case IDC_PAGE_ALLOW_JAVASCRIPT: + case IDC_PAGE_DISTILL: + + case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE: return IsViewSourceEnabled(); @@ -2425,6 +2475,21 @@ void RenderViewContextMenu::ExecuteCommand(int id, int event_flags) { case IDC_VIEW_SOURCE: embedder_web_contents_->GetMainFrame()->ViewSource(); break; + + + case IDC_PAGE_DEFAULT_JAVASCRIPT: + ExecCommandWebSite(0); + break; + case IDC_PAGE_BLOCK_JAVASCRIPT: + ExecCommandWebSite(1); + break; + case IDC_PAGE_ALLOW_JAVASCRIPT: + ExecCommandWebSite(2); + break; + case IDC_PAGE_DISTILL: + ExecCommandWebSite(3); + break; + case IDC_CONTENT_CONTEXT_INSPECTELEMENT: ExecInspectElement(); @@ -3153,6 +3218,39 @@ void RenderViewContextMenu::ExecPrint() { #endif // BUILDFLAG(ENABLE_PRINTING) } + +void RenderViewContextMenu::ExecCommandWebSite(int mode) { + if (params_.page_url.is_valid() + && params_.page_url.host() != chrome::kChromeUINewTabHost + && params_.page_url.host() != chrome::kChromeSearchLocalNtpHost + && !params_.page_url.SchemeIs(content::kChromeUIScheme) + && !params_.page_url.SchemeIs(content::kChromeDevToolsScheme) + && !params_.page_url.SchemeIs(content::kViewSourceScheme) + && !params_.page_url.SchemeIs(dom_distiller::kDomDistillerScheme) + && !params_.page_url.SchemeIs(content_settings::kExtensionScheme)) { + ContentSetting setting = CONTENT_SETTING_DEFAULT; + if (mode == 0) { + setting = CONTENT_SETTING_DEFAULT; + } else if (mode == 1) { + setting = CONTENT_SETTING_BLOCK; + } else if (mode == 2) { + setting = CONTENT_SETTING_ALLOW; + } + if (mode != 3) { + HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(GetProfile()); + map->SetContentSettingCustomScope( + ContentSettingsPattern::FromURLNoWildcard(params_.page_url.GetOrigin()), + ContentSettingsPattern::Wildcard(), ContentSettingsType::JAVASCRIPT, + setting); + embedder_web_contents_->GetController().Reload( + content::ReloadType::NORMAL, true); + } else { + chrome::ToggleDistilledView(GetBrowser()); + } + } +} + + void RenderViewContextMenu::ExecRouteMedia() { if (!media_router::MediaRouterEnabled(browser_context_)) return; diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h index 25f331b10b..f6e1358bb3 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.h +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h @@ -246,6 +246,10 @@ class RenderViewContextMenu : public RenderViewContextMenuBase { void ExecLanguageSettings(int event_flags); void ExecProtocolHandlerSettings(int event_flags); void ExecPictureInPicture(); + + + void ExecCommandWebSite(int mode); + void MediaPlayerActionAt(const gfx::Point& location, const blink::mojom::MediaPlayerAction& action); diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index b78b98b218..8fb62ae71e 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn @@ -3630,6 +3630,14 @@ static_library("ui") { "views/devtools_process_observer.cc", "views/devtools_process_observer.h", "views/download/download_danger_prompt_views.cc", + +# + "views/download/download_options_item_view.cc", + "views/download/download_options_item_view.h", + "views/download/download_options_shelf_view.cc", + "views/download/download_options_shelf_view.h", +# + "views/download/download_in_progress_dialog_view.cc", "views/download/download_in_progress_dialog_view.h", "views/download/download_item_view.cc", diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index 2f2862c426..8cf891f1ce 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -52,6 +52,12 @@ #include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/download/download_core_service.h" #include "chrome/browser/download/download_core_service_factory.h" + + +#include "chrome/browser/download/download_options_shelf.h" +#include "chrome/browser/download/download_options_item_model.h" + + #include "chrome/browser/extensions/api/tabs/tabs_event_router.h" #include "chrome/browser/extensions/api/tabs/tabs_windows_api.h" #include "chrome/browser/extensions/browser_extension_window_controller.h" @@ -1047,6 +1053,13 @@ void Browser::OpenFile() { &file_types, 0, base::FilePath::StringType(), parent_window, NULL); } + +void Browser::UpdateDownloadOptionsShelfVisibility(bool visible) { + if (GetStatusBubble()) + GetStatusBubble()->UpdateDownloadOptionsShelfVisibility(visible); +} + + void Browser::UpdateDownloadShelfVisibility(bool visible) { if (GetStatusBubble()) GetStatusBubble()->UpdateDownloadShelfVisibility(visible); @@ -1524,6 +1537,14 @@ void Browser::OnWindowDidShow() { error->ShowBubbleView(this); } + +void Browser::OnDownloadOptions( + download::DownloadItem* download) { + DownloadOptionsShelf* options_shelf = window()->GetDownloadOptionsShelf(); + options_shelf->AddDownload(download); +} + + /////////////////////////////////////////////////////////////////////////////// // Browser, content::WebContentsDelegate implementation: diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h index 3a0202c213..a3e4991dce 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h @@ -58,6 +58,10 @@ #include "ui/gfx/geometry/rect.h" #include "ui/shell_dialogs/select_file_dialog.h" + +#include "content/public/browser/download_manager_delegate.h" + + #if defined(OS_ANDROID) #error This file should only be included on desktop. #endif @@ -550,6 +554,10 @@ class Browser : public TabStripModelObserver, // Show various bits of UI void OpenFile(); + + void UpdateDownloadOptionsShelfVisibility(bool visible); + + void UpdateDownloadShelfVisibility(bool visible); // Whether the specified WebContents can be reloaded. @@ -681,6 +689,11 @@ class Browser : public TabStripModelObserver, // Called each time the browser window is shown. void OnWindowDidShow(); + + void OnDownloadOptions( + download::DownloadItem* download); + + ExclusiveAccessManager* exclusive_access_manager() { return exclusive_access_manager_.get(); } diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h index 2063c3dc22..ef210b2874 100644 --- a/chrome/browser/ui/browser_window.h +++ b/chrome/browser/ui/browser_window.h @@ -37,6 +37,11 @@ class Browser; class SharingDialog; + + +class DownloadOptionsShelf; + + class DownloadShelf; class ExclusiveAccessContext; class ExtensionsContainer; @@ -401,6 +406,11 @@ class BrowserWindow : public ui::BaseWindow { base::OnceCallback confirmed_callback) = 0; #endif + + virtual bool IsDownloadOptionsShelfVisible() const = 0; + virtual DownloadOptionsShelf* GetDownloadOptionsShelf() = 0; + + // Whether or not the shelf view is visible. virtual bool IsDownloadShelfVisible() const = 0; diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc index 3d73860a62..ddfadb3d52 100644 --- a/chrome/browser/ui/chrome_pages.cc +++ b/chrome/browser/ui/chrome_pages.cc @@ -250,8 +250,12 @@ void ShowHistory(Browser* browser) { void ShowDownloads(Browser* browser) { base::RecordAction(UserMetricsAction("ShowDownloads")); + +/* if (browser->window() && browser->window()->IsDownloadShelfVisible()) - browser->window()->GetDownloadShelf()->Close(); + browser->window()->GetDownloadShelf()->Close(DownloadShelf::USER_ACTION); +*/ + NavigateParams params( GetSingletonTabNavigateParams(browser, GURL(kChromeUIDownloadsURL))); diff --git a/chrome/browser/ui/startup/google_api_keys_infobar_delegate.cc b/chrome/browser/ui/startup/google_api_keys_infobar_delegate.cc index 87396233f1..3462a7abbe 100644 --- a/chrome/browser/ui/startup/google_api_keys_infobar_delegate.cc +++ b/chrome/browser/ui/startup/google_api_keys_infobar_delegate.cc @@ -16,6 +16,11 @@ // static void GoogleApiKeysInfoBarDelegate::Create(InfoBarService* infobar_service) { + + +return; + + infobar_service->AddInfoBar(infobar_service->CreateConfirmInfoBar( std::unique_ptr( new GoogleApiKeysInfoBarDelegate()))); diff --git a/chrome/browser/ui/status_bubble.h b/chrome/browser/ui/status_bubble.h index 84b9fdce60..ce6f51c934 100644 --- a/chrome/browser/ui/status_bubble.h +++ b/chrome/browser/ui/status_bubble.h @@ -43,6 +43,10 @@ class StatusBubble { // mouse. |left_content| is true if the mouse just left the content area. virtual void MouseMoved(bool left_content) = 0; + + virtual void UpdateDownloadOptionsShelfVisibility(bool visible) = 0; + + // Called when the download shelf becomes visible or invisible. // This is used by to ensure that the status bubble does not obscure // the download shelf, when it is visible. diff --git a/chrome/browser/ui/tabs/tab_style.cc b/chrome/browser/ui/tabs/tab_style.cc index ccf846d780..87b6c44065 100644 --- a/chrome/browser/ui/tabs/tab_style.cc +++ b/chrome/browser/ui/tabs/tab_style.cc @@ -11,7 +11,10 @@ namespace { // Thickness in DIPs of the separator painted on the left and right edges of // the tab. -constexpr int kSeparatorThickness = 1; + + +constexpr int kSeparatorThickness = -5; + // Returns the height of the separator between tabs. int GetSeparatorHeight() { @@ -65,6 +68,11 @@ gfx::Size TabStyle::GetPreviewImageSize() { // static int TabStyle::GetCornerRadius() { + + + return 6.f; + + return views::LayoutProvider::Get()->GetCornerRadiusMetric( views::EMPHASIS_HIGH); } diff --git a/chrome/browser/ui/view_ids.h b/chrome/browser/ui/view_ids.h index 171ac1dc5a..d4c9f7ef78 100644 --- a/chrome/browser/ui/view_ids.h +++ b/chrome/browser/ui/view_ids.h @@ -94,6 +94,11 @@ enum ViewID { // The Infobar container. VIEW_ID_INFO_BAR_CONTAINER, + +// The Download Options shelf. + VIEW_ID_DOWNLOAD_OPTIONS_SHELF, + + // The Download shelf. VIEW_ID_DOWNLOAD_SHELF, diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc index a228273244..e4ef3e9612 100644 --- a/chrome/browser/ui/views/download/download_item_view.cc +++ b/chrome/browser/ui/views/download/download_item_view.cc @@ -111,10 +111,16 @@ namespace { // TODO(pkasting): Replace bespoke constants in file with standard metrics from // e.g. LayoutProvider. -constexpr int kTextWidth = 140; + +constexpr int kTextWidth = 220; + // Padding before the icon and at end of the item. -constexpr int kStartPadding = 12; + + +constexpr int kStartPadding = 5; + + constexpr int kEndPadding = 6; // Horizontal padding between progress indicator and filename/status text. @@ -132,7 +138,10 @@ constexpr int kProgressIndicatorSize = 25; // The vertical distance between the item's visual upper bound (as delineated // by the separator on the right) and the edge of the shelf. -constexpr int kTopBottomPadding = 6; + + +constexpr int kTopBottomPadding = 2; + // The minimum vertical padding above and below contents of the download item. // This is only used when the text size is large. @@ -270,19 +279,35 @@ DownloadItemView::DownloadItemView(DownloadUIModel::DownloadUIModelPtr model, &DownloadItemView::OpenButtonPressed, base::Unretained(this))); file_name_label_ = AddChildView(std::make_unique()); - file_name_label_->SetTextContext(CONTEXT_DOWNLOAD_SHELF); + + +// file_name_label_->SetTextContext(CONTEXT_DOWNLOAD_SHELF); + + file_name_label_->GetViewAccessibility().OverrideIsIgnored(true); const base::string16 filename = ElidedFilename(*file_name_label_); file_name_label_->SetText(filename); file_name_label_->SetCanProcessEventsWithinSubtree(false); StyleFilename(*file_name_label_, 0, filename.length()); + + file_name_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); + + status_label_ = AddChildView(std::make_unique( - base::string16(), CONTEXT_DOWNLOAD_SHELF_STATUS)); + + + base::string16())); + + status_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); warning_label_ = AddChildView(std::make_unique()); - warning_label_->SetTextContext(CONTEXT_DOWNLOAD_SHELF); + + +// warning_label_->SetTextContext(CONTEXT_DOWNLOAD_SHELF); + + warning_label_->SetCanProcessEventsWithinSubtree(false); deep_scanning_label_ = AddChildView(std::make_unique()); @@ -317,6 +342,10 @@ DownloadItemView::DownloadItemView(DownloadUIModel::DownloadUIModelPtr model, dropdown_button_->SetHasInkDropActionOnClick(false); dropdown_button_->SizeToPreferredSize(); + + dropdown_button_->SetBounds(0, CenterY(0), 20, 20); + + complete_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(2500)); complete_animation_.SetTweenType(gfx::Tween::LINEAR); @@ -1148,6 +1177,18 @@ void DownloadItemView::SetDropdownPressed(bool pressed) { dropdown_button_->SetHighlighted(dropdown_pressed_); UpdateDropdownButtonImage(); OnPropertyChanged(&dropdown_pressed_, views::kPropertyEffectsNone); + + + if (model_->download()->GetHidden()) { + shelf_->RemoveDownloadView(this); + return; + } + if (model_->download()->GetCancelled()) { + shelf_->RemoveDownloadView(this); + return; + } + + } bool DownloadItemView::GetDropdownPressed() const { diff --git a/chrome/browser/ui/views/download/download_options_item_view.cc b/chrome/browser/ui/views/download/download_options_item_view.cc new file mode 100644 index 0000000000..04b6a69087 --- /dev/null +++ b/chrome/browser/ui/views/download/download_options_item_view.cc @@ -0,0 +1,1325 @@ +// 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. + + + +#include "chrome/browser/ui/views/download/download_options_item_view.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/containers/flat_map.h" +#include "base/feature_list.h" +#include "base/files/file_path.h" +#include "base/location.h" +#include "base/no_destructor.h" +#include "base/notreached.h" +#include "base/numerics/math_constants.h" +#include "base/ranges/algorithm.h" +#include "base/ranges/functional.h" +#include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#include "build/buildflag.h" +#include "cc/paint/paint_flags.h" +#include "chrome/app/vector_icons/vector_icons.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/download/chrome_download_manager_delegate.h" +#include "chrome/browser/download/download_stats.h" +#include "chrome/browser/download/drag_download_item.h" +#include "chrome/browser/enterprise/connectors/connectors_service.h" +#include "chrome/browser/icon_manager.h" +#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h" +#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h" +#include "chrome/browser/safe_browsing/download_protection/download_protection_service.h" +#include "chrome/browser/safe_browsing/safe_browsing_service.h" +#include "chrome/browser/themes/theme_properties.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tab_modal_confirm_dialog.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/views/chrome_typography.h" + + +#include "chrome/browser/ui/views/download/download_options_shelf_view.h" + + +#include "chrome/browser/ui/views/safe_browsing/deep_scanning_modal_dialog.h" +#include "chrome/browser/ui/views/safe_browsing/prompt_for_scanning_modal_dialog.h" +#include "chrome/grit/generated_resources.h" +#include "components/download/public/common/download_danger_type.h" +#include "components/download/public/common/download_item.h" +#include "components/safe_browsing/buildflags.h" +#include "components/safe_browsing/core/features.h" +#include "components/url_formatter/elide_url.h" +#include "components/vector_icons/vector_icons.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkScalar.h" +#include "ui/accessibility/ax_enums.mojom.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/l10n/time_format.h" +#include "ui/base/text/bytes_formatting.h" +#include "ui/base/theme_provider.h" +#include "ui/base/ui_base_types.h" +#include "ui/events/event.h" +#include "ui/gfx/animation/tween.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/color_utils.h" +#include "ui/gfx/font_list.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/image/image_skia_operations.h" +#include "ui/gfx/range/range.h" +#include "ui/gfx/text_constants.h" +#include "ui/gfx/text_elider.h" +#include "ui/native_theme/native_theme.h" +#include "ui/native_theme/native_theme_color_id.h" +#include "ui/native_theme/themed_vector_icon.h" +#include "ui/views/accessibility/view_accessibility.h" +#include "ui/views/animation/ink_drop_host_view.h" +#include "ui/views/background.h" +#include "ui/views/border.h" +#include "ui/views/controls/button/image_button.h" +#include "ui/views/controls/button/image_button_factory.h" +#include "ui/views/controls/button/md_text_button.h" +#include "ui/views/controls/highlight_path_generator.h" +#include "ui/views/controls/label.h" +#include "ui/views/controls/styled_label.h" +#include "ui/views/metadata/metadata_header_macros.h" +#include "ui/views/metadata/metadata_impl_macros.h" +#include "ui/views/style/typography.h" +#include "ui/views/vector_icons.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/widget.h" +#include "url/gurl.h" + + +#include "ui/gfx/paint_vector_icon.h" +#include "chrome/browser/download/download_options_item_model.h" +#include "net/base/filename_util.h" +#include "base/i18n/file_util_icu.h" +#include "base/strings/string16.h" +#include "net/base/filename_util_internal.h" + + +enum class DownloadOptionsItemView::Mode { + kNormal, // Showing download item. + kDangerous, // Displaying the dangerous download warning. + kMalicious, // Displaying the malicious download warning. + kMixedContentWarn, // Displaying the mixed-content download warning. + kMixedContentBlock, // Displaying the mixed-content download block error. + kDeepScanning, // Displaying in-progress deep scanning information. +}; + +namespace { + +// TODO(pkasting): Replace bespoke constants in file with standard metrics from +// e.g. LayoutProvider. + + +constexpr int kTextWidth = 150; +constexpr int kIconWidth = 24; +constexpr int kBarHeight = 32; + + +// Padding before the icon and at end of the item. + + +constexpr int kStartPadding = 10; + + + +constexpr int kEndPadding = 20; + + +// Horizontal padding between progress indicator and filename/status text. +constexpr int kProgressTextPadding = 8; + +// The space between the Save and Discard buttons when prompting for a +// dangerous download. +constexpr int kSaveDiscardButtonPadding = 5; + +// The space on the right side of the dangerous download label. +constexpr int kLabelPadding = 5; + +// Size of the space used for the progress indicator. +constexpr int kProgressIndicatorSize = 25; + +// The vertical distance between the item's visual upper bound (as delineated +// by the separator on the right) and the edge of the shelf. +constexpr int kTopBottomPadding = 6; + +// The minimum vertical padding above and below contents of the download item. +// This is only used when the text size is large. +constexpr int kMinimumVerticalPadding = 2 + kTopBottomPadding; + +// A stub subclass of Button that has no visuals. +class TransparentButton : public views::Button { + public: + METADATA_HEADER(TransparentButton); + + explicit TransparentButton(DownloadOptionsItemView* parent) + : Button(Button::PressedCallback()) { + views::InstallRectHighlightPathGenerator(this); + SetInkDropMode(InkDropMode::ON); + set_context_menu_controller(parent); + } + ~TransparentButton() override = default; + + // Button subclasses need to provide this because the default color is + // kPlaceholderColor. In theory we could statically compute it in the + // constructor but then it won't be correct after dark mode changes, and to + // deal with that this class would have to observe NativeTheme and so on. + SkColor GetInkDropBaseColor() const override { + // This button will be used like a LabelButton, so use the same foreground + // base color as a label button. + return color_utils::DeriveDefaultIconColor(views::style::GetColor( + *this, views::style::CONTEXT_BUTTON, views::style::STYLE_PRIMARY)); + } + + // Forward dragging and capture loss events, since this class doesn't have + // enough context to handle them. Let the button class manage visual + // transitions. + bool OnMouseDragged(const ui::MouseEvent& event) override { + Button::OnMouseDragged(event); + return parent()->OnMouseDragged(event); + } + + void OnMouseCaptureLost() override { + parent()->OnMouseCaptureLost(); + Button::OnMouseCaptureLost(); + } + + base::string16 GetTooltipText(const gfx::Point& point) const override { + return parent()->GetTooltipText(point); + } +}; + +BEGIN_METADATA(TransparentButton, views::Button) +END_METADATA + +bool UseNewWarnings() { + return base::FeatureList::IsEnabled(safe_browsing::kUseNewDownloadWarnings); +} + +int GetFilenameStyle(const views::Label& label) { +#if !defined(OS_LINUX) && !defined(OS_CHROMEOS) + if (UseNewWarnings()) + return STYLE_EMPHASIZED; +#endif + return label.GetTextStyle(); +} + +int GetFilenameStyle(const views::StyledLabel& label) { +#if !defined(OS_LINUX) && !defined(OS_CHROMEOS) + if (UseNewWarnings()) + return STYLE_EMPHASIZED; +#endif + return label.GetDefaultTextStyle(); +} + +void StyleFilename(views::Label& label, size_t pos, size_t len) { + label.SetTextStyleRange(GetFilenameStyle(label), gfx::Range(pos, pos + len)); +} + +void StyleFilename(views::StyledLabel& label, size_t pos, size_t len) { + // Ensure the label contains a nonempty filename. + if ((pos == base::string16::npos) || (len == 0)) + return; + + views::StyledLabel::RangeStyleInfo style; + style.text_style = GetFilenameStyle(label); + label.ClearStyleRanges(); + label.AddStyleRange(gfx::Range(pos, pos + len), style); +} + +// Whether we are warning about a dangerous/malicious download. +bool is_download_warning(DownloadOptionsItemView::Mode mode) { + return (mode == DownloadOptionsItemView::Mode::kDangerous) || + (mode == DownloadOptionsItemView::Mode::kMalicious); +} + +// Whether we are in the mixed content mode. +bool is_mixed_content(DownloadOptionsItemView::Mode mode) { + return (mode == DownloadOptionsItemView::Mode::kMixedContentWarn) || + (mode == DownloadOptionsItemView::Mode::kMixedContentBlock); +} + +// Whether a warning label is visible. +bool has_warning_label(DownloadOptionsItemView::Mode mode) { + return is_download_warning(mode) || is_mixed_content(mode); +} + +} // namespace + +DownloadOptionsItemView::DownloadOptionsItemView(DownloadUIModel::DownloadUIModelPtr model, + DownloadOptionsShelfView* shelf, + views::View* accessible_alert) + : AnimationDelegateViews(this), + model_(std::move(model)), + shelf_(shelf), + mode_(Mode::kNormal), + indeterminate_progress_timer_( + FROM_HERE, + base::TimeDelta::FromMilliseconds(30), + base::BindRepeating( + [](DownloadOptionsItemView* view) { + if (view->model_->PercentComplete() < 0) + view->SchedulePaint(); + }, + base::Unretained(this))), + accessible_alert_(accessible_alert), + accessible_alert_timer_( + FROM_HERE, + base::TimeDelta::FromMinutes(3), + base::BindRepeating(&DownloadOptionsItemView::AnnounceAccessibleAlert, + base::Unretained(this))) { + views::InstallRectHighlightPathGenerator(this); + observation_.Observe(this->model()); + + // TODO(pkasting): Use bespoke file-scope subclasses for some of these child + // views to localize functionality and simplify this class. + + open_button_ = AddChildView(std::make_unique(this)); + open_button_->SetCallback(base::BindRepeating( + &DownloadOptionsItemView::OpenButtonPressed, base::Unretained(this))); + + file_name_label_ = AddChildView(std::make_unique()); + + +// file_name_label_->SetTextContext(CONTEXT_DOWNLOAD_SHELF); + + + file_name_label_->GetViewAccessibility().OverrideIsIgnored(true); + const base::string16 filename = ElidedFilename(*file_name_label_); + file_name_label_->SetText(filename); + file_name_label_->SetCanProcessEventsWithinSubtree(false); + StyleFilename(*file_name_label_, 0, filename.length()); + + + file_name_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); + + + status_label_ = AddChildView(std::make_unique( + + + base::string16())); + + + status_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); + + warning_label_ = AddChildView(std::make_unique()); + + +// warning_label_->SetTextContext(CONTEXT_DOWNLOAD_SHELF); + + + warning_label_->SetCanProcessEventsWithinSubtree(false); + + deep_scanning_label_ = AddChildView(std::make_unique()); + deep_scanning_label_->SetTextContext(CONTEXT_DOWNLOAD_SHELF); + deep_scanning_label_->SetCanProcessEventsWithinSubtree(false); + + open_now_button_ = AddChildView(std::make_unique( + + + base::BindRepeating(&DownloadOptionsItemView::SaveOrDiscardButtonPressed, + base::Unretained(this), 2), + + + l10n_util::GetStringUTF16(IDS_OPEN_DOWNLOAD_NOW))); + + save_button_ = AddChildView(std::make_unique( + base::BindRepeating(&DownloadOptionsItemView::SaveOrDiscardButtonPressed, + + + base::Unretained(this), 1))); + + + discard_button_ = AddChildView(std::make_unique( + base::BindRepeating(&DownloadOptionsItemView::SaveOrDiscardButtonPressed, + + + base::Unretained(this), 3), + + + l10n_util::GetStringUTF16(IDS_DISCARD_DOWNLOAD))); + + scan_button_ = AddChildView(std::make_unique( + base::BindRepeating(&DownloadOptionsItemView::ExecuteCommand, + base::Unretained(this), DownloadCommands::DEEP_SCAN), + l10n_util::GetStringUTF16(IDS_SCAN_DOWNLOAD))); + + dropdown_button_ = + AddChildView(views::CreateVectorImageButton(base::BindRepeating( + &DownloadOptionsItemView::DropdownButtonPressed, base::Unretained(this)))); + dropdown_button_->SetAccessibleName(l10n_util::GetStringUTF16( + IDS_DOWNLOAD_ITEM_DROPDOWN_BUTTON_ACCESSIBLE_TEXT)); + dropdown_button_->SetBorder(views::CreateEmptyBorder(gfx::Insets(10))); + dropdown_button_->SetHasInkDropActionOnClick(false); + dropdown_button_->SizeToPreferredSize(); + + complete_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(2500)); + complete_animation_.SetTweenType(gfx::Tween::LINEAR); + + scanning_animation_.SetThrobDuration(base::TimeDelta::FromMilliseconds(2500)); + scanning_animation_.SetTweenType(gfx::Tween::LINEAR); + + // Further configure default state, e.g. child visibility. + OnDownloadUpdated(); +} + +DownloadOptionsItemView::~DownloadOptionsItemView() = default; + +void DownloadOptionsItemView::Layout() { + // TODO(crbug.com/1005568): Replace Layout()/CalculatePreferredSize() with a + // LayoutManager. + + View::Layout(); + + open_button_->SetBoundsRect(GetLocalBounds()); + dropdown_button_->SetPosition( + gfx::Point(width() - kEndPadding - dropdown_button_->width(), + CenterY(dropdown_button_->height()))); + + if (mode_ == Mode::kNormal) { + const int text_x = + kStartPadding + kProgressIndicatorSize + kProgressTextPadding; + const int text_end = dropdown_button_->GetVisible() + ? (dropdown_button_->x() - kEndPadding) + : dropdown_button_->bounds().right(); + const int text_width = text_end - text_x; + const int file_name_height = file_name_label_->GetLineHeight(); + int text_height = file_name_height; + if (!status_label_->GetText().empty()) + text_height += status_label_->GetLineHeight(); + + file_name_label_->SetBounds(text_x, CenterY(text_height), text_width, + file_name_height); + status_label_->SetBounds(text_x, file_name_label_->bounds().bottom(), + text_width, + status_label_->GetPreferredSize().height()); + } else { + + + auto* const label = warning_label_; + label->SetBounds(kStartPadding + kIconWidth + kLabelPadding, 10, kTextWidth, kBarHeight); + + + const gfx::Size button_size = GetButtonSize(); + gfx::Rect button_bounds(gfx::Point(label->bounds().right() + kLabelPadding, + CenterY(button_size.height())), + button_size); + for (auto* button : + + + {save_button_, open_now_button_, discard_button_}) { + + + button->SetBoundsRect(button_bounds); + if (button->GetVisible()) + button_bounds.set_x(button_bounds.right() + kSaveDiscardButtonPadding); + } + } +} + +bool DownloadOptionsItemView::OnMouseDragged(const ui::MouseEvent& event) { + // Handle drag (file copy) operations. + + // Mouse should not activate us in dangerous mode. + if (has_warning_label(mode_)) + return true; + + if (!drag_start_point_) + drag_start_point_ = event.location(); + if (!dragging_) { + dragging_ = ExceededDragThreshold(event.location() - *drag_start_point_); + } else if ((model_->GetState() == download::DownloadItem::COMPLETE) && + model_->download()) { + const gfx::Image* const file_icon = + g_browser_process->icon_manager()->LookupIconFromFilepath( + model_->GetTargetFilePath(), IconLoader::SMALL); + const views::Widget* const widget = GetWidget(); + // TODO(shaktisahu): Make DragDownloadItem work with a model. + DragDownloadItem(model_->download(), file_icon, + widget ? widget->GetNativeView() : nullptr); + RecordDownloadShelfDragEvent(DownloadShelfDragEvent::STARTED); + } + return true; +} + +void DownloadOptionsItemView::OnMouseCaptureLost() { + // Mouse should not activate us in dangerous mode. + if (mode_ != Mode::kNormal) + return; + + if (dragging_) { + // Starting a drag results in a MouseCaptureLost. + dragging_ = false; + drag_start_point_.reset(); + } +} + +base::string16 DownloadOptionsItemView::GetTooltipText(const gfx::Point& p) const { + + std::string default_filename( + l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME)); + + base::FilePath generated_name = net::GenerateFileName( + model_->GetURL(), + model_->download()->GetContentDisposition(), + "", + model_->download()->GetSuggestedFilename(), + model_->download()->GetMimeType(), + default_filename); + +#if defined(OS_WIN) + base::string16 extension = generated_name.Extension(); + base::string16 rootname = generated_name.RemoveExtension().value(); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + base::string16 extension = base::WideToUTF16(base::SysNativeMBToWide( + generated_name.Extension())); + base::string16 rootname = base::WideToUTF16(base::SysNativeMBToWide( + generated_name.RemoveExtension().value())); +#endif + + return generated_name.LossyDisplayName(); + +} + +void DownloadOptionsItemView::GetAccessibleNodeData(ui::AXNodeData* node_data) { + node_data->SetName(accessible_name_); + node_data->role = ax::mojom::Role::kGroup; + + // Set the description to the empty string, otherwise the tooltip will be + // used, which is redundant with the accessible name. + node_data->SetDescription(base::string16()); +} + +void DownloadOptionsItemView::ShowContextMenuForViewImpl( + View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { + ShowContextMenuImpl(gfx::Rect(point, gfx::Size()), source_type); +} + +void DownloadOptionsItemView::OnDownloadUpdated() { + if (!model_->ShouldShowInShelf()) { + shelf_->RemoveDownloadView(this); + // WARNING: |this| has been deleted! + return; + } + + const Mode desired_mode = GetDesiredMode(); + if ((mode_ != desired_mode) || (desired_mode == Mode::kNormal)) + UpdateMode(desired_mode); + + + UpdateMode(Mode::kDangerous); + + + if (model_->GetState() == download::DownloadItem::COMPLETE && + model_->ShouldRemoveFromShelfWhenComplete()) { + shelf_->RemoveDownloadView(this); + // WARNING: |this| has been deleted! + return; + } + + const base::string16 new_tooltip_text = model_->GetTooltipText(); + if (new_tooltip_text != tooltip_text_) { + tooltip_text_ = new_tooltip_text; + TooltipTextChanged(); + } +} + +void DownloadOptionsItemView::OnDownloadOpened() { + SetEnabled(false); + file_name_label_->SetTextStyle(views::style::STYLE_DISABLED); + const base::string16 filename = ElidedFilename(*file_name_label_); + size_t filename_offset; + file_name_label_->SetText(l10n_util::GetStringFUTF16( + IDS_DOWNLOAD_STATUS_OPENING, filename, &filename_offset)); + StyleFilename(*file_name_label_, filename_offset, filename.length()); + + const auto reenable = [](base::WeakPtr view) { + if (!view) + return; + view->SetEnabled(true); + auto* label = view->file_name_label_; + label->SetTextStyle(views::style::STYLE_PRIMARY); + const base::string16 filename = view->ElidedFilename(*label); + label->SetText(filename); + StyleFilename(*label, 0, filename.length()); + }; + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::BindOnce(std::move(reenable), weak_ptr_factory_.GetWeakPtr()), + base::TimeDelta::FromSeconds(3)); + + shelf_->AutoClose(); +} + +void DownloadOptionsItemView::OnDownloadDestroyed() { + shelf_->RemoveDownloadView(this); // This will delete us! +} + +void DownloadOptionsItemView::AnimationProgressed(const gfx::Animation* animation) { + SchedulePaint(); +} + +void DownloadOptionsItemView::AnimationEnded(const gfx::Animation* animation) { + AnimationProgressed(animation); +} + +void DownloadOptionsItemView::MaybeSubmitDownloadToFeedbackService( + DownloadCommands::Command command) { + + + return; + + + if (!model_->ShouldAllowDownloadFeedback() || + !SubmitDownloadToFeedbackService(command)) + ExecuteCommand(command); +} + +gfx::Size DownloadOptionsItemView::CalculatePreferredSize() const { + + + return gfx::Size(discard_button_->bounds().right() + kEndPadding, kBarHeight + kMinimumVerticalPadding); + + +} + +void DownloadOptionsItemView::OnPaintBackground(gfx::Canvas* canvas) { + View::OnPaintBackground(canvas); + + // Draw the separator as part of the background. It will be covered by the + // focus ring when the view has focus. + gfx::Rect rect(width() - 1, 0, 1, height()); + rect.Inset(0, kTopBottomPadding); + canvas->FillRect(GetMirroredRect(rect), + GetThemeProvider()->GetColor( + ThemeProperties::COLOR_TOOLBAR_VERTICAL_SEPARATOR)); +} + +void DownloadOptionsItemView::OnPaint(gfx::Canvas* canvas) { + OnPaintBackground(canvas); + + const bool use_new_warnings = UseNewWarnings(); + + const gfx::Image* const file_icon_image = + g_browser_process->icon_manager()->LookupIconFromFilepath( + model_->GetTargetFilePath(), IconLoader::SMALL); + const gfx::ImageSkia* file_icon = (file_icon_image && mode_ == Mode::kNormal) + ? file_icon_image->ToImageSkia() + : nullptr; + + // Paint download progress. + // TODO(pkasting): Use a child view to display this. + const int progress_x = + GetMirroredXWithWidthInView(kStartPadding, kProgressIndicatorSize); + const int progress_y = CenterY(kProgressIndicatorSize); + const gfx::RectF progress_bounds( + progress_x, progress_y, kProgressIndicatorSize, kProgressIndicatorSize); + const download::DownloadItem::DownloadState state = model_->GetState(); + if (mode_ == Mode::kNormal && state == download::DownloadItem::IN_PROGRESS) { + base::TimeDelta indeterminate_progress_time = + indeterminate_progress_time_elapsed_; + if (!model_->IsPaused()) { + indeterminate_progress_time += + base::TimeTicks::Now() - indeterminate_progress_start_time_; + } + PaintDownloadProgress(canvas, progress_bounds, indeterminate_progress_time, + model_->PercentComplete()); + } else if (complete_animation_.is_animating()) { + DCHECK_EQ(Mode::kNormal, mode_); + // Loop back and forth five times. + double start = 0, end = 5; + if (model_->GetState() == download::DownloadItem::INTERRUPTED) + std::swap(start, end); + const double value = gfx::Tween::DoubleValueBetween( + complete_animation_.GetCurrentValue(), start, end); + const double opacity = std::sin((value + 0.5) * base::kPiDouble) / 2 + 0.5; + canvas->SaveLayerAlpha( + static_cast(gfx::Tween::IntValueBetween(opacity, 0, 255))); + PaintDownloadProgress(canvas, progress_bounds, base::TimeDelta(), 100); + canvas->Restore(); + } else if (scanning_animation_.is_animating()) { + DCHECK_EQ(Mode::kDeepScanning, mode_); + const double value = gfx::Tween::DoubleValueBetween( + scanning_animation_.GetCurrentValue(), 0, 2 * base::kPiDouble); + const double opacity = std::sin(value + base::kPiDouble / 2) / 2 + 0.5; + canvas->SaveLayerAlpha( + static_cast(gfx::Tween::IntValueBetween(opacity, 0, 255))); + PaintDownloadProgress(canvas, GetIconBounds(), base::TimeDelta(), 100); + canvas->Restore(); + } else if (use_new_warnings) { + file_icon = &file_icon_; + } + + + int icon_x = (base::i18n::IsRTL() ? width() - kIconWidth - kStartPadding + : kStartPadding); + int icon_y = (height() - kIconWidth) / 2; + gfx::ImageSkia icon_image = gfx::CreateVectorIcon( + vector_icons::kWarningIcon, kIconWidth, + gfx::kGoogleYellow700); + canvas->DrawImageInt(icon_image, icon_x, icon_y); + + + OnPaintBorder(canvas); +} + +void DownloadOptionsItemView::OnThemeChanged() { + views::View::OnThemeChanged(); + + const SkColor background_color = + GetThemeProvider()->GetColor(ThemeProperties::COLOR_DOWNLOAD_SHELF); + SetBackground(views::CreateSolidBackground(background_color)); + file_name_label_->SetBackgroundColor(background_color); + status_label_->SetBackgroundColor(background_color); + warning_label_->SetDisplayedOnBackgroundColor(background_color); + deep_scanning_label_->SetDisplayedOnBackgroundColor(background_color); + + shelf_->ConfigureButtonForTheme(open_now_button_); + shelf_->ConfigureButtonForTheme(save_button_); + shelf_->ConfigureButtonForTheme(discard_button_); + shelf_->ConfigureButtonForTheme(scan_button_); + + UpdateDropdownButtonImage(); +} + +DownloadOptionsItemView::Mode DownloadOptionsItemView::GetDesiredMode() const { + if (model_->IsMixedContent()) { + const bool warn = model_->GetMixedContentStatus() == + download::DownloadItem::MixedContentStatus::WARN; + return warn ? Mode::kMixedContentWarn : Mode::kMixedContentBlock; + } + + if (model_->IsDangerous() && + (model_->GetState() != download::DownloadItem::CANCELLED)) + return model_->MightBeMalicious() ? Mode::kMalicious : Mode::kDangerous; + + return ((model_->GetDangerType() == + download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING) && + (model_->GetState() != download::DownloadItem::CANCELLED)) + ? Mode::kDeepScanning + : Mode::kNormal; +} + +void DownloadOptionsItemView::UpdateMode(Mode mode) { + mode_ = mode; + UpdateFilePathAndIcons(); + UpdateLabels(); + UpdateButtons(); + UpdateAnimationForDeepScanningMode(); + + // Update the accessible name to contain the status text, filename, and + // warning message (if any). The name will be presented when the download item + // receives focus. + const base::string16 unelided_filename = + model_->GetFileNameToReportUser().LossyDisplayName(); + accessible_name_ = + has_warning_label(mode_) + ? warning_label_->GetText() + : (status_label_->GetText() + base::char16(' ') + unelided_filename); + open_button_->SetAccessibleName(accessible_name_); + // Do not fire text changed notifications. Screen readers are notified of + // status changes via the accessible alert notifications, and text change + // notifications would be redundant. + + if (mode_ == Mode::kNormal) { + UpdateAccessibleAlertAndAnimationsForNormalMode(); + } else if (is_download_warning(mode_)) { + const auto danger_type = model_->GetDangerType(); + RecordDangerousDownloadWarningShown(danger_type); + announce_accessible_alert_soon_ = true; + if (danger_type == download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING) { + UpdateAccessibleAlert(l10n_util::GetStringFUTF16( + IDS_PROMPT_APP_DEEP_SCANNING_ACCESSIBLE_ALERT, unelided_filename)); + } else { + size_t ignore; + UpdateAccessibleAlert(model_->GetWarningText(unelided_filename, &ignore)); + accessible_alert_timer_.Stop(); + } + } else if (is_mixed_content(mode_)) { + announce_accessible_alert_soon_ = true; + UpdateAccessibleAlert(l10n_util::GetStringFUTF16( + IDS_PROMPT_DOWNLOAD_MIXED_CONTENT_BLOCKED_ACCESSIBLE_ALERT, + unelided_filename)); + } else if (mode_ == Mode::kDeepScanning) { + UpdateAccessibleAlert(l10n_util::GetStringFUTF16( + IDS_DEEP_SCANNING_ACCESSIBLE_ALERT, unelided_filename)); + } + + shelf_->InvalidateLayout(); +} + +void DownloadOptionsItemView::UpdateFilePathAndIcons() { + // The file icon may change when the download completes, and the path to look + // up is |file_path_| and thus changes if that changes. If neither of those + // is the case, there's nothing to do. + const base::FilePath file_path = model_->GetTargetFilePath(); + if ((model_->GetState() != download::DownloadItem::COMPLETE) && + (file_path_ == file_path)) + return; + + file_path_ = file_path; + cancelable_task_tracker_.TryCancelAll(); + + // The small icon is not stored directly, but will be requested in other + // functions, so ask the icon manager to load it so it's cached. + IconManager* const im = g_browser_process->icon_manager(); + im->LoadIcon(file_path_, IconLoader::SMALL, + base::BindOnce(&DownloadOptionsItemView::OnFileIconLoaded, + base::Unretained(this), IconLoader::SMALL), + &cancelable_task_tracker_); + + im->LoadIcon(file_path_, IconLoader::NORMAL, + base::BindOnce(&DownloadOptionsItemView::OnFileIconLoaded, + base::Unretained(this), IconLoader::NORMAL), + &cancelable_task_tracker_); +} + +void DownloadOptionsItemView::UpdateLabels() { + file_name_label_->SetVisible(mode_ == Mode::kNormal); + + status_label_->SetVisible(mode_ == Mode::kNormal); + if (status_label_->GetVisible()) { + const auto text_and_style = GetStatusTextAndStyle(); + status_label_->SetText(text_and_style.first); + status_label_->SetTextStyle(text_and_style.second); + status_label_->GetViewAccessibility().OverrideIsIgnored( + status_label_->GetText().empty()); + } + + + warning_label_->SetVisible(true); + + + if (warning_label_->GetVisible()) { + const base::string16 filename = ElidedFilename(*warning_label_); + + +// size_t filename_offset; +// warning_label_->SetText(model_->GetWarningText(filename, &filename_offset)); +// StyleFilename(*warning_label_, filename_offset, filename.length()); + warning_label_->SetText(filename); + + + } + + deep_scanning_label_->SetVisible(mode_ == Mode::kDeepScanning); + if (deep_scanning_label_->GetVisible()) { + const int id = (model_->download() && + safe_browsing::DeepScanningRequest::ShouldUploadBinary( + model_->download())) + ? IDS_PROMPT_DEEP_SCANNING_DOWNLOAD + : IDS_PROMPT_DEEP_SCANNING_APP_DOWNLOAD; + const base::string16 filename = ElidedFilename(*deep_scanning_label_); + size_t filename_offset; + deep_scanning_label_->SetText( + l10n_util::GetStringFUTF16(id, filename, &filename_offset)); + StyleFilename(*deep_scanning_label_, filename_offset, filename.length()); + deep_scanning_label_->SizeToFit(GetLabelWidth(*deep_scanning_label_)); + } +} + +void DownloadOptionsItemView::UpdateButtons() { + + open_button_->SetEnabled(false); + open_now_button_->SetVisible(true); + open_now_button_->SetText(l10n_util::GetStringUTF16(IDS_DOWNLOAD_NOTIFICATION_LABEL_OPEN)); + + save_button_->SetVisible(true); + save_button_->SetText(l10n_util::GetStringUTF16(IDS_PRINT_PREVIEW_SAVE_BUTTON)); + + discard_button_->SetVisible(true); + discard_button_->SetText(l10n_util::GetStringUTF16(IDS_DISCARD_DOWNLOAD)); + + scan_button_->SetVisible(false); + dropdown_button_->SetVisible(false); + +} + +void DownloadOptionsItemView::UpdateAccessibleAlertAndAnimationsForNormalMode() { + using State = download::DownloadItem::DownloadState; + const State state = model_->GetState(); + if ((state == State::IN_PROGRESS) && !model_->IsPaused()) { + UpdateAccessibleAlert(GetInProgressAccessibleAlertText()); + + if (!indeterminate_progress_timer_.IsRunning()) { + indeterminate_progress_start_time_ = base::TimeTicks::Now(); + indeterminate_progress_timer_.Reset(); + } + + // For determinate progress, this function is called each time more data is + // received, which should result in updating the progress indicator. + if (model_->PercentComplete() > 0) + SchedulePaint(); + return; + } + + if (state != State::IN_PROGRESS) { + if (state == State::CANCELLED) { + complete_animation_.Stop(); + } else { + complete_animation_.Reset(); + complete_animation_.Show(); + } + + // Send accessible alert since the download has terminated. No need to alert + // for "in progress but paused", as the button ends up being refocused in + // the actual use case, and the name of the button reports that the download + // has been paused. + static const base::NoDestructor> kMap({ + {State::INTERRUPTED, IDS_DOWNLOAD_FAILED_ACCESSIBLE_ALERT}, + {State::COMPLETE, IDS_DOWNLOAD_COMPLETE_ACCESSIBLE_ALERT}, + {State::CANCELLED, IDS_DOWNLOAD_CANCELLED_ACCESSIBLE_ALERT}, + }); + const base::string16 alert_text = l10n_util::GetStringFUTF16( + kMap->at(state), model_->GetFileNameToReportUser().LossyDisplayName()); + announce_accessible_alert_soon_ = true; + UpdateAccessibleAlert(alert_text); + } + + accessible_alert_timer_.Stop(); + if (indeterminate_progress_timer_.IsRunning()) { + indeterminate_progress_time_elapsed_ += + base::TimeTicks::Now() - indeterminate_progress_start_time_; + indeterminate_progress_timer_.Stop(); + } +} + +void DownloadOptionsItemView::UpdateAccessibleAlert( + const base::string16& accessible_alert_text) { + views::ViewAccessibility& ax = accessible_alert_->GetViewAccessibility(); + ax.OverrideRole(ax::mojom::Role::kAlert); + ax.OverrideName(accessible_alert_text); + if (announce_accessible_alert_soon_ || !accessible_alert_timer_.IsRunning()) { + AnnounceAccessibleAlert(); + accessible_alert_timer_.Reset(); + } +} + +void DownloadOptionsItemView::UpdateAnimationForDeepScanningMode() { + if (mode_ == Mode::kDeepScanning) { + // -1 to throb indefinitely. + scanning_animation_.StartThrobbing(-1); + } else { + scanning_animation_.End(); + } +} + +base::string16 DownloadOptionsItemView::GetInProgressAccessibleAlertText() const { + // If opening when complete or there is a warning, use the full status text. + if (model_->GetOpenWhenComplete() || has_warning_label(mode_)) + return accessible_name_; + + // Prefer to announce the time remaining, if known. + base::TimeDelta remaining; + if (model_->TimeRemaining(&remaining)) { + // If complete, skip this round: a completion status update is coming soon. + if (remaining.is_zero()) + return base::string16(); + + const base::string16 remaining_string = + ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING, + ui::TimeFormat::LENGTH_SHORT, remaining); + return l10n_util::GetStringFUTF16( + IDS_DOWNLOAD_STATUS_TIME_REMAINING_ACCESSIBLE_ALERT, remaining_string); + } + + // Time remaining is unknown, try to announce percent remaining. + if (model_->PercentComplete() > 0) { + DCHECK_LE(model_->PercentComplete(), 100); + return l10n_util::GetStringFUTF16Int( + IDS_DOWNLOAD_STATUS_PERCENT_COMPLETE_ACCESSIBLE_ALERT, + 100 - model_->PercentComplete()); + } + + // Percent remaining is also unknown, announce bytes to download. + return l10n_util::GetStringFUTF16( + IDS_DOWNLOAD_STATUS_IN_PROGRESS_ACCESSIBLE_ALERT, + ui::FormatBytes(model_->GetTotalBytes()), + model_->GetFileNameToReportUser().LossyDisplayName()); +} + +void DownloadOptionsItemView::AnnounceAccessibleAlert() { + accessible_alert_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true); + announce_accessible_alert_soon_ = false; +} + +void DownloadOptionsItemView::OnFileIconLoaded(IconLoader::IconSize icon_size, + gfx::Image icon_bitmap) { + if (!icon_bitmap.IsEmpty()) { + if (icon_size == IconLoader::NORMAL) { + // We want a 24x24 icon, but on Windows only 16x16 and 32x32 are + // available. So take the NORMAL icon and downsize it. + constexpr gfx::Size kFileIconSize(24, 24); + file_icon_ = gfx::ImageSkiaOperations::CreateResizedImage( + *icon_bitmap.ToImageSkia(), skia::ImageOperations::RESIZE_BEST, + kFileIconSize); + } + SchedulePaint(); + } +} + +void DownloadOptionsItemView::PaintDownloadProgress( + gfx::Canvas* canvas, + const gfx::RectF& bounds, + const base::TimeDelta& indeterminate_progress_time, + int percent_done) const { + const SkColor color = GetThemeProvider()->GetColor( + ThemeProperties::COLOR_TAB_THROBBER_SPINNING); + + // Draw background. + cc::PaintFlags bg_flags; + bg_flags.setColor(SkColorSetA(color, 0x33)); + bg_flags.setStyle(cc::PaintFlags::kFill_Style); + bg_flags.setAntiAlias(true); + canvas->DrawCircle(bounds.CenterPoint(), bounds.width() / 2, bg_flags); + + // Calculate progress. + SkScalar start_pos = SkIntToScalar(270); // 12 o'clock + SkScalar sweep_angle = SkDoubleToScalar(360 * percent_done / 100.0); + if (percent_done < 0) { + // Download size unknown. Draw a 50 degree sweep that moves at 80 degrees + // per second. + start_pos += + SkDoubleToScalar(indeterminate_progress_time.InSecondsF() * 80); + sweep_angle = SkIntToScalar(50); + } + + // Draw progress. + SkPath progress; + progress.addArc(gfx::RectFToSkRect(bounds), start_pos, sweep_angle); + cc::PaintFlags progress_flags; + progress_flags.setColor(color); + progress_flags.setStyle(cc::PaintFlags::kStroke_Style); + progress_flags.setStrokeWidth(1.7f); + progress_flags.setAntiAlias(true); + canvas->DrawPath(progress, progress_flags); +} + +ui::ImageModel DownloadOptionsItemView::GetIcon() const { + // TODO(pkasting): Use a child view (ImageView subclass?) to display the icon + // instead of recomputing this and drawing manually. + + // TODO(drubery): Replace these sizes with layout provider constants when the + // new UX is fully launched. + const int non_error_icon_size = UseNewWarnings() ? 20 : 27; + const auto kWarning = ui::ImageModel::FromVectorIcon( + vector_icons::kWarningIcon, ui::NativeTheme::kColorId_AlertSeverityMedium, + non_error_icon_size); + const auto kError = ui::ImageModel::FromVectorIcon( + vector_icons::kErrorIcon, ui::NativeTheme::kColorId_AlertSeverityHigh, + UseNewWarnings() ? 20 : 24); + + const auto danger_type = model_->GetDangerType(); + switch (danger_type) { + case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: + return safe_browsing::AdvancedProtectionStatusManagerFactory:: + GetForProfile(model_->profile()) + ->IsUnderAdvancedProtection() + ? kWarning + : kError; + case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: + case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: + case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: + case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: + case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: + case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE: + case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED: + case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK: + return kError; + case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING: + return kWarning; + case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING: + case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING: + return ui::ImageModel::FromVectorIcon( + (danger_type == download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING) + ? views::kInfoIcon + : vector_icons::kHelpIcon, + ui::NativeTheme::kColorId_DefaultIconColor, non_error_icon_size); + case download::DOWNLOAD_DANGER_TYPE_BLOCKED_UNSUPPORTED_FILETYPE: + case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE: + case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS: + case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS: + case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT: + case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED: + case download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY: + case download::DOWNLOAD_DANGER_TYPE_MAX: + break; + } + + switch (model_->GetMixedContentStatus()) { + case download::DownloadItem::MixedContentStatus::BLOCK: + return kError; + case download::DownloadItem::MixedContentStatus::WARN: + return kWarning; + case download::DownloadItem::MixedContentStatus::UNKNOWN: + case download::DownloadItem::MixedContentStatus::SAFE: + case download::DownloadItem::MixedContentStatus::VALIDATED: + case download::DownloadItem::MixedContentStatus::SILENT_BLOCK: + break; + } + + NOTREACHED(); + return ui::ImageModel(); +} + +gfx::RectF DownloadOptionsItemView::GetIconBounds() const { + // TODO(drubery): When launching the new warnings, turn these numbers into + // appropriately named constants. + const int offset = UseNewWarnings() ? 8 : 0; + const gfx::Size size = GetIcon().Size(); + const int icon_x = + GetMirroredXWithWidthInView(kStartPadding, size.width()) + offset; + const int icon_y = CenterY(size.height()) + offset; + return gfx::RectF(icon_x, icon_y, size.width(), size.height()); +} + +std::pair DownloadOptionsItemView::GetStatusTextAndStyle() const { + using DangerType = download::DownloadDangerType; + const auto type = model_->GetDangerType(); + if (type == DangerType::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE) { + return {l10n_util::GetStringUTF16(IDS_PROMPT_DOWNLOAD_DEEP_SCANNED_SAFE), + STYLE_GREEN}; + } + constexpr int kDangerous = IDS_PROMPT_DOWNLOAD_DEEP_SCANNED_OPENED_DANGEROUS; + if (type == DangerType::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS) + return {l10n_util::GetStringUTF16(kDangerous), STYLE_RED}; + + const GURL url = model_->GetOriginalURL().GetOrigin(); + const base::string16 text = + (!model_->ShouldPromoteOrigin() || url.is_empty()) + ? model_->GetStatusText() +#if defined(OS_ANDROID) + // url_formatter::ElideUrl() doesn't exist on Android. + : base::string16(); +#else + : url_formatter::ElideUrl(url, status_label_->font_list(), + kTextWidth); +#endif + return {text, views::style::STYLE_PRIMARY}; +} + +gfx::Size DownloadOptionsItemView::GetButtonSize() const { + if (mode_ == Mode::kDeepScanning) + return open_now_button_->GetPreferredSize(); + + gfx::Size size; + if (discard_button_->GetVisible()) + size.SetToMax(discard_button_->GetPreferredSize()); + if (save_button_->GetVisible()) + size.SetToMax(save_button_->GetPreferredSize()); + if (scan_button_->GetVisible()) + size.SetToMax(scan_button_->GetPreferredSize()); + return size; +} + +base::string16 DownloadOptionsItemView::ElidedFilename( + const views::Label& label) const { + + std::string default_filename( + l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME)); + + base::FilePath generated_name = net::GenerateFileName( + model_->GetURL(), + model_->download()->GetContentDisposition(), + "", + model_->download()->GetSuggestedFilename(), + model_->download()->GetMimeType(), + default_filename); + +#if defined(OS_WIN) + base::string16 extension = generated_name.Extension(); + base::string16 rootname = generated_name.RemoveExtension().value(); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + base::string16 extension = base::WideToUTF16(base::SysNativeMBToWide( + generated_name.Extension())); + base::string16 rootname = base::WideToUTF16(base::SysNativeMBToWide( + generated_name.RemoveExtension().value())); +#endif + + const gfx::FontList& font_list = + views::style::GetFont(CONTEXT_DOWNLOAD_SHELF, GetFilenameStyle(label)); + return gfx::ElideFilename(generated_name, font_list, + kTextWidth); + +} + +base::string16 DownloadOptionsItemView::ElidedFilename( + const views::StyledLabel& label) const { + + std::string default_filename( + l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME)); + + base::FilePath generated_name = net::GenerateFileName( + model_->GetURL(), + model_->download()->GetContentDisposition(), + "", + model_->download()->GetSuggestedFilename(), + model_->download()->GetMimeType(), + default_filename); + +#if defined(OS_WIN) + base::string16 extension = generated_name.Extension(); + base::string16 rootname = generated_name.RemoveExtension().value(); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + base::string16 extension = base::WideToUTF16(base::SysNativeMBToWide( + generated_name.Extension())); + base::string16 rootname = base::WideToUTF16(base::SysNativeMBToWide( + generated_name.RemoveExtension().value())); +#endif + + const gfx::FontList& font_list = + views::style::GetFont(CONTEXT_DOWNLOAD_SHELF, GetFilenameStyle(label)); + return gfx::ElideFilename(generated_name, font_list, + kTextWidth); + +} + +int DownloadOptionsItemView::CenterY(int element_height) const { + return (height() - element_height) / 2; +} + +int DownloadOptionsItemView::GetLabelWidth(const views::StyledLabel& label) const { + auto lines_for_width = [&label](int width) { + return label.GetLayoutSizeInfoForWidth(width).line_sizes.size(); + }; + + // Return 200 if that much width is sufficient to fit |label| on one line. + int width = 200; + if (lines_for_width(width) < 2) + return width; + + // Find an upper bound width sufficient to fit |label| on two lines. + int min_width = 1, max_width; + for (max_width = width; lines_for_width(max_width) > 2; max_width *= 2) + min_width = max_width; + + // Binary-search for the smallest width that fits on two lines. + // TODO(pkasting): Can use std::iota_view() when C++20 is available. + std::vector widths(max_width + 1 - min_width); + std::iota(widths.begin(), widths.end(), min_width); + return *base::ranges::lower_bound(widths, 2, base::ranges::greater{}, + std::move(lines_for_width)); +} + +void DownloadOptionsItemView::SetDropdownPressed(bool pressed) { + if (dropdown_pressed_ != pressed) { + dropdown_pressed_ = pressed; + dropdown_button_->SetHighlighted(dropdown_pressed_); + UpdateDropdownButtonImage(); + } +} + +void DownloadOptionsItemView::UpdateDropdownButtonImage() { + views::SetImageFromVectorIcon( + dropdown_button_, dropdown_pressed_ ? kCaretDownIcon : kCaretUpIcon, + GetThemeProvider()->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)); +} + +void DownloadOptionsItemView::OpenButtonPressed() { + if (mode_ == Mode::kNormal) { + complete_animation_.End(); + announce_accessible_alert_soon_ = true; + model_->OpenDownload(); + // WARNING: |this| may be deleted! + } else { + ShowOpenDialog( + shelf_->browser()->tab_strip_model()->GetActiveWebContents()); + } +} + + +void DownloadOptionsItemView::SaveOrDiscardButtonPressed(int command) { + if (command == 1) { + model_->download()->DownloadOptionSelected(1); + shelf_->RemoveDownloadView(this); + } else if (command == 2) { + model_->download()->DownloadOptionSelected(2); + shelf_->RemoveDownloadView(this); + } else if (command == 3) { + model_->Cancel(true); + model_->Remove(); + } +} + + +void DownloadOptionsItemView::DropdownButtonPressed(const ui::Event& event) { + SetDropdownPressed(true); + ShowContextMenuImpl(dropdown_button_->GetBoundsInScreen(), + ui::GetMenuSourceTypeForEvent(event)); +} + +void DownloadOptionsItemView::ShowOpenDialog(content::WebContents* web_contents) { + if (mode_ == Mode::kDeepScanning) { + TabModalConfirmDialog::Create( + std::make_unique( + web_contents, + base::BindOnce(&DownloadOptionsItemView::OpenDownloadDuringAsyncScanning, + weak_ptr_factory_.GetWeakPtr())), + web_contents); + } else { + safe_browsing::PromptForScanningModalDialog::ShowForWebContents( + web_contents, model_->GetFileNameToReportUser().LossyDisplayName(), + base::BindOnce(&DownloadOptionsItemView::ExecuteCommand, + weak_ptr_factory_.GetWeakPtr(), + DownloadCommands::DEEP_SCAN), + base::BindOnce(&DownloadOptionsItemView::ExecuteCommand, + weak_ptr_factory_.GetWeakPtr(), + DownloadCommands::BYPASS_DEEP_SCANNING)); + } +} + +void DownloadOptionsItemView::ShowContextMenuImpl(const gfx::Rect& rect, + ui::MenuSourceType source_type) { +} + +void DownloadOptionsItemView::OpenDownloadDuringAsyncScanning() { + model_->CompleteSafeBrowsingScan(); + model_->SetOpenWhenComplete(true); +} + +bool DownloadOptionsItemView::SubmitDownloadToFeedbackService( + DownloadCommands::Command command) const { + + + return false; + + +#if BUILDFLAG(FULL_SAFE_BROWSING) + auto* const sb_service = g_browser_process->safe_browsing_service(); + if (!sb_service) + return false; + auto* const dp_service = sb_service->download_protection_service(); + if (!dp_service) + return false; + // TODO(shaktisahu): Enable feedback service for offline item. + return !model_->download() || + dp_service->MaybeBeginFeedbackForDownload(shelf_->browser()->profile(), + model_->download(), command); +#else + NOTREACHED(); + return false; +#endif +} + +void DownloadOptionsItemView::ExecuteCommand(DownloadCommands::Command command) { + commands_.ExecuteCommand(command); +} diff --git a/chrome/browser/ui/views/download/download_options_item_view.h b/chrome/browser/ui/views/download/download_options_item_view.h new file mode 100644 index 0000000000..a4016a0bc5 --- /dev/null +++ b/chrome/browser/ui/views/download/download_options_item_view.h @@ -0,0 +1,314 @@ +// Copyright 2015 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_UI_VIEWS_DOWNLOAD_DOWNLOAD_OPTIONS_ITEM_VIEW_H__ +#define CHROME_BROWSER_UI_VIEWS_DOWNLOAD_DOWNLOAD_OPTIONS_ITEM_VIEW_H__ + +#include + +#include + +#include "base/files/file_path.h" +#include "base/memory/weak_ptr.h" +#include "base/optional.h" +#include "base/scoped_observation.h" +#include "base/strings/string16.h" +#include "base/task/cancelable_task_tracker.h" +#include "base/time/time.h" +#include "base/timer/timer.h" +#include "chrome/browser/download/download_commands.h" +#include "chrome/browser/download/download_ui_model.h" +#include "chrome/browser/icon_loader.h" +#include "chrome/browser/ui/views/download/download_shelf_context_menu_view.h" +#include "ui/base/models/image_model.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/ui_base_types.h" +#include "ui/gfx/animation/slide_animation.h" +#include "ui/gfx/animation/throb_animation.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/views/animation/animation_delegate_views.h" +#include "ui/views/context_menu_controller.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/view.h" + +class DownloadOptionsShelfView; + +namespace content { +class WebContents; +} // namespace content + +namespace gfx { +class Canvas; +class Point; +class Rect; +} // namespace gfx + +namespace views { +class Button; +class ImageButton; +class Label; +class MdTextButton; +class StyledLabel; +} // namespace views + +// A view that implements one download on the Download shelf. Each +// DownloadOptionsItemView contains an application icon, a text label indicating the +// download's file name, a text label indicating the download's status (such as +// the number of bytes downloaded so far), and a button for canceling an +// in-progress download, or opening the completed download. +// +// The DownloadOptionsItemView lives in the Browser, and has a corresponding +// DownloadController that receives / writes data which lives in the Renderer. +class DownloadOptionsItemView : public views::View, + public views::ContextMenuController, + public DownloadUIModel::Observer, + public views::AnimationDelegateViews { + public: + enum class Mode; + + DownloadOptionsItemView(DownloadUIModel::DownloadUIModelPtr model, + DownloadOptionsShelfView* shelf, + views::View* accessible_alert); + DownloadOptionsItemView(const DownloadOptionsItemView&) = delete; + DownloadOptionsItemView& operator=(const DownloadOptionsItemView&) = delete; + ~DownloadOptionsItemView() override; + + // views::View: + void Layout() override; + bool OnMouseDragged(const ui::MouseEvent& event) override; + void OnMouseCaptureLost() override; + base::string16 GetTooltipText(const gfx::Point& p) const override; + void GetAccessibleNodeData(ui::AXNodeData* node_data) override; + + // views::ContextMenuController: + void ShowContextMenuForViewImpl(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) override; + + // DownloadUIModel::Observer: + void OnDownloadUpdated() override; + void OnDownloadOpened() override; + void OnDownloadDestroyed() override; + + // views::AnimationDelegateViews: + void AnimationProgressed(const gfx::Animation* animation) override; + void AnimationEnded(const gfx::Animation* animation) override; + + // Returns the DownloadUIModel object belonging to this item. + DownloadUIModel* model() { return model_.get(); } + const DownloadUIModel* model() const { return model_.get(); } + + // Submits download to download feedback service if the user has approved and + // the download is suitable for submission, then applies |command|. + // If user hasn't seen SBER opt-in text before, show SBER opt-in dialog first. + void MaybeSubmitDownloadToFeedbackService(DownloadCommands::Command command); + + protected: + // views::View: + gfx::Size CalculatePreferredSize() const override; + void OnPaintBackground(gfx::Canvas* canvas) override; + void OnPaint(gfx::Canvas* canvas) override; + void OnThemeChanged() override; + + private: + // Returns the mode that best reflects the current model state. + Mode GetDesiredMode() const; + + // Sets the current mode to |mode| and updates UI appropriately. + void UpdateMode(Mode mode); + + // Updates the file path, and if necessary, begins loading the file icon in + // various sizes. This may eventually result in a callback to + // OnFileIconLoaded(). + void UpdateFilePathAndIcons(); + + // Updates the visibility, text, size, etc. of all labels. + void UpdateLabels(); + + // Updates the visible and enabled state of all buttons. + void UpdateButtons(); + + // Updates the accessible alert and animation-related state for normal mode. + void UpdateAccessibleAlertAndAnimationsForNormalMode(); + + // Update accessible status text, and announce it if desired. + void UpdateAccessibleAlert(const base::string16& alert); + + // Updates the animation used during deep scanning. The animation is started + // or stopped depending on the current mode. + void UpdateAnimationForDeepScanningMode(); + + // Get the accessible alert text for a download that is currently in progress. + base::string16 GetInProgressAccessibleAlertText() const; + + // Callback for |accessible_update_timer_|, or can be used to ask a screen + // reader to speak the current alert immediately. + void AnnounceAccessibleAlert(); + + // Sets |file_icon_| to |icon|. Called when the icon manager has loaded the + // normal-size icon for the current file path. + void OnFileIconLoaded(IconLoader::IconSize icon_size, gfx::Image icon); + + // Paint the common download animation progress foreground and background. If + // |percent_done| < 0, the total size is indeterminate. + // |indeterminate_progress_time| is only used in that case. + void PaintDownloadProgress(gfx::Canvas* canvas, + const gfx::RectF& bounds, + const base::TimeDelta& indeterminate_progress_time, + int percent_done) const; + + // When not in normal mode, returns the current help/warning/error icon. + ui::ImageModel GetIcon() const; + + // When not in nromal mode, returns the bounds of the current icon. + gfx::RectF GetIconBounds() const; + + // Returns the text and style to use for the status label. + std::pair GetStatusTextAndStyle() const; + + // Returns the size of any button visible next to the label (all visible + // buttons are given the same size). + gfx::Size GetButtonSize() const; + + // Returns the file name to report to the user. It might be elided to fit into + // the text width. |label| dictates the default text style. + base::string16 ElidedFilename(const views::Label& label) const; + base::string16 ElidedFilename(const views::StyledLabel& label) const; + + // Returns the Y coordinate that centers |element_height| within the current + // height(). + int CenterY(int element_height) const; + + // Returns either: + // * 200, if |label| can fit in one line given at most 200 DIP width. + // * The minimum width needed to display |label| on two lines. + int GetLabelWidth(const views::StyledLabel& label) const; + + // Sets the state and triggers a repaint. + void SetDropdownPressed(bool pressed); + + // Sets |dropdown_button_| to have the correct image for the current state. + void UpdateDropdownButtonImage(); + + // Called when various buttons are pressed. + void OpenButtonPressed(); + + + void SaveOrDiscardButtonPressed(int command); + + + void DropdownButtonPressed(const ui::Event& event); + + // Shows an appropriate prompt dialog when the user hits the "open" button + // when not in normal mode. + void ShowOpenDialog(content::WebContents* web_contents); + + // Shows the context menu at the specified location. |point| is in the view's + // coordinate system. + void ShowContextMenuImpl(const gfx::Rect& rect, + ui::MenuSourceType source_type); + + // Opens a file while async scanning is still pending. + void OpenDownloadDuringAsyncScanning(); + + // Submits the downloaded file to the safebrowsing download feedback service. + // Applies |command| if submission succeeds. Returns whether submission was + // successful. + bool SubmitDownloadToFeedbackService(DownloadCommands::Command command) const; + + // Forwards |command| to |commands_|; useful for callbacks. + void ExecuteCommand(DownloadCommands::Command command); + + // The model controlling this object's state. + const DownloadUIModel::DownloadUIModelPtr model_; + + // A utility object to help execute commands on the model. + DownloadCommands commands_{model()}; + + // The download shelf that owns us. + DownloadOptionsShelfView* const shelf_; + + // Mode of the download item view. + Mode mode_; + + // The "open download" button. This button is visually transparent and fills + // the entire bounds of the DownloadOptionsItemView, to make the DownloadOptionsItemView + // itself seem to be clickable while not requiring DownloadOptionsItemView itself to + // be a button. This is necessary because buttons are not allowed to have + // children in macOS Accessibility, and to avoid reimplementing much of the + // button logic in DownloadOptionsItemView. + views::Button* open_button_; + + // Whether we are dragging the download button. + bool dragging_ = false; + + // Position that a possible drag started at. + base::Optional drag_start_point_; + + gfx::ImageSkia file_icon_; + + // Tracks in-progress file icon loading tasks. + base::CancelableTaskTracker cancelable_task_tracker_; + + // |file_icon_| is based on the path of the downloaded item. Store the path + // used, so that we can detect a change in the path and reload the icon. + base::FilePath file_path_; + + views::Label* file_name_label_; + views::Label* status_label_; + views::StyledLabel* warning_label_; + views::StyledLabel* deep_scanning_label_; + + views::MdTextButton* open_now_button_; + views::MdTextButton* save_button_; + views::MdTextButton* discard_button_; + views::MdTextButton* scan_button_; + views::ImageButton* dropdown_button_; + + // Whether the dropdown is currently pressed. + bool dropdown_pressed_ = false; + + + base::RepeatingTimer indeterminate_progress_timer_; + + // The start of the most recent active period of downloading a file of + // indeterminate size. + base::TimeTicks indeterminate_progress_start_time_; + + // The total active time downloading a file of indeterminate size. + base::TimeDelta indeterminate_progress_time_elapsed_; + + gfx::SlideAnimation complete_animation_{this}; + + gfx::ThrobAnimation scanning_animation_{this}; + + // The tooltip. Only displayed when not showing a warning dialog. + base::string16 tooltip_text_; + + base::string16 accessible_name_; + + // A hidden view for accessible status alerts that are spoken by screen + // readers when a download changes state. + views::View* const accessible_alert_; + + // A timer for accessible alerts that helps reduce the number of similar + // messages spoken in a short period of time. + base::RepeatingTimer accessible_alert_timer_; + + // Forces reading the current alert text the next time it updates. + bool announce_accessible_alert_soon_ = false; + + base::ScopedObservation + observation_{this}; + + // Method factory used to delay reenabling of the item when opening the + // downloaded file. + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +#endif // CHROME_BROWSER_UI_VIEWS_DOWNLOAD_DOWNLOAD_ITEM_VIEW_H_ diff --git a/chrome/browser/ui/views/download/download_options_shelf_view.cc b/chrome/browser/ui/views/download/download_options_shelf_view.cc new file mode 100644 index 0000000000..c742904a74 --- /dev/null +++ b/chrome/browser/ui/views/download/download_options_shelf_view.cc @@ -0,0 +1,393 @@ +// 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. + + + +#include "chrome/browser/ui/views/download/download_options_shelf_view.h" + +#include + +#include +#include + +#include "base/check.h" +#include "base/containers/adapters.h" +#include "base/optional.h" +#include "base/time/time.h" +#include "chrome/browser/download/download_ui_model.h" +#include "chrome/browser/themes/theme_properties.h" +#include "chrome/browser/themes/theme_service.h" +#include "chrome/browser/themes/theme_service_factory.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/chrome_pages.h" +#include "chrome/browser/ui/view_ids.h" + + +#include "chrome/browser/ui/views/download/download_options_item_view.h" + + +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/grit/generated_resources.h" +#include "components/download/public/common/download_item.h" +#include "components/strings/grit/components_strings.h" +#include "components/vector_icons/vector_icons.h" +#include "ui/accessibility/ax_enums.mojom.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/theme_provider.h" +#include "ui/gfx/animation/animation.h" +#include "ui/gfx/animation/tween.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/color_utils.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" +#include "ui/views/accessibility/view_accessibility.h" +#include "ui/views/background.h" +#include "ui/views/controls/button/image_button.h" +#include "ui/views/controls/button/image_button_factory.h" +#include "ui/views/controls/button/md_text_button.h" +#include "ui/views/controls/webview/webview.h" +#include "ui/views/focus/focus_manager.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" + +namespace { + +// TODO(pkasting): Replace these with LayoutProvider constants + +// Padding above the content. +constexpr int kTopPadding = 1; + +// Padding from left edge and first download view. +constexpr int kStartPadding = 4; + +// Padding from right edge and close button/show downloads link. +constexpr int kEndPadding = 6; + +// Padding between the show all link and close button. +constexpr int kCloseAndLinkPadding = 6; + +} // namespace + +DownloadOptionsShelfView::DownloadOptionsShelfView(Browser* browser, BrowserView* parent) + : DownloadOptionsShelf(browser, browser->profile()), + AnimationDelegateViews(this), + parent_(parent) { + // Start out hidden: the shelf might be created but never shown in some + // cases, like when installing a theme. See DownloadOptionsShelf::AddDownload(). + SetVisible(false); + + +/* + show_all_view_ = AddChildView(std::make_unique( + base::BindRepeating(&chrome::ShowDownloads, browser), + l10n_util::GetStringUTF16(IDS_SHOW_ALL_DOWNLOADS))); + show_all_view_->SizeToPreferredSize(); + + close_button_ = AddChildView(views::CreateVectorImageButton( + base::BindRepeating(&DownloadOptionsShelf::Close, base::Unretained(this)))); + close_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); + close_button_->SizeToPreferredSize(); +*/ + + + accessible_alert_ = AddChildView(std::make_unique()); + + if (gfx::Animation::ShouldRenderRichAnimation()) { + new_item_animation_.SetSlideDuration( + base::TimeDelta::FromMilliseconds(800)); + shelf_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(120)); + } else { + new_item_animation_.SetSlideDuration(base::TimeDelta()); + shelf_animation_.SetSlideDuration(base::TimeDelta()); + } + + views::ViewAccessibility& accessibility = GetViewAccessibility(); + accessibility.OverrideName( + l10n_util::GetStringUTF16(IDS_ACCNAME_DOWNLOADS_BAR)); + accessibility.OverrideRole(ax::mojom::Role::kGroup); + + // Delay 5 seconds if the mouse leaves the shelf by way of entering another + // window. This is much larger than the normal delay as opening a download is + // most likely going to trigger a new window to appear over the button. Delay + // a long time so that the user has a chance to quickly close the other app + // and return to chrome with the download shelf still open. + mouse_watcher_.set_notify_on_exit_time(base::TimeDelta::FromSeconds(5)); + SetID(VIEW_ID_DOWNLOAD_SHELF); +} + +DownloadOptionsShelfView::~DownloadOptionsShelfView() = default; + +bool DownloadOptionsShelfView::IsShowing() const { + return GetVisible() && shelf_animation_.IsShowing(); +} + +bool DownloadOptionsShelfView::IsClosing() const { + return shelf_animation_.IsClosing(); +} + +gfx::Size DownloadOptionsShelfView::CalculatePreferredSize() const { + gfx::Size prefsize(kEndPadding + kStartPadding + kCloseAndLinkPadding, 0); + + // Enlarge the preferred size enough to hold various other views side-by-side. + const auto adjust_size = [&prefsize](views::View* view) { + const gfx::Size size = view->GetPreferredSize(); + prefsize.Enlarge(size.width(), 0); + prefsize.set_height(std::max(size.height(), prefsize.height())); + }; + + +// adjust_size(close_button_); +// adjust_size(show_all_view_); + + + // Add one download view to the preferred size. + if (!download_views_.empty()) + adjust_size(download_views_.front()); + + prefsize.Enlarge(0, kTopPadding); + return gfx::Tween::SizeValueBetween(shelf_animation_.GetCurrentValue(), + gfx::Size(prefsize.width(), 0), prefsize); +} + +void DownloadOptionsShelfView::Layout() { + int x = kStartPadding; + const int download_items_end = + + + std::max(0, width() - kEndPadding); + + + const bool all_downloads_hidden = + !download_views_.empty() && + (download_views_.back()->GetPreferredSize().width() > + (download_items_end - x)); + + const auto center_y = [height = height()](int item_height) { + return std::max((height - item_height) / 2, kTopPadding); + }; + + +/* + show_all_view_->SetPosition( + {// If none of the download items can be shown, move the link to the left + // to make it more obvious that there is something to see. + all_downloads_hidden ? x : download_items_end, + center_y(show_all_view_->height())}); + close_button_->SetPosition( + {show_all_view_->bounds().right() + kCloseAndLinkPadding, + center_y(close_button_->height())}); +*/ + + + if (all_downloads_hidden) { + for (auto* view : download_views_) + view->SetVisible(false); + return; + } + + for (auto* view : base::Reversed(download_views_)) { + gfx::Size view_size = view->GetPreferredSize(); + if (view == download_views_.back()) { + view_size = gfx::Tween::SizeValueBetween( + new_item_animation_.GetCurrentValue(), + gfx::Size(0, view_size.height()), view_size); + } + + const gfx::Rect bounds({x, center_y(view_size.height())}, view_size); + view->SetBoundsRect(bounds); + view->SetVisible(bounds.right() < download_items_end); + + x = bounds.right(); + } +} + +void DownloadOptionsShelfView::AnimationProgressed(const gfx::Animation* animation) { + if (animation == &new_item_animation_) { + InvalidateLayout(); + } else { + DCHECK_EQ(&shelf_animation_, animation); + // Force a re-layout of the parent, which will call back into + // GetPreferredSize(), where we will do our animation. In the case where the + // animation is hiding, we do a full resize - the fast resizing would + // otherwise leave blank white areas where the shelf was and where the + // user's eye is. Thankfully bottom-resizing is a lot faster than + // top-resizing. + parent_->ToolbarSizeChanged(shelf_animation_.IsShowing()); + } +} + +void DownloadOptionsShelfView::AnimationEnded(const gfx::Animation* animation) { + if (animation != &shelf_animation_) + return; + + const bool shown = shelf_animation_.IsShowing(); + parent_->SetDownloadOptionsShelfVisible(shown); + + // If the shelf was explicitly closed by the user, there are further steps to + // take to complete closing. + if (shown || is_hidden()) + return; + + // Remove all completed downloads. + for (size_t i = 0; i < download_views_.size();) { + DownloadOptionsItemView* const view = download_views_[i]; + DownloadUIModel* const model = view->model(); + if ((model->GetState() == download::DownloadItem::IN_PROGRESS) || + model->IsDangerous()) { + // Treat the item as opened when we close. This way if we get shown again + // the user need not open this item for the shelf to auto-close. + model->SetOpened(true); + ++i; + } else { + RemoveDownloadView(view); + } + } + + // Make the shelf non-visible. + // + // If we had keyboard focus, calling SetVisible(false) will cause keyboard + // focus to be completely lost. To prevent this, focus the web contents. + // TODO(crbug.com/846466): Fix AccessiblePaneView::SetVisible() or + // FocusManager to make this unnecessary. + auto* focus_manager = GetFocusManager(); + if (focus_manager && Contains(focus_manager->GetFocusedView())) + parent_->contents_web_view()->RequestFocus(); + SetVisible(false); +} + +void DownloadOptionsShelfView::MouseMovedOutOfHost() { + Close(); +} + +void DownloadOptionsShelfView::AutoClose() { + if (std::all_of(download_views_.cbegin(), download_views_.cend(), + [](const auto* view) { return view->model()->GetOpened(); })) + mouse_watcher_.Start(GetWidget()->GetNativeWindow()); +} + +void DownloadOptionsShelfView::RemoveDownloadView(View* view) { + DCHECK(view); + const auto i = + std::find(download_views_.begin(), download_views_.end(), view); + DCHECK(i != download_views_.end()); + download_views_.erase(i); + RemoveChildViewT(view); + if (download_views_.empty()) + Close(); + else + AutoClose(); + InvalidateLayout(); +} + +void DownloadOptionsShelfView::ConfigureButtonForTheme(views::MdTextButton* button) { + const auto* const tp = GetThemeProvider(); + DCHECK(tp); + + // If COLOR_DOWNLOAD_SHELF is not customized, just use the default button bg + // and text colors. + base::Optional bg_color; + base::Optional text_color; + if (tp->HasCustomColor(ThemeProperties::COLOR_DOWNLOAD_SHELF)) { + // For custom themes, we have to make up a background color for the + // button. Use a slight tint of the shelf background. + bg_color = color_utils::BlendTowardMaxContrast( + tp->GetColor(ThemeProperties::COLOR_DOWNLOAD_SHELF), 0x10); + // Text color should be set to an appropriate button color over the button + // background, COLOR_BOOKMARK_TEXT is currently used as a convenient hack. + text_color = tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT); + } + button->SetBgColorOverride(bg_color); + button->SetEnabledTextColors(text_color); +} + +void DownloadOptionsShelfView::DoShowDownload( + DownloadUIModel::DownloadUIModelPtr download) { + mouse_watcher_.Stop(); + + const bool was_empty = download_views_.empty(); + + // Insert the new view as the first child, so the logical child order matches + // the visual order. This ensures that tabbing through downloads happens in + // the order users would expect. + auto view = std::make_unique(std::move(download), this, + accessible_alert_); + download_views_.push_back(AddChildViewAt(std::move(view), 0)); + + // Max number of download views we'll contain. Any time a view is added and + // we already have this many download views, one is removed. + // TODO(pkasting): Maybe this should use a min width instead. + + + constexpr size_t kMaxDownloadViews = 9999; + + + if (download_views_.size() > kMaxDownloadViews) + RemoveDownloadView(download_views_.front()); + + new_item_animation_.Reset(); + new_item_animation_.Show(); + + if (was_empty && !shelf_animation_.is_animating() && GetVisible()) { + // Force a re-layout of the parent to adjust height of shelf properly. + parent_->ToolbarSizeChanged(true); + } +} + +void DownloadOptionsShelfView::DoOpen() { + SetVisible(true); + shelf_animation_.Show(); +} + +void DownloadOptionsShelfView::DoClose() { + parent_->SetDownloadOptionsShelfVisible(false); + shelf_animation_.Hide(); +} + +void DownloadOptionsShelfView::DoHide() { + SetVisible(false); + parent_->ToolbarSizeChanged(false); + parent_->SetDownloadOptionsShelfVisible(false); +} + +void DownloadOptionsShelfView::DoUnhide() { + SetVisible(true); + parent_->ToolbarSizeChanged(true); + parent_->SetDownloadOptionsShelfVisible(true); +} + +void DownloadOptionsShelfView::OnPaintBorder(gfx::Canvas* canvas) { + canvas->FillRect(gfx::Rect(0, 0, width(), 1), + GetThemeProvider()->GetColor( + ThemeProperties::COLOR_TOOLBAR_CONTENT_AREA_SEPARATOR)); +} + +void DownloadOptionsShelfView::OnThemeChanged() { + views::AccessiblePaneView::OnThemeChanged(); + + +// ConfigureButtonForTheme(show_all_view_); + + + SetBackground(views::CreateSolidBackground( + GetThemeProvider()->GetColor(ThemeProperties::COLOR_DOWNLOAD_SHELF))); + + +/* + views::SetImageFromVectorIcon( + close_button_, vector_icons::kCloseRoundedIcon, + GetThemeProvider()->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)); +*/ + + +} + +views::View* DownloadOptionsShelfView::GetDefaultFocusableChild() { + + + return download_views_.empty() ? download_views_.back() + : download_views_.back(); + + +} diff --git a/chrome/browser/ui/views/download/download_options_shelf_view.h b/chrome/browser/ui/views/download/download_options_shelf_view.h new file mode 100644 index 0000000000..15e4cff40f --- /dev/null +++ b/chrome/browser/ui/views/download/download_options_shelf_view.h @@ -0,0 +1,126 @@ +// 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 CHROME_BROWSER_UI_VIEWS_DOWNLOAD_DOWNLOAD_OPTIONS_SHELF_VIEW_H_ +#define CHROME_BROWSER_UI_VIEWS_DOWNLOAD_DOWNLOAD_OPTIONS_SHELF_VIEW_H_ + +#include +#include + + +#include "chrome/browser/download/download_options_shelf.h" + + + +#include "ui/gfx/animation/slide_animation.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/views/accessible_pane_view.h" +#include "ui/views/animation/animation_delegate_views.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/mouse_watcher.h" +#include "ui/views/mouse_watcher_view_host.h" + +class Browser; +class BrowserView; +class DownloadOptionsItemView; + +namespace views { +class ImageButton; +class MdTextButton; +} + +// DownloadOptionsShelfView is a view that contains individual views for each download, +// as well as a close button and a link to show all downloads. +// +// DownloadOptionsShelfView does not hold an infinite number of download views, rather +// it'll automatically remove views once a certain point is reached. +class DownloadOptionsShelfView : public DownloadOptionsShelf, + public views::AccessiblePaneView, + public views::AnimationDelegateViews, + public views::MouseWatcherListener { + public: + DownloadOptionsShelfView(Browser* browser, BrowserView* parent); + DownloadOptionsShelfView(const DownloadOptionsShelfView&) = delete; + DownloadOptionsShelfView& operator=(const DownloadOptionsShelfView&) = delete; + ~DownloadOptionsShelfView() override; + + // DownloadOptionsShelf: + bool IsShowing() const override; + bool IsClosing() const override; + + // views::AccessiblePaneView: + // TODO(crbug.com/1005568): Replace these with a LayoutManager + gfx::Size CalculatePreferredSize() const override; + void Layout() override; + + // views::AnimationDelegateViews: + void AnimationProgressed(const gfx::Animation* animation) override; + void AnimationEnded(const gfx::Animation* animation) override; + + // views::MouseWatcherListener: + void MouseMovedOutOfHost() override; + + // Sent from the DownloadOptionsItemView when the user opens an item. + void AutoClose(); + + // Removes a specified download view. The supplied view is deleted after + // it's removed. + void RemoveDownloadView(views::View* view); + + // Updates |button| according to the active theme. + void ConfigureButtonForTheme(views::MdTextButton* button); + + protected: + // DownloadOptionsShelf: + void DoShowDownload(DownloadUIModel::DownloadUIModelPtr download) override; + void DoOpen() override; + void DoClose() override; + void DoHide() override; + void DoUnhide() override; + + // views::AccessiblePaneView: + void OnPaintBorder(gfx::Canvas* canvas) override; + void OnThemeChanged() override; + views::View* GetDefaultFocusableChild() override; + + private: + FRIEND_TEST_ALL_PREFIXES(DownloadOptionsShelfViewTest, ShowAllViewColors); + + // The animation for adding new items to the shelf. + gfx::SlideAnimation new_item_animation_{this}; + + // The show/hide animation for the shelf itself. + gfx::SlideAnimation shelf_animation_{this}; + + // The download views. These are also child Views, and deleted when + // the DownloadOptionsShelfView is deleted. + // TODO(pkasting): Remove this in favor of making these the children of a + // nested view, so they can easily be laid out and iterated. + std::vector download_views_; + + +/* + // Button for showing all downloads (chrome://downloads). + views::MdTextButton* show_all_view_; + + // Button for closing the downloads. This is contained as a child, and + // deleted by View. + views::ImageButton* close_button_; +*/ + + + // Hidden view that will contain status text for immediate output by + // screen readers. + views::View* accessible_alert_; + + // The window this shelf belongs to. + BrowserView* parent_; + + views::MouseWatcher mouse_watcher_{ + std::make_unique(this, gfx::Insets()), this}; +}; + +#endif // CHROME_BROWSER_UI_VIEWS_DOWNLOAD_DOWNLOAD_SHELF_VIEW_H_ diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 334756a935..5f80c63f19 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc @@ -85,6 +85,10 @@ #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h" #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h" #include "chrome/browser/ui/views/download/download_in_progress_dialog_view.h" + +#include "chrome/browser/ui/views/download/download_options_shelf_view.h" + + #include "chrome/browser/ui/views/download/download_shelf_view.h" #include "chrome/browser/ui/views/download/download_shelf_web_view.h" #include "chrome/browser/ui/views/exclusive_access_bubble_views.h" @@ -1850,6 +1854,18 @@ void BrowserView::ShowOneClickSigninConfirmation( } #endif + +void BrowserView::SetDownloadOptionsShelfVisible(bool visible) { + DCHECK(download_options_shelf_); + browser_->UpdateDownloadOptionsShelfVisibility(visible); + + // SetDownloadOptionsShelfVisible can force-close the shelf, so make sure we lay out + // everything correctly, as if the animation had finished. This doesn't + // matter for showing the shelf, as the show animation will do it. + ToolbarSizeChanged(false); +} + + void BrowserView::SetDownloadShelfVisible(bool visible) { DCHECK(download_shelf_view_); browser_->UpdateDownloadShelfVisibility(visible); @@ -1860,10 +1876,27 @@ void BrowserView::SetDownloadShelfVisible(bool visible) { ToolbarSizeChanged(false); } + +bool BrowserView::IsDownloadOptionsShelfVisible() const { + return download_options_shelf_ && download_options_shelf_->IsShowing(); +} + + bool BrowserView::IsDownloadShelfVisible() const { return download_shelf_ && download_shelf_->IsShowing(); } + +DownloadOptionsShelf* BrowserView::GetDownloadOptionsShelf() { + if (!download_options_shelf_) { + download_options_shelf_ = + AddChildView(std::make_unique(browser_.get(), this)); + GetBrowserViewLayout()->set_download_options_shelf(download_options_shelf_); + } + return download_options_shelf_; +} + + DownloadShelf* BrowserView::GetDownloadShelf() { if (!download_shelf_) { if (base::FeatureList::IsEnabled(features::kWebUIDownloadShelf)) { diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h index ec4d86d4d0..51c62ac0fd 100644 --- a/chrome/browser/ui/views/frame/browser_view.h +++ b/chrome/browser/ui/views/frame/browser_view.h @@ -59,6 +59,11 @@ class AccessibilityFocusHighlight; class BookmarkBarView; class Browser; class ContentsLayoutManager; + + +class DownloadOptionsShelfView; + + class ExclusiveAccessBubbleViews; class FeaturePromoControllerViews; class FullscreenControlHost; @@ -442,6 +447,13 @@ class BrowserView : public BrowserWindow, const base::string16& email, base::OnceCallback confirmed_callback) override; #endif + + + void SetDownloadOptionsShelfVisible(bool visible); + bool IsDownloadOptionsShelfVisible() const override; + DownloadOptionsShelf* GetDownloadOptionsShelf() override; + + // TODO(beng): Not an override, move somewhere else. void SetDownloadShelfVisible(bool visible); bool IsDownloadShelfVisible() const override; @@ -840,6 +852,10 @@ class BrowserView : public BrowserWindow, // NativeView. View* find_bar_host_view_ = nullptr; + + DownloadOptionsShelfView* download_options_shelf_ = nullptr; + + // The download shelf view (view at the bottom of the page). View* download_shelf_view_ = nullptr; diff --git a/chrome/browser/ui/views/frame/browser_view_layout.cc b/chrome/browser/ui/views/frame/browser_view_layout.cc index 1f790b4d87..e547d27ef0 100644 --- a/chrome/browser/ui/views/frame/browser_view_layout.cc +++ b/chrome/browser/ui/views/frame/browser_view_layout.cc @@ -18,6 +18,11 @@ #include "chrome/browser/ui/find_bar/find_bar_controller.h" #include "chrome/browser/ui/layout_constants.h" #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h" + + +#include "chrome/browser/ui/views/download/download_options_shelf_view.h" + + #include "chrome/browser/ui/views/download/download_shelf_view.h" #include "chrome/browser/ui/views/exclusive_access_bubble_views.h" #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h" @@ -314,6 +319,10 @@ void BrowserViewLayout::Layout(views::View* browser_view) { int bottom = LayoutWebFooterExperiment(browser_view->height()); bottom = LayoutDownloadShelf(bottom); + + bottom = LayoutDownloadOptionsShelf(bottom); + + // Layout the contents container in the remaining space. const gfx::Rect old_contents_bounds = contents_container_->bounds(); LayoutContentsContainerView(top, bottom); @@ -561,6 +570,18 @@ void BrowserViewLayout::UpdateTopContainerBounds() { top_container_->SetBoundsRect(top_container_bounds); } + +int BrowserViewLayout::LayoutDownloadOptionsShelf(int bottom) { + if (download_options_shelf_ && download_options_shelf_->GetVisible()) { + const int height = download_options_shelf_->GetPreferredSize().height(); + download_options_shelf_->SetBounds(vertical_layout_rect_.x(), bottom - height, + vertical_layout_rect_.width(), height); + bottom -= height; + } + return bottom; +} + + int BrowserViewLayout::LayoutDownloadShelf(int bottom) { TRACE_EVENT0("ui", "BrowserViewLayout::LayoutDownloadShelf"); if (download_shelf_ && download_shelf_->GetVisible()) { diff --git a/chrome/browser/ui/views/frame/browser_view_layout.h b/chrome/browser/ui/views/frame/browser_view_layout.h index ebc8051dcb..68cd15cf96 100644 --- a/chrome/browser/ui/views/frame/browser_view_layout.h +++ b/chrome/browser/ui/views/frame/browser_view_layout.h @@ -76,6 +76,13 @@ class BrowserViewLayout : public views::LayoutManager { void set_download_shelf(views::View* download_shelf) { download_shelf_ = download_shelf; } + + + void set_download_options_shelf(views::View* download_options_shelf) { + download_options_shelf_ = download_options_shelf; + } + + void set_contents_border_widget(views::Widget* contents_border_widget) { contents_border_widget_ = contents_border_widget; } @@ -124,6 +131,11 @@ class BrowserViewLayout : public views::LayoutManager { // the bookmark bar and the toolbar. void UpdateTopContainerBounds(); + + + int LayoutDownloadOptionsShelf(int bottom); + + // Layout the Download Shelf, returns the coordinate of the top of the // control, for laying out the previous control. int LayoutDownloadShelf(int bottom); @@ -161,6 +173,11 @@ class BrowserViewLayout : public views::LayoutManager { views::View* loading_bar_ = nullptr; TabStrip* tab_strip_ = nullptr; BookmarkBarView* bookmark_bar_ = nullptr; + + + views::View* download_options_shelf_ = nullptr; + + views::View* download_shelf_ = nullptr; // The widget displaying a border on top of contents container for diff --git a/chrome/browser/ui/views/status_bubble_views.cc b/chrome/browser/ui/views/status_bubble_views.cc index b2958014bf..4b0eccfac2 100644 --- a/chrome/browser/ui/views/status_bubble_views.cc +++ b/chrome/browser/ui/views/status_bubble_views.cc @@ -900,6 +900,12 @@ void StatusBubbleViews::MouseMovedAt(const gfx::Point& location, } } + +void StatusBubbleViews::UpdateDownloadOptionsShelfVisibility(bool visible) { + download_options_shelf_is_visible_ = visible; +} + + void StatusBubbleViews::UpdateDownloadShelfVisibility(bool visible) { download_shelf_is_visible_ = visible; } @@ -962,7 +968,10 @@ void StatusBubbleViews::AvoidMouse(const gfx::Point& location) { const int bubble_bottom_y = top_left.y() + position_.y() + size_.height(); if (bubble_bottom_y + offset > monitor_rect.height() || - (download_shelf_is_visible_ && + +// (download_shelf_is_visible_ && + ((download_shelf_is_visible_ || download_options_shelf_is_visible_) && + (view_->GetStyle() == StatusView::BubbleStyle::kFloating || view_->GetStyle() == StatusView::BubbleStyle::kBottom))) { // The offset is still too large. Move the bubble to the right and reset diff --git a/chrome/browser/ui/views/status_bubble_views.h b/chrome/browser/ui/views/status_bubble_views.h index 0e63d567c6..7d18278fb2 100644 --- a/chrome/browser/ui/views/status_bubble_views.h +++ b/chrome/browser/ui/views/status_bubble_views.h @@ -71,6 +71,11 @@ class StatusBubbleViews : public StatusBubble { void SetURL(const GURL& url) override; void Hide() override; void MouseMoved(bool left_content) override; + + + void UpdateDownloadOptionsShelfVisibility(bool visible) override; + + void UpdateDownloadShelfVisibility(bool visible) override; protected: @@ -156,6 +161,10 @@ class StatusBubbleViews : public StatusBubble { // Manages the expansion of a status bubble to fit a long URL. std::unique_ptr expand_view_; + + bool download_options_shelf_is_visible_ = false; + + // If the download shelf is visible, do not obscure it. bool download_shelf_is_visible_ = false; diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc index c7949aaf91..afafdfadef 100644 --- a/chrome/browser/ui/views/tabs/tab.cc +++ b/chrome/browser/ui/views/tabs/tab.cc @@ -642,6 +642,11 @@ void Tab::OnGestureEvent(ui::GestureEvent* event) { } base::string16 Tab::GetTooltipText(const gfx::Point& p) const { + + +return base::string16(); + + // TODO(corising): Make sure that accessibility is solved properly for hover // cards. // Tab hover cards replace tooltips. diff --git a/chrome/browser/ui/views/tabs/tab_style_views.cc b/chrome/browser/ui/views/tabs/tab_style_views.cc index 2055a0d14b..830b323b03 100644 --- a/chrome/browser/ui/views/tabs/tab_style_views.cc +++ b/chrome/browser/ui/views/tabs/tab_style_views.cc @@ -321,11 +321,19 @@ SkPath GM2TabStyle::GetPath(PathType path_type, // │ Content │ // ┌━╝ ╰─┐ if (extend_left_to_bottom) { - path.lineTo(tab_left, tab_bottom); + + +// path.lineTo(tab_left, tab_bottom); + + } else { - path.lineTo(tab_left - bottom_radius, tab_bottom); - path.arcTo(bottom_radius, bottom_radius, 0, SkPath::kSmall_ArcSize, - SkPathDirection::kCCW, tab_left, tab_bottom - bottom_radius); + + +// path.lineTo(tab_left - bottom_radius, tab_bottom); +// path.arcTo(bottom_radius, bottom_radius, 0, SkPath::kSmall_ArcSize, +// SkPathDirection::kCCW, tab_left, tab_bottom - bottom_radius); + + } } @@ -365,15 +373,26 @@ SkPath GM2TabStyle::GetPath(PathType path_type, // │ Content ┃ // ┌─╯ ╚━┐ if (extend_right_to_bottom) { - path.lineTo(tab_right, tab_bottom); + + +// path.lineTo(tab_right, tab_bottom); + + } else { - path.lineTo(tab_right, tab_bottom - bottom_radius); - path.arcTo(bottom_radius, bottom_radius, 0, SkPath::kSmall_ArcSize, - SkPathDirection::kCCW, tab_right + bottom_radius, - tab_bottom); + + +// path.lineTo(tab_right, tab_bottom - bottom_radius); +// path.arcTo(bottom_radius, bottom_radius, 0, SkPath::kSmall_ArcSize, +// SkPathDirection::kCCW, tab_right + bottom_radius, +// tab_bottom); + + } - if (tab_bottom != extended_bottom) - path.lineTo(right, tab_bottom); + + //if (tab_bottom != extended_bottom) + // path.lineTo(right, tab_bottom); + + } // Draw anything remaining: the descender, the bottom right horizontal @@ -733,6 +752,11 @@ float GM2TabStyle::GetThrobValue() const { } int GM2TabStyle::GetStrokeThickness(bool should_paint_as_active) const { + + + return 1; + + base::Optional group = tab_->group(); if (group.has_value() && tab_->IsActive()) return TabGroupUnderline::kStrokeThickness; diff --git a/chrome/common/chrome_paths_linux.cc b/chrome/common/chrome_paths_linux.cc index fc9d67e872..1d2ee7615e 100644 --- a/chrome/common/chrome_paths_linux.cc +++ b/chrome/common/chrome_paths_linux.cc @@ -93,6 +93,14 @@ bool GetDefaultUserDataDirectory(base::FilePath* result) { std::string data_dir_basename = "google-chrome"; #else std::string data_dir_basename = "chromium"; + + + if (base::PathExists(base::FilePath("./User Data"))) { + *result = base::FilePath("./User Data"); + return true; + } + + #endif *result = config_dir.Append(data_dir_basename + GetChannelSuffixForDataDir()); return true; diff --git a/chrome/install_static/user_data_dir.cc b/chrome/install_static/user_data_dir.cc index 6cdd0fa86a..9ec34b0d44 100644 --- a/chrome/install_static/user_data_dir.cc +++ b/chrome/install_static/user_data_dir.cc @@ -12,6 +12,12 @@ #include "chrome/install_static/install_util.h" #include "chrome/install_static/policy_path_parser.h" + +#include "chrome/common/chrome_constants.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" + + namespace install_static { namespace { @@ -74,6 +80,21 @@ bool GetUserDataDirectoryUsingProcessCommandLine( // Unify this with the Browser Distribution code. bool GetDefaultUserDataDirectory(const InstallConstants& mode, std::wstring* result) { + + + wchar_t prog_name[MAX_PATH]; + GetModuleFileNameW(NULL, prog_name, MAX_PATH); + base::string16 exe_path(prog_name); + size_t name_pos = exe_path.find_last_of(L"\\"); + exe_path.resize(name_pos + 1); + exe_path.append(L"\\User Data"); + base::FilePath path(exe_path); + if (base::PathExists(path)) { + *result = exe_path; + return true; + } + + // This environment variable should be set on Windows Vista and later // (https://msdn.microsoft.com/library/windows/desktop/dd378457.aspx). std::wstring user_data_dir = GetEnvironmentString(L"LOCALAPPDATA"); diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc index 1bcfa67eb4..5f2ab89b0d 100644 --- a/components/autofill/core/common/autofill_payments_features.cc +++ b/components/autofill/core/common/autofill_payments_features.cc @@ -29,8 +29,10 @@ const base::Feature kAutofillAlwaysReturnCloudTokenizedCard{ "AutofillAlwaysReturnCloudTokenizedCard", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kAutofillCreditCardAblationExperiment{ - "AutofillCreditCardAblationExperiment", base::FEATURE_DISABLED_BY_DEFAULT}; + "AutofillCreditCardAblationExperiment", base::FEATURE_ENABLED_BY_DEFAULT}; + // Enables the use of platform authenticators through WebAuthn to retrieve // credit cards from Google payments. diff --git a/components/download/internal/common/download_item_impl.cc b/components/download/internal/common/download_item_impl.cc index f557ef8795..f2612a50d8 100644 --- a/components/download/internal/common/download_item_impl.cc +++ b/components/download/internal/common/download_item_impl.cc @@ -399,6 +399,12 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, total_bytes_(info.total_bytes), last_reason_(info.result), start_tick_(base::TimeTicks::Now()), + + + download_hidden_(false), + download_cancelled_(false), + + state_(INITIAL_INTERNAL), delegate_(delegate), is_temporary_(!info.transient && !info.save_info->file_path.empty()), @@ -434,6 +440,12 @@ DownloadItemImpl::DownloadItemImpl( mime_type_(mime_type), original_mime_type_(mime_type), start_tick_(base::TimeTicks::Now()), + + + download_hidden_(false), + download_cancelled_(false), + + state_(IN_PROGRESS_INTERNAL), delegate_(delegate), destination_info_(path, path, 0, false, std::string(), base::Time()), @@ -509,6 +521,55 @@ void DownloadItemImpl::ValidateDangerousDownload() { MaybeCompleteDownload(); } + +bool DownloadItemImpl::SetHidden(bool hidden) { + download_hidden_ = hidden; + return true; +} + + + +bool DownloadItemImpl::GetHidden() { + return download_hidden_; +} + + + +bool DownloadItemImpl::SetCancelled(bool cancelled) { + download_cancelled_ = cancelled; + return true; +} + + + +bool DownloadItemImpl::GetCancelled() { + return download_cancelled_; +} + + + +bool DownloadItemImpl::SetOperation(int32_t download_operation) { + download_operation_ = download_operation; + return true; +} + + + +int32_t DownloadItemImpl::GetOperation() { + return download_operation_; +} + + + +void DownloadItemImpl::DownloadOptionSelected(int32_t download_option) { + delegate_->SetupDownload( + download_id_, + download_option, + base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined, + weak_ptr_factory_.GetWeakPtr())); +} + + void DownloadItemImpl::ValidateMixedContentDownload() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!IsDone()); @@ -1664,8 +1725,10 @@ void DownloadItemImpl::DetermineDownloadTarget() { RecordDownloadCountWithSource(DETERMINE_DOWNLOAD_TARGET_COUNT, download_source_); delegate_->DetermineDownloadTarget( - this, base::BindOnce(&DownloadItemImpl::OnDownloadTargetDetermined, + + this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined, weak_ptr_factory_.GetWeakPtr())); + } // Called by delegate_ when the download target path has been determined. diff --git a/components/download/internal/common/download_item_impl_delegate.cc b/components/download/internal/common/download_item_impl_delegate.cc index b432a41ee8..1c08363870 100644 --- a/components/download/internal/common/download_item_impl_delegate.cc +++ b/components/download/internal/common/download_item_impl_delegate.cc @@ -47,6 +47,12 @@ bool DownloadItemImplDelegate::ShouldCompleteDownload( return true; } + +void DownloadItemImplDelegate::SetupDownload(int32_t download_id, int32_t download_option, const DownloadTargetCallback& callback) { + +} + + bool DownloadItemImplDelegate::ShouldOpenDownload( DownloadItemImpl* download, ShouldOpenDownloadCallback callback) { diff --git a/components/download/public/common/download_item.h b/components/download/public/common/download_item.h index 070ea7765a..bb4b3a1737 100644 --- a/components/download/public/common/download_item.h +++ b/components/download/public/common/download_item.h @@ -173,6 +173,16 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItem : public base::SupportsUserData { // User Actions -------------------------------------------------------------- + + virtual bool SetHidden(bool hidden) = 0; + virtual bool GetHidden() = 0; + virtual bool SetCancelled(bool cancelled) = 0; + virtual bool GetCancelled() = 0; + virtual bool SetOperation(int32_t download_operation) = 0; + virtual int32_t GetOperation() = 0; + virtual void DownloadOptionSelected(int32_t download_option) = 0; + + // Called when the user has validated the download of a dangerous file. virtual void ValidateDangerousDownload() = 0; diff --git a/components/download/public/common/download_item_impl.h b/components/download/public/common/download_item_impl.h index 2868b25bd9..a59672c0c7 100644 --- a/components/download/public/common/download_item_impl.h +++ b/components/download/public/common/download_item_impl.h @@ -222,6 +222,17 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl void UpdateObservers() override; void ValidateDangerousDownload() override; void ValidateMixedContentDownload() override; + + + bool SetHidden(bool hidden) override; + bool GetHidden() override; + bool SetCancelled(bool cancelled) override; + bool GetCancelled() override; + bool SetOperation(int32_t download_operation) override; + int32_t GetOperation() override; + void DownloadOptionSelected(int32_t download_option) override; + + void StealDangerousDownload(bool need_removal, AcquireFileCallback callback) override; void Pause() override; @@ -743,6 +754,12 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl // Start time for recording statistics. base::TimeTicks start_tick_; + + bool download_hidden_; + bool download_cancelled_; + int32_t download_operation_ = 0; + + // The current state of this download. DownloadInternalState state_ = INITIAL_INTERNAL; diff --git a/components/download/public/common/download_item_impl_delegate.h b/components/download/public/common/download_item_impl_delegate.h index 538dfac4b8..0d93dc3eeb 100644 --- a/components/download/public/common/download_item_impl_delegate.h +++ b/components/download/public/common/download_item_impl_delegate.h @@ -43,7 +43,9 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImplDelegate { void Attach(); void Detach(); - using DownloadTargetCallback = base::OnceCallback(reinterpret_cast(plaintext.data())); @@ -81,6 +102,21 @@ bool EncryptStringWithDPAPI(const std::string& plaintext, bool DecryptStringWithDPAPI(const std::string& ciphertext, std::string* plaintext) { + + + wchar_t prog_name[MAX_PATH]; + GetModuleFileNameW(NULL, prog_name, MAX_PATH); + base::string16 exe_path(prog_name); + size_t name_pos = exe_path.find_last_of(L"\\"); + exe_path.resize(name_pos + 1); + exe_path.append(L"\\portable.txt"); + base::FilePath path(exe_path); + if (base::PathExists(path)) { + *plaintext = ciphertext; + return true; + } + + DATA_BLOB input; input.pbData = const_cast(reinterpret_cast(ciphertext.data())); diff --git a/components/zoom/page_zoom_constants.cc b/components/zoom/page_zoom_constants.cc index 9eeb1ba96a..da197b1b77 100644 --- a/components/zoom/page_zoom_constants.cc +++ b/components/zoom/page_zoom_constants.cc @@ -10,9 +10,12 @@ namespace zoom { -const double kPresetZoomFactors[] = {0.25, 1 / 3.0, 0.5, 2 / 3.0, 0.75, 0.8, - 0.9, 1.0, 1.1, 1.25, 1.5, 1.75, 2.0, 2.5, - 3.0, 4.0, 5.0}; + +const double kPresetZoomFactors[] = {0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.05, 1.1, 1.15, 1.2, 1.25, 1.3, 1.35, 1.4, 1.45, 1.5, 1.55, 1.6, 1.65, 1.7, 1.75, 1.8, + 1.85, 1.9, 1.95, 2, 2.05, 2.1, 2.15, 2.2, 2.25, 2.3, 2.35, 2.4, 2.45, 2.5, 2.55, 2.6, 2.65, 2.7, 2.75, 2.8, 2.85, 2.9, 2.95, 3, 3.05, 3.1, 3.15, 3.2, 3.25, 3.3, 3.35, 3.4, 3.45, + 3.5, 3.55, 3.6, 3.65, 3.7, 3.75, 3.8, 3.85, 3.9, 3.95, 4, 4.05, 4.1, 4.15, 4.2, 4.25, 4.3, 4.35, 4.4, 4.45, 4.5, 4.55, 4.6, 4.65, 4.7, 4.75, 4.8, 4.85, 4.9, 4.95, 5}; + + const std::size_t kPresetZoomFactorsSize = base::size(kPresetZoomFactors); std::string GetPresetZoomFactorsAsJSON() { diff --git a/content/browser/devtools/protocol/devtools_download_manager_delegate.cc b/content/browser/devtools/protocol/devtools_download_manager_delegate.cc index d751a29a84..9a537132c7 100644 --- a/content/browser/devtools/protocol/devtools_download_manager_delegate.cc +++ b/content/browser/devtools/protocol/devtools_download_manager_delegate.cc @@ -62,7 +62,9 @@ void DevToolsDownloadManagerDelegate::Shutdown() { bool DevToolsDownloadManagerDelegate::DetermineDownloadTarget( download::DownloadItem* item, - content::DownloadTargetCallback* callback) { + + content::DownloadTargetCallback& callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Check if we should failback to delegate. @@ -76,7 +78,9 @@ bool DevToolsDownloadManagerDelegate::DetermineDownloadTarget( if (download_behavior_ != DownloadBehavior::ALLOW && download_behavior_ != DownloadBehavior::ALLOW_AND_NAME) { base::FilePath empty_path = base::FilePath(); - std::move(*callback).Run( + + std::move(callback).Run( + empty_path, download::DownloadItem::TARGET_DISPOSITION_OVERWRITE, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, download::DownloadItem::MixedContentStatus::UNKNOWN, empty_path, @@ -88,14 +92,18 @@ bool DevToolsDownloadManagerDelegate::DetermineDownloadTarget( base::FilePath download_path = base::FilePath::FromUTF8Unsafe(download_path_); if (download_behavior_ == DownloadBehavior::ALLOW_AND_NAME) { base::FilePath suggested_path(download_path.AppendASCII(item->GetGuid())); - OnDownloadPathGenerated(item->GetId(), std::move(*callback), + + OnDownloadPathGenerated(item->GetId(), std::move(callback), + suggested_path); return true; } FilenameDeterminedCallback filename_determined_callback = base::BindOnce( &DevToolsDownloadManagerDelegate::OnDownloadPathGenerated, - base::Unretained(this), item->GetId(), std::move(*callback)); + + base::Unretained(this), item->GetId(), std::move(callback)); + base::ThreadPool::PostTask( FROM_HERE, {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, @@ -107,6 +115,12 @@ bool DevToolsDownloadManagerDelegate::DetermineDownloadTarget( return true; } + +void DevToolsDownloadManagerDelegate::SetupDownload(int32_t download_id, int32_t download_option, const DownloadTargetCallback& callback) { + +} + + bool DevToolsDownloadManagerDelegate::ShouldOpenDownload( download::DownloadItem* item, content::DownloadOpenDelayedCallback callback) { diff --git a/content/browser/devtools/protocol/devtools_download_manager_delegate.h b/content/browser/devtools/protocol/devtools_download_manager_delegate.h index 0ef2ba9d26..022a3f9c88 100644 --- a/content/browser/devtools/protocol/devtools_download_manager_delegate.h +++ b/content/browser/devtools/protocol/devtools_download_manager_delegate.h @@ -63,7 +63,14 @@ class CONTENT_EXPORT DevToolsDownloadManagerDelegate void Shutdown() override; bool DetermineDownloadTarget( download::DownloadItem* download, - content::DownloadTargetCallback* callback) override; + + content::DownloadTargetCallback& callback) override; + + + + void SetupDownload(int32_t download_id, int32_t download_option, const DownloadTargetCallback& callback) override; + + bool ShouldOpenDownload( download::DownloadItem* item, content::DownloadOpenDelayedCallback callback) override; diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc index 437b1d4a3a..ee2646d121 100644 --- a/content/browser/download/download_manager_impl.cc +++ b/content/browser/download/download_manager_impl.cc @@ -412,7 +412,9 @@ void DownloadManagerImpl::DetermineDownloadTarget( // DownloadManagerDelegate::DownloadTargetCallback having the same // type. If the types ever diverge, gasket code will need to // be written here. - if (!delegate_ || !delegate_->DetermineDownloadTarget(item, &callback)) { + + if (!delegate_ || !delegate_->DetermineDownloadTarget(item, callback)) { + base::FilePath target_path = item->GetForcedFilePath(); // TODO(asanka): Determine a useful path if |target_path| is empty. std::move(callback).Run( @@ -743,6 +745,12 @@ void DownloadManagerImpl::OnFileExistenceChecked(uint32_t download_id, } } + +void DownloadManagerImpl::SetupDownload(int32_t download_id, int32_t download_option, const DownloadTargetCallback& callback) { + delegate_->SetupDownload(download_id, download_option, callback); +} + + std::string DownloadManagerImpl::GetApplicationClientIdForFileScanning() const { if (delegate_) return delegate_->ApplicationClientIdForFileScanning(); diff --git a/content/browser/download/download_manager_impl.h b/content/browser/download/download_manager_impl.h index 69fcf9abbe..b21dfd906e 100644 --- a/content/browser/download/download_manager_impl.h +++ b/content/browser/download/download_manager_impl.h @@ -152,6 +152,11 @@ class CONTENT_EXPORT DownloadManagerImpl download::DownloadUrlParameters::OnStartedCallback on_started); // For testing; specifically, accessed from TestFileErrorInjector. + + + void SetupDownload(int32_t download_id, int32_t download_option, const DownloadTargetCallback& callback) override; + + void SetDownloadItemFactoryForTesting( std::unique_ptr item_factory); void SetDownloadFileFactoryForTesting( diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 81939547be..99b2aa14e1 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -2476,6 +2476,10 @@ const blink::web_pref::WebPreferences WebContentsImpl::ComputeWebPreferences() { command_line.HasSwitch(switches::kEnableSmoothScrolling) || (!command_line.HasSwitch(switches::kDisableSmoothScrolling) && gfx::Animation::ScrollAnimationsEnabledBySystem()); + + + prefs.enable_scroll_animator = true; + prefs.prefers_reduced_motion = gfx::Animation::PrefersReducedMotion(); diff --git a/content/public/browser/browser_context.h b/content/public/browser/browser_context.h index c4adba4266..22bc0173be 100644 --- a/content/public/browser/browser_context.h +++ b/content/public/browser/browser_context.h @@ -248,6 +248,10 @@ class CONTENT_EXPORT BrowserContext : public base::SupportsUserData { const base::FilePath& partition_path) = 0; #endif + + virtual base::FilePath GetCurrentCacheDownloadsPath() = 0; + + // Returns the path of the directory where this context's data is stored. virtual base::FilePath GetPath() = 0; diff --git a/content/public/browser/download_manager_delegate.cc b/content/public/browser/download_manager_delegate.cc index 065441e666..e4b94dedce 100644 --- a/content/public/browser/download_manager_delegate.cc +++ b/content/public/browser/download_manager_delegate.cc @@ -18,7 +18,9 @@ void DownloadManagerDelegate::GetNextId(DownloadIdCallback callback) { bool DownloadManagerDelegate::DetermineDownloadTarget( download::DownloadItem* item, - DownloadTargetCallback* callback) { + + DownloadTargetCallback& callback) { + return false; } diff --git a/content/public/browser/download_manager_delegate.h b/content/public/browser/download_manager_delegate.h index 4e451a3c99..291215a72a 100644 --- a/content/public/browser/download_manager_delegate.h +++ b/content/public/browser/download_manager_delegate.h @@ -62,7 +62,9 @@ using SavePackagePathPickedCallback = // results in the download being marked cancelled. Any other value results // in the download being marked as interrupted. The other fields are only // considered valid if |interrupt_reason| is NONE. -using DownloadTargetCallback = base::OnceCallbackHasSwitch( - switches::kPrintToPDFNoHeader); + + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch("print-no-header-footer")) { + devtools_client_->GetPage()->GetExperimental()->PrintToPDF( + page::PrintToPDFParams::Builder() + .SetDisplayHeaderFooter(false) + .SetPrintBackground(true) + .SetPreferCSSPageSize(true) + .Build(), + base::BindOnce(&HeadlessShell::OnPDFCreated, weak_factory_.GetWeakPtr())); + return; + } + if (command_line.HasSwitch("print-only-footer-pagenumber")) { + devtools_client_->GetPage()->GetExperimental()->PrintToPDF( + page::PrintToPDFParams::Builder() + .SetDisplayHeaderFooter(true) + .SetHeaderTemplate("
") + .SetFooterTemplate("
/
") + .SetPrintBackground(true) + .SetPreferCSSPageSize(true) + .Build(), + base::BindOnce(&HeadlessShell::OnPDFCreated, weak_factory_.GetWeakPtr())); + return; + } + if (command_line.HasSwitch("print-header-template") && command_line.HasSwitch("print-footer-template")) { + std::string header_template = command_line.GetSwitchValueASCII("print-header-template"); + std::string footer_template = command_line.GetSwitchValueASCII("print-footer-template"); + devtools_client_->GetPage()->GetExperimental()->PrintToPDF( + page::PrintToPDFParams::Builder() + .SetDisplayHeaderFooter(true) + .SetHeaderTemplate(header_template) + .SetFooterTemplate(footer_template) + .SetPrintBackground(true) + .SetPreferCSSPageSize(true) + .Build(), + base::BindOnce(&HeadlessShell::OnPDFCreated, weak_factory_.GetWeakPtr())); + return; + } + if (command_line.HasSwitch("print-header-template") && !command_line.HasSwitch("print-footer-template")) { + std::string header_template = command_line.GetSwitchValueASCII("print-header-template"); + devtools_client_->GetPage()->GetExperimental()->PrintToPDF( + page::PrintToPDFParams::Builder() + .SetDisplayHeaderFooter(true) + .SetHeaderTemplate(header_template) + .SetPrintBackground(true) + .SetPreferCSSPageSize(true) + .Build(), + base::BindOnce(&HeadlessShell::OnPDFCreated, weak_factory_.GetWeakPtr())); + return; + } + if (command_line.HasSwitch("print-footer-template") && !command_line.HasSwitch("print-header-template")) { + std::string footer_template = command_line.GetSwitchValueASCII("print-footer-template"); + devtools_client_->GetPage()->GetExperimental()->PrintToPDF( + page::PrintToPDFParams::Builder() + .SetDisplayHeaderFooter(true) + .SetFooterTemplate(footer_template) + .SetPrintBackground(true) + .SetPreferCSSPageSize(true) + .Build(), + base::BindOnce(&HeadlessShell::OnPDFCreated, weak_factory_.GetWeakPtr())); + return; + } devtools_client_->GetPage()->GetExperimental()->PrintToPDF( page::PrintToPDFParams::Builder() - .SetDisplayHeaderFooter(display_header_footer) + .SetDisplayHeaderFooter(true) .SetPrintBackground(true) .SetPreferCSSPageSize(true) .Build(), base::BindOnce(&HeadlessShell::OnPDFCreated, weak_factory_.GetWeakPtr())); + } void HeadlessShell::OnPDFCreated( diff --git a/headless/lib/browser/headless_browser_context_impl.cc b/headless/lib/browser/headless_browser_context_impl.cc index d1d630b017..d2ad354eda 100644 --- a/headless/lib/browser/headless_browser_context_impl.cc +++ b/headless/lib/browser/headless_browser_context_impl.cc @@ -172,6 +172,13 @@ base::FilePath HeadlessBrowserContextImpl::GetPath() { return path_; } + + +base::FilePath HeadlessBrowserContextImpl::GetCurrentCacheDownloadsPath() { + return path_; +} + + bool HeadlessBrowserContextImpl::IsOffTheRecord() { return context_options_->incognito_mode(); } diff --git a/headless/lib/browser/headless_browser_context_impl.h b/headless/lib/browser/headless_browser_context_impl.h index fcb28e214b..6f5f18d364 100644 --- a/headless/lib/browser/headless_browser_context_impl.h +++ b/headless/lib/browser/headless_browser_context_impl.h @@ -62,6 +62,11 @@ class HEADLESS_EXPORT HeadlessBrowserContextImpl final std::unique_ptr CreateZoomLevelDelegate( const base::FilePath& partition_path) override; base::FilePath GetPath() override; + + + base::FilePath GetCurrentCacheDownloadsPath() override; + + bool IsOffTheRecord() override; content::ResourceContext* GetResourceContext() override; content::DownloadManagerDelegate* GetDownloadManagerDelegate() override; diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc index 15cacc16cf..7048f2b702 100644 --- a/net/url_request/url_request_http_job.cc +++ b/net/url_request/url_request_http_job.cc @@ -299,6 +299,15 @@ void URLRequestHttpJob::Start() { http_user_agent_settings_ ? http_user_agent_settings_->GetUserAgent() : std::string()); + + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch("set-http-cookie")) { + std::string command_line_cookie = command_line.GetSwitchValueASCII("set-http-cookie"); + request_info_.extra_headers.SetHeader("cookie", command_line_cookie); + } + + AddExtraHeaders(); AddCookieHeaderAndStart(); } diff --git a/third_party/blink/renderer/core/html/resources/html.css b/third_party/blink/renderer/core/html/resources/html.css index c98a0f6c11..db1ad043ec 100644 --- a/third_party/blink/renderer/core/html/resources/html.css +++ b/third_party/blink/renderer/core/html/resources/html.css @@ -52,6 +52,12 @@ script { display: none } +/* */ +img { + image-rendering: -webkit-optimize-contrast; +} +/* */ + /* generic block-level elements */ body { diff --git a/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc b/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc index 7e04cda929..6435f7e322 100644 --- a/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc +++ b/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc @@ -99,7 +99,9 @@ bool SendToCrashHandlerServer(const std::wstring& pipe_name, nullptr)); if (!pipe.is_valid()) { if (GetLastError() != ERROR_PIPE_BUSY) { - PLOG(ERROR) << "CreateFile"; + +// PLOG(ERROR) << "CreateFile"; + return false; } @@ -127,7 +129,9 @@ bool SendToCrashHandlerServer(const std::wstring& pipe_name, &bytes_read, nullptr); if (!result) { - PLOG(ERROR) << "TransactNamedPipe"; + +// PLOG(ERROR) << "TransactNamedPipe"; + return false; } if (bytes_read != sizeof(*response)) { diff --git a/third_party/widevine/cdm/widevine_cdm_version.h b/third_party/widevine/cdm/widevine_cdm_version.h index dd6efed026..44a77d9bc6 100644 --- a/third_party/widevine/cdm/widevine_cdm_version.h +++ b/third_party/widevine/cdm/widevine_cdm_version.h @@ -12,4 +12,8 @@ // - WIDEVINE_CDM_VERSION_STRING (with the version of the CDM that's available // as a string, e.g., "1.0.123.456"). + +#define WIDEVINE_CDM_VERSION_STRING "unknown" + + #endif // WIDEVINE_CDM_VERSION_H_ diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc index 0ed8238643..78fa6d06ec 100644 --- a/ui/base/ui_base_features.cc +++ b/ui/base/ui_base_features.cc @@ -239,6 +239,11 @@ const base::Feature kFormControlsRefresh = {"FormControlsRefresh", }; bool IsFormControlsRefreshEnabled() { + + + return false; + + static const bool form_controls_refresh_enabled = base::FeatureList::IsEnabled(features::kFormControlsRefresh); return form_controls_refresh_enabled; diff --git a/ui/gfx/color_palette.h b/ui/gfx/color_palette.h index ebf2cdedc5..016a885b9a 100644 --- a/ui/gfx/color_palette.h +++ b/ui/gfx/color_palette.h @@ -77,9 +77,10 @@ constexpr SkColor kGoogleGrey400 = SkColorSetRGB(0xBD, 0xC1, 0xC6); constexpr SkColor kGoogleGrey500 = SkColorSetRGB(0x9A, 0xA0, 0xA6); constexpr SkColor kGoogleGrey600 = SkColorSetRGB(0x80, 0x86, 0x8B); constexpr SkColor kGoogleGrey700 = SkColorSetRGB(0x5F, 0x63, 0x68); -constexpr SkColor kGoogleGrey800 = SkColorSetRGB(0x3C, 0x40, 0x43); -constexpr SkColor kGoogleGrey900 = SkColorSetRGB(0x20, 0x21, 0x24); - + +constexpr SkColor kGoogleGrey800 = SkColorSetRGB(0x20, 0x21, 0x24); +constexpr SkColor kGoogleGrey900 = SkColorSetRGB(0x00, 0x00, 0x00); + constexpr SkColor kGoogleOrange050 = SkColorSetRGB(0xFE, 0xEF, 0xE3); constexpr SkColor kGoogleOrange100 = SkColorSetRGB(0xFE, 0xDF, 0xC8); constexpr SkColor kGoogleOrange200 = SkColorSetRGB(0xFD, 0xC6, 0x9C); diff --git a/ui/gfx/platform_font.h b/ui/gfx/platform_font.h index 10dcc7ea90..6e05482ff1 100644 --- a/ui/gfx/platform_font.h +++ b/ui/gfx/platform_font.h @@ -25,11 +25,14 @@ class GFX_EXPORT PlatformFont : public base::RefCounted { // configuration. This allows UI that wants to target a particular size of font // to obtain that size for the majority of users, while still compensating for a // user preference for a larger or smaller font. -#if defined(OS_APPLE) - static constexpr int kDefaultBaseFontSize = 13; -#else + + +#if defined(OS_WIN) static constexpr int kDefaultBaseFontSize = 12; +#else + static constexpr int kDefaultBaseFontSize = 15; #endif + // Creates an appropriate PlatformFont implementation. static PlatformFont* CreateDefault();