page_load_tracker_decorator_helper.cc 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Copyright 2020 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 "components/performance_manager/public/decorators/page_load_tracker_decorator_helper.h"
  5. #include "base/bind.h"
  6. #include "base/memory/raw_ptr.h"
  7. #include "components/performance_manager/decorators/page_load_tracker_decorator.h"
  8. #include "components/performance_manager/graph/page_node_impl.h"
  9. #include "components/performance_manager/performance_manager_impl.h"
  10. #include "components/performance_manager/public/performance_manager.h"
  11. #include "content/public/browser/browser_thread.h"
  12. #include "content/public/browser/web_contents.h"
  13. #include "content/public/browser/web_contents_observer.h"
  14. namespace performance_manager {
  15. namespace {
  16. void NotifyPageLoadTrackerDecoratorOnPMSequence(content::WebContents* contents,
  17. void (*method)(PageNodeImpl*)) {
  18. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  19. PerformanceManagerImpl::CallOnGraphImpl(
  20. FROM_HERE,
  21. base::BindOnce(
  22. [](base::WeakPtr<PageNode> node, void (*method)(PageNodeImpl*)) {
  23. if (node) {
  24. PageNodeImpl* page_node = PageNodeImpl::FromNode(node.get());
  25. method(page_node);
  26. }
  27. },
  28. PerformanceManager::GetPrimaryPageNodeForWebContents(contents),
  29. method));
  30. }
  31. } // namespace
  32. // Listens to content::WebContentsObserver notifications for a given WebContents
  33. // and updates the PageLoadTracker accordingly. Destroys itself when the
  34. // WebContents it observes is destroyed.
  35. class PageLoadTrackerDecoratorHelper::WebContentsObserver
  36. : public content::WebContentsObserver {
  37. public:
  38. explicit WebContentsObserver(content::WebContents* web_contents,
  39. PageLoadTrackerDecoratorHelper* outer)
  40. : content::WebContentsObserver(web_contents),
  41. outer_(outer),
  42. prev_(nullptr),
  43. next_(outer->first_web_contents_observer_) {
  44. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  45. DCHECK(web_contents);
  46. if (next_) {
  47. DCHECK(!next_->prev_);
  48. next_->prev_ = this;
  49. }
  50. outer_->first_web_contents_observer_ = this;
  51. // |web_contents| must not be loading when it starts being tracked by this
  52. // observer. Otherwise, loading state wouldn't be tracked correctly.
  53. DCHECK(!web_contents->ShouldShowLoadingUI());
  54. }
  55. WebContentsObserver(const WebContentsObserver&) = delete;
  56. WebContentsObserver& operator=(const WebContentsObserver&) = delete;
  57. ~WebContentsObserver() override {
  58. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  59. }
  60. // content::WebContentsObserver:
  61. void DidStartLoading() override {
  62. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  63. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  64. DCHECK(web_contents()->IsLoading());
  65. DCHECK_EQ(loading_state_, LoadingState::kNotLoading);
  66. // Only observe top-level navigation to a different document.
  67. if (!web_contents()->ShouldShowLoadingUI())
  68. return;
  69. loading_state_ = LoadingState::kWaitingForNavigation;
  70. NotifyPageLoadTrackerDecoratorOnPMSequence(
  71. web_contents(), &PageLoadTrackerDecorator::DidStartLoading);
  72. }
  73. void PrimaryPageChanged(content::Page& page) override {
  74. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  75. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  76. // Only observe top-level navigation that will show navigation UI.
  77. if (!web_contents()->ShouldShowLoadingUI())
  78. return;
  79. DCHECK(web_contents()->IsLoading());
  80. // Return if the state is already updated, since it can be called in
  81. // multiple times between DidStartLoading() and DidStopLoading().
  82. if (loading_state_ == LoadingState::kLoading)
  83. return;
  84. DCHECK_EQ(loading_state_, LoadingState::kWaitingForNavigation);
  85. loading_state_ = LoadingState::kLoading;
  86. NotifyPageLoadTrackerDecoratorOnPMSequence(
  87. web_contents(), &PageLoadTrackerDecorator::PrimaryPageChanged);
  88. }
  89. void DidStopLoading() override {
  90. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  91. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  92. // The state can be |kNotLoading| if this isn't a top-level navigation to a
  93. // different document.
  94. if (loading_state_ == LoadingState::kNotLoading)
  95. return;
  96. loading_state_ = LoadingState::kNotLoading;
  97. NotifyPageLoadTrackerDecoratorOnPMSequence(
  98. web_contents(), &PageLoadTrackerDecorator::DidStopLoading);
  99. }
  100. void WebContentsDestroyed() override {
  101. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  102. DetachAndDestroy();
  103. }
  104. // Removes the WebContentsObserver from the linked list and deletes it.
  105. void DetachAndDestroy() {
  106. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  107. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  108. if (prev_) {
  109. DCHECK_CALLED_ON_VALID_SEQUENCE(prev_->sequence_checker_);
  110. DCHECK_EQ(prev_->next_, this);
  111. prev_->next_ = next_;
  112. } else {
  113. DCHECK_EQ(outer_->first_web_contents_observer_, this);
  114. outer_->first_web_contents_observer_ = next_;
  115. }
  116. if (next_) {
  117. DCHECK_CALLED_ON_VALID_SEQUENCE(next_->sequence_checker_);
  118. DCHECK_EQ(next_->prev_, this);
  119. next_->prev_ = prev_;
  120. }
  121. delete this;
  122. }
  123. private:
  124. // TODO(https://crbug.com/1048719): Extract the logic to manage a linked list
  125. // of WebContentsObservers to a helper class.
  126. const raw_ptr<PageLoadTrackerDecoratorHelper> outer_;
  127. raw_ptr<WebContentsObserver> prev_ GUARDED_BY_CONTEXT(sequence_checker_);
  128. raw_ptr<WebContentsObserver> next_ GUARDED_BY_CONTEXT(sequence_checker_);
  129. enum class LoadingState {
  130. // Initial state.
  131. // DidStartLoading(): Transition to kWaitingForNavigation.
  132. // PrimaryPageChanged(): Invalid from this state.
  133. // DidStopLoading(): Invalid from this state.
  134. kNotLoading,
  135. // DidStartLoading(): Invalid from this state.
  136. // PrimaryPageChanged(): Transition to kLoading.
  137. // DidStopLoading(): Transition to kNotLoading.
  138. kWaitingForNavigation,
  139. // DidStartLoading(): Invalid from this state.
  140. // PrimaryPageChanged(): Invalid from this state.
  141. // DidStopLoading(): Transition to kNotLoading.
  142. kLoading,
  143. };
  144. LoadingState loading_state_ GUARDED_BY_CONTEXT(sequence_checker_) =
  145. LoadingState::kNotLoading;
  146. SEQUENCE_CHECKER(sequence_checker_);
  147. };
  148. PageLoadTrackerDecoratorHelper::PageLoadTrackerDecoratorHelper() {
  149. PerformanceManager::AddObserver(this);
  150. }
  151. PageLoadTrackerDecoratorHelper::~PageLoadTrackerDecoratorHelper() {
  152. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  153. // Destroy all WebContentsObserver to ensure that PageLiveStateDecorators are
  154. // no longer maintained.
  155. while (first_web_contents_observer_)
  156. first_web_contents_observer_->DetachAndDestroy();
  157. PerformanceManager::RemoveObserver(this);
  158. }
  159. void PageLoadTrackerDecoratorHelper::OnPageNodeCreatedForWebContents(
  160. content::WebContents* web_contents) {
  161. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  162. DCHECK(web_contents);
  163. // Start observing the WebContents. See comment on
  164. // |first_web_contents_observer_| for lifetime management details.
  165. new WebContentsObserver(web_contents, this);
  166. }
  167. } // namespace performance_manager