| // Copyright 2020 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 "chromecast/browser/webview/cast_content_window_embedded.h" | 
 |  | 
 | #include <string> | 
 |  | 
 | #include "base/check.h" | 
 | #include "base/json/json_reader.h" | 
 | #include "base/json/json_writer.h" | 
 | #include "base/logging.h" | 
 | #include "base/values.h" | 
 | #include "chromecast/browser/cast_web_contents.h" | 
 | #include "chromecast/graphics/cast_window_manager.h" | 
 | #include "content/public/browser/web_contents.h" | 
 | #include "ui/aura/window.h" | 
 |  | 
 | namespace chromecast { | 
 |  | 
 | namespace { | 
 | constexpr char kKeyAppId[] = "appId"; | 
 | constexpr char kKeyAppSessionId[] = "appSessionId"; | 
 | constexpr char kKeyRemoteControlModeEnabled[] = "remoteControlModeEnabled"; | 
 | }  // namespace | 
 |  | 
 | CastContentWindowEmbedded::CastContentWindowEmbedded( | 
 |     const CastContentWindow::CreateParams& params, | 
 |     CastWindowEmbedder* cast_window_embedder, | 
 |     bool force_720p_resolution) | 
 |     : CastContentWindow(params), | 
 |       is_touch_enabled_(params.enable_touch_input), | 
 |       cast_window_embedder_(cast_window_embedder), | 
 |       force_720p_resolution_(force_720p_resolution) { | 
 |   DCHECK(delegate_); | 
 |   DCHECK(cast_window_embedder_); | 
 |  | 
 |   cast_window_embedder_->AddEmbeddedWindow(this); | 
 |   window_id_ = cast_window_embedder_->GenerateWindowId(); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::SendWindowRequest( | 
 |     CastWindowEmbedder::WindowRequestType request_type) { | 
 |   cast_window_embedder_->OnWindowRequest(request_type, | 
 |                                          PopulateCastWindowProperties()); | 
 | } | 
 |  | 
 | CastContentWindowEmbedded::~CastContentWindowEmbedded() { | 
 |   if (window_) { | 
 |     window_->RemoveObserver(this); | 
 |     window_ = nullptr; | 
 |   } | 
 |   if (cast_window_embedder_) { | 
 |     cast_window_embedder_->RemoveEmbeddedWindow(this); | 
 |   } | 
 |  | 
 |   SendWindowRequest(CastWindowEmbedder::WindowRequestType::CLOSE_WINDOW); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::CreateWindowForWebContents( | 
 |     CastWebContents* cast_web_contents, | 
 |     ::chromecast::mojom::ZOrder z_order, | 
 |     VisibilityPriority visibility_priority) { | 
 |   if (!cast_web_contents) { | 
 |     LOG(ERROR) << "cast_web_contents is null"; | 
 |     return; | 
 |   } | 
 |   cast_web_contents_ = cast_web_contents; | 
 |   Observe(cast_web_contents_->web_contents()); | 
 |   window_ = cast_web_contents_->web_contents()->GetNativeView(); | 
 |   visibility_priority_ = visibility_priority; | 
 |   if (!window_->HasObserver(this)) { | 
 |     window_->AddObserver(this); | 
 |   } | 
 |   if (!cast_web_contents_->web_contents()->IsLoading()) { | 
 |     MaybeSendOpenWindowRequest(); | 
 |   } | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::GrantScreenAccess() { | 
 |   has_screen_access_ = true; | 
 |   if (!open_window_sent_ && window_) | 
 |     MaybeSendOpenWindowRequest(); | 
 |   else if (open_window_sent_) | 
 |     RequestFocus(); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::RevokeScreenAccess() { | 
 |   has_screen_access_ = false; | 
 |   ReleaseFocus(); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::EnableTouchInput(bool enabled) {} | 
 |  | 
 | void CastContentWindowEmbedded::RequestVisibility( | 
 |     VisibilityPriority visibility_priority) { | 
 |   visibility_priority_ = visibility_priority; | 
 |  | 
 |   // Since STICKY is sent to the embedder window manager when the app requests | 
 |   // HIDDEN, the app must be removed from focus before new visibility properties | 
 |   // are sent. | 
 |   if (visibility_priority == VisibilityPriority::HIDDEN || | 
 |       visibility_priority == VisibilityPriority::HIDDEN_STICKY) { | 
 |     ReleaseFocus(); | 
 |     SendCastWindowProperties(); | 
 |   } else { | 
 |     SendCastWindowProperties(); | 
 |     RequestFocus(); | 
 |   } | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::SetActivityContext( | 
 |     base::Value activity_context) { | 
 |   activity_context_ = activity_context.Clone(); | 
 |  | 
 |   auto* found_app_id = activity_context.FindKey(kKeyAppId); | 
 |   if (found_app_id) { | 
 |     app_id_ = found_app_id->GetString(); | 
 |   } else { | 
 |     LOG(ERROR) << "App ID not found"; | 
 |   } | 
 |  | 
 |   auto* found_remote_control = | 
 |       activity_context.FindKey(kKeyRemoteControlModeEnabled); | 
 |   if (found_remote_control) { | 
 |     is_remote_control_ = found_remote_control->GetBool(); | 
 |   } else { | 
 |     LOG(ERROR) << "Is remote control not found"; | 
 |   } | 
 |  | 
 |   auto* found_session_id = activity_context.FindKey(kKeyAppSessionId); | 
 |   if (found_session_id) { | 
 |     session_id_ = found_session_id->GetString(); | 
 |   } else { | 
 |     LOG(ERROR) << "Session ID not found"; | 
 |   } | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::SetHostContext(base::Value host_context) { | 
 |   host_context_ = host_context.Clone(); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::NotifyVisibilityChange( | 
 |     VisibilityType visibility_type) { | 
 |   if (delegate_) { | 
 |     delegate_->OnVisibilityChange(visibility_type); | 
 |   } | 
 |   for (auto& observer : observer_list_) { | 
 |     observer.OnVisibilityChange(visibility_type); | 
 |   } | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::RequestMoveOut() {} | 
 |  | 
 | void CastContentWindowEmbedded::OnWindowDestroyed(aura::Window* window) { | 
 |   window_ = nullptr; | 
 |   SendWindowRequest(CastWindowEmbedder::WindowRequestType::CLOSE_WINDOW); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::OnEmbedderWindowEvent( | 
 |     const CastWindowEmbedder::EmbedderWindowEvent& request) { | 
 |   if (window_id_ != request.window_id) | 
 |     return; | 
 |  | 
 |   if (request.navigation && request.navigation.value() == | 
 |                                 CastWindowEmbedder::NavigationType::GO_BACK) { | 
 |     if (delegate_ && delegate_->CanHandleGesture(GestureType::GO_BACK)) { | 
 |       delegate_->ConsumeGesture( | 
 |           GestureType::GO_BACK, | 
 |           base::BindOnce(&CastContentWindowEmbedded::ConsumeGestureCompleted, | 
 |                          base::Unretained(this))); | 
 |     } else { | 
 |       cast_window_embedder_->GenerateAndSendNavigationHandleResult( | 
 |           window_id_, session_id_, false /* handled  */, | 
 |           CastWindowEmbedder::NavigationType::GO_BACK); | 
 |     } | 
 |     return; | 
 |   } | 
 |  | 
 |   if (request.visibility_changed) { | 
 |     switch (request.visibility_changed.value()) { | 
 |       case CastWindowEmbedder::VisibilityChange::UNKNOWN: | 
 |         NotifyVisibilityChange(VisibilityType::UNKNOWN); | 
 |         break; | 
 |       case CastWindowEmbedder::VisibilityChange::NOT_VISIBLE: | 
 |         NotifyVisibilityChange(VisibilityType::HIDDEN); | 
 |         break; | 
 |       case CastWindowEmbedder::VisibilityChange::FULL_SCREEN: | 
 |         NotifyVisibilityChange(VisibilityType::FULL_SCREEN); | 
 |         break; | 
 |       case CastWindowEmbedder::VisibilityChange::OBSCURED: | 
 |         NotifyVisibilityChange(VisibilityType::TRANSIENTLY_HIDDEN); | 
 |         break; | 
 |       case CastWindowEmbedder::VisibilityChange::INTERRUPTION: | 
 |         NotifyVisibilityChange(VisibilityType::PARTIAL_OUT); | 
 |         break; | 
 |       case CastWindowEmbedder::VisibilityChange::INTERRUPTED: | 
 |         NotifyVisibilityChange(VisibilityType::FULL_SCREEN); | 
 |         break; | 
 |     } | 
 |     return; | 
 |   } | 
 |  | 
 |   if (request.back_gesture_progress_event) { | 
 |     if (delegate_ && delegate_->CanHandleGesture(GestureType::GO_BACK)) | 
 |       delegate_->GestureProgress( | 
 |           GestureType::GO_BACK, | 
 |           gfx::Point(request.back_gesture_progress_event.value().x, | 
 |                      request.back_gesture_progress_event.value().y)); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (request.back_gesture_cancel_event) { | 
 |     if (delegate_ && delegate_->CanHandleGesture(GestureType::GO_BACK)) | 
 |       delegate_->CancelGesture(GestureType::GO_BACK); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::ConsumeGestureCompleted(bool handled) { | 
 |   cast_window_embedder_->GenerateAndSendNavigationHandleResult( | 
 |       window_id_, session_id_, handled, | 
 |       CastWindowEmbedder::NavigationType::GO_BACK); | 
 | } | 
 |  | 
 | int CastContentWindowEmbedded::GetWindowId() { | 
 |   return window_id_; | 
 | } | 
 |  | 
 | std::string CastContentWindowEmbedded::GetAppId() { | 
 |   return app_id_; | 
 | } | 
 |  | 
 | content::WebContents* CastContentWindowEmbedded::GetWebContents() { | 
 |   DCHECK(cast_web_contents_); | 
 |   return cast_web_contents_->web_contents(); | 
 | } | 
 |  | 
 | CastWebContents* CastContentWindowEmbedded::GetCastWebContents() { | 
 |   return cast_web_contents_; | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::DispatchState() { | 
 |   SendOpenWindowRequest(); | 
 |   RequestFocus(); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::SendAppContext(const std::string& context) { | 
 |   auto cast_window_properties = PopulateCastWindowProperties(); | 
 |   cast_window_properties.app_context = context; | 
 |   cast_window_embedder_->OnWindowRequest( | 
 |       CastWindowEmbedder::WindowRequestType::SET_PROPERTIES, | 
 |       cast_window_properties); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::Stop() { | 
 |   if (cast_web_contents_) | 
 |     cast_web_contents_->Stop(net::ERR_FAILED); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::SetCanGoBack(bool can_go_back) { | 
 |   can_go_back_ = can_go_back; | 
 |   if (open_window_sent_) | 
 |     SendCastWindowProperties(); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::RegisterBackGestureRouter( | 
 |     ::chromecast::BackGestureRouter* back_gesture_router) { | 
 |   back_gesture_router->SetBackGestureDelegate(this); | 
 | } | 
 |  | 
 | CastWindowEmbedder::CastWindowProperties | 
 | CastContentWindowEmbedded::PopulateCastWindowProperties() { | 
 |   CastWindowEmbedder::CastWindowProperties window_properties; | 
 |   window_properties.window_id = window_id_; | 
 |   window_properties.session_id = session_id_; | 
 |   window_properties.app_id = app_id_; | 
 |   window_properties.is_system_setup_window = false; | 
 |   window_properties.is_touch_enabled = is_touch_enabled_; | 
 |   window_properties.is_remote_control = is_remote_control_; | 
 |   window_properties.visibility_priority = visibility_priority_; | 
 |   window_properties.force_720p_resolution = force_720p_resolution_; | 
 |   window_properties.supports_go_back_inside = can_go_back_; | 
 |   window_properties.host_context = host_context_.Clone(); | 
 |   return window_properties; | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::ReleaseFocus() { | 
 |   if (!window_) { | 
 |     LOG(WARNING) << "window_ is null"; | 
 |     return; | 
 |   } | 
 |   SendWindowRequest(CastWindowEmbedder::WindowRequestType::RELEASE_FOCUS); | 
 |  | 
 |   // Because rendering a larger window may require more system resources, | 
 |   // resize the window to one pixel while hidden. | 
 |   LOG(INFO) << "Resizing window to 1x1 pixel while hidden"; | 
 |   window_->SetBounds(gfx::Rect(1, 1)); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::RequestFocus() { | 
 |   if (!has_screen_access_) | 
 |     return; | 
 |  | 
 |   if (!window_) { | 
 |     LOG(WARNING) << "window_ is null"; | 
 |     return; | 
 |   } | 
 |  | 
 |   SendWindowRequest(CastWindowEmbedder::WindowRequestType::REQUEST_FOCUS); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::MaybeSendOpenWindowRequest() { | 
 |   if (open_window_sent_ || !has_screen_access_) | 
 |     return; | 
 |  | 
 |   SendOpenWindowRequest(); | 
 |   RequestFocus(); | 
 |   open_window_sent_ = true; | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::SendCastWindowProperties() { | 
 |   SendWindowRequest(CastWindowEmbedder::WindowRequestType::SET_PROPERTIES); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::SendOpenWindowRequest() { | 
 |   SendWindowRequest(CastWindowEmbedder::WindowRequestType::OPEN_WINDOW); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::DidFinishLoad( | 
 |     content::RenderFrameHost* render_frame_host, | 
 |     const GURL& validated_url) { | 
 |   MaybeSendOpenWindowRequest(); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::DidFirstVisuallyNonEmptyPaint() { | 
 |   MaybeSendOpenWindowRequest(); | 
 | } | 
 |  | 
 | void CastContentWindowEmbedded::WebContentsDestroyed() { | 
 |   cast_web_contents_ = nullptr; | 
 | } | 
 |  | 
 | }  // namespace chromecast |