Commit 1b2b965b authored by horo's avatar horo Committed by Commit bot

Support PerformanceResourceTiming for Service Worker Navigation Preload

BUG=712809

Review-Url: https://codereview.chromium.org/2847893003
Cr-Commit-Position: refs/heads/master@{#467979}
parent 8bd0a4b2
......@@ -421,7 +421,10 @@ class ServiceWorkerContextClient::NavigationPreloadRequest final
client->OnNavigationPreloadResponse(fetch_event_id_, std::move(response_),
nullptr);
// This will delete |this|.
client->OnNavigationPreloadComplete(fetch_event_id_);
client->OnNavigationPreloadComplete(
fetch_event_id_, response_head.response_start,
response_head.encoded_data_length, 0 /* encoded_body_length */,
0 /* decoded_body_length */);
}
void OnDataDownloaded(int64_t data_length,
......@@ -484,7 +487,9 @@ class ServiceWorkerContextClient::NavigationPreloadRequest final
nullptr);
}
// This will delete |this|.
client->OnNavigationPreloadComplete(fetch_event_id_);
client->OnNavigationPreloadComplete(
fetch_event_id_, status.completion_time, status.encoded_data_length,
status.encoded_body_length, status.decoded_body_length);
}
private:
......@@ -1636,7 +1641,14 @@ void ServiceWorkerContextClient::OnNavigationPreloadError(
}
void ServiceWorkerContextClient::OnNavigationPreloadComplete(
int fetch_event_id) {
int fetch_event_id,
base::TimeTicks completion_time,
int64_t encoded_data_length,
int64_t encoded_body_length,
int64_t decoded_body_length) {
proxy_->OnNavigationPreloadComplete(
fetch_event_id, (completion_time - base::TimeTicks()).InSecondsF(),
encoded_data_length, encoded_body_length, decoded_body_length);
context_->preload_requests.Remove(fetch_event_id);
}
......
......@@ -318,7 +318,11 @@ class ServiceWorkerContextClient : public blink::WebServiceWorkerContextClient,
// Called when the navigation preload request completed. Either
// OnNavigationPreloadComplete() or OnNavigationPreloadError() must be called
// to release the preload related resources.
void OnNavigationPreloadComplete(int fetch_event_id);
void OnNavigationPreloadComplete(int fetch_event_id,
base::TimeTicks completion_time,
int64_t encoded_data_length,
int64_t encoded_body_length,
int64_t decoded_body_length);
// Called when an error occurred while receiving the response of the
// navigation preload request.
void OnNavigationPreloadError(
......
<!DOCTYPE html>
<meta charset="utf-8">
<title>Navigation Preload Resource Timing</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/test-helpers.sub.js"></script>
<script>
function check_timing_entry(entry, url, decodedBodySize, encodedBodySize) {
assert_equals(entry.name, url, 'The entry name of '+ url);
assert_equals(
entry.entryType, 'resource',
'The entryType of preload response timing entry must be "resource' +
'" :' + url);
assert_equals(
entry.initiatorType, 'other',
'The initiatorType of preload response timing entry must be ' +
'"other":' + url);
// If the server returns the redirect response, |decodedBodySize| is null and
// |entry.decodedBodySize| shuld be 0. Otherwise |entry.decodedBodySize| must
// same as |decodedBodySize|
assert_equals(
entry.decodedBodySize, Number(decodedBodySize),
'decodedBodySize must same as the decoded size in the server:' + url);
// If the server returns the redirect response, |encodedBodySize| is null and
// |entry.encodedBodySize| shuld be 0. Otherwise |entry.encodedBodySize| must
// same as |encodedBodySize|
assert_equals(
entry.encodedBodySize, Number(encodedBodySize),
'encodedBodySize must same as the encoded size in the server:' + url);
assert_greater_than(
entry.transferSize, entry.decodedBodySize,
'transferSize must greater then encodedBodySize.');
assert_greater_than(entry.startTime, 0, 'startTime of ' + url);
assert_greater_than_equal(entry.fetchStart, entry.startTime,
'fetchStart >= startTime of ' + url);
assert_greater_than_equal(entry.domainLookupStart, entry.fetchStart,
'domainLookupStart >= fetchStart of ' + url);
assert_greater_than_equal(entry.domainLookupEnd, entry.domainLookupStart,
'domainLookupEnd >= domainLookupStart of ' + url);
assert_greater_than_equal(entry.connectStart, entry.domainLookupEnd,
'connectStart >= domainLookupEnd of ' + url);
assert_greater_than_equal(entry.connectEnd, entry.connectStart,
'connectEnd >= connectStart of ' + url);
assert_greater_than_equal(entry.requestStart, entry.connectEnd,
'requestStart >= connectEnd of ' + url);
assert_greater_than_equal(entry.responseStart, entry.requestStart,
'domainLookupStart >= requestStart of ' + url);
assert_greater_than_equal(entry.responseEnd, entry.responseStart,
'responseEnd >= responseStart of ' + url);
assert_greater_than(entry.duration, 0, 'duration of ' + url);
}
promise_test(t => {
var script = 'resources/resource-timing-worker.js';
var scope = 'resources/resource-timing-scope.py';
var registration;
var frames = [];
return service_worker_unregister_and_register(t, script, scope)
.then(reg => {
registration = reg;
add_completion_callback(_ => registration.unregister());
return wait_for_state(t, registration.installing, 'activated');
})
.then(_ => with_iframe(scope + '?type=normal'))
.then(frame => {
frames.push(frame);
return with_iframe(scope + '?type=redirect');
})
.then(frame => {
frames.push(frame);
frames.forEach(frame => {
var result = JSON.parse(frame.contentDocument.body.textContent);
assert_equals(
result.timingEntries.length, 1,
'performance.getEntriesByName() must returns one ' +
'PerformanceResourceTiming entry for the navigation preload.');
var entry = result.timingEntries[0];
check_timing_entry(entry, frame.src, result.decodedBodySize,
result.encodedBodySize);
frame.remove();
});
return registration.unregister();
});
}, 'Navigation Preload Resource Timing.');
</script>
import zlib
def main(request, response):
type = request.GET.first("type")
if type == "normal":
content = "This is Navigation Preload Resource Timing test."
output = zlib.compress(content, 9)
headers = [("Content-type", "text/plain"),
("Content-Encoding", "deflate"),
("X-Decoded-Body-Size", len(content)),
("X-Encoded-Body-Size", len(output)),
("Content-Length", len(output))]
return headers, output
if type == "redirect":
response.status = 302
response.headers.append("Location", "redirect-redirected.html")
return ""
self.addEventListener('activate', event => {
event.waitUntil(self.registration.navigationPreload.enable());
});
self.addEventListener('fetch', event => {
event.respondWith(
event.preloadResponse
.then(response => {
var headers = response.headers;
return response.text().then(text =>
new Response(
JSON.stringify({
decodedBodySize: headers.get('X-Decoded-Body-Size'),
encodedBodySize: headers.get('X-Encoded-Body-Size'),
timingEntries: performance.getEntriesByName(event.request.url)
}),
{headers: {'Content-Type': 'text/html'}}));
}));
});
<!DOCTYPE html>
<meta charset="utf-8">
<title>Navigation Preload Resource Timing after terminating SW</title>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../resources/test-helpers.js"></script>
<script>
function check_timing_entry(entry, allow_negative_value) {
var name = entry.name;
if (!allow_negative_value) {
assert_greater_than(entry.startTime, 0, 'startTime of ' + name);
assert_greater_than(entry.fetchStart, 0, 'fetchStart of ' + name);
assert_greater_than(entry.domainLookupStart, 0,
'domainLookupStart of ' + name);
assert_greater_than(entry.domainLookupEnd, 0,
'domainLookupEnd of ' + name);
assert_greater_than(entry.connectStart, 0, 'connectStart of ' + name);
assert_greater_than(entry.connectEnd, 0, 'connectEnd of ' + name);
assert_greater_than(entry.requestStart, 0, 'requestStart of ' + name);
assert_greater_than(entry.responseStart, 0, 'responseStart of ' + name);
assert_greater_than(entry.responseEnd, 0, 'responseEnd of ' + name);
}
assert_greater_than_equal(entry.fetchStart, entry.startTime,
'fetchStart >= startTime of ' + name);
assert_greater_than_equal(entry.domainLookupStart, entry.fetchStart,
'domainLookupStart >= fetchStart of ' + name);
assert_greater_than_equal(entry.domainLookupEnd, entry.domainLookupStart,
'domainLookupEnd >= domainLookupStart of ' + name);
assert_greater_than_equal(entry.connectStart, entry.domainLookupEnd,
'connectStart >= domainLookupEnd of ' + name);
assert_greater_than_equal(entry.connectEnd, entry.connectStart,
'connectEnd >= connectStart of ' + name);
assert_greater_than_equal(entry.requestStart, entry.connectEnd,
'requestStart >= connectEnd of ' + name);
assert_greater_than_equal(entry.responseStart, entry.requestStart,
'domainLookupStart >= requestStart of ' + name);
assert_greater_than_equal(entry.responseEnd, entry.responseStart,
'responseEnd >= responseStart of ' + name);
assert_greater_than(entry.duration, 0, 'duration of ' + name);
}
promise_test(t => {
var script = 'resources/navigation-preload-resource-timing-worker.js';
var scope = 'resources/navigation-preload-resource-timing-scope.html';
var registration;
var frames = [];
return service_worker_unregister_and_register(t, script, scope)
.then(reg => {
registration = reg;
add_completion_callback(_ => registration.unregister());
return wait_for_state(t, registration.installing, 'activated');
})
.then(_ => with_iframe(scope + '?1'))
.then(frame => {
frames.push(frame);
return with_iframe(scope + '?2');
})
.then(frame => {
frames.push(frame);
return internals.terminateServiceWorker(registration.active);
})
.then(_ => with_iframe(scope + '?3'))
.then(frame => {
frames.push(frame);
var results = frames.map(frame => {
var result = JSON.parse(frame.contentDocument.body.textContent);
return result.timingEntries.filter(entry => {
return entry.name.indexOf(scope) != -1;
})
});
assert_equals(results.length, 3);
assert_equals(results[0].length, 1,
'The first result must contain only one entry.');
assert_equals(results[0][0].name, frames[0].src,
'The entry of the first result must be the entry for ' +
'the first iframe.');
assert_equals(results[1].length, 2,
'The second result must contain two entry.');
assert_object_equals(results[1][0], results[0][0],
'The first entry of the second result must be ' +
'same as the entry of the first result.');
assert_equals(results[1][1].name, frames[1].src,
'The second entry of the second result must be the ' +
'entry for the second iframe.');
assert_equals(results[2].length, 1,
'The third result must contain only one entry.');
assert_equals(results[2][0].name, frames[2].src,
'The entry of the third result must be the entry for ' +
'the third iframe.');
check_timing_entry(results[1][0], false /* allow_negative_value */);
check_timing_entry(results[1][1], false /* allow_negative_value */);
check_timing_entry(results[2][0], true /* allow_negative_value */);
frames.forEach(frame => frame.remove());
return registration.unregister();
});
}, 'Navigation Preload Resource Timing after terminating SW.');
</script>
self.addEventListener('activate', event => {
event.waitUntil(self.registration.navigationPreload.enable());
});
self.addEventListener('fetch', event => {
event.respondWith(
event.preloadResponse
.then(response => response.text())
.then(text =>
new Response(
JSON.stringify({timingEntries: performance.getEntries()}),
{headers: {'Content-Type': 'text/html'}})));
});
......@@ -505,15 +505,17 @@ double PerformanceBase::ClampTimeResolution(double time_seconds) {
return floor(time_seconds / kResolutionSeconds) * kResolutionSeconds;
}
// static
DOMHighResTimeStamp PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
double time_origin,
double monotonic_time) {
double monotonic_time,
bool allow_negative_value) {
// Avoid exposing raw platform timestamps.
if (!monotonic_time || !time_origin)
return 0.0;
double time_in_seconds = monotonic_time - time_origin;
if (time_in_seconds < 0)
if (time_in_seconds < 0 && !allow_negative_value)
return 0.0;
return ConvertSecondsToDOMHighResTimeStamp(
ClampTimeResolution(time_in_seconds));
......@@ -521,7 +523,8 @@ DOMHighResTimeStamp PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
DOMHighResTimeStamp PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
double monotonic_time) const {
return MonotonicTimeToDOMHighResTimeStamp(time_origin_, monotonic_time);
return MonotonicTimeToDOMHighResTimeStamp(time_origin_, monotonic_time,
false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceBase::now() const {
......
......@@ -76,7 +76,8 @@ class CORE_EXPORT PerformanceBase : public EventTargetWithInlineData {
static DOMHighResTimeStamp MonotonicTimeToDOMHighResTimeStamp(
double time_origin,
double monotonic_time);
double monotonic_time,
bool allow_negative_value);
// Translate given platform monotonic time in seconds into a high resolution
// DOMHighResTimeStamp in milliseconds. The result timestamp is relative to
......
......@@ -133,7 +133,8 @@ DOMHighResTimeStamp PerformanceNavigationTiming::unloadEventStart() const {
!timing->HasSameOriginAsPreviousDocument())
return 0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->UnloadEventStart());
time_origin_, timing->UnloadEventStart(),
false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::unloadEventEnd() const {
......@@ -144,7 +145,7 @@ DOMHighResTimeStamp PerformanceNavigationTiming::unloadEventEnd() const {
!timing->HasSameOriginAsPreviousDocument())
return 0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->UnloadEventEnd());
time_origin_, timing->UnloadEventEnd(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::domInteractive() const {
......@@ -152,7 +153,7 @@ DOMHighResTimeStamp PerformanceNavigationTiming::domInteractive() const {
if (!timing)
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->DomInteractive());
time_origin_, timing->DomInteractive(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::domContentLoadedEventStart()
......@@ -161,7 +162,8 @@ DOMHighResTimeStamp PerformanceNavigationTiming::domContentLoadedEventStart()
if (!timing)
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->DomContentLoadedEventStart());
time_origin_, timing->DomContentLoadedEventStart(),
false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::domContentLoadedEventEnd()
......@@ -170,7 +172,8 @@ DOMHighResTimeStamp PerformanceNavigationTiming::domContentLoadedEventEnd()
if (!timing)
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->DomContentLoadedEventEnd());
time_origin_, timing->DomContentLoadedEventEnd(),
false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::domComplete() const {
......@@ -178,7 +181,7 @@ DOMHighResTimeStamp PerformanceNavigationTiming::domComplete() const {
if (!timing)
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->DomComplete());
time_origin_, timing->DomComplete(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::loadEventStart() const {
......@@ -186,7 +189,7 @@ DOMHighResTimeStamp PerformanceNavigationTiming::loadEventStart() const {
if (!timing)
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->LoadEventStart());
time_origin_, timing->LoadEventStart(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::loadEventEnd() const {
......@@ -194,7 +197,7 @@ DOMHighResTimeStamp PerformanceNavigationTiming::loadEventEnd() const {
if (!timing)
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->LoadEventEnd());
time_origin_, timing->LoadEventEnd(), false /* allow_negative_value */);
}
AtomicString PerformanceNavigationTiming::type() const {
......@@ -219,7 +222,7 @@ DOMHighResTimeStamp PerformanceNavigationTiming::redirectStart() const {
if (!allow_redirect_details || !timing)
return 0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->RedirectStart());
time_origin_, timing->RedirectStart(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::redirectEnd() const {
......@@ -228,7 +231,7 @@ DOMHighResTimeStamp PerformanceNavigationTiming::redirectEnd() const {
if (!allow_redirect_details || !timing)
return 0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->RedirectEnd());
time_origin_, timing->RedirectEnd(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::fetchStart() const {
......@@ -236,7 +239,7 @@ DOMHighResTimeStamp PerformanceNavigationTiming::fetchStart() const {
if (!timing)
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->FetchStart());
time_origin_, timing->FetchStart(), false /* allow_negative_value */);
}
DOMHighResTimeStamp PerformanceNavigationTiming::responseEnd() const {
......@@ -244,7 +247,7 @@ DOMHighResTimeStamp PerformanceNavigationTiming::responseEnd() const {
if (!timing)
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->ResponseEnd());
time_origin_, timing->ResponseEnd(), false /* allow_negative_value */);
}
// Overriding PerformanceEntry's attributes.
......
......@@ -46,14 +46,16 @@ PerformanceResourceTiming::PerformanceResourceTiming(
double last_redirect_end_time,
bool allow_timing_details,
bool allow_redirect_details)
: PerformanceEntry(
info.InitialURL().GetString(),
: PerformanceEntry(info.InitialURL().GetString(),
"resource",
PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(time_origin,
start_time),
PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin,
info.LoadFinishTime())),
start_time,
info.NegativeAllowed()),
PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin,
info.LoadFinishTime(),
info.NegativeAllowed())),
initiator_type_(info.InitiatorType()),
time_origin_(time_origin),
timing_(info.FinalResponse().GetResourceLoadTiming()),
......@@ -64,7 +66,8 @@ PerformanceResourceTiming::PerformanceResourceTiming(
decoded_body_size_(info.FinalResponse().DecodedBodyLength()),
did_reuse_connection_(info.FinalResponse().ConnectionReused()),
allow_timing_details_(allow_timing_details),
allow_redirect_details_(allow_redirect_details) {}
allow_redirect_details_(allow_redirect_details),
allow_negative_value_(info.NegativeAllowed()) {}
// This constructor is for PerformanceNavigationTiming.
PerformanceResourceTiming::PerformanceResourceTiming(const String& name,
......@@ -109,7 +112,7 @@ DOMHighResTimeStamp PerformanceResourceTiming::workerStart() const {
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->WorkerStart());
time_origin_, timing->WorkerStart(), allow_negative_value_);
}
DOMHighResTimeStamp PerformanceResourceTiming::WorkerReady() const {
......@@ -118,7 +121,7 @@ DOMHighResTimeStamp PerformanceResourceTiming::WorkerReady() const {
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->WorkerReady());
time_origin_, timing->WorkerReady(), allow_negative_value_);
}
DOMHighResTimeStamp PerformanceResourceTiming::redirectStart() const {
......@@ -136,7 +139,7 @@ DOMHighResTimeStamp PerformanceResourceTiming::redirectEnd() const {
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, last_redirect_end_time_);
time_origin_, last_redirect_end_time_, allow_negative_value_);
}
DOMHighResTimeStamp PerformanceResourceTiming::fetchStart() const {
......@@ -146,7 +149,7 @@ DOMHighResTimeStamp PerformanceResourceTiming::fetchStart() const {
if (last_redirect_end_time_) {
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->RequestTime());
time_origin_, timing->RequestTime(), allow_negative_value_);
}
if (DOMHighResTimeStamp worker_ready_time = WorkerReady())
......@@ -163,7 +166,7 @@ DOMHighResTimeStamp PerformanceResourceTiming::domainLookupStart() const {
return fetchStart();
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->DnsStart());
time_origin_, timing->DnsStart(), allow_negative_value_);
}
DOMHighResTimeStamp PerformanceResourceTiming::domainLookupEnd() const {
......@@ -173,8 +176,8 @@ DOMHighResTimeStamp PerformanceResourceTiming::domainLookupEnd() const {
if (!timing || timing->DnsEnd() == 0.0)
return domainLookupStart();
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(time_origin_,
timing->DnsEnd());
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->DnsEnd(), allow_negative_value_);
}
DOMHighResTimeStamp PerformanceResourceTiming::connectStart() const {
......@@ -190,8 +193,8 @@ DOMHighResTimeStamp PerformanceResourceTiming::connectStart() const {
if (timing->DnsEnd() > 0.0)
connect_start = timing->DnsEnd();
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(time_origin_,
connect_start);
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, connect_start, allow_negative_value_);
}
DOMHighResTimeStamp PerformanceResourceTiming::connectEnd() const {
......@@ -203,7 +206,7 @@ DOMHighResTimeStamp PerformanceResourceTiming::connectEnd() const {
return connectStart();
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->ConnectEnd());
time_origin_, timing->ConnectEnd(), allow_negative_value_);
}
DOMHighResTimeStamp PerformanceResourceTiming::secureConnectionStart() const {
......@@ -215,7 +218,7 @@ DOMHighResTimeStamp PerformanceResourceTiming::secureConnectionStart() const {
return 0.0;
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->SslStart());
time_origin_, timing->SslStart(), allow_negative_value_);
}
DOMHighResTimeStamp PerformanceResourceTiming::requestStart() const {
......@@ -226,7 +229,7 @@ DOMHighResTimeStamp PerformanceResourceTiming::requestStart() const {
return connectEnd();
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->SendStart());
time_origin_, timing->SendStart(), allow_negative_value_);
}
DOMHighResTimeStamp PerformanceResourceTiming::responseStart() const {
......@@ -239,15 +242,15 @@ DOMHighResTimeStamp PerformanceResourceTiming::responseStart() const {
// FIXME: This number isn't exactly correct. See the notes in
// PerformanceTiming::responseStart().
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, timing->ReceiveHeadersEnd());
time_origin_, timing->ReceiveHeadersEnd(), allow_negative_value_);
}
DOMHighResTimeStamp PerformanceResourceTiming::responseEnd() const {
if (!finish_time_)
return responseStart();
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(time_origin_,
finish_time_);
return PerformanceBase::MonotonicTimeToDOMHighResTimeStamp(
time_origin_, finish_time_, allow_negative_value_);
}
unsigned long long PerformanceResourceTiming::transferSize() const {
......
......@@ -121,6 +121,7 @@ class CORE_EXPORT PerformanceResourceTiming : public PerformanceEntry {
bool did_reuse_connection_;
bool allow_timing_details_;
bool allow_redirect_details_;
bool allow_negative_value_;
};
} // namespace blink
......
......@@ -31,6 +31,7 @@
#ifndef WorkerGlobalScopePerformance_h
#define WorkerGlobalScopePerformance_h
#include "core/CoreExport.h"
#include "core/timing/WorkerPerformance.h"
#include "core/workers/WorkerGlobalScope.h"
#include "platform/Supplementable.h"
......@@ -40,7 +41,7 @@ namespace blink {
class WorkerGlobalScope;
class WorkerGlobalScopePerformance final
class CORE_EXPORT WorkerGlobalScopePerformance final
: public GarbageCollected<WorkerGlobalScopePerformance>,
public Supplement<WorkerGlobalScope> {
USING_GARBAGE_COLLECTED_MIXIN(WorkerGlobalScopePerformance);
......
......@@ -8,12 +8,14 @@
#include "bindings/core/v8/ToV8ForCore.h"
#include "bindings/core/v8/V8PrivateProperty.h"
#include "core/dom/ExecutionContext.h"
#include "core/timing/WorkerGlobalScopePerformance.h"
#include "modules/fetch/BytesConsumerForDataConsumerHandle.h"
#include "modules/fetch/Request.h"
#include "modules/fetch/Response.h"
#include "modules/serviceworkers/FetchRespondWithObserver.h"
#include "modules/serviceworkers/ServiceWorkerError.h"
#include "modules/serviceworkers/ServiceWorkerGlobalScope.h"
#include "platform/loader/fetch/ResourceTimingInfo.h"
#include "platform/network/NetworkUtils.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/RefPtr.h"
......@@ -114,7 +116,9 @@ void FetchEvent::OnNavigationPreloadResponse(
if (!script_state->ContextIsValid())
return;
DCHECK(preload_response_property_);
DCHECK(!preload_response_);
ScriptState::Scope scope(script_state);
preload_response_ = std::move(response);
FetchResponseData* response_data =
data_consume_handle
? FetchResponseData::CreateWithBuffer(new BodyStreamBuffer(
......@@ -123,18 +127,19 @@ void FetchEvent::OnNavigationPreloadResponse(
std::move(data_consume_handle))))
: FetchResponseData::Create();
Vector<KURL> url_list(1);
url_list[0] = response->Url();
url_list[0] = preload_response_->Url();
response_data->SetURLList(url_list);
response_data->SetStatus(response->HttpStatusCode());
response_data->SetStatusMessage(response->HttpStatusText());
response_data->SetResponseTime(response->ToResourceResponse().ResponseTime());
response_data->SetStatus(preload_response_->HttpStatusCode());
response_data->SetStatusMessage(preload_response_->HttpStatusText());
response_data->SetResponseTime(
preload_response_->ToResourceResponse().ResponseTime());
const HTTPHeaderMap& headers(
response->ToResourceResponse().HttpHeaderFields());
preload_response_->ToResourceResponse().HttpHeaderFields());
for (const auto& header : headers) {
response_data->HeaderList()->Append(header.key, header.value);
}
FetchResponseData* tainted_response =
NetworkUtils::IsRedirectResponseCode(response->HttpStatusCode())
NetworkUtils::IsRedirectResponseCode(preload_response_->HttpStatusCode())
? response_data->CreateOpaqueRedirectFilteredResponse()
: response_data->CreateBasicFilteredResponse();
preload_response_property_->Resolve(
......@@ -151,6 +156,33 @@ void FetchEvent::OnNavigationPreloadError(
ServiceWorkerError::Take(nullptr, *error.get()));
}
void FetchEvent::OnNavigationPreloadComplete(
WorkerGlobalScope* worker_global_scope,
double completion_time,
int64_t encoded_data_length,
int64_t encoded_body_length,
int64_t decoded_body_length) {
DCHECK(preload_response_);
std::unique_ptr<WebURLResponse> response = std::move(preload_response_);
ResourceResponse resource_response = response->ToResourceResponse();
resource_response.SetEncodedDataLength(encoded_data_length);
resource_response.SetEncodedBodyLength(encoded_body_length);
resource_response.SetDecodedBodyLength(decoded_body_length);
// According to the current spec of Resource Timing, the initiator type of
// navigation preload request must be "other". But it may change when the spec
// discussion is settled. https://github.com/w3c/resource-timing/issues/110
RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::Create(
"other", resource_response.GetResourceLoadTiming()->RequestTime(),
false /* is_main_resource */);
info->SetNegativeAllowed(true);
info->SetLoadFinishTime(completion_time);
info->SetInitialURL(request_->url());
info->SetFinalResponse(resource_response);
info->AddFinalTransferSize(encoded_data_length);
WorkerGlobalScopePerformance::performance(*worker_global_scope)
->AddResourceTiming(*info);
}
DEFINE_TRACE(FetchEvent) {
visitor->Trace(observer_);
visitor->Trace(request_);
......
......@@ -5,6 +5,8 @@
#ifndef FetchEvent_h
#define FetchEvent_h
#include <memory>
#include "bindings/core/v8/ScriptPromise.h"
#include "bindings/core/v8/ScriptPromiseProperty.h"
#include "modules/EventModules.h"
......@@ -14,6 +16,7 @@
#include "modules/serviceworkers/FetchEventInit.h"
#include "modules/serviceworkers/WaitUntilObserver.h"
#include "platform/heap/Handle.h"
#include "platform/loader/fetch/ResourceResponse.h"
namespace blink {
......@@ -25,6 +28,7 @@ class ScriptState;
class WebDataConsumerHandle;
struct WebServiceWorkerError;
class WebURLResponse;
class WorkerGlobalScope;
// A fetch event is dispatched by the client to a service worker's script
// context. FetchRespondWithObserver can be used to notify the client about the
......@@ -58,6 +62,11 @@ class MODULES_EXPORT FetchEvent final : public ExtendableEvent {
std::unique_ptr<WebDataConsumerHandle>);
void OnNavigationPreloadError(ScriptState*,
std::unique_ptr<WebServiceWorkerError>);
void OnNavigationPreloadComplete(WorkerGlobalScope*,
double completion_time,
int64_t encoded_data_length,
int64_t encoded_body_length,
int64_t decoded_body_length);
const AtomicString& InterfaceName() const override;
......@@ -75,6 +84,7 @@ class MODULES_EXPORT FetchEvent final : public ExtendableEvent {
Member<FetchRespondWithObserver> observer_;
Member<Request> request_;
Member<PreloadResponseProperty> preload_response_property_;
std::unique_ptr<WebURLResponse> preload_response_;
String client_id_;
bool is_reload_;
};
......
......@@ -22,6 +22,7 @@ PassRefPtr<ResourceTimingInfo> ResourceTimingInfo::Adopt(
for (auto& response_data : data->redirect_chain_)
info->redirect_chain_.push_back(ResourceResponse(response_data.get()));
info->transfer_size_ = data->transfer_size_;
info->negative_allowed_ = data->negative_allowed_;
return info.Release();
}
......@@ -40,6 +41,7 @@ ResourceTimingInfo::CopyData() const {
data->redirect_chain_.push_back(response.CopyData());
data->transfer_size_ = transfer_size_;
data->is_main_resource_ = is_main_resource_;
data->negative_allowed_ = negative_allowed_;
return data;
}
......
......@@ -104,15 +104,22 @@ class PLATFORM_EXPORT ResourceTimingInfo
redirect.SetResourceLoadTiming(nullptr);
}
// The timestamps in PerformanceResourceTiming are measured relative from the
// time origin. In most cases these timestamps must be positive value, so we
// use 0 for invalid negative values. But the timestamps for Service Worker
// navigation preload requests may be negative, because these requests may
// be started before the service worker started. We set this flag true, to
// support such case.
void SetNegativeAllowed(bool negative_allowed) {
negative_allowed_ = negative_allowed;
}
bool NegativeAllowed() const { return negative_allowed_; }
private:
ResourceTimingInfo(const AtomicString& type,
const double time,
bool is_main_resource)
: type_(type),
initial_time_(time),
transfer_size_(0),
is_main_resource_(is_main_resource),
has_cross_origin_redirect_(false) {}
: type_(type), initial_time_(time), is_main_resource_(is_main_resource) {}
AtomicString type_;
AtomicString original_timing_allow_origin_;
......@@ -121,9 +128,10 @@ class PLATFORM_EXPORT ResourceTimingInfo
KURL initial_url_;
ResourceResponse final_response_;
Vector<ResourceResponse> redirect_chain_;
long long transfer_size_;
long long transfer_size_ = 0;
bool is_main_resource_;
bool has_cross_origin_redirect_;
bool has_cross_origin_redirect_ = false;
bool negative_allowed_ = false;
};
struct CrossThreadResourceTimingInfoData {
......@@ -142,6 +150,7 @@ struct CrossThreadResourceTimingInfoData {
Vector<std::unique_ptr<CrossThreadResourceResponseData>> redirect_chain_;
long long transfer_size_;
bool is_main_resource_;
bool negative_allowed_;
};
template <>
......
......@@ -72,6 +72,7 @@
#include "modules/serviceworkers/WaitUntilObserver.h"
#include "platform/CrossThreadFunctional.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/loader/fetch/ResourceResponse.h"
#include "platform/wtf/Assertions.h"
#include "platform/wtf/Functional.h"
#include "platform/wtf/PtrUtil.h"
......@@ -278,7 +279,7 @@ void ServiceWorkerGlobalScopeProxy::DispatchFetchEvent(
script_state, EventTypeNames::fetch, event_init, respond_with_observer,
wait_until_observer, navigation_preload_sent);
if (navigation_preload_sent) {
// Keep |fetchEvent| until onNavigationPreloadResponse() or
// Keep |fetchEvent| until OnNavigationPreloadComplete() or
// onNavigationPreloadError() will be called.
pending_preload_fetch_events_.insert(fetch_event_id, fetch_event);
}
......@@ -296,7 +297,9 @@ void ServiceWorkerGlobalScopeProxy::OnNavigationPreloadResponse(
int fetch_event_id,
std::unique_ptr<WebURLResponse> response,
std::unique_ptr<WebDataConsumerHandle> data_consume_handle) {
FetchEvent* fetch_event = pending_preload_fetch_events_.Take(fetch_event_id);
auto it = pending_preload_fetch_events_.find(fetch_event_id);
DCHECK(it != pending_preload_fetch_events_.end());
FetchEvent* fetch_event = it->value.Get();
DCHECK(fetch_event);
fetch_event->OnNavigationPreloadResponse(
WorkerGlobalScope()->ScriptController()->GetScriptState(),
......@@ -307,9 +310,7 @@ void ServiceWorkerGlobalScopeProxy::OnNavigationPreloadError(
int fetch_event_id,
std::unique_ptr<WebServiceWorkerError> error) {
FetchEvent* fetch_event = pending_preload_fetch_events_.Take(fetch_event_id);
// This method may be called after onNavigationPreloadResponse() was called.
if (!fetch_event)
return;
DCHECK(fetch_event);
// Display an unsanitized console message.
if (!error->unsanitized_message.IsEmpty()) {
WorkerGlobalScope()->AddConsoleMessage(ConsoleMessage::Create(
......@@ -322,6 +323,19 @@ void ServiceWorkerGlobalScopeProxy::OnNavigationPreloadError(
std::move(error));
}
void ServiceWorkerGlobalScopeProxy::OnNavigationPreloadComplete(
int fetch_event_id,
double completion_time,
int64_t encoded_data_length,
int64_t encoded_body_length,
int64_t decoded_body_length) {
FetchEvent* fetch_event = pending_preload_fetch_events_.Take(fetch_event_id);
DCHECK(fetch_event);
fetch_event->OnNavigationPreloadComplete(
WorkerGlobalScope(), completion_time, encoded_data_length,
encoded_body_length, decoded_body_length);
}
void ServiceWorkerGlobalScopeProxy::DispatchForeignFetchEvent(
int fetch_event_id,
const WebServiceWorkerRequest& web_request) {
......
......@@ -130,6 +130,11 @@ class ServiceWorkerGlobalScopeProxy final
void OnNavigationPreloadError(
int fetch_event_id,
std::unique_ptr<WebServiceWorkerError>) override;
void OnNavigationPreloadComplete(int fetch_event_id,
double completion_time,
int64_t encoded_data_length,
int64_t encoded_body_length,
int64_t decoded_body_length) override;
// WorkerReportingProxy overrides:
void CountFeature(UseCounter::Feature) override;
......
......@@ -125,6 +125,11 @@ class WebServiceWorkerContextProxy {
virtual void OnNavigationPreloadError(
int fetch_event_id,
std::unique_ptr<WebServiceWorkerError>) = 0;
virtual void OnNavigationPreloadComplete(int fetch_event_id,
double completion_time,
int64_t encoded_data_length,
int64_t encoded_body_length,
int64_t decoded_body_length) = 0;
};
} // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment