blob: 4c442c4449a30cdab5ca455999c46a6d99bc59e5 [file] [log] [blame]
// Copyright 2019 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 "weblayer/browser/browser_controller_impl.h"
#include "base/auto_reset.h"
#include "base/logging.h"
#include "content/public/browser/interstitial_page.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/browser_controls_state.h"
#include "weblayer/browser/navigation_controller_impl.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/public/browser_observer.h"
#include "weblayer/public/download_delegate.h"
#include "weblayer/public/fullscreen_delegate.h"
#if !defined(OS_ANDROID)
#include "ui/views/controls/webview/webview.h"
#endif
#if defined(OS_ANDROID)
#include "base/android/callback_android.h"
#include "base/android/jni_string.h"
#include "base/json/json_writer.h"
#include "weblayer/browser/isolated_world_ids.h"
#include "weblayer/browser/java/jni/BrowserControllerImpl_jni.h"
#include "weblayer/browser/top_controls_container_view.h"
#endif
namespace weblayer {
namespace {
// Pointer value of this is used as a key in base::SupportsUserData for
// WebContents. Value of the key is an instance of |UserData|.
constexpr int kWebContentsUserDataKey = 0;
struct UserData : public base::SupportsUserData::Data {
BrowserControllerImpl* controller = nullptr;
};
#if defined(OS_ANDROID)
BrowserController* g_last_browser_controller;
void JavaScriptResultCallback(
const base::android::ScopedJavaGlobalRef<jobject>& callback,
base::Value result) {
std::string json;
base::JSONWriter::Write(result, &json);
base::android::RunStringCallbackAndroid(callback, json);
}
#endif
} // namespace
BrowserControllerImpl::BrowserControllerImpl(ProfileImpl* profile)
: profile_(profile) {
#if defined(OS_ANDROID)
g_last_browser_controller = this;
#endif
content::WebContents::CreateParams create_params(
profile_->GetBrowserContext());
web_contents_ = content::WebContents::Create(create_params);
std::unique_ptr<UserData> user_data = std::make_unique<UserData>();
user_data->controller = this;
web_contents_->SetUserData(&kWebContentsUserDataKey, std::move(user_data));
web_contents_->SetDelegate(this);
Observe(web_contents_.get());
navigation_controller_ = std::make_unique<NavigationControllerImpl>(this);
}
BrowserControllerImpl::~BrowserControllerImpl() {
// Destruct this now to avoid it calling back when this object is partially
// destructed.
web_contents_.reset();
}
// static
BrowserControllerImpl* BrowserControllerImpl::FromWebContents(
content::WebContents* web_contents) {
return reinterpret_cast<UserData*>(
web_contents->GetUserData(&kWebContentsUserDataKey))
->controller;
}
void BrowserControllerImpl::SetDownloadDelegate(DownloadDelegate* delegate) {
download_delegate_ = delegate;
}
void BrowserControllerImpl::SetFullscreenDelegate(
FullscreenDelegate* delegate) {
if (delegate == fullscreen_delegate_)
return;
const bool had_delegate = (fullscreen_delegate_ != nullptr);
const bool has_delegate = (delegate != nullptr);
// If currently fullscreen, and the delegate is being set to null, force an
// exit now (otherwise the delegate can't take us out of fullscreen).
if (is_fullscreen_ && fullscreen_delegate_ && had_delegate != has_delegate)
OnExitFullscreen();
fullscreen_delegate_ = delegate;
// Whether fullscreen is enabled depends upon whether there is a delegate. If
// having a delegate changed, then update the renderer (which is where
// fullscreen enabled is tracked).
content::RenderViewHost* host = web_contents_->GetRenderViewHost();
if (had_delegate != has_delegate && host)
host->OnWebkitPreferencesChanged();
}
void BrowserControllerImpl::AddObserver(BrowserObserver* observer) {
observers_.AddObserver(observer);
}
void BrowserControllerImpl::RemoveObserver(BrowserObserver* observer) {
observers_.RemoveObserver(observer);
}
NavigationController* BrowserControllerImpl::GetNavigationController() {
return navigation_controller_.get();
}
#if !defined(OS_ANDROID)
void BrowserControllerImpl::AttachToView(views::WebView* web_view) {
web_view->SetWebContents(web_contents_.get());
web_contents_->Focus();
}
#endif
#if defined(OS_ANDROID)
static jlong JNI_BrowserControllerImpl_CreateBrowserController(JNIEnv* env,
jlong profile) {
return reinterpret_cast<intptr_t>(
new BrowserControllerImpl(reinterpret_cast<ProfileImpl*>(profile)));
}
static void JNI_BrowserControllerImpl_DeleteBrowserController(
JNIEnv* env,
jlong browser_controller) {
delete reinterpret_cast<BrowserControllerImpl*>(browser_controller);
}
base::android::ScopedJavaLocalRef<jobject>
BrowserControllerImpl::GetWebContents(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj) {
return web_contents_->GetJavaWebContents();
}
void BrowserControllerImpl::SetTopControlsContainerView(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& caller,
jlong native_top_controls_container_view) {
top_controls_container_view_ = reinterpret_cast<TopControlsContainerView*>(
native_top_controls_container_view);
}
void BrowserControllerImpl::ExecuteScript(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& script,
const base::android::JavaParamRef<jobject>& callback) {
base::android::ScopedJavaGlobalRef<jobject> jcallback(env, callback);
web_contents_->GetMainFrame()->ExecuteJavaScriptInIsolatedWorld(
base::android::ConvertJavaStringToUTF16(script),
base::BindOnce(&JavaScriptResultCallback, jcallback),
ISOLATED_WORLD_ID_WEBLAYER);
}
#endif
void BrowserControllerImpl::LoadingStateChanged(content::WebContents* source,
bool to_different_document) {
bool is_loading = web_contents_->IsLoading();
for (auto& observer : observers_)
observer.LoadingStateChanged(is_loading, to_different_document);
}
void BrowserControllerImpl::LoadProgressChanged(content::WebContents* source,
double progress) {
for (auto& observer : observers_)
observer.LoadProgressChanged(progress);
}
void BrowserControllerImpl::DidNavigateMainFramePostCommit(
content::WebContents* web_contents) {
for (auto& observer : observers_)
observer.DisplayedUrlChanged(web_contents->GetVisibleURL());
}
int BrowserControllerImpl::GetTopControlsHeight() {
#if defined(OS_ANDROID)
return top_controls_container_view_->GetTopControlsHeight();
#else
return 0;
#endif
}
bool BrowserControllerImpl::DoBrowserControlsShrinkRendererSize(
const content::WebContents* web_contents) {
return true;
}
bool BrowserControllerImpl::EmbedsFullscreenWidget() {
return true;
}
void BrowserControllerImpl::EnterFullscreenModeForTab(
content::WebContents* web_contents,
const GURL& origin,
const blink::mojom::FullscreenOptions& options) {
// TODO: support |options|.
is_fullscreen_ = true;
auto exit_fullscreen_closure = base::BindOnce(
&BrowserControllerImpl::OnExitFullscreen, weak_ptr_factory_.GetWeakPtr());
base::AutoReset<bool> reset(&processing_enter_fullscreen_, true);
fullscreen_delegate_->EnterFullscreen(std::move(exit_fullscreen_closure));
}
void BrowserControllerImpl::ExitFullscreenModeForTab(
content::WebContents* web_contents) {
is_fullscreen_ = false;
fullscreen_delegate_->ExitFullscreen();
}
bool BrowserControllerImpl::IsFullscreenForTabOrPending(
const content::WebContents* web_contents) {
return is_fullscreen_;
}
blink::mojom::DisplayMode BrowserControllerImpl::GetDisplayMode(
const content::WebContents* web_contents) {
return is_fullscreen_ ? blink::mojom::DisplayMode::kFullscreen
: blink::mojom::DisplayMode::kBrowser;
}
void BrowserControllerImpl::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
#if defined(OS_ANDROID)
web_contents_->GetMainFrame()->UpdateBrowserControlsState(
content::BROWSER_CONTROLS_STATE_BOTH,
content::BROWSER_CONTROLS_STATE_SHOWN, false);
if (web_contents_->ShowingInterstitialPage()) {
web_contents_->GetInterstitialPage()
->GetMainFrame()
->UpdateBrowserControlsState(content::BROWSER_CONTROLS_STATE_SHOWN,
content::BROWSER_CONTROLS_STATE_SHOWN,
false);
}
#endif
}
void BrowserControllerImpl::OnExitFullscreen() {
// If |processing_enter_fullscreen_| is true, it means the callback is being
// called while processing EnterFullscreenModeForTab(). WebContents doesn't
// deal well with this. FATAL as Android generally doesn't run with DCHECKs.
LOG_IF(FATAL, !processing_enter_fullscreen_)
<< "exiting fullscreen while entering fullscreen is not supported";
web_contents_->ExitFullscreen(/* will_cause_resize */ false);
}
std::unique_ptr<BrowserController> BrowserController::Create(Profile* profile) {
return std::make_unique<BrowserControllerImpl>(
static_cast<ProfileImpl*>(profile));
}
#if defined(OS_ANDROID)
BrowserController* BrowserController::GetLastControllerForTesting() {
return g_last_browser_controller;
}
#endif
} // namespace weblayer