metrics_render_frame_observer.cc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. // Copyright 2015 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/page_load_metrics/renderer/metrics_render_frame_observer.h"
  5. #include <string>
  6. #include <utility>
  7. #include "base/memory/ptr_util.h"
  8. #include "base/time/time.h"
  9. #include "base/timer/timer.h"
  10. #include "components/page_load_metrics/renderer/page_timing_metrics_sender.h"
  11. #include "components/page_load_metrics/renderer/page_timing_sender.h"
  12. #include "content/public/renderer/render_frame.h"
  13. #include "services/network/public/mojom/url_response_head.mojom.h"
  14. #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
  15. #include "third_party/blink/public/common/loader/resource_type_util.h"
  16. #include "third_party/blink/public/common/mobile_metrics/mobile_friendliness.h"
  17. #include "third_party/blink/public/web/web_document.h"
  18. #include "third_party/blink/public/web/web_document_loader.h"
  19. #include "third_party/blink/public/web/web_local_frame.h"
  20. #include "third_party/blink/public/web/web_performance.h"
  21. #include "url/gurl.h"
  22. namespace page_load_metrics {
  23. namespace {
  24. base::TimeDelta ClampDelta(double event, double start) {
  25. if (event - start < 0)
  26. event = start;
  27. return base::Time::FromDoubleT(event) - base::Time::FromDoubleT(start);
  28. }
  29. base::TimeTicks ClampToStart(base::TimeTicks event, base::TimeTicks start) {
  30. return event < start ? start : event;
  31. }
  32. class MojoPageTimingSender : public PageTimingSender {
  33. public:
  34. explicit MojoPageTimingSender(content::RenderFrame* render_frame,
  35. bool limited_sending_mode)
  36. : limited_sending_mode_(limited_sending_mode) {
  37. DCHECK(render_frame);
  38. render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
  39. &page_load_metrics_);
  40. }
  41. ~MojoPageTimingSender() override = default;
  42. void SendTiming(
  43. const mojom::PageLoadTimingPtr& timing,
  44. const mojom::FrameMetadataPtr& metadata,
  45. const std::vector<blink::UseCounterFeature>& new_features,
  46. std::vector<mojom::ResourceDataUpdatePtr> resources,
  47. const mojom::FrameRenderDataUpdate& render_data,
  48. const mojom::CpuTimingPtr& cpu_timing,
  49. mojom::InputTimingPtr input_timing_delta,
  50. const absl::optional<blink::MobileFriendliness>& mobile_friendliness,
  51. uint32_t soft_navigation_count) override {
  52. DCHECK(page_load_metrics_);
  53. page_load_metrics_->UpdateTiming(
  54. limited_sending_mode_ ? CreatePageLoadTiming() : timing->Clone(),
  55. metadata->Clone(), new_features, std::move(resources),
  56. render_data.Clone(), cpu_timing->Clone(), std::move(input_timing_delta),
  57. std::move(mobile_friendliness), soft_navigation_count);
  58. }
  59. void SetUpSmoothnessReporting(
  60. base::ReadOnlySharedMemoryRegion shared_memory) override {
  61. DCHECK(page_load_metrics_);
  62. page_load_metrics_->SetUpSharedMemoryForSmoothness(
  63. std::move(shared_memory));
  64. }
  65. private:
  66. // Indicates that this sender should not send timing updates or frame render
  67. // data updates.
  68. // TODO(https://crbug.com/1097127): When timing updates are handled for cases
  69. // where we have a subframe document and no committed navigation, this can be
  70. // removed.
  71. bool limited_sending_mode_ = false;
  72. // Use associated interface to make sure mojo messages are ordered with regard
  73. // to legacy IPC messages.
  74. mojo::AssociatedRemote<mojom::PageLoadMetrics> page_load_metrics_;
  75. };
  76. } // namespace
  77. MetricsRenderFrameObserver::MetricsRenderFrameObserver(
  78. content::RenderFrame* render_frame)
  79. : content::RenderFrameObserver(render_frame) {}
  80. MetricsRenderFrameObserver::~MetricsRenderFrameObserver() {
  81. if (page_timing_metrics_sender_)
  82. page_timing_metrics_sender_->SendLatest();
  83. }
  84. void MetricsRenderFrameObserver::DidChangePerformanceTiming() {
  85. SendMetrics();
  86. }
  87. void MetricsRenderFrameObserver::DidObserveInputDelay(
  88. base::TimeDelta input_delay) {
  89. if (!page_timing_metrics_sender_ || HasNoRenderFrame()) {
  90. return;
  91. }
  92. page_timing_metrics_sender_->DidObserveInputDelay(input_delay);
  93. }
  94. void MetricsRenderFrameObserver::DidObserveUserInteraction(
  95. base::TimeDelta max_event_duration,
  96. blink::UserInteractionType interaction_type) {
  97. if (!page_timing_metrics_sender_ || HasNoRenderFrame()) {
  98. return;
  99. }
  100. page_timing_metrics_sender_->DidObserveUserInteraction(max_event_duration,
  101. interaction_type);
  102. }
  103. void MetricsRenderFrameObserver::DidChangeCpuTiming(base::TimeDelta time) {
  104. if (!page_timing_metrics_sender_)
  105. return;
  106. if (HasNoRenderFrame())
  107. return;
  108. page_timing_metrics_sender_->UpdateCpuTiming(time);
  109. }
  110. void MetricsRenderFrameObserver::DidObserveLoadingBehavior(
  111. blink::LoadingBehaviorFlag behavior) {
  112. if (page_timing_metrics_sender_)
  113. page_timing_metrics_sender_->DidObserveLoadingBehavior(behavior);
  114. }
  115. void MetricsRenderFrameObserver::DidObserveNewFeatureUsage(
  116. const blink::UseCounterFeature& feature) {
  117. if (page_timing_metrics_sender_)
  118. page_timing_metrics_sender_->DidObserveNewFeatureUsage(feature);
  119. }
  120. void MetricsRenderFrameObserver::DidObserveSoftNavigation(uint32_t count) {
  121. if (page_timing_metrics_sender_) {
  122. page_timing_metrics_sender_->DidObserveSoftNavigation(count);
  123. }
  124. }
  125. void MetricsRenderFrameObserver::DidObserveLayoutShift(
  126. double score,
  127. bool after_input_or_scroll) {
  128. if (page_timing_metrics_sender_)
  129. page_timing_metrics_sender_->DidObserveLayoutShift(score,
  130. after_input_or_scroll);
  131. }
  132. void MetricsRenderFrameObserver::DidObserveLayoutNg(uint32_t all_block_count,
  133. uint32_t ng_block_count,
  134. uint32_t all_call_count,
  135. uint32_t ng_call_count) {
  136. if (page_timing_metrics_sender_)
  137. page_timing_metrics_sender_->DidObserveLayoutNg(
  138. all_block_count, ng_block_count, all_call_count, ng_call_count);
  139. }
  140. void MetricsRenderFrameObserver::DidStartResponse(
  141. const url::SchemeHostPort& final_response_url,
  142. int request_id,
  143. const network::mojom::URLResponseHead& response_head,
  144. network::mojom::RequestDestination request_destination) {
  145. if (provisional_frame_resource_data_use_ &&
  146. blink::IsRequestDestinationFrame(request_destination)) {
  147. // TODO(rajendrant): This frame request might start before the provisional
  148. // load starts, and data use of the frame request might be missed in that
  149. // case. There should be a guarantee that DidStartProvisionalLoad be called
  150. // before DidStartResponse for the frame request.
  151. provisional_frame_resource_data_use_->DidStartResponse(
  152. final_response_url, request_id, response_head, request_destination);
  153. } else if (page_timing_metrics_sender_) {
  154. page_timing_metrics_sender_->DidStartResponse(
  155. final_response_url, request_id, response_head, request_destination);
  156. UpdateResourceMetadata(request_id);
  157. }
  158. }
  159. void MetricsRenderFrameObserver::DidCompleteResponse(
  160. int request_id,
  161. const network::URLLoaderCompletionStatus& status) {
  162. if (provisional_frame_resource_data_use_ &&
  163. provisional_frame_resource_data_use_->resource_id() == request_id) {
  164. provisional_frame_resource_data_use_->DidCompleteResponse(status);
  165. } else if (page_timing_metrics_sender_) {
  166. page_timing_metrics_sender_->DidCompleteResponse(request_id, status);
  167. MaybeSetCompletedBeforeFCP(request_id);
  168. UpdateResourceMetadata(request_id);
  169. }
  170. }
  171. void MetricsRenderFrameObserver::DidCancelResponse(int request_id) {
  172. if (provisional_frame_resource_data_use_ &&
  173. provisional_frame_resource_data_use_->resource_id() == request_id) {
  174. provisional_frame_resource_data_use_.reset();
  175. } else if (page_timing_metrics_sender_) {
  176. page_timing_metrics_sender_->DidCancelResponse(request_id);
  177. UpdateResourceMetadata(request_id);
  178. }
  179. }
  180. void MetricsRenderFrameObserver::DidReceiveTransferSizeUpdate(
  181. int request_id,
  182. int received_data_length) {
  183. if (provisional_frame_resource_data_use_ &&
  184. provisional_frame_resource_data_use_->resource_id() == request_id) {
  185. provisional_frame_resource_data_use_->DidReceiveTransferSizeUpdate(
  186. received_data_length);
  187. } else if (page_timing_metrics_sender_) {
  188. page_timing_metrics_sender_->DidReceiveTransferSizeUpdate(
  189. request_id, received_data_length);
  190. UpdateResourceMetadata(request_id);
  191. }
  192. }
  193. void MetricsRenderFrameObserver::DidLoadResourceFromMemoryCache(
  194. const GURL& response_url,
  195. int request_id,
  196. int64_t encoded_body_length,
  197. const std::string& mime_type,
  198. bool from_archive) {
  199. // Resources from archives, such as subresources from a MHTML archive, do not
  200. // have valid request ids and should not be reported to PLM.
  201. if (from_archive)
  202. return;
  203. // A provisional frame resource cannot be serviced from the memory cache, so
  204. // we do not need to check |provisional_frame_resource_data_use_|.
  205. if (page_timing_metrics_sender_) {
  206. page_timing_metrics_sender_->DidLoadResourceFromMemoryCache(
  207. response_url, request_id, encoded_body_length, mime_type);
  208. MaybeSetCompletedBeforeFCP(request_id);
  209. UpdateResourceMetadata(request_id);
  210. }
  211. }
  212. void MetricsRenderFrameObserver::WillDetach() {
  213. if (page_timing_metrics_sender_) {
  214. page_timing_metrics_sender_->SendLatest();
  215. page_timing_metrics_sender_.reset();
  216. }
  217. }
  218. void MetricsRenderFrameObserver::DidStartNavigation(
  219. const GURL& url,
  220. absl::optional<blink::WebNavigationType> navigation_type) {
  221. // Send current metrics, as we might create a new RenderFrame later due to
  222. // this navigation (that might end up in a different process entirely, and
  223. // won't notify us until the current RenderFrameHost in the browser changed).
  224. // If that happens, it will be too late to send the metrics from WillDetach
  225. // or the destructor, because the browser ignores metrics update from
  226. // non-current RenderFrameHosts. See crbug.com/1150242 for more details.
  227. // TODO(crbug.com/1150242): Remove this when we have the full fix for the bug.
  228. if (page_timing_metrics_sender_)
  229. page_timing_metrics_sender_->SendLatest();
  230. }
  231. void MetricsRenderFrameObserver::DidSetPageLifecycleState() {
  232. // Send current metrics, as this RenderFrame might be replaced by a new
  233. // RenderFrame or its process might be killed, and this might be the last
  234. // point we can send the metrics to the browser. See crbug.com/1150242 for
  235. // more details.
  236. // TODO(crbug.com/1150242): Remove this when we have the full fix for the bug.
  237. if (page_timing_metrics_sender_)
  238. page_timing_metrics_sender_->SendLatest();
  239. }
  240. void MetricsRenderFrameObserver::ReadyToCommitNavigation(
  241. blink::WebDocumentLoader* document_loader) {
  242. // Create a new data use tracker for the new document load.
  243. provisional_frame_resource_data_use_ =
  244. std::make_unique<PageResourceDataUse>();
  245. provisional_frame_resource_id_ = 0;
  246. // Send current metrics before the next page load commits. Don't reset here
  247. // as it may be a same document load.
  248. if (page_timing_metrics_sender_)
  249. page_timing_metrics_sender_->SendLatest();
  250. }
  251. void MetricsRenderFrameObserver::DidFailProvisionalLoad() {
  252. // Clear the data use tracker for the provisional navigation that started.
  253. provisional_frame_resource_data_use_.reset();
  254. }
  255. void MetricsRenderFrameObserver::DidCreateDocumentElement() {
  256. // If we do not have a render frame or are already tracking this frame, ignore
  257. // the new document element.
  258. if (HasNoRenderFrame() || page_timing_metrics_sender_)
  259. return;
  260. // We should only track committed navigations for the main frame so ignore new
  261. // document elements in the main frame.
  262. if (render_frame()->IsMainFrame())
  263. return;
  264. // A new document element was created in a frame that did not commit a
  265. // provisional load. This can be due to a doc.write in the frame that aborted
  266. // a navigation. Create a page timing sender to track this load. This sender
  267. // will only send resource usage updates to the browser process. There
  268. // currently is not infrastructure in the browser process to monitor this case
  269. // and properly handle timing updates without a committed load.
  270. // TODO(https://crbug.com/1097127): Implement proper handling of timing
  271. // updates in the browser process and create a normal page timing sender.
  272. // It should not be possible to have a |provisional_frame_resource_data_use_|
  273. // object at this point. If we did, it means we reached
  274. // ReadyToCommitNavigation() and aborted prior to load commit which should not
  275. // be possible.
  276. DCHECK(!provisional_frame_resource_data_use_);
  277. Timing timing = GetTiming();
  278. page_timing_metrics_sender_ = std::make_unique<PageTimingMetricsSender>(
  279. CreatePageTimingSender(true /* limited_sending_mode */), CreateTimer(),
  280. std::move(timing.relative_timing), timing.monotonic_timing,
  281. std::make_unique<PageResourceDataUse>());
  282. OnMetricsSenderCreated();
  283. }
  284. void MetricsRenderFrameObserver::DidCommitProvisionalLoad(
  285. ui::PageTransition transition) {
  286. // Make sure to release the sender for a previous navigation, if we have one.
  287. page_timing_metrics_sender_.reset();
  288. if (HasNoRenderFrame())
  289. return;
  290. // Update metadata once the load has been committed. There is no guarantee
  291. // that the provisional resource will have been reported as an ad by this
  292. // point. Therefore, need to update metadata for the resource after the load.
  293. // Consumers may receive the correct ad information late.
  294. UpdateResourceMetadata(provisional_frame_resource_data_use_->resource_id());
  295. provisional_frame_resource_id_ =
  296. provisional_frame_resource_data_use_->resource_id();
  297. Timing timing = GetTiming();
  298. page_timing_metrics_sender_ = std::make_unique<PageTimingMetricsSender>(
  299. CreatePageTimingSender(false /* limited_sending_mode*/), CreateTimer(),
  300. std::move(timing.relative_timing), timing.monotonic_timing,
  301. std::move(provisional_frame_resource_data_use_));
  302. OnMetricsSenderCreated();
  303. }
  304. void MetricsRenderFrameObserver::SetAdResourceTracker(
  305. subresource_filter::AdResourceTracker* ad_resource_tracker) {
  306. // Remove all sources and set a new source for the observer.
  307. scoped_ad_resource_observation_.Reset();
  308. scoped_ad_resource_observation_.Observe(ad_resource_tracker);
  309. }
  310. void MetricsRenderFrameObserver::OnAdResourceTrackerGoingAway() {
  311. DCHECK(scoped_ad_resource_observation_.IsObserving());
  312. scoped_ad_resource_observation_.Reset();
  313. }
  314. void MetricsRenderFrameObserver::OnAdResourceObserved(int request_id) {
  315. ad_request_ids_.insert(request_id);
  316. }
  317. void MetricsRenderFrameObserver::OnMainFrameIntersectionChanged(
  318. const gfx::Rect& main_frame_intersection_rect) {
  319. if (page_timing_metrics_sender_) {
  320. page_timing_metrics_sender_->OnMainFrameIntersectionChanged(
  321. main_frame_intersection_rect);
  322. return;
  323. }
  324. main_frame_intersection_rect_before_metrics_sender_created_ =
  325. main_frame_intersection_rect;
  326. }
  327. void MetricsRenderFrameObserver::OnMainFrameViewportRectangleChanged(
  328. const gfx::Rect& main_frame_viewport_rect) {
  329. if (page_timing_metrics_sender_) {
  330. page_timing_metrics_sender_->OnMainFrameViewportRectangleChanged(
  331. main_frame_viewport_rect);
  332. }
  333. }
  334. void MetricsRenderFrameObserver::OnMobileFriendlinessChanged(
  335. const blink::MobileFriendliness& mf) {
  336. if (page_timing_metrics_sender_)
  337. page_timing_metrics_sender_->DidObserveMobileFriendlinessChanged(mf);
  338. }
  339. bool MetricsRenderFrameObserver::SetUpSmoothnessReporting(
  340. base::ReadOnlySharedMemoryRegion& shared_memory) {
  341. if (page_timing_metrics_sender_) {
  342. page_timing_metrics_sender_->SetUpSmoothnessReporting(
  343. std::move(shared_memory));
  344. } else {
  345. ukm_smoothness_data_ = std::move(shared_memory);
  346. }
  347. return true;
  348. }
  349. void MetricsRenderFrameObserver::MaybeSetCompletedBeforeFCP(int request_id) {
  350. if (HasNoRenderFrame())
  351. return;
  352. const blink::WebPerformance& perf =
  353. render_frame()->GetWebFrame()->Performance();
  354. // Blink returns 0 if the performance metrics are unavailable. Check that
  355. // navigation start is set to determine if performance metrics are
  356. // available.
  357. if (perf.NavigationStart() == 0)
  358. return;
  359. // This should not be possible, but none the less occasionally fails in edge
  360. // case tests. Since we don't expect this to be valid, throw out this entry.
  361. // See crbug.com/1027535.
  362. if (base::Time::Now() < base::Time::FromDoubleT(perf.NavigationStart()))
  363. return;
  364. if (perf.FirstContentfulPaint() == 0)
  365. before_fcp_request_ids_.insert(request_id);
  366. }
  367. MetricsRenderFrameObserver::Timing::Timing(
  368. mojom::PageLoadTimingPtr relative_timing,
  369. const PageTimingMetadataRecorder::MonotonicTiming& monotonic_timing)
  370. : relative_timing(std::move(relative_timing)),
  371. monotonic_timing(monotonic_timing) {}
  372. MetricsRenderFrameObserver::Timing::~Timing() = default;
  373. MetricsRenderFrameObserver::Timing::Timing(Timing&&) = default;
  374. MetricsRenderFrameObserver::Timing& MetricsRenderFrameObserver::Timing::
  375. operator=(Timing&&) = default;
  376. void MetricsRenderFrameObserver::UpdateResourceMetadata(int request_id) {
  377. if (!page_timing_metrics_sender_)
  378. return;
  379. // If the request is an ad, pop it off the list of known ad requests.
  380. auto ad_resource_it = ad_request_ids_.find(request_id);
  381. bool reported_as_ad_resource =
  382. ad_request_ids_.find(request_id) != ad_request_ids_.end();
  383. if (reported_as_ad_resource)
  384. ad_request_ids_.erase(ad_resource_it);
  385. // If the request completed before fcp, pop it off the list of known
  386. // before-fcp requests.
  387. auto before_fcp_it = before_fcp_request_ids_.find(request_id);
  388. bool completed_before_fcp = before_fcp_it != before_fcp_request_ids_.end();
  389. if (completed_before_fcp) {
  390. before_fcp_request_ids_.erase(before_fcp_it);
  391. }
  392. bool is_main_frame_resource = render_frame()->IsMainFrame();
  393. if (provisional_frame_resource_data_use_ &&
  394. provisional_frame_resource_data_use_->resource_id() == request_id) {
  395. if (reported_as_ad_resource)
  396. provisional_frame_resource_data_use_->SetReportedAsAdResource(
  397. reported_as_ad_resource);
  398. provisional_frame_resource_data_use_->SetIsMainFrameResource(
  399. is_main_frame_resource);
  400. // Don't bother with before-fcp metrics for a provisional frame.
  401. } else {
  402. page_timing_metrics_sender_->UpdateResourceMetadata(
  403. request_id, reported_as_ad_resource, is_main_frame_resource,
  404. completed_before_fcp);
  405. }
  406. }
  407. void MetricsRenderFrameObserver::SendMetrics() {
  408. if (!page_timing_metrics_sender_)
  409. return;
  410. if (HasNoRenderFrame())
  411. return;
  412. Timing timing = GetTiming();
  413. page_timing_metrics_sender_->Update(std::move(timing.relative_timing),
  414. timing.monotonic_timing);
  415. }
  416. void MetricsRenderFrameObserver::OnMetricsSenderCreated() {
  417. if (ukm_smoothness_data_.IsValid()) {
  418. page_timing_metrics_sender_->SetUpSmoothnessReporting(
  419. std::move(ukm_smoothness_data_));
  420. }
  421. // Send the latest the frame intersection update, as otherwise we may miss
  422. // this information for a frame completely if there are no future updates.
  423. if (main_frame_intersection_rect_before_metrics_sender_created_) {
  424. page_timing_metrics_sender_->OnMainFrameIntersectionChanged(
  425. *main_frame_intersection_rect_before_metrics_sender_created_);
  426. main_frame_intersection_rect_before_metrics_sender_created_.reset();
  427. }
  428. }
  429. MetricsRenderFrameObserver::Timing MetricsRenderFrameObserver::GetTiming()
  430. const {
  431. const blink::WebPerformance& perf =
  432. render_frame()->GetWebFrame()->Performance();
  433. mojom::PageLoadTimingPtr timing(CreatePageLoadTiming());
  434. PageTimingMetadataRecorder::MonotonicTiming monotonic_timing;
  435. double start = perf.NavigationStart();
  436. timing->navigation_start = base::Time::FromDoubleT(start);
  437. monotonic_timing.navigation_start = perf.NavigationStartAsMonotonicTime();
  438. if (perf.InputForNavigationStart() > 0.0) {
  439. timing->input_to_navigation_start =
  440. ClampDelta(start, perf.InputForNavigationStart());
  441. }
  442. if (perf.FirstInputDelay().has_value()) {
  443. timing->interactive_timing->first_input_delay = *perf.FirstInputDelay();
  444. monotonic_timing.first_input_delay = perf.FirstInputDelay();
  445. }
  446. if (perf.FirstInputTimestamp().has_value()) {
  447. timing->interactive_timing->first_input_timestamp =
  448. ClampDelta((*perf.FirstInputTimestamp()).InSecondsF(), start);
  449. }
  450. if (perf.FirstInputTimestampAsMonotonicTime()) {
  451. monotonic_timing.first_input_timestamp =
  452. perf.FirstInputTimestampAsMonotonicTime();
  453. }
  454. if (perf.LongestInputDelay().has_value()) {
  455. timing->interactive_timing->longest_input_delay = *perf.LongestInputDelay();
  456. }
  457. if (perf.LongestInputTimestamp().has_value()) {
  458. timing->interactive_timing->longest_input_timestamp =
  459. ClampDelta((*perf.LongestInputTimestamp()).InSecondsF(), start);
  460. }
  461. if (perf.FirstInputProcessingTime().has_value()) {
  462. timing->interactive_timing->first_input_processing_time =
  463. *perf.FirstInputProcessingTime();
  464. }
  465. if (perf.FirstScrollDelay().has_value()) {
  466. timing->interactive_timing->first_scroll_delay = *perf.FirstScrollDelay();
  467. }
  468. if (perf.FirstScrollTimestamp().has_value()) {
  469. timing->interactive_timing->first_scroll_timestamp =
  470. ClampDelta((*perf.FirstScrollTimestamp()).InSecondsF(), start);
  471. }
  472. if (perf.ResponseStart() > 0.0)
  473. timing->response_start = ClampDelta(perf.ResponseStart(), start);
  474. if (perf.DomContentLoadedEventStart() > 0.0) {
  475. timing->document_timing->dom_content_loaded_event_start =
  476. ClampDelta(perf.DomContentLoadedEventStart(), start);
  477. }
  478. if (perf.LoadEventStart() > 0.0) {
  479. timing->document_timing->load_event_start =
  480. ClampDelta(perf.LoadEventStart(), start);
  481. }
  482. if (perf.FirstPaint() > 0.0)
  483. timing->paint_timing->first_paint = ClampDelta(perf.FirstPaint(), start);
  484. if (!perf.BackForwardCacheRestore().empty()) {
  485. blink::WebPerformance::BackForwardCacheRestoreTimings restore_timings =
  486. perf.BackForwardCacheRestore();
  487. for (const auto& restore_timing : restore_timings) {
  488. double navigation_start = restore_timing.navigation_start;
  489. double first_paint = restore_timing.first_paint;
  490. absl::optional<base::TimeDelta> first_input_delay =
  491. restore_timing.first_input_delay;
  492. auto back_forward_cache_timing = mojom::BackForwardCacheTiming::New();
  493. if (first_paint) {
  494. back_forward_cache_timing
  495. ->first_paint_after_back_forward_cache_restore =
  496. ClampDelta(first_paint, navigation_start);
  497. }
  498. for (double raf : restore_timing.request_animation_frames) {
  499. if (!raf)
  500. break;
  501. back_forward_cache_timing
  502. ->request_animation_frames_after_back_forward_cache_restore
  503. .push_back(ClampDelta(raf, navigation_start));
  504. }
  505. if (first_input_delay.has_value()) {
  506. back_forward_cache_timing
  507. ->first_input_delay_after_back_forward_cache_restore =
  508. first_input_delay;
  509. }
  510. timing->back_forward_cache_timings.push_back(
  511. std::move(back_forward_cache_timing));
  512. }
  513. }
  514. if (perf.FirstImagePaint() > 0.0) {
  515. timing->paint_timing->first_image_paint =
  516. ClampDelta(perf.FirstImagePaint(), start);
  517. }
  518. if (perf.FirstContentfulPaint() > 0.0) {
  519. DCHECK(perf.FirstEligibleToPaint() > 0);
  520. timing->paint_timing->first_contentful_paint =
  521. ClampDelta(perf.FirstContentfulPaint(), start);
  522. monotonic_timing.first_contentful_paint =
  523. ClampToStart(perf.FirstContentfulPaintAsMonotonicTime(),
  524. perf.NavigationStartAsMonotonicTime());
  525. }
  526. if (perf.FirstMeaningfulPaint() > 0.0) {
  527. timing->paint_timing->first_meaningful_paint =
  528. ClampDelta(perf.FirstMeaningfulPaint(), start);
  529. }
  530. if (perf.LargestImagePaintSize() > 0) {
  531. timing->paint_timing->largest_contentful_paint->largest_image_paint_size =
  532. perf.LargestImagePaintSize();
  533. // Note that size can be nonzero while the time is 0 since a time of 0 is
  534. // sent when the image is painting. We assign the time even when it is 0 so
  535. // that it's not ignored, but need to be careful when doing operations on
  536. // the value.
  537. timing->paint_timing->largest_contentful_paint->largest_image_paint =
  538. perf.LargestImagePaint() == 0.0
  539. ? base::TimeDelta()
  540. : ClampDelta(perf.LargestImagePaint(), start);
  541. timing->paint_timing->largest_contentful_paint->type =
  542. LargestContentfulPaintTypeToUKMFlags(perf.LargestContentfulPaintType());
  543. timing->paint_timing->largest_contentful_paint->image_bpp =
  544. perf.LargestContentfulPaintImageBPP();
  545. }
  546. if (perf.LargestTextPaintSize() > 0) {
  547. // LargestTextPaint and LargestTextPaintSize should be available at the
  548. // same time. This is a renderer side DCHECK to ensure this.
  549. DCHECK(perf.LargestTextPaint());
  550. timing->paint_timing->largest_contentful_paint->largest_text_paint =
  551. ClampDelta(perf.LargestTextPaint(), start);
  552. timing->paint_timing->largest_contentful_paint->largest_text_paint_size =
  553. perf.LargestTextPaintSize();
  554. }
  555. if (perf.ExperimentalLargestImagePaintSize() > 0) {
  556. timing->paint_timing->experimental_largest_contentful_paint
  557. ->largest_image_paint_size = perf.ExperimentalLargestImagePaintSize();
  558. // Note that size can be nonzero while the time is 0 since a time of 0 is
  559. // sent when the image is painting. We assign the time even when it is 0 so
  560. // that it's not ignored, but need to be careful when doing operations on
  561. // the value.
  562. timing->paint_timing->experimental_largest_contentful_paint
  563. ->largest_image_paint =
  564. perf.ExperimentalLargestImagePaint() == 0.0
  565. ? base::TimeDelta()
  566. : ClampDelta(perf.ExperimentalLargestImagePaint(), start);
  567. timing->paint_timing->experimental_largest_contentful_paint->type =
  568. LargestContentfulPaintTypeToUKMFlags(perf.LargestContentfulPaintType());
  569. }
  570. if (perf.ExperimentalLargestTextPaintSize() > 0) {
  571. // ExperimentalLargestTextPaint and ExperimentalLargestTextPaintSize should
  572. // be available at the same time. This is a renderer side DCHECK to ensure
  573. // this.
  574. DCHECK(perf.ExperimentalLargestTextPaint());
  575. timing->paint_timing->experimental_largest_contentful_paint
  576. ->largest_text_paint =
  577. ClampDelta(perf.ExperimentalLargestTextPaint(), start);
  578. timing->paint_timing->experimental_largest_contentful_paint
  579. ->largest_text_paint_size = perf.ExperimentalLargestTextPaintSize();
  580. }
  581. // It is possible for a frame to switch from eligible for painting to
  582. // ineligible for it prior to the first paint. If this occurs, we need to
  583. // propagate the null value.
  584. if (perf.FirstEligibleToPaint() > 0) {
  585. timing->paint_timing->first_eligible_to_paint =
  586. ClampDelta(perf.FirstEligibleToPaint(), start);
  587. } else {
  588. timing->paint_timing->first_eligible_to_paint.reset();
  589. }
  590. if (perf.FirstInputOrScrollNotifiedTimestamp() > 0) {
  591. timing->paint_timing->first_input_or_scroll_notified_timestamp =
  592. ClampDelta(perf.FirstInputOrScrollNotifiedTimestamp(), start);
  593. }
  594. if (perf.ParseStart() > 0.0)
  595. timing->parse_timing->parse_start = ClampDelta(perf.ParseStart(), start);
  596. if (perf.ParseStop() > 0.0)
  597. timing->parse_timing->parse_stop = ClampDelta(perf.ParseStop(), start);
  598. if (timing->parse_timing->parse_start) {
  599. // If we started parsing, record all parser durations such as the amount of
  600. // time blocked on script load, even if those values are zero.
  601. timing->parse_timing->parse_blocked_on_script_load_duration =
  602. base::Seconds(perf.ParseBlockedOnScriptLoadDuration());
  603. timing->parse_timing
  604. ->parse_blocked_on_script_load_from_document_write_duration =
  605. base::Seconds(perf.ParseBlockedOnScriptLoadFromDocumentWriteDuration());
  606. timing->parse_timing->parse_blocked_on_script_execution_duration =
  607. base::Seconds(perf.ParseBlockedOnScriptExecutionDuration());
  608. timing->parse_timing
  609. ->parse_blocked_on_script_execution_from_document_write_duration =
  610. base::Seconds(
  611. perf.ParseBlockedOnScriptExecutionFromDocumentWriteDuration());
  612. }
  613. if (perf.LastPortalActivatedPaint().has_value()) {
  614. timing->paint_timing->portal_activated_paint =
  615. *perf.LastPortalActivatedPaint();
  616. }
  617. if (perf.PrerenderActivationStart().has_value())
  618. timing->activation_start = perf.PrerenderActivationStart();
  619. if (perf.UserTimingMarkFullyLoaded().has_value())
  620. timing->user_timing_mark_fully_loaded = perf.UserTimingMarkFullyLoaded();
  621. if (perf.UserTimingMarkFullyVisible().has_value())
  622. timing->user_timing_mark_fully_visible = perf.UserTimingMarkFullyVisible();
  623. if (perf.UserTimingMarkInteractive().has_value())
  624. timing->user_timing_mark_interactive = perf.UserTimingMarkInteractive();
  625. return Timing(std::move(timing), monotonic_timing);
  626. }
  627. std::unique_ptr<base::OneShotTimer> MetricsRenderFrameObserver::CreateTimer() {
  628. return std::make_unique<base::OneShotTimer>();
  629. }
  630. std::unique_ptr<PageTimingSender>
  631. MetricsRenderFrameObserver::CreatePageTimingSender(bool limited_sending_mode) {
  632. return base::WrapUnique<PageTimingSender>(
  633. new MojoPageTimingSender(render_frame(), limited_sending_mode));
  634. }
  635. bool MetricsRenderFrameObserver::HasNoRenderFrame() const {
  636. bool no_frame = !render_frame() || !render_frame()->GetWebFrame();
  637. DCHECK(!no_frame);
  638. return no_frame;
  639. }
  640. void MetricsRenderFrameObserver::OnDestruct() {
  641. delete this;
  642. }
  643. } // namespace page_load_metrics