blob: 2a0d18560e1a2870736d7faa5f2ebdf68588355e [file] [log] [blame]
// Copyright 2023 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/web/content/web_state/content_web_state.h"
#import "base/apple/foundation_util.h"
#import "base/strings/sys_string_conversions.h"
#import "base/strings/utf_string_conversions.h"
#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_ios.h"
#import "components/embedder_support/ios/delegate/file_chooser/file_select_helper_ios.h"
#import "components/javascript_dialogs/tab_modal_dialog_manager.h"
#import "content/public/browser/file_select_listener.h"
#import "content/public/browser/javascript_dialog_manager.h"
#import "content/public/browser/navigation_entry.h"
#import "content/public/browser/visibility.h"
#import "content/public/browser/web_contents.h"
#import "ios/web/content/content_browser_context.h"
#import "ios/web/content/navigation/content_navigation_context.h"
#import "ios/web/content/web_state/content_web_state_builder.h"
#import "ios/web/content/web_state/crc_web_view_proxy_impl.h"
#import "ios/web/content/web_state/crc_web_viewport_container_view.h"
#import "ios/web/public/favicon/favicon_url.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/navigation/navigation_util.h"
#import "ios/web/public/navigation/web_state_policy_decider.h"
#import "ios/web/public/session/crw_session_storage.h"
#import "ios/web/public/session/proto/metadata.pb.h"
#import "ios/web/public/session/proto/proto_util.h"
#import "ios/web/public/session/proto/storage.pb.h"
#import "ios/web/public/web_state_delegate.h"
#import "ios/web/public/web_state_observer.h"
#import "ios/web/util/content_type_util.h"
#import "net/cert/x509_util.h"
#import "net/cert/x509_util_apple.h"
#import "services/network/public/mojom/referrer_policy.mojom-shared.h"
#import "skia/ext/skia_utils_ios.h"
#import "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
#import "third_party/blink/public/mojom/page/page_visibility_state.mojom.h"
#import "ui/display/display.h"
#import "ui/display/screen.h"
namespace web {
namespace {
// The content navigation machinery should not use this so we will use a dummy.
// TODO(crbug.com/40257932): enable returning nullptr for the cache.
class DummySessionCertificatePolicyCache
: public SessionCertificatePolicyCache {
public:
explicit DummySessionCertificatePolicyCache(BrowserState* browser_state)
: SessionCertificatePolicyCache(browser_state) {}
void UpdateCertificatePolicyCache() const override {}
void RegisterAllowedCertificate(
const scoped_refptr<net::X509Certificate>& certificate,
const std::string& host,
net::CertStatus status) override {}
};
FaviconURL::IconType IconTypeFromContentIconType(
blink::mojom::FaviconIconType icon_type) {
switch (icon_type) {
case blink::mojom::FaviconIconType::kFavicon:
return FaviconURL::IconType::kFavicon;
case blink::mojom::FaviconIconType::kTouchIcon:
return FaviconURL::IconType::kTouchIcon;
case blink::mojom::FaviconIconType::kTouchPrecomposedIcon:
return FaviconURL::IconType::kTouchPrecomposedIcon;
case blink::mojom::FaviconIconType::kInvalid:
return FaviconURL::IconType::kInvalid;
}
NOTREACHED();
}
} // namespace
// Stores ContentWebstate serialized state.
class ContentWebState::SerializedState {
public:
// Returns a new instance built from `session_storage`.
static std::unique_ptr<SerializedState> FromSessionStorage(
CRWSessionStorage* session_storage) {
DCHECK(session_storage);
proto::WebStateMetadataStorage metadata;
[session_storage serializeMetadataToProto:metadata];
return base::WrapUnique(
new SerializedState(session_storage, std::move(metadata),
base::BindOnce(
[](CRWSessionStorage* session_storage)
-> std::optional<proto::WebStateStorage> {
proto::WebStateStorage storage;
[session_storage serializeToProto:storage];
return storage;
},
session_storage)));
}
// Returns a new instance built from `metadata` and `storage_loader`.
static std::unique_ptr<SerializedState> FromStorage(
proto::WebStateMetadataStorage metadata,
WebStateStorageLoader storage_loader) {
return base::WrapUnique(new SerializedState(nil, std::move(metadata),
std::move(storage_loader)));
}
// Returns the current navigation title from serialized data.
const std::u16string& GetTitle() const { return cached_title_; }
// Returns the number of navigation items from serialized data.
int GetNavigationItemCount() const { return navigation_item_count_; }
// Loads from disk the `web::proto::WebStateStorage` and returns it.
web::proto::WebStateStorage LoadStorage() {
web::proto::WebStateStorage storage;
if (auto optional_storage = std::move(storage_loader_).Run()) {
storage = std::move(optional_storage).value();
} else {
const GURL page_visible_url = GURL(metadata_.active_page().page_url());
if (page_visible_url.is_valid()) {
storage = CreateWebStateStorage(
NavigationManager::WebLoadParams(page_visible_url),
base::UTF8ToUTF16(metadata_.active_page().page_title()),
/* created_with_opener= */ false,
/* user_agent= */ UserAgentType::AUTOMATIC,
web::TimeFromProto(metadata_.creation_time()));
}
}
*storage.mutable_metadata() = std::move(metadata_);
return storage;
}
// Serializes metadata to `metadata`.
void SerializeMetadata(web::proto::WebStateMetadataStorage& metadata) {
metadata = metadata_;
}
// Returns the `CRWSessionStorage`. Can only be called if the constructed
// with a `CRWSessionStorage` object.
CRWSessionStorage* session_storage() {
DCHECK(session_storage_);
return session_storage_;
}
private:
// Private constructor.
SerializedState(CRWSessionStorage* session_storage,
proto::WebStateMetadataStorage metadata,
WebStateStorageLoader storage_loader)
: session_storage_(session_storage),
metadata_(std::move(metadata)),
storage_loader_(std::move(storage_loader)) {
navigation_item_count_ = metadata_.navigation_item_count();
if (metadata_.has_active_page()) {
cached_title_ = base::UTF8ToUTF16(metadata_.active_page().page_title());
}
}
CRWSessionStorage* session_storage_;
std::u16string cached_title_;
int navigation_item_count_ = 0;
proto::WebStateMetadataStorage metadata_;
WebStateStorageLoader storage_loader_;
};
ContentWebState::ContentWebState(const CreateParams& params)
: ContentWebState(params, WebStateID::NewUnique(), nullptr) {}
ContentWebState::ContentWebState(const CreateParams& params,
CRWSessionStorage* session_storage,
NativeSessionFetcher session_fetcher)
: ContentWebState(params,
session_storage.uniqueIdentifier,
SerializedState::FromSessionStorage(session_storage)) {}
ContentWebState::ContentWebState(BrowserState* browser_state,
WebStateID unique_identifier,
proto::WebStateMetadataStorage metadata,
WebStateStorageLoader storage_loader,
NativeSessionFetcher session_fetcher)
: ContentWebState(CreateParams(browser_state),
unique_identifier,
SerializedState::FromStorage(std::move(metadata),
std::move(storage_loader))) {
}
ContentWebState::ContentWebState(
const CreateParams& params,
WebStateID unique_identifier,
std::unique_ptr<SerializedState> serialized_state)
: unique_identifier_(unique_identifier) {
content::BrowserContext* browser_context =
ContentBrowserContext::FromBrowserState(params.browser_state);
scoped_refptr<content::SiteInstance> site_instance;
content::WebContents::CreateParams createParams(browser_context,
site_instance);
created_with_opener_ = params.created_with_opener;
if (created_with_opener_) {
ContentWebState* opener_web_state =
static_cast<ContentWebState*>(params.opener_web_state);
DCHECK(opener_web_state->child_web_contents_);
web_contents_ = std::move(opener_web_state->child_web_contents_);
} else {
web_contents_ = content::WebContents::Create(createParams);
}
web_contents_->SetDelegate(this);
WebContentsObserver::Observe(web_contents_.get());
certificate_policy_cache_ =
std::make_unique<DummySessionCertificatePolicyCache>(
params.browser_state);
navigation_manager_ = std::make_unique<ContentNavigationManager>(
this, params.browser_state, web_contents_->GetController());
managers_[ContentWorld::kAllContentWorlds] =
std::make_unique<ContentWebFramesManager>(this);
managers_[ContentWorld::kPageContentWorld] =
std::make_unique<ContentWebFramesManager>(this);
managers_[ContentWorld::kIsolatedWorld] =
std::make_unique<ContentWebFramesManager>(this);
UIScrollView* web_contents_view = base::apple::ObjCCastStrict<UIScrollView>(
web_contents_->GetNativeView().Get());
web_view_ = [[CRCWebViewportContainerView alloc] init];
// Comment this back in to show visual glitches that might be present.
// web_view_.backgroundColor = UIColor.redColor;
CRCWebViewProxyImpl* proxy = [[CRCWebViewProxyImpl alloc] init];
proxy.contentView = web_contents_view;
web_view_proxy_ = proxy;
[web_view_ addSubview:web_contents_view];
serialized_state_ = std::move(serialized_state);
creation_time_ = base::Time::Now();
last_active_time_ = params.last_active_time.value_or(creation_time_);
RegisterNotificationObservers();
}
ContentWebState::~ContentWebState() {
WebContentsObserver::Observe(nullptr);
for (auto& observer : observers_) {
observer.WebStateDestroyed(this);
}
for (auto& observer : policy_deciders_) {
observer.WebStateDestroyed();
}
for (auto& observer : policy_deciders_) {
observer.ResetWebState();
}
NSNotificationCenter* default_center = [NSNotificationCenter defaultCenter];
[default_center removeObserver:keyboard_showing_observer_];
[default_center removeObserver:keyboard_hiding_observer_];
}
content::WebContents* ContentWebState::GetWebContents() {
return web_contents_.get();
}
void ContentWebState::SerializeToProto(proto::WebStateStorage& storage) const {
DCHECK(IsRealized());
SerializeContentStorage(this, navigation_manager_.get(), storage);
}
void ContentWebState::SerializeMetadataToProto(
proto::WebStateMetadataStorage& metadata) const {
if (serialized_state_) {
serialized_state_->SerializeMetadata(metadata);
return;
}
proto::WebStateStorage storage;
SerializeToProto(storage);
metadata = std::move(*storage.mutable_metadata());
}
WebStateDelegate* ContentWebState::GetDelegate() {
return delegate_;
}
std::unique_ptr<WebState> ContentWebState::Clone() const {
proto::WebStateStorage storage;
SerializeToProto(storage);
proto::WebStateMetadataStorage metadata;
std::swap(metadata, *storage.mutable_metadata());
auto clone = std::make_unique<ContentWebState>(
GetBrowserState(), WebStateID::NewUnique(), std::move(metadata),
base::ReturnValueOnce(std::make_optional(std::move(storage))),
base::ReturnValueOnce<NSData*>(nil));
IgnoreOverRealizationCheck();
clone->ForceRealized();
return clone;
}
void ContentWebState::SetDelegate(WebStateDelegate* delegate) {
if (delegate == delegate_) {
return;
}
if (delegate_) {
delegate_->Detach(this);
}
delegate_ = delegate;
if (delegate_) {
delegate_->Attach(this);
}
}
bool ContentWebState::IsRealized() const {
return serialized_state_ == nullptr;
}
WebState* ContentWebState::ForceRealizedWithPolicy(RealizationPolicy policy) {
if (serialized_state_) {
auto serialized_state = std::exchange(serialized_state_, nullptr);
web::proto::WebStateStorage storage = serialized_state->LoadStorage();
ExtractContentSessionStorage(this, web_contents_->GetController(),
GetBrowserState(), std::move(storage));
// Notify all observers that the WebState has become realized but take
// care to not notify any observer that is registered while iterating.
NotifyWebStateRealized(observers_);
}
return this;
}
bool ContentWebState::IsWebUsageEnabled() const {
return true;
}
void ContentWebState::SetWebUsageEnabled(bool enabled) {}
UIView* ContentWebState::GetView() {
return web_view_;
}
void ContentWebState::DidCoverWebContent() {}
void ContentWebState::DidRevealWebContent() {}
base::Time ContentWebState::GetLastActiveTime() const {
return last_active_time_;
}
base::Time ContentWebState::GetCreationTime() const {
return creation_time_;
}
void ContentWebState::WasShown() {
ForceRealized();
// Update last active time when the ContentWebState transition to visible.
last_active_time_ = base::Time::Now();
for (auto& observer : observers_) {
observer.WasShown(this);
}
}
void ContentWebState::WasHidden() {
ForceRealized();
for (auto& observer : observers_) {
observer.WasHidden(this);
}
}
void ContentWebState::SetKeepRenderProcessAlive(bool keep_alive) {}
BrowserState* ContentWebState::GetBrowserState() const {
return navigation_manager_->GetBrowserState();
}
base::WeakPtr<WebState> ContentWebState::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void ContentWebState::OpenURL(const OpenURLParams& params) {
if (delegate_) {
delegate_->OpenURLFromWebState(this, params);
}
}
void ContentWebState::LoadSimulatedRequest(const GURL& url,
NSString* response_html_string) {}
void ContentWebState::LoadSimulatedRequest(const GURL& url,
NSData* response_data,
NSString* mime_type) {}
void ContentWebState::Stop() {
DCHECK(web_contents_);
web_contents_->Stop();
}
const NavigationManager* ContentWebState::GetNavigationManager() const {
return navigation_manager_.get();
}
NavigationManager* ContentWebState::GetNavigationManager() {
return navigation_manager_.get();
}
WebFramesManager* ContentWebState::GetPageWorldWebFramesManager() {
return managers_[ContentWorld::kPageContentWorld].get();
}
const SessionCertificatePolicyCache*
ContentWebState::GetSessionCertificatePolicyCache() const {
return certificate_policy_cache_.get();
}
SessionCertificatePolicyCache*
ContentWebState::GetSessionCertificatePolicyCache() {
return certificate_policy_cache_.get();
}
CRWSessionStorage* ContentWebState::BuildSessionStorage() const {
if (serialized_state_) {
return serialized_state_->session_storage();
}
web::proto::WebStateStorage storage;
SerializeContentStorage(this, navigation_manager_.get(), storage);
return [[CRWSessionStorage alloc] initWithProto:storage
uniqueIdentifier:unique_identifier_
stableIdentifier:[[NSUUID UUID] UUIDString]];
}
void ContentWebState::LoadData(NSData* data,
NSString* mime_type,
const GURL& url) {}
void ContentWebState::ExecuteUserJavaScript(NSString* javaScript) {
auto* primary_main_frame = web_contents_->GetPrimaryMainFrame();
DCHECK(primary_main_frame);
primary_main_frame->ExecuteJavaScript(base::SysNSStringToUTF16(javaScript),
{});
}
WebStateID ContentWebState::GetUniqueIdentifier() const {
return unique_identifier_;
}
const std::string& ContentWebState::GetContentsMimeType() const {
return web_contents_->GetContentsMimeType();
}
bool ContentWebState::ContentIsHTML() const {
return web::IsContentTypeHtml(GetContentsMimeType());
}
const std::u16string& ContentWebState::GetTitle() const {
if (serialized_state_) {
return serialized_state_->GetTitle();
}
return web_contents_->GetTitle();
}
bool ContentWebState::IsLoading() const {
return serialized_state_ ? false : web_contents_->IsLoading();
}
double ContentWebState::GetLoadingProgress() const {
return serialized_state_ ? 0.0 : web_contents_->GetLoadProgress();
}
bool ContentWebState::IsVisible() const {
DCHECK(web_contents_);
return web_contents_->GetVisibility() == content::Visibility::VISIBLE ? true
: false;
}
bool ContentWebState::IsCrashed() const {
DCHECK(web_contents_);
return web_contents_->IsCrashed();
}
bool ContentWebState::IsEvicted() const {
return false;
}
bool ContentWebState::IsBeingDestroyed() const {
DCHECK(web_contents_);
return web_contents_->IsBeingDestroyed();
}
bool ContentWebState::IsWebPageInFullscreenMode() const {
DCHECK(web_contents_);
return web_contents_->IsFullscreen();
}
const FaviconStatus& ContentWebState::GetFaviconStatus() const {
auto* item = navigation_manager_->GetVisibleItem();
if (item && item->GetFaviconStatus().valid) {
return item->GetFaviconStatus();
}
return favicon_status_;
}
void ContentWebState::SetFaviconStatus(const FaviconStatus& favicon_status) {
favicon_status_ = favicon_status;
}
int ContentWebState::GetNavigationItemCount() const {
if (serialized_state_) {
return serialized_state_->GetNavigationItemCount();
}
return navigation_manager_->GetItemCount();
}
const GURL& ContentWebState::GetVisibleURL() const {
auto* item = navigation_manager_->GetVisibleItem();
return item ? item->GetURL() : GURL::EmptyGURL();
}
const GURL& ContentWebState::GetLastCommittedURL() const {
auto* item = navigation_manager_->GetLastCommittedItem();
return item ? item->GetURL() : GURL::EmptyGURL();
}
std::optional<GURL> ContentWebState::GetLastCommittedURLIfTrusted() const {
return GetLastCommittedURL();
}
WebFramesManager* ContentWebState::GetWebFramesManager(ContentWorld world) {
return managers_[world].get();
}
CRWWebViewProxyType ContentWebState::GetWebViewProxy() const {
return web_view_proxy_;
}
void ContentWebState::AddObserver(WebStateObserver* observer) {
observers_.AddObserver(observer);
}
void ContentWebState::RemoveObserver(WebStateObserver* observer) {
observers_.RemoveObserver(observer);
}
void ContentWebState::CloseWebState() {
if (delegate_) {
delegate_->CloseWebState(this);
}
}
bool ContentWebState::SetSessionStateData(NSData* data) {
return false;
}
NSData* ContentWebState::SessionStateData() {
return nil;
}
PermissionState ContentWebState::GetStateForPermission(
Permission permission) const {
return PermissionState();
}
void ContentWebState::SetStateForPermission(PermissionState state,
Permission permission) {}
NSDictionary<NSNumber*, NSNumber*>*
ContentWebState::GetStatesForAllPermissions() const {
return nil;
}
void ContentWebState::DownloadCurrentPage(
NSString* destination_file,
id<CRWWebViewDownloadDelegate> delegate,
void (^handler)(id<CRWWebViewDownload>)) {}
bool ContentWebState::IsFindInteractionSupported() {
return false;
}
bool ContentWebState::IsFindInteractionEnabled() {
return false;
}
void ContentWebState::SetFindInteractionEnabled(bool enabled) {}
id<CRWFindInteraction> ContentWebState::GetFindInteraction() {
return nil;
}
id ContentWebState::GetActivityItem() {
return nil;
}
UIColor* ContentWebState::GetThemeColor() {
auto color = web_contents_->GetThemeColor();
if (color) {
return skia::UIColorFromSkColor(*color);
}
return nil;
}
UIColor* ContentWebState::GetUnderPageBackgroundColor() {
auto color = web_contents_->GetBackgroundColor();
if (color) {
return skia::UIColorFromSkColor(*color);
}
return nil;
}
void ContentWebState::AddPolicyDecider(WebStatePolicyDecider* decider) {
policy_deciders_.AddObserver(decider);
}
void ContentWebState::RemovePolicyDecider(WebStatePolicyDecider* decider) {
policy_deciders_.RemoveObserver(decider);
}
void ContentWebState::DidChangeVisibleSecurityState() {
for (auto& observer : observers_) {
observer.DidChangeVisibleSecurityState(this);
}
}
bool ContentWebState::HasOpener() const {
return created_with_opener_;
}
void ContentWebState::SetHasOpener(bool has_opener) {
created_with_opener_ = has_opener;
}
bool ContentWebState::CanTakeSnapshot() const {
return false;
}
void ContentWebState::TakeSnapshot(const CGRect rect,
SnapshotCallback callback) {}
void ContentWebState::CreateFullPagePdf(base::OnceCallback<void(NSData*)>) {}
void ContentWebState::CloseMediaPresentations() {}
void ContentWebState::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInPrimaryMainFrame()) {
return;
}
auto* context =
ContentNavigationContext::GetOrCreate(navigation_handle, this);
for (auto& observer : observers_) {
observer.DidStartNavigation(this, context);
}
}
void ContentWebState::DidRedirectNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInPrimaryMainFrame()) {
return;
}
auto* context =
ContentNavigationContext::GetOrCreate(navigation_handle, this);
for (auto& observer : observers_) {
observer.DidRedirectNavigation(this, context);
}
}
void ContentWebState::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInPrimaryMainFrame()) {
return;
}
auto* context =
ContentNavigationContext::GetOrCreate(navigation_handle, this);
for (auto& observer : observers_) {
observer.DidFinishNavigation(this, context);
}
}
void ContentWebState::DidStartLoading() {
for (auto& observer : observers_) {
observer.DidStartLoading(this);
}
}
void ContentWebState::DidStopLoading() {
for (auto& observer : observers_) {
observer.DidStopLoading(this);
}
}
void ContentWebState::DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) {
if (!render_frame_host->IsInPrimaryMainFrame()) {
return;
}
for (auto& observer : observers_) {
observer.PageLoaded(this, web::PageLoadCompletionStatus::SUCCESS);
}
}
void ContentWebState::DidFailLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url,
int error_code) {
if (!render_frame_host->IsInPrimaryMainFrame()) {
return;
}
for (auto& observer : observers_) {
observer.PageLoaded(this, web::PageLoadCompletionStatus::FAILURE);
}
}
void ContentWebState::LoadProgressChanged(double progress) {
for (auto& observer : observers_) {
observer.LoadProgressChanged(this, progress);
}
}
void ContentWebState::OnVisibilityChanged(content::Visibility visibility) {
// Occlusion is not supported on iOS.
DCHECK_NE(visibility, content::Visibility::OCCLUDED);
if (visibility == content::Visibility::VISIBLE) {
WasShown();
} else {
WasHidden();
}
}
void ContentWebState::TitleWasSet(content::NavigationEntry* entry) {
for (auto& observer : observers_) {
observer.TitleWasSet(this);
}
}
void ContentWebState::DidUpdateFaviconURL(
content::RenderFrameHost* render_frame_host,
const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
if (!render_frame_host->IsInPrimaryMainFrame()) {
return;
}
std::vector<FaviconURL> favicon_urls;
for (const auto& c : candidates) {
FaviconURL favicon_url;
favicon_url.icon_url = c->icon_url;
favicon_url.icon_type = IconTypeFromContentIconType(c->icon_type);
favicon_url.icon_sizes = c->icon_sizes;
favicon_urls.push_back(favicon_url);
}
for (auto& observer : observers_) {
observer.FaviconUrlUpdated(this, favicon_urls);
}
}
void ContentWebState::RenderFrameCreated(
content::RenderFrameHost* render_frame_host) {
// TODO(crbug.com/40257932): handle WebFrames.
}
void ContentWebState::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
// TODO(crbug.com/40257932): handle WebFrames.
}
void ContentWebState::DocumentOnLoadCompletedInPrimaryMainFrame() {
for (auto& observer : observers_) {
observer.PageLoaded(this, web::PageLoadCompletionStatus::SUCCESS);
}
}
void ContentWebState::RenderFrameHostStateChanged(
content::RenderFrameHost* render_frame_host,
content::RenderFrameHost::LifecycleState old_state,
content::RenderFrameHost::LifecycleState new_state) {}
void ContentWebState::PrimaryMainFrameRenderProcessGone(
base::TerminationStatus status) {
for (auto& observer : observers_) {
observer.RenderProcessGone(this);
}
}
content::WebContents* ContentWebState::AddNewContents(
content::WebContents* source,
std::unique_ptr<content::WebContents> new_contents,
const GURL& target_url,
WindowOpenDisposition disposition,
const blink::mojom::WindowFeatures& window_features,
bool user_gesture,
bool* was_blocked) {
// TODO: Add a constructor that takes the new_contents.
child_web_contents_ = std::move(new_contents);
delegate_->CreateNewWebState(this, target_url, GetLastCommittedURL(),
user_gesture);
DCHECK(!child_web_contents_);
return nullptr;
}
int ContentWebState::GetTopControlsHeight() {
return ([web_view_ maxViewportInsets].top -
[web_view_ minViewportInsets].top) *
display::Screen::Get()
->GetDisplayNearestWindow(web_contents_->GetTopLevelNativeWindow())
.device_scale_factor();
}
int ContentWebState::GetTopControlsMinHeight() {
return 0;
}
int ContentWebState::GetBottomControlsHeight() {
return ([web_view_ maxViewportInsets].bottom -
[web_view_ minViewportInsets].bottom) *
display::Screen::Get()
->GetDisplayNearestWindow(web_contents_->GetTopLevelNativeWindow())
.device_scale_factor();
}
int ContentWebState::GetBottomControlsMinHeight() {
return 0;
}
bool ContentWebState::ShouldAnimateBrowserControlsHeightChanges() {
return true;
}
bool ContentWebState::DoBrowserControlsShrinkRendererSize(
content::WebContents* web_contents) {
// We want to remain consistent while scroll is in progress because
// we only resize the WebContents at the end of a gesture.
if (top_control_scroll_in_progress_) {
return cached_shrink_controls_;
}
UIScrollView* web_contents_view = base::apple::ObjCCastStrict<UIScrollView>(
web_contents->GetNativeView().Get());
if (web_contents_view.contentInset.top > [web_view_ minViewportInsets].top) {
return true;
}
return false;
}
int ContentWebState::GetVirtualKeyboardHeight(
content::WebContents* web_contents) {
return keyboard_height_;
}
bool ContentWebState::OnlyExpandTopControlsAtPageTop() {
return false;
}
void ContentWebState::SetTopControlsGestureScrollInProgress(bool in_progress) {
if (in_progress) {
cached_shrink_controls_ =
DoBrowserControlsShrinkRendererSize(web_contents_.get());
}
top_control_scroll_in_progress_ = in_progress;
}
// TODO(crbug.com/333624335): Consider moving notification observers to a
// browser-level observer.
void ContentWebState::RegisterNotificationObservers() {
base::RepeatingCallback<void(NSNotification * notification)>
keyboard_showing_closure = base::BindRepeating(
&ContentWebState::OnKeyboardShow, weak_factory_.GetWeakPtr());
base::RepeatingCallback<void(NSNotification * notification)>
keyboard_hiding_closure = base::BindRepeating(
&ContentWebState::OnKeyboardHide, weak_factory_.GetWeakPtr());
keyboard_showing_observer_ = [[NSNotificationCenter defaultCenter]
addObserverForName:UIKeyboardDidShowNotification
object:nil
queue:nil
usingBlock:base::CallbackToBlock(keyboard_showing_closure)];
keyboard_hiding_observer_ = [[NSNotificationCenter defaultCenter]
addObserverForName:UIKeyboardWillHideNotification
object:nil
queue:nil
usingBlock:base::CallbackToBlock(keyboard_hiding_closure)];
}
void ContentWebState::OnKeyboardShow(NSNotification* notification) {
NSDictionary* info = [notification userInfo];
CGFloat height =
[[info valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]
.size.height;
keyboard_height_ = static_cast<int>(height);
}
void ContentWebState::OnKeyboardHide(NSNotification* notification) {
keyboard_height_ = 0;
}
std::unique_ptr<content::ColorChooser> ContentWebState::OpenColorChooser(
content::WebContents* web_contents,
SkColor color,
const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions) {
return std::make_unique<web_contents_delegate_ios::ColorChooserIOS>(
web_contents, color, suggestions);
}
// TODO(crbug.com/40255112): Need to consider showing a context menu that
// contains 'Photo Library', 'Take Photo', and 'Choose File' sub menus as
// browsers based on WebKit.
void ContentWebState::RunFileChooser(
content::RenderFrameHost* render_frame_host,
scoped_refptr<content::FileSelectListener> listener,
const blink::mojom::FileChooserParams& params) {
web_contents_delegate_ios::FileSelectHelperIOS::RunFileChooser(
render_frame_host, listener, params);
}
content::JavaScriptDialogManager* ContentWebState::GetJavaScriptDialogManager(
content::WebContents* source) {
content::JavaScriptDialogManager* dialog =
javascript_dialogs::TabModalDialogManager::FromWebContents(source);
return dialog;
}
} // namespace web