// 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 "content/public/browser/file_select_listener.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/find_in_page/java_script_find_in_page_manager_impl.h"
#import "ios/web/public/favicon/favicon_url.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/navigation/web_state_policy_decider.h"
#import "ios/web/public/session/crw_navigation_item_storage.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/storage.pb.h"
#import "ios/web/public/web_state_delegate.h"
#import "ios/web/public/web_state_observer.h"
#import "ios/web/text_fragments/text_fragments_manager_impl.h"
#import "ios/web/web_view/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_IN_MIGRATION();
return FaviconURL::IconType::kInvalid;
}
// Creates a CRWSessionStorage instance from protobuf message.
// TODO(crbug.com/40245950): remove when ContentWebState supports serialization
// using protobuf message format directly.
CRWSessionStorage* CreateSessionStorage(
WebStateID unique_identifier,
proto::WebStateMetadataStorage metadata,
WebState::WebStateStorageLoader storage_loader) {
// Load the data from disk as this is needed to create the CRWSessionStorage.
proto::WebStateStorage storage = std::move(storage_loader).Run();
*storage.mutable_metadata() = std::move(metadata);
return [[CRWSessionStorage alloc] initWithProto:storage
uniqueIdentifier:unique_identifier
stableIdentifier:[[NSUUID UUID] UUIDString]];
}
} // namespace
ContentWebState::ContentWebState(const CreateParams& params)
: ContentWebState(params, nil, base::ReturnValueOnce<NSData*>(nil)) {}
ContentWebState::ContentWebState(const CreateParams& params,
CRWSessionStorage* session_storage,
NativeSessionFetcher session_fetcher)
: unique_identifier_(session_storage ? session_storage.uniqueIdentifier
: WebStateID::NewUnique()) {
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());
web_frames_manager_ = 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];
// These should be moved when the are removed from CRWWebController.
web::JavaScriptFindInPageManagerImpl::CreateForWebState(this);
web::TextFragmentsManagerImpl::CreateForWebState(this);
session_storage_ = session_storage;
if (session_storage) {
UUID_ = [session_storage.stableIdentifier copy];
} else {
UUID_ = [[[NSUUID UUID] UUIDString] copy];
}
creation_time_ = base::Time::Now();
last_active_time_ = params.last_active_time.value_or(creation_time_);
RegisterNotificationObservers();
}
ContentWebState::ContentWebState(BrowserState* browser_state,
WebStateID unique_identifier,
proto::WebStateMetadataStorage metadata,
WebStateStorageLoader storage_loader,
NativeSessionFetcher session_fetcher)
: ContentWebState(CreateParams(browser_state),
CreateSessionStorage(unique_identifier,
std::move(metadata),
std::move(storage_loader)),
base::ReturnValueOnce<NSData*>(nil)) {}
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 {
// TODO(crbug.com/40245950): implement directly instead of serialising to
// CRWSessionStorage and then converting to protobuf message format.
DCHECK(IsRealized());
CRWSessionStorage* session_storage = BuildSessionStorage();
storage.set_has_opener(created_with_opener_);
[session_storage serializeToProto:storage];
}
void ContentWebState::SerializeMetadataToProto(
proto::WebStateMetadataStorage& storage) const {
CRWSessionStorage* session_storage = BuildSessionStorage();
[session_storage serializeMetadataToProto:storage];
}
WebStateDelegate* ContentWebState::GetDelegate() {
return delegate_;
}
std::unique_ptr<WebState> ContentWebState::Clone() const {
CreateParams params(GetBrowserState());
params.last_active_time = base::Time::Now();
CRWSessionStorage* session_storage = BuildSessionStorage();
session_storage.stableIdentifier = [[NSUUID UUID] UUIDString];
session_storage.uniqueIdentifier = WebStateID::NewUnique();
auto clone = std::make_unique<ContentWebState>(
params, session_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 session_storage_ == nil;
}
WebState* ContentWebState::ForceRealized() {
if (session_storage_) {
ExtractContentSessionStorage(this, web_contents_->GetController(),
GetBrowserState(), session_storage_);
session_storage_ = nil;
for (auto& observer : observers_) {
observer.WebStateRealized(this);
}
}
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 web_frames_manager_.get();
}
const SessionCertificatePolicyCache*
ContentWebState::GetSessionCertificatePolicyCache() const {
return certificate_policy_cache_.get();
}
SessionCertificatePolicyCache*
ContentWebState::GetSessionCertificatePolicyCache() {
return certificate_policy_cache_.get();
}
CRWSessionStorage* ContentWebState::BuildSessionStorage() const {
if (session_storage_) {
return session_storage_;
}
return BuildContentSessionStorage(this, navigation_manager_.get());
}
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),
{});
}
NSString* ContentWebState::GetStableIdentifier() const {
return UUID_;
}
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 (session_storage_) {
const NSUInteger index = session_storage_.lastCommittedItemIndex;
if (index > 0u && index <= session_storage_.itemStorages.count) {
return session_storage_.itemStorages[index].title;
}
}
return web_contents_->GetTitle();
}
bool ContentWebState::IsLoading() const {
return session_storage_ ? false : web_contents_->IsLoading();
}
double ContentWebState::GetLoadingProgress() const {
return session_storage_ ? 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 (session_storage_) {
return session_storage_.itemStorages.count;
}
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 web_frames_manager_.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::GetScreen()
->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::GetScreen()
->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);
}
} // namespace web