// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FUCHSIA_WEB_WEBENGINE_BROWSER_FRAME_IMPL_H_
#define FUCHSIA_WEB_WEBENGINE_BROWSER_FRAME_IMPL_H_
#include <fuchsia/logger/cpp/fidl.h>
#include <fuchsia/mem/cpp/fidl.h>
#include <fuchsia/web/cpp/fidl.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/inspect/cpp/vmo/types.h>
#include <lib/zx/channel.h>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/fuchsia/scoped_fx_logger.h"
#include "base/gtest_prod_util.h"
#include "base/logging.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/timer/timer.h"
#include "build/chromecast_buildflags.h"
#include "components/media_control/browser/media_blocker.h"
#include "components/on_load_script_injector/browser/on_load_script_injector_host.h"
#include "components/url_rewrite/browser/url_request_rewrite_rules_manager.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "fuchsia_web/webengine/browser/event_filter.h"
#include "fuchsia_web/webengine/browser/frame_permission_controller.h"
#include "fuchsia_web/webengine/browser/navigation_controller_impl.h"
#include "fuchsia_web/webengine/browser/theme_manager.h"
#include "fuchsia_web/webengine/web_engine_export.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "ui/accessibility/platform/fuchsia/accessibility_bridge_fuchsia_impl.h"
#include "ui/aura/window_tree_host.h"
#include "ui/platform_window/fuchsia/view_ref_pair.h"
#include "ui/wm/core/focus_controller.h"
#include "url/gurl.h"
namespace content {
class FromRenderFrameHost;
class ScopedAccessibilityMode;
} // namespace content
class ContextImpl;
class FrameWindowTreeHost;
class FrameLayoutManager;
class MediaPlayerImpl;
class NavigationPolicyHandler;
#if BUILDFLAG(ENABLE_CAST_RECEIVER)
class ReceiverSessionClient;
#endif
// Implementation of fuchsia.web.Frame based on content::WebContents.
class WEB_ENGINE_EXPORT FrameImpl : public fuchsia::web::Frame,
public content::WebContentsObserver,
public content::WebContentsDelegate {
public:
// Returns FrameImpl that owns the |web_contents| or nullptr if the
// |web_contents| is nullptr. Returns nullptr if there is no FrameImpl that
// owns the |web_contents|, which can happen if FrameImpl has not been
// initialized yet.
static FrameImpl* FromWebContents(content::WebContents* web_contents);
// Returns FrameImpl that owns the |render_frame_host| or nullptr if the
// |render_frame_host| is nullptr. Returns nullptr if there is no FrameImpl
// that owns the |web_contents|, which can happen if FrameImpl has not been
// initialized yet.
static FrameImpl* FromRenderFrameHost(
content::RenderFrameHost* render_frame_host);
// |context| must out-live |this|.
// |params| apply both to this Frame, and also to any popup Frames it creates
// and must be clonable.
// TODO(fxbug.dev/65750): Consider removing this restriction if clients become
// responsible for providing parameters for [each] popup and (cloning)
// |params_for_popups_| is no longer necessary.
// |inspect_node| will be populated with diagnostic data for this Frame.
// DestroyFrame() is automatically called on |context| if the |frame_request|
// channel disconnects.
FrameImpl(std::unique_ptr<content::WebContents> web_contents,
ContextImpl* context,
fuchsia::web::CreateFrameParams params,
inspect::Node inspect_node,
fidl::InterfaceRequest<fuchsia::web::Frame> frame_request);
~FrameImpl() override;
FrameImpl(const FrameImpl&) = delete;
FrameImpl& operator=(const FrameImpl&) = delete;
const fuchsia::web::FrameMediaSettings& media_settings() const {
return media_settings_;
}
FramePermissionController* permission_controller() {
return &permission_controller_;
}
url_rewrite::UrlRequestRewriteRulesManager*
url_request_rewrite_rules_manager() {
return &url_request_rewrite_rules_manager_;
}
NavigationPolicyHandler* navigation_policy_handler() {
return navigation_policy_handler_.get();
}
// Enables explicit sites filtering and set the error page. If |error_page| is
// empty, the default error page will be used.
void EnableExplicitSitesFilter(std::string error_page);
const std::optional<std::string>& explicit_sites_filter_error_page() const {
return explicit_sites_filter_error_page_;
}
// Override |blink_prefs| with settings defined in |content_settings_|.
//
// This method is called when WebPreferences is first created and when it is
// recomputed.
void OverrideWebPreferences(blink::web_pref::WebPreferences* web_prefs);
// Accessors required by tests.
zx::unowned_channel GetBindingChannelForTest() const;
content::WebContents* web_contents_for_test() const {
return web_contents_.get();
}
bool has_view_for_test() const { return window_tree_host_ != nullptr; }
FrameWindowTreeHost* window_tree_host_for_test() {
return window_tree_host_.get();
}
void set_window_size_for_test(gfx::Size size) {
window_size_for_test_ = size;
}
void set_device_scale_factor_for_test(float device_scale_factor) {
device_scale_factor_for_test_ = device_scale_factor;
}
private:
FRIEND_TEST_ALL_PREFIXES(FrameImplTest, DelayedNavigationEventAck);
FRIEND_TEST_ALL_PREFIXES(FrameImplTest, NavigationObserverDisconnected);
FRIEND_TEST_ALL_PREFIXES(FrameImplTest, NoNavigationObserverAttached);
FRIEND_TEST_ALL_PREFIXES(FrameImplTest, ReloadFrame);
FRIEND_TEST_ALL_PREFIXES(FrameImplTest, Stop);
FRIEND_TEST_ALL_PREFIXES(FuchsiaFrameAccessibilityTest, HitTest);
// Used for storing awaiting popup frames in |pending_popups_|
struct PendingPopup {
PendingPopup(FrameImpl* frame_ptr,
fidl::InterfaceHandle<fuchsia::web::Frame> handle,
fuchsia::web::PopupFrameCreationInfo creation_info);
PendingPopup(PendingPopup&& other);
~PendingPopup();
FrameImpl* frame_ptr;
fidl::InterfaceHandle<fuchsia::web::Frame> handle;
fuchsia::web::PopupFrameCreationInfo creation_info;
};
aura::Window* root_window() const;
// Shared implementation for the ExecuteJavaScript[NoResult]() APIs.
void ExecuteJavaScriptInternal(std::vector<std::string> origins,
fuchsia::mem::Buffer script,
ExecuteJavaScriptCallback callback,
bool need_result);
// Sends the next entry in |pending_popups_| to |popup_listener_|.
void MaybeSendPopup();
void OnPopupListenerDisconnected(zx_status_t status);
// Cleans up the MediaPlayerImpl on disconnect.
void OnMediaPlayerDisconnect();
// An error handler for |accessibility_bridge_|.
bool OnAccessibilityError(zx_status_t error);
// Creates and initializes WindowTreeHost for the view with the specified
// |view_token|. |view_token| may be uninitialized in headless mode.
void SetupWindowTreeHost(fuchsia::ui::views::ViewToken view_token,
ui::ViewRefPair view_ref_pair);
// Creates and initializes WindowTreeHost for the view with the specified
// |view_creation_token|. |view_creation_token| may be uninitialized in
// headless mode.
void SetupWindowTreeHost(
fuchsia::ui::views::ViewCreationToken view_creation_token,
ui::ViewRefPair view_ref_pair);
// Initializes WindowTreeHost.
void InitWindowTreeHost();
// Destroys the WindowTreeHost along with its view or other associated
// resources.
void DestroyWindowTreeHost();
// Destroys |this| and sends the FIDL |error| to the client.
void CloseAndDestroyFrame(zx_status_t error);
#if BUILDFLAG(ENABLE_CAST_RECEIVER)
// Determines whether |message| is a Cast Streaming message and if so, handles
// it. Returns whether it handled the message, regardless of whether that was
// successful. If true is returned, |callback| has been called. Returns false
// immediately if Cast Streaming support is not enabled. Called by
// PostMessage().
bool MaybeHandleCastStreamingMessage(std::string* origin,
fuchsia::web::WebMessage* message,
PostMessageCallback* callback);
void MaybeStartCastStreaming(content::NavigationHandle* navigation_handle);
#endif
// Updates zoom level for the specified |render_frame_host|.
void UpdateRenderFrameZoomLevel(content::RenderFrameHost* render_frame_host);
// Helper method for connecting to AccessibilityBridge on
// |accessibility_bridge_|.
void ConnectToAccessibilityBridge();
// Shared implementation of CreateView and CreateViewWithViewRef.
void CreateViewImpl(fuchsia::ui::views::ViewToken view_token,
fuchsia::ui::views::ViewRefControl control_ref,
fuchsia::ui::views::ViewRef view_ref);
// fuchsia::web::Frame implementation.
void CreateView(fuchsia::ui::views::ViewToken view_token) override;
void CreateViewWithViewRef(fuchsia::ui::views::ViewToken view_token,
fuchsia::ui::views::ViewRefControl control_ref,
fuchsia::ui::views::ViewRef view_ref) override;
void CreateView2(fuchsia::web::CreateView2Args view_args) override;
void GetMediaPlayer(fidl::InterfaceRequest<fuchsia::media::sessions2::Player>
player) override;
void GetNavigationController(
fidl::InterfaceRequest<fuchsia::web::NavigationController> controller)
override;
void ExecuteJavaScript(std::vector<std::string> origins,
fuchsia::mem::Buffer script,
ExecuteJavaScriptCallback callback) override;
void ExecuteJavaScriptNoResult(
std::vector<std::string> origins,
fuchsia::mem::Buffer script,
ExecuteJavaScriptNoResultCallback callback) override;
void AddBeforeLoadJavaScript(
uint64_t id,
std::vector<std::string> origins,
fuchsia::mem::Buffer script,
AddBeforeLoadJavaScriptCallback callback) override;
void RemoveBeforeLoadJavaScript(uint64_t id) override;
void PostMessage(std::string origin,
fuchsia::web::WebMessage message,
fuchsia::web::Frame::PostMessageCallback callback) override;
void SetNavigationEventListener(
fidl::InterfaceHandle<fuchsia::web::NavigationEventListener> listener)
override;
void SetNavigationEventListener2(
fidl::InterfaceHandle<fuchsia::web::NavigationEventListener> listener,
fuchsia::web::NavigationEventListenerFlags flags) override;
void SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel level) override;
void SetConsoleLogSink(fuchsia::logger::LogSinkHandle sink) override;
void ConfigureInputTypes(fuchsia::web::InputTypes types,
fuchsia::web::AllowInputState allow) override;
void SetPopupFrameCreationListener(
fidl::InterfaceHandle<fuchsia::web::PopupFrameCreationListener> listener)
override;
void SetUrlRequestRewriteRules(
std::vector<fuchsia::web::UrlRequestRewriteRule> rules,
SetUrlRequestRewriteRulesCallback callback) override;
void EnableHeadlessRendering() override;
void DisableHeadlessRendering() override;
void SetMediaSettings(
fuchsia::web::FrameMediaSettings media_settings) override;
void ForceContentDimensions(
std::unique_ptr<fuchsia::ui::gfx::vec2> web_dips) override;
void SetPermissionState(fuchsia::web::PermissionDescriptor permission,
std::string web_origin,
fuchsia::web::PermissionState state) override;
void SetBlockMediaLoading(bool blocked) override;
void GetPrivateMemorySize(GetPrivateMemorySizeCallback callback) override;
void SetNavigationPolicyProvider(
fuchsia::web::NavigationPolicyProviderParams params,
fidl::InterfaceHandle<fuchsia::web::NavigationPolicyProvider> provider)
override;
void SetContentAreaSettings(
fuchsia::web::ContentAreaSettings settings) override;
void ResetContentAreaSettings() override;
void Close(fuchsia::web::FrameCloseRequest request) override;
// content::WebContentsDelegate implementation.
void CloseContents(content::WebContents* source) override;
bool DidAddMessageToConsole(content::WebContents* source,
blink::mojom::ConsoleMessageLevel log_level,
const std::u16string& message,
int32_t line_no,
const std::u16string& source_id) override;
bool IsWebContentsCreationOverridden(
content::SiteInstance* source_site_instance,
content::mojom::WindowContainerType window_container_type,
const GURL& opener_url,
const std::string& frame_name,
const GURL& target_url) override;
void WebContentsCreated(content::WebContents* source_contents,
int opener_render_process_id,
int opener_render_frame_id,
const std::string& frame_name,
const GURL& target_url,
content::WebContents* new_contents) override;
content::WebContents* 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) override;
void RequestMediaAccessPermission(
content::WebContents* web_contents,
const content::MediaStreamRequest& request,
content::MediaResponseCallback callback) override;
bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
const url::Origin& security_origin,
blink::mojom::MediaStreamType type) override;
std::unique_ptr<content::AudioStreamBrokerFactory>
CreateAudioStreamBrokerFactory(content::WebContents* web_contents) override;
bool CanOverscrollContent() override;
// content::WebContentsObserver implementation.
void ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) override;
void DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) override;
void RenderFrameCreated(content::RenderFrameHost* frame_host) override;
void RenderFrameHostChanged(content::RenderFrameHost* old_host,
content::RenderFrameHost* new_host) override;
void DidFirstVisuallyNonEmptyPaint() override;
void ResourceLoadComplete(
content::RenderFrameHost* render_frame_host,
const content::GlobalRequestID& request_id,
const blink::mojom::ResourceLoadInfo& resource_load_info) override;
void MediaStartedPlaying(const MediaPlayerInfo& video_type,
const content::MediaPlayerId& id) override;
void MediaStoppedPlaying(
const MediaPlayerInfo& video_type,
const content::MediaPlayerId& id,
WebContentsObserver::MediaStoppedReason reason) override;
// Notified whenever the pixel scale of the `Frame`'s `View` changes.
void OnPixelScaleUpdate(float pixel_scale);
// Called by the `accessibility_bridge_` in response to changes in the
// system's "semantics mode" setting.
void SetAccessibilityEnabled(bool enabled);
// Called by `theme_manager_` if it is unable to determine the system theme.
void OnThemeManagerError();
const std::unique_ptr<content::WebContents> web_contents_;
ContextImpl* const context_;
// Optional tag to apply when emitting web console logs.
const std::string console_log_tag_;
// Logger used for console messages from content, depending on |log_level_|.
base::ScopedFxLogger console_logger_;
logging::LogSeverity log_level_ = logging::LOGGING_NUM_SEVERITIES;
// Parameters applied to popups created by content running in this Frame.
const fuchsia::web::CreateFrameParams params_for_popups_;
base::RepeatingCallback<void(fuchsia::media::AudioRenderUsage output_usage)>
set_audio_output_usage_callback_;
std::unique_ptr<FrameWindowTreeHost> window_tree_host_;
std::unique_ptr<wm::FocusController> focus_controller_;
// Owned via |window_tree_host_|.
FrameLayoutManager* layout_manager_ = nullptr;
std::unique_ptr<ui::AccessibilityBridgeFuchsiaImpl> accessibility_bridge_;
// Test settings.
std::optional<gfx::Size> window_size_for_test_;
std::optional<float> device_scale_factor_for_test_;
EventFilter event_filter_;
NavigationControllerImpl navigation_controller_;
url_rewrite::UrlRequestRewriteRulesManager url_request_rewrite_rules_manager_;
FramePermissionController permission_controller_;
std::unique_ptr<NavigationPolicyHandler> navigation_policy_handler_;
fuchsia::web::FrameMediaSettings media_settings_;
// Stored settings for web contents in the current Frame.
fuchsia::web::ContentAreaSettings content_area_settings_;
// Used for receiving and dispatching popup created by this Frame.
fuchsia::web::PopupFrameCreationListenerPtr popup_listener_;
std::list<PendingPopup> pending_popups_;
bool popup_ack_outstanding_ = false;
gfx::Size render_size_override_;
std::unique_ptr<MediaPlayerImpl> media_player_;
on_load_script_injector::OnLoadScriptInjectorHost<uint64_t> script_injector_;
fidl::Binding<fuchsia::web::Frame> binding_;
media_control::MediaBlocker media_blocker_;
ThemeManager theme_manager_;
// The error page to be displayed when a navigation to an explicit site is
// filtered. Explicit sites are filtered if it has a value. If set to the
// empty string, the default error page will be displayed.
std::optional<std::string> explicit_sites_filter_error_page_;
// Used to publish Frame details to Inspect.
inspect::Node inspect_node_;
const inspect::StringProperty inspect_name_property_;
#if BUILDFLAG(ENABLE_CAST_RECEIVER)
std::unique_ptr<ReceiverSessionClient> receiver_session_client_;
#endif
// Used to implement graceful `Close()` with `timeout` specified.
base::OneShotTimer close_page_timeout_;
std::unique_ptr<content::ScopedAccessibilityMode> scoped_accessibility_mode_;
base::WeakPtrFactory<FrameImpl> weak_factory_{this};
};
#endif // FUCHSIA_WEB_WEBENGINE_BROWSER_FRAME_IMPL_H_