blob: 441620f5276ee6f7c97d9996af895ac256045903 [file] [log] [blame]
// Copyright 2016 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 "third_party/blink/renderer/core/timing/performance_navigation_timing.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/document_timing.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/loader/document_load_timing.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/performance_entry_names.h"
#include "third_party/blink/renderer/core/timing/performance.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
namespace blink {
namespace {
bool PassesSameOriginCheck(const ResourceResponse& response,
const SecurityOrigin& initiator_security_origin) {
const KURL& response_url = response.ResponseUrl();
scoped_refptr<const SecurityOrigin> resource_origin =
SecurityOrigin::Create(response_url);
return resource_origin->IsSameOriginWith(&initiator_security_origin);
}
bool AllowNavigationTimingRedirect(
const Vector<ResourceResponse>& redirect_chain,
const ResourceResponse& final_response,
const SecurityOrigin& initiator_security_origin) {
if (!PassesSameOriginCheck(final_response, initiator_security_origin)) {
return false;
}
for (const ResourceResponse& response : redirect_chain) {
if (!PassesSameOriginCheck(response, initiator_security_origin))
return false;
}
return true;
}
} // namespace
PerformanceNavigationTiming::PerformanceNavigationTiming(
LocalDOMWindow* window,
ResourceTimingInfo* info,
base::TimeTicks time_origin,
HeapVector<Member<PerformanceServerTiming>> server_timing)
: PerformanceResourceTiming(
info ? AtomicString(
info->FinalResponse().CurrentRequestUrl().GetString())
: g_empty_atom,
time_origin,
// TODO(crbug.com/1153336) Use network::IsUrlPotentiallyTrustworthy().
SecurityOrigin::IsSecure(window->Url()),
std::move(server_timing),
window),
ExecutionContextClient(window),
resource_timing_info_(info) {
DCHECK(window);
DCHECK(info);
}
PerformanceNavigationTiming::~PerformanceNavigationTiming() = default;
AtomicString PerformanceNavigationTiming::entryType() const {
return performance_entry_names::kNavigation;
}
PerformanceEntryType PerformanceNavigationTiming::EntryTypeEnum() const {
return PerformanceEntry::EntryType::kNavigation;
}
void PerformanceNavigationTiming::Trace(Visitor* visitor) const {
ExecutionContextClient::Trace(visitor);
PerformanceResourceTiming::Trace(visitor);
}
DocumentLoadTiming* PerformanceNavigationTiming::GetDocumentLoadTiming() const {
DocumentLoader* loader = GetDocumentLoader();
if (!loader)
return nullptr;
return &loader->GetTiming();
}
DocumentLoader* PerformanceNavigationTiming::GetDocumentLoader() const {
return DomWindow() ? DomWindow()->document()->Loader() : nullptr;
}
const DocumentTiming* PerformanceNavigationTiming::GetDocumentTiming() const {
return DomWindow() ? &DomWindow()->document()->GetTiming() : nullptr;
}
ResourceLoadTiming* PerformanceNavigationTiming::GetResourceLoadTiming() const {
return resource_timing_info_->FinalResponse().GetResourceLoadTiming();
}
bool PerformanceNavigationTiming::AllowTimingDetails() const {
return true;
}
bool PerformanceNavigationTiming::DidReuseConnection() const {
return resource_timing_info_->FinalResponse().ConnectionReused();
}
uint64_t PerformanceNavigationTiming::GetTransferSize() const {
return resource_timing_info_->TransferSize();
}
uint64_t PerformanceNavigationTiming::GetEncodedBodySize() const {
return resource_timing_info_->FinalResponse().EncodedBodyLength();
}
uint64_t PerformanceNavigationTiming::GetDecodedBodySize() const {
return resource_timing_info_->FinalResponse().DecodedBodyLength();
}
AtomicString PerformanceNavigationTiming::GetNavigationType(
WebNavigationType type,
const Document* document) {
switch (type) {
case kWebNavigationTypeReload:
return "reload";
case kWebNavigationTypeBackForward:
return "back_forward";
case kWebNavigationTypeLinkClicked:
case kWebNavigationTypeFormSubmitted:
case kWebNavigationTypeFormResubmitted:
case kWebNavigationTypeOther:
return "navigate";
}
NOTREACHED();
return "navigate";
}
AtomicString PerformanceNavigationTiming::initiatorType() const {
return performance_entry_names::kNavigation;
}
bool PerformanceNavigationTiming::GetAllowRedirectDetails() const {
if (!GetExecutionContext())
return false;
// TODO(sunjian): Think about how to make this flag deterministic.
// crbug/693183.
const blink::SecurityOrigin* security_origin =
GetExecutionContext()->GetSecurityOrigin();
return AllowNavigationTimingRedirect(resource_timing_info_->RedirectChain(),
resource_timing_info_->FinalResponse(),
*security_origin);
}
AtomicString PerformanceNavigationTiming::AlpnNegotiatedProtocol() const {
return resource_timing_info_->FinalResponse().AlpnNegotiatedProtocol();
}
AtomicString PerformanceNavigationTiming::ConnectionInfo() const {
return resource_timing_info_->FinalResponse().ConnectionInfoString();
}
DOMHighResTimeStamp PerformanceNavigationTiming::unloadEventStart() const {
bool allow_redirect_details = GetAllowRedirectDetails();
DocumentLoadTiming* timing = GetDocumentLoadTiming();
if (!allow_redirect_details || !timing ||
!timing->CanRequestFromPreviousDocument())
return 0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->UnloadEventStart(),
false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::unloadEventEnd() const {
bool allow_redirect_details = GetAllowRedirectDetails();
DocumentLoadTiming* timing = GetDocumentLoadTiming();
if (!allow_redirect_details || !timing ||
!timing->CanRequestFromPreviousDocument())
return 0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->UnloadEventEnd(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::domInteractive() const {
const DocumentTiming* timing = GetDocumentTiming();
if (!timing)
return 0.0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->DomInteractive(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::domContentLoadedEventStart()
const {
const DocumentTiming* timing = GetDocumentTiming();
if (!timing)
return 0.0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->DomContentLoadedEventStart(),
false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::domContentLoadedEventEnd()
const {
const DocumentTiming* timing = GetDocumentTiming();
if (!timing)
return 0.0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->DomContentLoadedEventEnd(),
false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::domComplete() const {
const DocumentTiming* timing = GetDocumentTiming();
if (!timing)
return 0.0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->DomComplete(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::loadEventStart() const {
DocumentLoadTiming* timing = GetDocumentLoadTiming();
if (!timing)
return 0.0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->LoadEventStart(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::loadEventEnd() const {
DocumentLoadTiming* timing = GetDocumentLoadTiming();
if (!timing)
return 0.0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->LoadEventEnd(), false /* allow_negative_value */);
}
AtomicString PerformanceNavigationTiming::type() const {
if (DomWindow()) {
return GetNavigationType(GetDocumentLoader()->GetNavigationType(),
DomWindow()->document());
}
return "navigate";
}
uint16_t PerformanceNavigationTiming::redirectCount() const {
bool allow_redirect_details = GetAllowRedirectDetails();
DocumentLoadTiming* timing = GetDocumentLoadTiming();
if (!allow_redirect_details || !timing)
return 0;
return timing->RedirectCount();
}
DOMHighResTimeStamp PerformanceNavigationTiming::redirectStart() const {
bool allow_redirect_details = GetAllowRedirectDetails();
DocumentLoadTiming* timing = GetDocumentLoadTiming();
if (!allow_redirect_details || !timing)
return 0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->RedirectStart(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::redirectEnd() const {
bool allow_redirect_details = GetAllowRedirectDetails();
DocumentLoadTiming* timing = GetDocumentLoadTiming();
if (!allow_redirect_details || !timing)
return 0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->RedirectEnd(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::fetchStart() const {
DocumentLoadTiming* timing = GetDocumentLoadTiming();
if (!timing)
return 0.0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->FetchStart(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::responseEnd() const {
DocumentLoadTiming* timing = GetDocumentLoadTiming();
if (!timing)
return 0.0;
return Performance::MonotonicTimeToDOMHighResTimeStamp(
TimeOrigin(), timing->ResponseEnd(), false /* allow_negative_value */);
}
// Overriding PerformanceEntry's attributes.
DOMHighResTimeStamp PerformanceNavigationTiming::duration() const {
return loadEventEnd();
}
void PerformanceNavigationTiming::BuildJSONValue(
V8ObjectBuilder& builder) const {
PerformanceResourceTiming::BuildJSONValue(builder);
builder.AddNumber("unloadEventStart", unloadEventStart());
builder.AddNumber("unloadEventEnd", unloadEventEnd());
builder.AddNumber("domInteractive", domInteractive());
builder.AddNumber("domContentLoadedEventStart", domContentLoadedEventStart());
builder.AddNumber("domContentLoadedEventEnd", domContentLoadedEventEnd());
builder.AddNumber("domComplete", domComplete());
builder.AddNumber("loadEventStart", loadEventStart());
builder.AddNumber("loadEventEnd", loadEventEnd());
builder.AddString("type", type());
builder.AddNumber("redirectCount", redirectCount());
}
} // namespace blink