Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "PerformanceMainThread.h"
8 : #include "PerformanceNavigation.h"
9 : #include "nsICacheInfoChannel.h"
10 :
11 : namespace mozilla {
12 : namespace dom {
13 :
14 : NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread)
15 :
16 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread,
17 : Performance)
18 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming,
19 : mNavigation)
20 0 : tmp->mMozMemory = nullptr;
21 0 : mozilla::DropJSObjects(this);
22 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
23 :
24 7 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread,
25 : Performance)
26 7 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTiming,
27 : mNavigation)
28 7 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
29 :
30 14 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMainThread,
31 : Performance)
32 14 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMozMemory)
33 14 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
34 :
35 19 : NS_IMPL_ADDREF_INHERITED(PerformanceMainThread, Performance)
36 2 : NS_IMPL_RELEASE_INHERITED(PerformanceMainThread, Performance)
37 :
38 : // QueryInterface implementation for PerformanceMainThread
39 126 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceMainThread)
40 7 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
41 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
42 0 : NS_INTERFACE_MAP_END_INHERITING(Performance)
43 :
44 7 : PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner* aWindow,
45 : nsDOMNavigationTiming* aDOMTiming,
46 7 : nsITimedChannel* aChannel)
47 : : Performance(aWindow)
48 : , mDOMTiming(aDOMTiming)
49 7 : , mChannel(aChannel)
50 : {
51 7 : MOZ_ASSERT(aWindow, "Parent window object should be provided");
52 7 : }
53 :
54 0 : PerformanceMainThread::~PerformanceMainThread()
55 : {
56 0 : mozilla::DropJSObjects(this);
57 0 : }
58 :
59 : void
60 0 : PerformanceMainThread::GetMozMemory(JSContext *aCx,
61 : JS::MutableHandle<JSObject*> aObj)
62 : {
63 0 : if (!mMozMemory) {
64 0 : mMozMemory = js::gc::NewMemoryInfoObject(aCx);
65 0 : if (mMozMemory) {
66 0 : mozilla::HoldJSObjects(this);
67 : }
68 : }
69 :
70 0 : aObj.set(mMozMemory);
71 0 : }
72 :
73 : PerformanceTiming*
74 0 : PerformanceMainThread::Timing()
75 : {
76 0 : if (!mTiming) {
77 : // For navigation timing, the third argument (an nsIHttpChannel) is null
78 : // since the cross-domain redirect were already checked. The last argument
79 : // (zero time) for performance.timing is the navigation start value.
80 : mTiming = new PerformanceTiming(this, mChannel, nullptr,
81 0 : mDOMTiming->GetNavigationStart());
82 : }
83 :
84 0 : return mTiming;
85 : }
86 :
87 : void
88 0 : PerformanceMainThread::DispatchBufferFullEvent()
89 : {
90 0 : RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
91 : // it bubbles, and it isn't cancelable
92 0 : event->InitEvent(NS_LITERAL_STRING("resourcetimingbufferfull"), true, false);
93 0 : event->SetTrusted(true);
94 0 : DispatchDOMEvent(nullptr, event, nullptr, nullptr);
95 0 : }
96 :
97 : PerformanceNavigation*
98 0 : PerformanceMainThread::Navigation()
99 : {
100 0 : if (!mNavigation) {
101 0 : mNavigation = new PerformanceNavigation(this);
102 : }
103 :
104 0 : return mNavigation;
105 : }
106 :
107 : /**
108 : * An entry should be added only after the resource is loaded.
109 : * This method is not thread safe and can only be called on the main thread.
110 : */
111 : void
112 2 : PerformanceMainThread::AddEntry(nsIHttpChannel* channel,
113 : nsITimedChannel* timedChannel)
114 : {
115 2 : MOZ_ASSERT(NS_IsMainThread());
116 :
117 : // Check if resource timing is prefed off.
118 2 : if (!nsContentUtils::IsResourceTimingEnabled()) {
119 0 : return;
120 : }
121 :
122 : // Don't add the entry if the buffer is full
123 2 : if (IsResourceEntryLimitReached()) {
124 0 : return;
125 : }
126 :
127 2 : if (channel && timedChannel) {
128 4 : nsAutoCString name;
129 4 : nsAutoString initiatorType;
130 4 : nsCOMPtr<nsIURI> originalURI;
131 :
132 2 : timedChannel->GetInitiatorType(initiatorType);
133 :
134 : // According to the spec, "The name attribute must return the resolved URL
135 : // of the requested resource. This attribute must not change even if the
136 : // fetch redirected to a different URL."
137 2 : channel->GetOriginalURI(getter_AddRefs(originalURI));
138 2 : originalURI->GetSpec(name);
139 4 : NS_ConvertUTF8toUTF16 entryName(name);
140 :
141 : // The nsITimedChannel argument will be used to gather all the timings.
142 : // The nsIHttpChannel argument will be used to check if any cross-origin
143 : // redirects occurred.
144 : // The last argument is the "zero time" (offset). Since we don't want
145 : // any offset for the resource timing, this will be set to "0" - the
146 : // resource timing returns a relative timing (no offset).
147 : RefPtr<PerformanceTiming> performanceTiming =
148 : new PerformanceTiming(this, timedChannel, channel,
149 4 : 0);
150 :
151 : // The PerformanceResourceTiming object will use the PerformanceTiming
152 : // object to get all the required timings.
153 : RefPtr<PerformanceResourceTiming> performanceEntry =
154 6 : new PerformanceResourceTiming(performanceTiming, this, entryName);
155 :
156 4 : nsAutoCString protocol;
157 : // Can be an empty string.
158 2 : Unused << channel->GetProtocolVersion(protocol);
159 :
160 : // If this is a local fetch, nextHopProtocol should be set to empty string.
161 4 : nsCOMPtr<nsICacheInfoChannel> cachedChannel = do_QueryInterface(channel);
162 2 : if (cachedChannel) {
163 : bool isFromCache;
164 4 : if (NS_SUCCEEDED(cachedChannel->IsFromCache(&isFromCache))
165 2 : && isFromCache) {
166 2 : protocol.Truncate();
167 : }
168 : }
169 :
170 2 : performanceEntry->SetNextHopProtocol(NS_ConvertUTF8toUTF16(protocol));
171 :
172 2 : uint64_t encodedBodySize = 0;
173 2 : Unused << channel->GetEncodedBodySize(&encodedBodySize);
174 2 : performanceEntry->SetEncodedBodySize(encodedBodySize);
175 :
176 2 : uint64_t transferSize = 0;
177 2 : Unused << channel->GetTransferSize(&transferSize);
178 2 : performanceEntry->SetTransferSize(transferSize);
179 :
180 2 : uint64_t decodedBodySize = 0;
181 2 : Unused << channel->GetDecodedBodySize(&decodedBodySize);
182 2 : if (decodedBodySize == 0) {
183 2 : decodedBodySize = encodedBodySize;
184 : }
185 2 : performanceEntry->SetDecodedBodySize(decodedBodySize);
186 :
187 : // If the initiator type had no valid value, then set it to the default
188 : // ("other") value.
189 2 : if (initiatorType.IsEmpty()) {
190 0 : initiatorType = NS_LITERAL_STRING("other");
191 : }
192 2 : performanceEntry->SetInitiatorType(initiatorType);
193 2 : InsertResourceEntry(performanceEntry);
194 : }
195 : }
196 :
197 : // To be removed once bug 1124165 lands
198 : bool
199 0 : PerformanceMainThread::IsPerformanceTimingAttribute(const nsAString& aName)
200 : {
201 : // Note that toJSON is added to this list due to bug 1047848
202 : static const char* attributes[] =
203 : {"navigationStart", "unloadEventStart", "unloadEventEnd", "redirectStart",
204 : "redirectEnd", "fetchStart", "domainLookupStart", "domainLookupEnd",
205 : "connectStart", "connectEnd", "requestStart", "responseStart",
206 : "responseEnd", "domLoading", "domInteractive",
207 : "domContentLoadedEventStart", "domContentLoadedEventEnd", "domComplete",
208 : "loadEventStart", "loadEventEnd", nullptr};
209 :
210 0 : for (uint32_t i = 0; attributes[i]; ++i) {
211 0 : if (aName.EqualsASCII(attributes[i])) {
212 0 : return true;
213 : }
214 : }
215 :
216 0 : return false;
217 : }
218 :
219 : DOMHighResTimeStamp
220 0 : PerformanceMainThread::GetPerformanceTimingFromString(const nsAString& aProperty)
221 : {
222 0 : if (!IsPerformanceTimingAttribute(aProperty)) {
223 0 : return 0;
224 : }
225 0 : if (aProperty.EqualsLiteral("navigationStart")) {
226 : // DOMHighResTimeStamp is in relation to navigationStart, so this will be
227 : // zero.
228 0 : return GetDOMTiming()->GetNavigationStart();
229 : }
230 0 : if (aProperty.EqualsLiteral("unloadEventStart")) {
231 0 : return GetDOMTiming()->GetUnloadEventStart();
232 : }
233 0 : if (aProperty.EqualsLiteral("unloadEventEnd")) {
234 0 : return GetDOMTiming()->GetUnloadEventEnd();
235 : }
236 0 : if (aProperty.EqualsLiteral("redirectStart")) {
237 0 : return Timing()->RedirectStart();
238 : }
239 0 : if (aProperty.EqualsLiteral("redirectEnd")) {
240 0 : return Timing()->RedirectEnd();
241 : }
242 0 : if (aProperty.EqualsLiteral("fetchStart")) {
243 0 : return Timing()->FetchStart();
244 : }
245 0 : if (aProperty.EqualsLiteral("domainLookupStart")) {
246 0 : return Timing()->DomainLookupStart();
247 : }
248 0 : if (aProperty.EqualsLiteral("domainLookupEnd")) {
249 0 : return Timing()->DomainLookupEnd();
250 : }
251 0 : if (aProperty.EqualsLiteral("connectStart")) {
252 0 : return Timing()->ConnectStart();
253 : }
254 0 : if (aProperty.EqualsLiteral("connectEnd")) {
255 0 : return Timing()->ConnectEnd();
256 : }
257 0 : if (aProperty.EqualsLiteral("requestStart")) {
258 0 : return Timing()->RequestStart();
259 : }
260 0 : if (aProperty.EqualsLiteral("responseStart")) {
261 0 : return Timing()->ResponseStart();
262 : }
263 0 : if (aProperty.EqualsLiteral("responseEnd")) {
264 0 : return Timing()->ResponseEnd();
265 : }
266 0 : if (aProperty.EqualsLiteral("domLoading")) {
267 0 : return GetDOMTiming()->GetDomLoading();
268 : }
269 0 : if (aProperty.EqualsLiteral("domInteractive")) {
270 0 : return GetDOMTiming()->GetDomInteractive();
271 : }
272 0 : if (aProperty.EqualsLiteral("domContentLoadedEventStart")) {
273 0 : return GetDOMTiming()->GetDomContentLoadedEventStart();
274 : }
275 0 : if (aProperty.EqualsLiteral("domContentLoadedEventEnd")) {
276 0 : return GetDOMTiming()->GetDomContentLoadedEventEnd();
277 : }
278 0 : if (aProperty.EqualsLiteral("domComplete")) {
279 0 : return GetDOMTiming()->GetDomComplete();
280 : }
281 0 : if (aProperty.EqualsLiteral("loadEventStart")) {
282 0 : return GetDOMTiming()->GetLoadEventStart();
283 : }
284 0 : if (aProperty.EqualsLiteral("loadEventEnd")) {
285 0 : return GetDOMTiming()->GetLoadEventEnd();
286 : }
287 0 : MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync");
288 : return 0;
289 : }
290 :
291 : void
292 0 : PerformanceMainThread::InsertUserEntry(PerformanceEntry* aEntry)
293 : {
294 0 : MOZ_ASSERT(NS_IsMainThread());
295 :
296 0 : nsAutoCString uri;
297 0 : uint64_t markCreationEpoch = 0;
298 :
299 0 : if (nsContentUtils::IsUserTimingLoggingEnabled() ||
300 0 : nsContentUtils::SendPerformanceTimingNotifications()) {
301 0 : nsresult rv = NS_ERROR_FAILURE;
302 0 : nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
303 0 : if (owner && owner->GetDocumentURI()) {
304 0 : rv = owner->GetDocumentURI()->GetHost(uri);
305 : }
306 :
307 0 : if(NS_FAILED(rv)) {
308 : // If we have no URI, just put in "none".
309 0 : uri.AssignLiteral("none");
310 : }
311 0 : markCreationEpoch = static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC);
312 :
313 0 : if (nsContentUtils::IsUserTimingLoggingEnabled()) {
314 0 : Performance::LogEntry(aEntry, uri);
315 : }
316 : }
317 :
318 0 : if (nsContentUtils::SendPerformanceTimingNotifications()) {
319 0 : TimingNotification(aEntry, uri, markCreationEpoch);
320 : }
321 :
322 0 : Performance::InsertUserEntry(aEntry);
323 0 : }
324 :
325 : TimeStamp
326 58 : PerformanceMainThread::CreationTimeStamp() const
327 : {
328 58 : return GetDOMTiming()->GetNavigationStartTimeStamp();
329 : }
330 :
331 : DOMHighResTimeStamp
332 0 : PerformanceMainThread::CreationTime() const
333 : {
334 0 : return GetDOMTiming()->GetNavigationStart();
335 : }
336 :
337 : } // dom namespace
338 : } // mozilla namespace
|