navigation_controller_impl.cc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "weblayer/browser/navigation_controller_impl.h"
  5. #include <utility>
  6. #include "base/auto_reset.h"
  7. #include "base/containers/contains.h"
  8. #include "base/memory/raw_ptr.h"
  9. #include "base/strings/utf_string_conversions.h"
  10. #include "build/build_config.h"
  11. #include "content/public/browser/navigation_controller.h"
  12. #include "content/public/browser/navigation_entry.h"
  13. #include "content/public/browser/navigation_handle.h"
  14. #include "content/public/browser/navigation_throttle.h"
  15. #include "content/public/browser/web_contents.h"
  16. #include "third_party/blink/public/mojom/navigation/was_activated_option.mojom-shared.h"
  17. #include "ui/base/page_transition_types.h"
  18. #include "weblayer/browser/navigation_entry_data.h"
  19. #include "weblayer/browser/navigation_ui_data_impl.h"
  20. #include "weblayer/browser/page_impl.h"
  21. #include "weblayer/browser/tab_impl.h"
  22. #include "weblayer/public/navigation_observer.h"
  23. #if BUILDFLAG(IS_ANDROID)
  24. #include "base/android/jni_string.h"
  25. #include "base/trace_event/trace_event.h"
  26. #include "components/embedder_support/android/util/web_resource_response.h"
  27. #include "weblayer/browser/java/jni/NavigationControllerImpl_jni.h"
  28. #endif
  29. #if BUILDFLAG(IS_ANDROID)
  30. using base::android::AttachCurrentThread;
  31. using base::android::JavaParamRef;
  32. using base::android::ScopedJavaLocalRef;
  33. #endif
  34. namespace weblayer {
  35. class NavigationControllerImpl::DelayDeletionHelper {
  36. public:
  37. explicit DelayDeletionHelper(NavigationControllerImpl* controller)
  38. : controller_(controller->weak_ptr_factory_.GetWeakPtr()) {
  39. // This should never be called reentrantly.
  40. DCHECK(!controller->should_delay_web_contents_deletion_);
  41. controller->should_delay_web_contents_deletion_ = true;
  42. }
  43. DelayDeletionHelper(const DelayDeletionHelper&) = delete;
  44. DelayDeletionHelper& operator=(const DelayDeletionHelper&) = delete;
  45. ~DelayDeletionHelper() {
  46. if (controller_)
  47. controller_->should_delay_web_contents_deletion_ = false;
  48. }
  49. bool WasControllerDeleted() { return controller_.get() == nullptr; }
  50. private:
  51. base::WeakPtr<NavigationControllerImpl> controller_;
  52. };
  53. // NavigationThrottle implementation responsible for delaying certain
  54. // operations and performing them when safe. This is necessary as content
  55. // does allow certain operations to be called at certain times. For example,
  56. // content does not allow calling WebContents::Stop() from
  57. // WebContentsObserver::DidStartNavigation() (to do so crashes). To work around
  58. // this NavigationControllerImpl detects these scenarios and delays processing
  59. // until safe.
  60. //
  61. // Most of the support for these scenarios is handled by a custom
  62. // NavigationThrottle. To make things interesting, the NavigationThrottle is
  63. // created after some of the scenarios this code wants to handle. As such,
  64. // NavigationImpl does some amount of caching until the NavigationThrottle is
  65. // created.
  66. class NavigationControllerImpl::NavigationThrottleImpl
  67. : public content::NavigationThrottle {
  68. public:
  69. NavigationThrottleImpl(NavigationControllerImpl* controller,
  70. content::NavigationHandle* handle)
  71. : NavigationThrottle(handle), controller_(controller) {}
  72. NavigationThrottleImpl(const NavigationThrottleImpl&) = delete;
  73. NavigationThrottleImpl& operator=(const NavigationThrottleImpl&) = delete;
  74. ~NavigationThrottleImpl() override = default;
  75. void ScheduleCancel() { should_cancel_ = true; }
  76. // content::NavigationThrottle:
  77. ThrottleCheckResult WillStartRequest() override {
  78. return should_cancel_ ? CANCEL : PROCEED;
  79. }
  80. ThrottleCheckResult WillRedirectRequest() override {
  81. controller_->WillRedirectRequest(this, navigation_handle());
  82. return should_cancel_ ? CANCEL : PROCEED;
  83. }
  84. const char* GetNameForLogging() override {
  85. return "WebLayerNavigationControllerThrottle";
  86. }
  87. private:
  88. raw_ptr<NavigationControllerImpl> controller_;
  89. bool should_cancel_ = false;
  90. };
  91. NavigationControllerImpl::NavigationControllerImpl(TabImpl* tab)
  92. : WebContentsObserver(tab->web_contents()) {}
  93. NavigationControllerImpl::~NavigationControllerImpl() = default;
  94. std::unique_ptr<content::NavigationThrottle>
  95. NavigationControllerImpl::CreateNavigationThrottle(
  96. content::NavigationHandle* handle) {
  97. if (!handle->IsInMainFrame())
  98. return nullptr;
  99. auto throttle = std::make_unique<NavigationThrottleImpl>(this, handle);
  100. DCHECK(navigation_map_.find(handle) != navigation_map_.end());
  101. auto* navigation = navigation_map_[handle].get();
  102. if (navigation->should_stop_when_throttle_created())
  103. throttle->ScheduleCancel();
  104. return throttle;
  105. }
  106. NavigationImpl* NavigationControllerImpl::GetNavigationImplFromHandle(
  107. content::NavigationHandle* handle) {
  108. auto iter = navigation_map_.find(handle);
  109. return iter == navigation_map_.end() ? nullptr : iter->second.get();
  110. }
  111. NavigationImpl* NavigationControllerImpl::GetNavigationImplFromId(
  112. int64_t navigation_id) {
  113. for (const auto& iter : navigation_map_) {
  114. if (iter.first->GetNavigationId() == navigation_id)
  115. return iter.second.get();
  116. }
  117. return nullptr;
  118. }
  119. void NavigationControllerImpl::OnFirstContentfulPaint(
  120. const base::TimeTicks& navigation_start,
  121. const base::TimeDelta& first_contentful_paint) {
  122. #if BUILDFLAG(IS_ANDROID)
  123. TRACE_EVENT0("weblayer",
  124. "Java_NavigationControllerImpl_onFirstContentfulPaint2");
  125. int64_t first_contentful_paint_ms = first_contentful_paint.InMilliseconds();
  126. Java_NavigationControllerImpl_onFirstContentfulPaint2(
  127. AttachCurrentThread(), java_controller_,
  128. navigation_start.ToUptimeMillis(), first_contentful_paint_ms);
  129. #endif
  130. for (auto& observer : observers_)
  131. observer.OnFirstContentfulPaint(navigation_start, first_contentful_paint);
  132. }
  133. void NavigationControllerImpl::OnLargestContentfulPaint(
  134. const base::TimeTicks& navigation_start,
  135. const base::TimeDelta& largest_contentful_paint) {
  136. #if BUILDFLAG(IS_ANDROID)
  137. TRACE_EVENT0("weblayer",
  138. "Java_NavigationControllerImpl_onLargestContentfulPaint2");
  139. int64_t largest_contentful_paint_ms =
  140. largest_contentful_paint.InMilliseconds();
  141. Java_NavigationControllerImpl_onLargestContentfulPaint(
  142. AttachCurrentThread(), java_controller_,
  143. navigation_start.ToUptimeMillis(), largest_contentful_paint_ms);
  144. #endif
  145. for (auto& observer : observers_)
  146. observer.OnLargestContentfulPaint(navigation_start,
  147. largest_contentful_paint);
  148. }
  149. void NavigationControllerImpl::OnPageDestroyed(Page* page) {
  150. for (auto& observer : observers_)
  151. observer.OnPageDestroyed(page);
  152. }
  153. void NavigationControllerImpl::OnPageLanguageDetermined(
  154. Page* page,
  155. const std::string& language) {
  156. #if BUILDFLAG(IS_ANDROID)
  157. JNIEnv* env = AttachCurrentThread();
  158. Java_NavigationControllerImpl_onPageLanguageDetermined(
  159. env, java_controller_, static_cast<PageImpl*>(page)->java_page(),
  160. base::android::ConvertUTF8ToJavaString(env, language));
  161. #endif
  162. for (auto& observer : observers_)
  163. observer.OnPageLanguageDetermined(page, language);
  164. }
  165. #if BUILDFLAG(IS_ANDROID)
  166. void NavigationControllerImpl::SetNavigationControllerImpl(
  167. JNIEnv* env,
  168. const JavaParamRef<jobject>& java_controller) {
  169. java_controller_ = java_controller;
  170. }
  171. void NavigationControllerImpl::Navigate(
  172. JNIEnv* env,
  173. const JavaParamRef<jstring>& url,
  174. jboolean should_replace_current_entry,
  175. jboolean disable_intent_processing,
  176. jboolean allow_intent_launches_in_background,
  177. jboolean disable_network_error_auto_reload,
  178. jboolean enable_auto_play,
  179. const base::android::JavaParamRef<jobject>& response) {
  180. auto params = std::make_unique<content::NavigationController::LoadURLParams>(
  181. GURL(base::android::ConvertJavaStringToUTF8(env, url)));
  182. params->should_replace_current_entry = should_replace_current_entry;
  183. // On android, the transition type largely dictates whether intent processing
  184. // happens. PAGE_TRANSITION_TYPED does not process intents, where as
  185. // PAGE_TRANSITION_LINK will (with the caveat that even links may not trigger
  186. // intent processing under some circumstances).
  187. params->transition_type = disable_intent_processing
  188. ? ui::PAGE_TRANSITION_TYPED
  189. : ui::PAGE_TRANSITION_LINK;
  190. auto data = std::make_unique<NavigationUIDataImpl>();
  191. if (disable_network_error_auto_reload)
  192. data->set_disable_network_error_auto_reload(true);
  193. data->set_allow_intent_launches_in_background(
  194. allow_intent_launches_in_background);
  195. if (!response.is_null()) {
  196. data->SetResponse(
  197. std::make_unique<embedder_support::WebResourceResponse>(response));
  198. }
  199. params->navigation_ui_data = std::move(data);
  200. if (enable_auto_play)
  201. params->was_activated = blink::mojom::WasActivatedOption::kYes;
  202. DoNavigate(std::move(params));
  203. }
  204. ScopedJavaLocalRef<jstring>
  205. NavigationControllerImpl::GetNavigationEntryDisplayUri(JNIEnv* env, int index) {
  206. return ScopedJavaLocalRef<jstring>(base::android::ConvertUTF8ToJavaString(
  207. env, GetNavigationEntryDisplayURL(index).spec()));
  208. }
  209. ScopedJavaLocalRef<jstring> NavigationControllerImpl::GetNavigationEntryTitle(
  210. JNIEnv* env,
  211. int index) {
  212. return ScopedJavaLocalRef<jstring>(base::android::ConvertUTF8ToJavaString(
  213. env, GetNavigationEntryTitle(index)));
  214. }
  215. bool NavigationControllerImpl::IsNavigationEntrySkippable(JNIEnv* env,
  216. int index) {
  217. return IsNavigationEntrySkippable(index);
  218. }
  219. base::android::ScopedJavaGlobalRef<jobject>
  220. NavigationControllerImpl::GetNavigationImplFromId(JNIEnv* env, int64_t id) {
  221. auto* navigation_impl = GetNavigationImplFromId(id);
  222. return navigation_impl ? navigation_impl->java_navigation() : nullptr;
  223. }
  224. #endif
  225. void NavigationControllerImpl::WillRedirectRequest(
  226. NavigationThrottleImpl* throttle,
  227. content::NavigationHandle* navigation_handle) {
  228. DCHECK(navigation_handle->IsInMainFrame());
  229. DCHECK(navigation_map_.find(navigation_handle) != navigation_map_.end());
  230. auto* navigation = navigation_map_[navigation_handle].get();
  231. navigation->set_safe_to_set_request_headers(true);
  232. DCHECK(!active_throttle_);
  233. base::AutoReset<NavigationThrottleImpl*> auto_reset(&active_throttle_,
  234. throttle);
  235. #if BUILDFLAG(IS_ANDROID)
  236. if (java_controller_) {
  237. TRACE_EVENT0("weblayer",
  238. "Java_NavigationControllerImpl_navigationRedirected");
  239. Java_NavigationControllerImpl_navigationRedirected(
  240. AttachCurrentThread(), java_controller_, navigation->java_navigation());
  241. }
  242. #endif
  243. for (auto& observer : observers_)
  244. observer.NavigationRedirected(navigation);
  245. navigation->set_safe_to_set_request_headers(false);
  246. }
  247. void NavigationControllerImpl::AddObserver(NavigationObserver* observer) {
  248. observers_.AddObserver(observer);
  249. }
  250. void NavigationControllerImpl::RemoveObserver(NavigationObserver* observer) {
  251. observers_.RemoveObserver(observer);
  252. }
  253. void NavigationControllerImpl::Navigate(const GURL& url) {
  254. DoNavigate(
  255. std::make_unique<content::NavigationController::LoadURLParams>(url));
  256. }
  257. void NavigationControllerImpl::Navigate(
  258. const GURL& url,
  259. const NavigationController::NavigateParams& params) {
  260. auto load_params =
  261. std::make_unique<content::NavigationController::LoadURLParams>(url);
  262. load_params->should_replace_current_entry =
  263. params.should_replace_current_entry;
  264. if (params.enable_auto_play)
  265. load_params->was_activated = blink::mojom::WasActivatedOption::kYes;
  266. DoNavigate(std::move(load_params));
  267. }
  268. void NavigationControllerImpl::GoBack() {
  269. web_contents()->GetController().GoBack();
  270. }
  271. void NavigationControllerImpl::GoForward() {
  272. web_contents()->GetController().GoForward();
  273. }
  274. bool NavigationControllerImpl::CanGoBack() {
  275. return web_contents()->GetController().CanGoBack();
  276. }
  277. bool NavigationControllerImpl::CanGoForward() {
  278. return web_contents()->GetController().CanGoForward();
  279. }
  280. void NavigationControllerImpl::GoToIndex(int index) {
  281. web_contents()->GetController().GoToIndex(index);
  282. }
  283. void NavigationControllerImpl::Reload() {
  284. web_contents()->GetController().Reload(content::ReloadType::NORMAL, true);
  285. }
  286. void NavigationControllerImpl::Stop() {
  287. CancelDelayedLoad();
  288. NavigationImpl* navigation = nullptr;
  289. if (navigation_starting_) {
  290. navigation_starting_->set_should_stop_when_throttle_created();
  291. navigation = navigation_starting_;
  292. } else if (active_throttle_) {
  293. active_throttle_->ScheduleCancel();
  294. DCHECK(navigation_map_.find(active_throttle_->navigation_handle()) !=
  295. navigation_map_.end());
  296. navigation = navigation_map_[active_throttle_->navigation_handle()].get();
  297. } else {
  298. web_contents()->Stop();
  299. }
  300. if (navigation)
  301. navigation->set_was_stopped();
  302. }
  303. int NavigationControllerImpl::GetNavigationListSize() {
  304. content::NavigationEntry* current_entry =
  305. web_contents()->GetController().GetLastCommittedEntry();
  306. if (current_entry && current_entry->IsInitialEntry()) {
  307. // If we're currently on the initial NavigationEntry, no navigation has
  308. // committed, so the initial NavigationEntry should not be part of the
  309. // "Navigation List", and we should return 0 as the navigation list size.
  310. // This also preserves the old behavior where we used to not have the
  311. // initial NavigationEntry.
  312. return 0;
  313. }
  314. return web_contents()->GetController().GetEntryCount();
  315. }
  316. int NavigationControllerImpl::GetNavigationListCurrentIndex() {
  317. content::NavigationEntry* current_entry =
  318. web_contents()->GetController().GetLastCommittedEntry();
  319. if (current_entry && current_entry->IsInitialEntry()) {
  320. // If we're currently on the initial NavigationEntry, no navigation has
  321. // committed, so the initial NavigationEntry should not be part of the
  322. // "Navigation List", and we should return -1 as the current index. This
  323. // also preserves the old behavior where we used to not have the initial
  324. // NavigationEntry.
  325. return -1;
  326. }
  327. return web_contents()->GetController().GetCurrentEntryIndex();
  328. }
  329. GURL NavigationControllerImpl::GetNavigationEntryDisplayURL(int index) {
  330. auto* entry = web_contents()->GetController().GetEntryAtIndex(index);
  331. // This function should never be called when GetNavigationListSize() is 0
  332. // because `index` should be between 0 and GetNavigationListSize() - 1, which
  333. // also means `entry` must not be the initial NavigationEntry.
  334. DCHECK_NE(0, GetNavigationListSize());
  335. DCHECK(!entry->IsInitialEntry());
  336. return entry->GetVirtualURL();
  337. }
  338. std::string NavigationControllerImpl::GetNavigationEntryTitle(int index) {
  339. auto* entry = web_contents()->GetController().GetEntryAtIndex(index);
  340. // This function should never be called when GetNavigationListSize() is 0
  341. // because `index` should be between 0 and GetNavigationListSize() - 1, which
  342. // also means `entry` must not be the initial NavigationEntry.
  343. DCHECK_NE(0, GetNavigationListSize());
  344. DCHECK(!entry->IsInitialEntry());
  345. return base::UTF16ToUTF8(entry->GetTitle());
  346. }
  347. bool NavigationControllerImpl::IsNavigationEntrySkippable(int index) {
  348. return web_contents()->GetController().IsEntryMarkedToBeSkipped(index);
  349. }
  350. void NavigationControllerImpl::DidStartNavigation(
  351. content::NavigationHandle* navigation_handle) {
  352. // TODO(https://crbug.com/1218946): With MPArch there may be multiple main
  353. // frames. This caller was converted automatically to the primary main frame
  354. // to preserve its semantics. Follow up to confirm correctness.
  355. if (!navigation_handle->IsInPrimaryMainFrame())
  356. return;
  357. // This function should not be called reentrantly.
  358. DCHECK(!navigation_starting_);
  359. DCHECK(!base::Contains(navigation_map_, navigation_handle));
  360. navigation_map_[navigation_handle] =
  361. std::make_unique<NavigationImpl>(navigation_handle);
  362. auto* navigation = navigation_map_[navigation_handle].get();
  363. base::AutoReset<NavigationImpl*> auto_reset(&navigation_starting_,
  364. navigation);
  365. navigation->set_safe_to_set_request_headers(true);
  366. navigation->set_safe_to_disable_network_error_auto_reload(true);
  367. navigation->set_safe_to_disable_intent_processing(true);
  368. #if BUILDFLAG(IS_ANDROID)
  369. // Desktop mode and per-navigation UA use the same mechanism and so don't
  370. // interact well. It's not possible to support both at the same time since
  371. // if there's a per-navigation UA active and desktop mode is turned on, or
  372. // was on previously, the WebContent's state would have to change before
  373. // navigation even though that would be wrong for the previous navigation if
  374. // the new navigation didn't commit.
  375. if (!TabImpl::FromWebContents(web_contents())->desktop_user_agent_enabled())
  376. #endif
  377. navigation->set_safe_to_set_user_agent(true);
  378. #if BUILDFLAG(IS_ANDROID)
  379. NavigationUIDataImpl* navigation_ui_data = static_cast<NavigationUIDataImpl*>(
  380. navigation_handle->GetNavigationUIData());
  381. if (navigation_ui_data) {
  382. auto response = navigation_ui_data->TakeResponse();
  383. if (response)
  384. navigation->SetResponse(std::move(response));
  385. }
  386. if (java_controller_) {
  387. JNIEnv* env = AttachCurrentThread();
  388. {
  389. TRACE_EVENT0("weblayer",
  390. "Java_NavigationControllerImpl_createNavigation");
  391. ScopedJavaLocalRef<jobject> java_navigation =
  392. Java_NavigationControllerImpl_createNavigation(
  393. env, java_controller_, reinterpret_cast<jlong>(navigation));
  394. navigation->SetJavaNavigation(
  395. base::android::ScopedJavaGlobalRef<jobject>(java_navigation));
  396. }
  397. TRACE_EVENT0("weblayer", "Java_NavigationControllerImpl_navigationStarted");
  398. Java_NavigationControllerImpl_navigationStarted(
  399. env, java_controller_, navigation->java_navigation());
  400. }
  401. #endif
  402. for (auto& observer : observers_)
  403. observer.NavigationStarted(navigation);
  404. navigation->set_safe_to_set_user_agent(false);
  405. navigation->set_safe_to_set_request_headers(false);
  406. navigation->set_safe_to_disable_network_error_auto_reload(false);
  407. navigation->set_safe_to_disable_intent_processing(false);
  408. }
  409. void NavigationControllerImpl::DidRedirectNavigation(
  410. content::NavigationHandle* navigation_handle) {
  411. // NOTE: this implementation should remain empty. Real implementation is in
  412. // WillRedirectNavigation(). See description of NavigationThrottleImpl for
  413. // more information.
  414. }
  415. void NavigationControllerImpl::ReadyToCommitNavigation(
  416. content::NavigationHandle* navigation_handle) {
  417. #if BUILDFLAG(IS_ANDROID)
  418. // TODO(https://crbug.com/1218946): With MPArch there may be multiple main
  419. // frames. This caller was converted automatically to the primary main frame
  420. // to preserve its semantics. Follow up to confirm correctness.
  421. if (!navigation_handle->IsInPrimaryMainFrame())
  422. return;
  423. DCHECK(navigation_map_.find(navigation_handle) != navigation_map_.end());
  424. auto* navigation = navigation_map_[navigation_handle].get();
  425. if (java_controller_) {
  426. TRACE_EVENT0("weblayer",
  427. "Java_NavigationControllerImpl_readyToCommitNavigation");
  428. Java_NavigationControllerImpl_readyToCommitNavigation(
  429. AttachCurrentThread(), java_controller_, navigation->java_navigation());
  430. }
  431. #endif
  432. }
  433. void NavigationControllerImpl::DidFinishNavigation(
  434. content::NavigationHandle* navigation_handle) {
  435. // TODO(https://crbug.com/1218946): With MPArch there may be multiple main
  436. // frames. This caller was converted automatically to the primary main frame
  437. // to preserve its semantics. Follow up to confirm correctness.
  438. if (!navigation_handle->IsInPrimaryMainFrame())
  439. return;
  440. DelayDeletionHelper deletion_helper(this);
  441. DCHECK(navigation_map_.find(navigation_handle) != navigation_map_.end());
  442. auto* navigation = navigation_map_[navigation_handle].get();
  443. navigation->set_finished();
  444. if (navigation_handle->HasCommitted()) {
  445. // Set state on NavigationEntry user data if a per-navigation user agent was
  446. // specified. This can't be done earlier because a NavigationEntry might not
  447. // have existed at the time that SetUserAgentString was called.
  448. if (navigation->set_user_agent_string_called()) {
  449. auto* entry = web_contents()->GetController().GetLastCommittedEntry();
  450. if (entry) {
  451. auto* entry_data = NavigationEntryData::Get(entry);
  452. if (entry_data)
  453. entry_data->set_per_navigation_user_agent_override(true);
  454. }
  455. }
  456. auto* rfh = navigation_handle->GetRenderFrameHost();
  457. PageImpl::GetOrCreateForPage(rfh->GetPage());
  458. navigation->set_safe_to_get_page();
  459. #if BUILDFLAG(IS_ANDROID)
  460. // Ensure that the Java-side Page object for this navigation is
  461. // populated from and linked to the native Page object. Without this
  462. // call, the Java-side navigation object won't be created and linked to
  463. // the native object until/unless the client calls Navigation#getPage(),
  464. // which is problematic when implementation-side callers need to bridge
  465. // the C++ Page object into Java (e.g., to fire
  466. // NavigationCallback#onPageLanguageDetermined()).
  467. Java_NavigationControllerImpl_getOrCreatePageForNavigation(
  468. AttachCurrentThread(), java_controller_, navigation->java_navigation());
  469. #endif
  470. }
  471. // In some corner cases (e.g., a tab closing with an ongoing navigation)
  472. // navigations finish without committing but without any other error state.
  473. // Such navigations are regarded as failed by WebLayer.
  474. if (navigation_handle->HasCommitted() &&
  475. navigation_handle->GetNetErrorCode() == net::OK &&
  476. !navigation_handle->IsErrorPage()) {
  477. #if BUILDFLAG(IS_ANDROID)
  478. if (java_controller_) {
  479. TRACE_EVENT0("weblayer",
  480. "Java_NavigationControllerImpl_navigationCompleted");
  481. Java_NavigationControllerImpl_navigationCompleted(
  482. AttachCurrentThread(), java_controller_,
  483. navigation->java_navigation());
  484. if (deletion_helper.WasControllerDeleted())
  485. return;
  486. }
  487. #endif
  488. for (auto& observer : observers_) {
  489. observer.NavigationCompleted(navigation);
  490. if (deletion_helper.WasControllerDeleted())
  491. return;
  492. }
  493. } else {
  494. #if BUILDFLAG(IS_ANDROID)
  495. if (java_controller_) {
  496. TRACE_EVENT0("weblayer",
  497. "Java_NavigationControllerImpl_navigationFailed");
  498. Java_NavigationControllerImpl_navigationFailed(
  499. AttachCurrentThread(), java_controller_,
  500. navigation->java_navigation());
  501. if (deletion_helper.WasControllerDeleted())
  502. return;
  503. }
  504. #endif
  505. for (auto& observer : observers_) {
  506. observer.NavigationFailed(navigation);
  507. if (deletion_helper.WasControllerDeleted())
  508. return;
  509. }
  510. }
  511. // Note InsertVisualStateCallback currently does not take into account
  512. // any delays from surface sync, ie a frame submitted by renderer may not
  513. // be displayed immediately. Such situations should be rare however, so
  514. // this should be good enough for the purposes needed.
  515. web_contents()->GetPrimaryMainFrame()->InsertVisualStateCallback(
  516. base::BindOnce(&NavigationControllerImpl::OldPageNoLongerRendered,
  517. weak_ptr_factory_.GetWeakPtr(),
  518. navigation_handle->GetURL()));
  519. navigation_map_.erase(navigation_map_.find(navigation_handle));
  520. }
  521. void NavigationControllerImpl::DidStartLoading() {
  522. NotifyLoadStateChanged();
  523. }
  524. void NavigationControllerImpl::DidStopLoading() {
  525. NotifyLoadStateChanged();
  526. }
  527. void NavigationControllerImpl::LoadProgressChanged(double progress) {
  528. #if BUILDFLAG(IS_ANDROID)
  529. if (java_controller_) {
  530. TRACE_EVENT0("weblayer",
  531. "Java_NavigationControllerImpl_loadProgressChanged");
  532. Java_NavigationControllerImpl_loadProgressChanged(
  533. AttachCurrentThread(), java_controller_, progress);
  534. }
  535. #endif
  536. for (auto& observer : observers_)
  537. observer.LoadProgressChanged(progress);
  538. }
  539. void NavigationControllerImpl::DidFirstVisuallyNonEmptyPaint() {
  540. #if BUILDFLAG(IS_ANDROID)
  541. TRACE_EVENT0("weblayer",
  542. "Java_NavigationControllerImpl_onFirstContentfulPaint");
  543. Java_NavigationControllerImpl_onFirstContentfulPaint(AttachCurrentThread(),
  544. java_controller_);
  545. #endif
  546. for (auto& observer : observers_)
  547. observer.OnFirstContentfulPaint();
  548. }
  549. void NavigationControllerImpl::OldPageNoLongerRendered(const GURL& url,
  550. bool success) {
  551. #if BUILDFLAG(IS_ANDROID)
  552. TRACE_EVENT0("weblayer",
  553. "Java_NavigationControllerImpl_onOldPageNoLongerRendered");
  554. JNIEnv* env = AttachCurrentThread();
  555. Java_NavigationControllerImpl_onOldPageNoLongerRendered(
  556. env, java_controller_,
  557. base::android::ConvertUTF8ToJavaString(env, url.spec()));
  558. #endif
  559. for (auto& observer : observers_)
  560. observer.OnOldPageNoLongerRendered(url);
  561. }
  562. void NavigationControllerImpl::NotifyLoadStateChanged() {
  563. #if BUILDFLAG(IS_ANDROID)
  564. if (java_controller_) {
  565. TRACE_EVENT0("weblayer", "Java_NavigationControllerImpl_loadStateChanged");
  566. Java_NavigationControllerImpl_loadStateChanged(
  567. AttachCurrentThread(), java_controller_, web_contents()->IsLoading(),
  568. web_contents()->ShouldShowLoadingUI());
  569. }
  570. #endif
  571. for (auto& observer : observers_) {
  572. observer.LoadStateChanged(web_contents()->IsLoading(),
  573. web_contents()->ShouldShowLoadingUI());
  574. }
  575. }
  576. void NavigationControllerImpl::DoNavigate(
  577. std::unique_ptr<content::NavigationController::LoadURLParams> params) {
  578. CancelDelayedLoad();
  579. // Navigations should use the default user-agent (which may be overridden if
  580. // desktop mode is turned on). If the embedder wants a custom user-agent, the
  581. // embedder will call Navigation::SetUserAgentString() in DidStartNavigation.
  582. #if BUILDFLAG(IS_ANDROID)
  583. // We need to set UA_OVERRIDE_FALSE if per navigation UA is set. However at
  584. // this point we don't know if the embedder will call that later. Since we
  585. // ensure that the two can't be set at the same time, it's sufficient to
  586. // not enable it if desktop mode is turned on.
  587. if (!TabImpl::FromWebContents(web_contents())->desktop_user_agent_enabled())
  588. #endif
  589. params->override_user_agent =
  590. content::NavigationController::UA_OVERRIDE_FALSE;
  591. if (navigation_starting_ || active_throttle_) {
  592. // DoNavigate() is being called reentrantly. Delay processing until it's
  593. // safe.
  594. Stop();
  595. ScheduleDelayedLoad(std::move(params));
  596. return;
  597. }
  598. params->has_user_gesture = true;
  599. web_contents()->GetController().LoadURLWithParams(*params);
  600. // So that if the user had entered the UI in a bar it stops flashing the
  601. // caret.
  602. web_contents()->Focus();
  603. }
  604. void NavigationControllerImpl::ScheduleDelayedLoad(
  605. std::unique_ptr<content::NavigationController::LoadURLParams> params) {
  606. delayed_load_params_ = std::move(params);
  607. base::SequencedTaskRunnerHandle::Get()->PostTask(
  608. FROM_HERE, base::BindOnce(&NavigationControllerImpl::ProcessDelayedLoad,
  609. weak_ptr_factory_.GetWeakPtr()));
  610. }
  611. void NavigationControllerImpl::CancelDelayedLoad() {
  612. delayed_load_params_.reset();
  613. }
  614. void NavigationControllerImpl::ProcessDelayedLoad() {
  615. if (delayed_load_params_)
  616. DoNavigate(std::move(delayed_load_params_));
  617. }
  618. #if BUILDFLAG(IS_ANDROID)
  619. static jlong JNI_NavigationControllerImpl_GetNavigationController(JNIEnv* env,
  620. jlong tab) {
  621. return reinterpret_cast<jlong>(
  622. reinterpret_cast<Tab*>(tab)->GetNavigationController());
  623. }
  624. #endif
  625. } // namespace weblayer