| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/chrome/browser/snapshots/model/snapshot_tab_helper.h" |
| |
| #import "base/functional/bind.h" |
| #import "base/memory/ptr_util.h" |
| #import "base/metrics/histogram_functions.h" |
| #import "base/task/sequenced_task_runner.h" |
| #import "ios/chrome/browser/snapshots/model/features.h" |
| #import "ios/chrome/browser/snapshots/model/legacy_snapshot_generator.h" |
| #import "ios/chrome/browser/snapshots/model/legacy_snapshot_manager.h" |
| #import "ios/chrome/browser/snapshots/model/model_swift.h" |
| #import "ios/chrome/browser/snapshots/model/snapshot_id_wrapper.h" |
| #import "ios/chrome/browser/snapshots/model/snapshot_storage_wrapper.h" |
| #import "ios/chrome/browser/snapshots/model/web_state_snapshot_info.h" |
| #import "ios/web/public/web_state.h" |
| |
| namespace { |
| // Possible results of snapshotting when the page has been loaded. These values |
| // are persisted to logs. Entries should not be renumbered and numeric values |
| // should never be reused. |
| enum class PageLoadedSnapshotResult { |
| // Snapshot was not attempted, since the loading page will result in a stale |
| // snapshot. |
| kSnapshotNotAttemptedBecausePageLoadFailed = 0, |
| // Snapshot was attempted, but the image is either the default image or nil. |
| kSnapshotAttemptedAndFailed = 1, |
| // Snapshot successfully taken. |
| kSnapshotSucceeded = 2, |
| // kMaxValue should share the value of the highest enumerator. |
| kMaxValue = kSnapshotSucceeded, |
| }; |
| |
| // Generates an ID for WebState's snapshot. |
| SnapshotID GenerateSnapshotID(const web::WebState* web_state) { |
| DCHECK(web_state->GetUniqueIdentifier().valid()); |
| DCHECK_GT(web_state->GetUniqueIdentifier().identifier(), 0); |
| |
| static_assert(sizeof(decltype(web::WebStateID().identifier())) == |
| sizeof(int32_t)); |
| return SnapshotID(web_state->GetUniqueIdentifier().identifier()); |
| } |
| |
| } // namespace |
| |
| SnapshotTabHelper::~SnapshotTabHelper() { |
| DCHECK(!web_state_); |
| } |
| |
| void SnapshotTabHelper::SetDelegate(id<SnapshotGeneratorDelegate> delegate) { |
| if (base::FeatureList::IsEnabled(kSnapshotInSwift)) { |
| CHECK(snapshot_manager_); |
| [snapshot_manager_ setDelegate:delegate]; |
| } else { |
| CHECK(legacy_snapshot_manager_); |
| [legacy_snapshot_manager_ setDelegate:delegate]; |
| } |
| } |
| |
| void SnapshotTabHelper::SetSnapshotStorage(SnapshotStorageWrapper* wrapper) { |
| if (base::FeatureList::IsEnabled(kSnapshotInSwift)) { |
| CHECK(snapshot_manager_); |
| SnapshotStorage* storage = nil; |
| // `wrapper` is nil when a WebState is detached. |
| if (wrapper) { |
| storage = wrapper.snapshotStorage; |
| CHECK(storage); |
| } |
| // Note that `snapshot_manager_.snapshotStorage` is SnapshotStorage. On the |
| // other hand, `legacy_snapshot_manager_.snapshotStorage` is |
| // SnapshotStorageWrapper. The type is different to avoid a dependency |
| // cycle. |
| snapshot_manager_.snapshotStorage = storage; |
| } else { |
| CHECK(legacy_snapshot_manager_); |
| legacy_snapshot_manager_.snapshotStorage = wrapper; |
| } |
| } |
| |
| void SnapshotTabHelper::RetrieveColorSnapshot(void (^callback)(UIImage*)) { |
| if (base::FeatureList::IsEnabled(kSnapshotInSwift)) { |
| CHECK(snapshot_manager_); |
| [snapshot_manager_ retrieveSnapshotWithCompletion:callback]; |
| } else { |
| CHECK(legacy_snapshot_manager_); |
| [legacy_snapshot_manager_ retrieveSnapshot:callback]; |
| } |
| } |
| |
| void SnapshotTabHelper::RetrieveGreySnapshot(void (^callback)(UIImage*)) { |
| if (base::FeatureList::IsEnabled(kSnapshotInSwift)) { |
| CHECK(snapshot_manager_); |
| [snapshot_manager_ retrieveGreySnapshotWithCompletion:callback]; |
| } else { |
| CHECK(legacy_snapshot_manager_); |
| [legacy_snapshot_manager_ retrieveGreySnapshot:callback]; |
| } |
| } |
| |
| void SnapshotTabHelper::UpdateSnapshotWithCallback(void (^callback)(UIImage*)) { |
| was_loading_during_last_snapshot_ = web_state_->IsLoading(); |
| |
| if (base::FeatureList::IsEnabled(kSnapshotInSwift)) { |
| CHECK(snapshot_manager_); |
| [snapshot_manager_ updateSnapshotWithCompletion:callback]; |
| } else { |
| CHECK(legacy_snapshot_manager_); |
| [legacy_snapshot_manager_ updateSnapshotWithCompletion:callback]; |
| } |
| } |
| |
| UIImage* SnapshotTabHelper::GenerateSnapshotWithoutOverlays() { |
| if (base::FeatureList::IsEnabled(kSnapshotInSwift)) { |
| CHECK(snapshot_manager_); |
| return [snapshot_manager_ generateUIViewSnapshot]; |
| } else { |
| CHECK(legacy_snapshot_manager_); |
| return [legacy_snapshot_manager_ generateUIViewSnapshot]; |
| } |
| } |
| |
| void SnapshotTabHelper::RemoveSnapshot() { |
| if (base::FeatureList::IsEnabled(kSnapshotInSwift)) { |
| CHECK(snapshot_manager_); |
| [snapshot_manager_ removeSnapshot]; |
| } else { |
| CHECK(legacy_snapshot_manager_); |
| [legacy_snapshot_manager_ removeSnapshot]; |
| } |
| } |
| |
| void SnapshotTabHelper::IgnoreNextLoad() { |
| ignore_next_load_ = true; |
| } |
| |
| SnapshotID SnapshotTabHelper::GetSnapshotID() const { |
| if (base::FeatureList::IsEnabled(kSnapshotInSwift)) { |
| CHECK(snapshot_manager_); |
| return snapshot_manager_.snapshotID.snapshot_id; |
| } else { |
| CHECK(legacy_snapshot_manager_); |
| return legacy_snapshot_manager_.snapshotID; |
| } |
| } |
| |
| SnapshotTabHelper::SnapshotTabHelper(web::WebState* web_state) |
| : web_state_(web_state) { |
| DCHECK(web_state_); |
| if (base::FeatureList::IsEnabled(kSnapshotInSwift)) { |
| snapshot_manager_ = [[SnapshotManager alloc] |
| initWithGenerator: |
| [[SnapshotGenerator alloc] |
| initWithWebStateInfo:[[WebStateSnapshotInfo alloc] |
| initWithWebState:web_state_]] |
| snapshotID:[[SnapshotIDWrapper alloc] |
| initWithSnapshotID:GenerateSnapshotID( |
| web_state_)]]; |
| } else { |
| legacy_snapshot_manager_ = [[LegacySnapshotManager alloc] |
| initWithGenerator:[[LegacySnapshotGenerator alloc] |
| initWithWebState:web_state_] |
| snapshotID:GenerateSnapshotID(web_state_)]; |
| } |
| |
| web_state_observation_.Observe(web_state_.get()); |
| } |
| |
| void SnapshotTabHelper::PageLoaded( |
| web::WebState* web_state, |
| web::PageLoadCompletionStatus load_completion_status) { |
| // Snapshots taken while page is loading will eventually be stale. It |
| // is important that another snapshot is taken after the new |
| // page has loaded to replace the stale snapshot. The |
| // `IOS.PageLoadedSnapshotResult` histogram shows the outcome of |
| // snapshot attempts when the page is loaded after having taken |
| // a stale snapshot. |
| switch (load_completion_status) { |
| case web::PageLoadCompletionStatus::FAILURE: |
| // Only log histogram for when a stale snapshot needs to be replaced. |
| if (was_loading_during_last_snapshot_) { |
| base::UmaHistogramEnumeration( |
| "IOS.PageLoadedSnapshotResult", |
| PageLoadedSnapshotResult:: |
| kSnapshotNotAttemptedBecausePageLoadFailed); |
| } |
| break; |
| |
| case web::PageLoadCompletionStatus::SUCCESS: |
| if (ignore_next_load_) { |
| break; |
| } |
| |
| bool was_loading = was_loading_during_last_snapshot_; |
| base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce( |
| &SnapshotTabHelper::UpdateSnapshotWithCallback, |
| weak_ptr_factory_.GetWeakPtr(), |
| ^(UIImage* snapshot) { |
| // Only log histogram for when a stale snapshot needs to be |
| // replaced. |
| if (!was_loading) { |
| return; |
| } |
| base::UmaHistogramEnumeration( |
| "IOS.PageLoadedSnapshotResult", |
| snapshot ? PageLoadedSnapshotResult::kSnapshotSucceeded |
| : PageLoadedSnapshotResult:: |
| kSnapshotAttemptedAndFailed); |
| }), |
| base::Seconds(1)); |
| break; |
| } |
| ignore_next_load_ = false; |
| was_loading_during_last_snapshot_ = false; |
| } |
| |
| void SnapshotTabHelper::WebStateDestroyed(web::WebState* web_state) { |
| DCHECK_EQ(web_state_, web_state); |
| DCHECK(web_state_observation_.IsObservingSource(web_state)); |
| web_state_observation_.Reset(); |
| web_state_ = nullptr; |
| } |
| |
| WEB_STATE_USER_DATA_KEY_IMPL(SnapshotTabHelper) |