// 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/navigation_impl.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents.h" #include "net/base/net_errors.h" #include "net/http/http_util.h" #include "third_party/blink/public/common/user_agent/user_agent_metadata.h" #include "third_party/blink/public/mojom/referrer.mojom.h" #if defined(OS_ANDROID) #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "weblayer/browser/java/jni/NavigationImpl_jni.h" #endif #if defined(OS_ANDROID) using base::android::AttachCurrentThread; using base::android::ScopedJavaLocalRef; #endif namespace weblayer { NavigationImpl::NavigationImpl(content::NavigationHandle* navigation_handle) : navigation_handle_(navigation_handle) {} NavigationImpl::~NavigationImpl() { #if defined(OS_ANDROID) if (java_navigation_) { Java_NavigationImpl_onNativeDestroyed(AttachCurrentThread(), java_navigation_); } #endif } void NavigationImpl::SetParamsToLoadWhenSafe( std::unique_ptr params) { scheduled_load_params_ = std::move(params); } std::unique_ptr NavigationImpl::TakeParamsToLoadWhenSafe() { return std::move(scheduled_load_params_); } #if defined(OS_ANDROID) void NavigationImpl::SetJavaNavigation( JNIEnv* env, const base::android::JavaParamRef& java_navigation) { java_navigation_ = java_navigation; } ScopedJavaLocalRef NavigationImpl::GetUri(JNIEnv* env) { return ScopedJavaLocalRef( base::android::ConvertUTF8ToJavaString(env, GetURL().spec())); } ScopedJavaLocalRef NavigationImpl::GetRedirectChain(JNIEnv* env) { std::vector jni_redirects; for (const GURL& redirect : GetRedirectChain()) jni_redirects.push_back(redirect.spec()); return base::android::ToJavaArrayOfStrings(env, jni_redirects); } jboolean NavigationImpl::SetRequestHeader( JNIEnv* env, const base::android::JavaParamRef& name, const base::android::JavaParamRef& value) { if (!safe_to_set_request_headers_) return false; SetRequestHeader(ConvertJavaStringToUTF8(name), ConvertJavaStringToUTF8(value)); return true; } jboolean NavigationImpl::SetUserAgentString( JNIEnv* env, const base::android::JavaParamRef& value) { if (!safe_to_set_user_agent_) return false; SetUserAgentString(ConvertJavaStringToUTF8(value)); return true; } #endif GURL NavigationImpl::GetURL() { return navigation_handle_->GetURL(); } const std::vector& NavigationImpl::GetRedirectChain() { return navigation_handle_->GetRedirectChain(); } NavigationState NavigationImpl::GetState() { if (navigation_handle_->IsErrorPage() || navigation_handle_->IsDownload()) return NavigationState::kFailed; if (navigation_handle_->HasCommitted()) return NavigationState::kComplete; if (navigation_handle_->GetResponseHeaders()) return NavigationState::kReceivingBytes; return NavigationState::kWaitingResponse; } int NavigationImpl::GetHttpStatusCode() { auto* response_headers = navigation_handle_->GetResponseHeaders(); return response_headers ? response_headers->response_code() : 0; } bool NavigationImpl::IsSameDocument() { return navigation_handle_->IsSameDocument(); } bool NavigationImpl::IsErrorPage() { return navigation_handle_->IsErrorPage(); } bool NavigationImpl::IsDownload() { return navigation_handle_->IsDownload(); } bool NavigationImpl::WasStopCalled() { return was_stopped_; } Navigation::LoadError NavigationImpl::GetLoadError() { auto error_code = navigation_handle_->GetNetErrorCode(); if (auto* response_headers = navigation_handle_->GetResponseHeaders()) { auto response_code = response_headers->response_code(); if (response_code >= 400 && response_code < 500) return kHttpClientError; if (response_code >= 500 && response_code < 600) return kHttpServerError; } if (error_code == net::OK) return kNoError; if (net::IsCertificateError(error_code)) return kSSLError; if (error_code <= -100 && error_code > -200) return kConnectivityError; return kOtherError; } void NavigationImpl::SetRequestHeader(const std::string& name, const std::string& value) { if (base::ToLowerASCII(name) == "referer") { // The referrer needs to be special cased as content maintains it // separately. auto referrer = blink::mojom::Referrer::New(); referrer->url = GURL(value); referrer->policy = network::mojom::ReferrerPolicy::kDefault; navigation_handle_->SetReferrer(std::move(referrer)); } else { // Any headers coming from the client should be exempt from CORS checks. navigation_handle_->SetCorsExemptRequestHeader(name, value); } } void NavigationImpl::SetUserAgentString(const std::string& value) { DCHECK(safe_to_set_user_agent_); navigation_handle_->GetWebContents()->SetUserAgentOverride( blink::UserAgentOverride::UserAgentOnly(value), /* override_in_new_tabs */ false); navigation_handle_->SetIsOverridingUserAgent(!value.empty()); } #if defined(OS_ANDROID) static jboolean JNI_NavigationImpl_IsValidRequestHeaderName( JNIEnv* env, const base::android::JavaParamRef& name) { return net::HttpUtil::IsValidHeaderName(ConvertJavaStringToUTF8(name)); } static jboolean JNI_NavigationImpl_IsValidRequestHeaderValue( JNIEnv* env, const base::android::JavaParamRef& value) { return net::HttpUtil::IsValidHeaderValue(ConvertJavaStringToUTF8(value)); } #endif } // namespace weblayer