1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2012 Intel Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "config.h"
33 #include "core/timing/Performance.h"
34
35 #include "core/dom/Document.h"
36 #include "core/frame/LocalFrame.h"
37 #include "core/loader/DocumentLoader.h"
38 #include "core/timing/ResourceTimingInfo.h"
39 #include "core/timing/PerformanceResourceTiming.h"
40 #include "core/timing/PerformanceUserTiming.h"
41 #include "platform/weborigin/SecurityOrigin.h"
42 #include "wtf/CurrentTime.h"
43
44 namespace blink {
45
46 static const size_t defaultResourceTimingBufferSize = 150;
47
Performance(LocalFrame * frame)48 Performance::Performance(LocalFrame* frame)
49 : DOMWindowProperty(frame)
50 , m_resourceTimingBufferSize(defaultResourceTimingBufferSize)
51 , m_referenceTime(frame && frame->host() ? frame->document()->loader()->timing()->referenceMonotonicTime() : 0.0)
52 , m_userTiming(nullptr)
53 {
54 }
55
~Performance()56 Performance::~Performance()
57 {
58 }
59
interfaceName() const60 const AtomicString& Performance::interfaceName() const
61 {
62 return EventTargetNames::Performance;
63 }
64
executionContext() const65 ExecutionContext* Performance::executionContext() const
66 {
67 if (!frame())
68 return 0;
69 return frame()->document();
70 }
71
memory() const72 PassRefPtrWillBeRawPtr<MemoryInfo> Performance::memory() const
73 {
74 return MemoryInfo::create();
75 }
76
navigation() const77 PerformanceNavigation* Performance::navigation() const
78 {
79 if (!m_navigation)
80 m_navigation = PerformanceNavigation::create(m_frame);
81
82 return m_navigation.get();
83 }
84
timing() const85 PerformanceTiming* Performance::timing() const
86 {
87 if (!m_timing)
88 m_timing = PerformanceTiming::create(m_frame);
89
90 return m_timing.get();
91 }
92
getEntries() const93 PerformanceEntryVector Performance::getEntries() const
94 {
95 PerformanceEntryVector entries;
96
97 entries.appendVector(m_resourceTimingBuffer);
98
99 if (m_userTiming) {
100 entries.appendVector(m_userTiming->getMarks());
101 entries.appendVector(m_userTiming->getMeasures());
102 }
103
104 std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan);
105 return entries;
106 }
107
getEntriesByType(const String & entryType)108 PerformanceEntryVector Performance::getEntriesByType(const String& entryType)
109 {
110 PerformanceEntryVector entries;
111
112 if (equalIgnoringCase(entryType, "resource"))
113 for (PerformanceEntryVector::const_iterator resource = m_resourceTimingBuffer.begin(); resource != m_resourceTimingBuffer.end(); ++resource)
114 entries.append(*resource);
115
116 if (m_userTiming) {
117 if (equalIgnoringCase(entryType, "mark"))
118 entries.appendVector(m_userTiming->getMarks());
119 else if (equalIgnoringCase(entryType, "measure"))
120 entries.appendVector(m_userTiming->getMeasures());
121 }
122
123 std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan);
124 return entries;
125 }
126
getEntriesByName(const String & name,const String & entryType)127 PerformanceEntryVector Performance::getEntriesByName(const String& name, const String& entryType)
128 {
129 PerformanceEntryVector entries;
130
131 if (entryType.isNull() || equalIgnoringCase(entryType, "resource"))
132 for (PerformanceEntryVector::const_iterator resource = m_resourceTimingBuffer.begin(); resource != m_resourceTimingBuffer.end(); ++resource)
133 if ((*resource)->name() == name)
134 entries.append(*resource);
135
136 if (m_userTiming) {
137 if (entryType.isNull() || equalIgnoringCase(entryType, "mark"))
138 entries.appendVector(m_userTiming->getMarks(name));
139 if (entryType.isNull() || equalIgnoringCase(entryType, "measure"))
140 entries.appendVector(m_userTiming->getMeasures(name));
141 }
142
143 std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan);
144 return entries;
145 }
146
webkitClearResourceTimings()147 void Performance::webkitClearResourceTimings()
148 {
149 m_resourceTimingBuffer.clear();
150 }
151
webkitSetResourceTimingBufferSize(unsigned size)152 void Performance::webkitSetResourceTimingBufferSize(unsigned size)
153 {
154 m_resourceTimingBufferSize = size;
155 if (isResourceTimingBufferFull())
156 dispatchEvent(Event::create(EventTypeNames::webkitresourcetimingbufferfull));
157 }
158
passesTimingAllowCheck(const ResourceResponse & response,Document * requestingDocument,const AtomicString & originalTimingAllowOrigin)159 static bool passesTimingAllowCheck(const ResourceResponse& response, Document* requestingDocument, const AtomicString& originalTimingAllowOrigin)
160 {
161 AtomicallyInitializedStatic(AtomicString&, timingAllowOrigin = *new AtomicString("timing-allow-origin"));
162
163 RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::create(response.url());
164 if (resourceOrigin->isSameSchemeHostPort(requestingDocument->securityOrigin()))
165 return true;
166
167 const AtomicString& timingAllowOriginString = originalTimingAllowOrigin.isEmpty() ? response.httpHeaderField(timingAllowOrigin) : originalTimingAllowOrigin;
168 if (timingAllowOriginString.isEmpty() || equalIgnoringCase(timingAllowOriginString, "null"))
169 return false;
170
171 if (timingAllowOriginString == starAtom)
172 return true;
173
174 const String& securityOrigin = requestingDocument->securityOrigin()->toString();
175 Vector<String> timingAllowOrigins;
176 timingAllowOriginString.string().split(' ', timingAllowOrigins);
177 for (size_t i = 0; i < timingAllowOrigins.size(); ++i) {
178 if (timingAllowOrigins[i] == securityOrigin)
179 return true;
180 }
181
182 return false;
183 }
184
allowsTimingRedirect(const Vector<ResourceResponse> & redirectChain,const ResourceResponse & finalResponse,Document * initiatorDocument)185 static bool allowsTimingRedirect(const Vector<ResourceResponse>& redirectChain, const ResourceResponse& finalResponse, Document* initiatorDocument)
186 {
187 if (!passesTimingAllowCheck(finalResponse, initiatorDocument, emptyAtom))
188 return false;
189
190 for (size_t i = 0; i < redirectChain.size(); i++) {
191 if (!passesTimingAllowCheck(redirectChain[i], initiatorDocument, emptyAtom))
192 return false;
193 }
194
195 return true;
196 }
197
addResourceTiming(const ResourceTimingInfo & info,Document * initiatorDocument)198 void Performance::addResourceTiming(const ResourceTimingInfo& info, Document* initiatorDocument)
199 {
200 if (isResourceTimingBufferFull())
201 return;
202
203 const ResourceResponse& finalResponse = info.finalResponse();
204 bool allowTimingDetails = passesTimingAllowCheck(finalResponse, initiatorDocument, info.originalTimingAllowOrigin());
205 double startTime = info.initialTime();
206
207 if (info.redirectChain().isEmpty()) {
208 RefPtrWillBeRawPtr<PerformanceEntry> entry = PerformanceResourceTiming::create(info, initiatorDocument, startTime, allowTimingDetails);
209 addResourceTimingBuffer(entry);
210 return;
211 }
212
213 const Vector<ResourceResponse>& redirectChain = info.redirectChain();
214 bool allowRedirectDetails = allowsTimingRedirect(redirectChain, finalResponse, initiatorDocument);
215
216 if (!allowRedirectDetails) {
217 ResourceLoadTiming* finalTiming = finalResponse.resourceLoadTiming();
218 ASSERT(finalTiming);
219 if (finalTiming)
220 startTime = finalTiming->requestTime;
221 }
222
223 ResourceLoadTiming* lastRedirectTiming = redirectChain.last().resourceLoadTiming();
224 ASSERT(lastRedirectTiming);
225 double lastRedirectEndTime = lastRedirectTiming->receiveHeadersEnd;
226
227 RefPtrWillBeRawPtr<PerformanceEntry> entry = PerformanceResourceTiming::create(info, initiatorDocument, startTime, lastRedirectEndTime, allowTimingDetails, allowRedirectDetails);
228 addResourceTimingBuffer(entry);
229 }
230
addResourceTimingBuffer(PassRefPtrWillBeRawPtr<PerformanceEntry> entry)231 void Performance::addResourceTimingBuffer(PassRefPtrWillBeRawPtr<PerformanceEntry> entry)
232 {
233 m_resourceTimingBuffer.append(entry);
234
235 if (isResourceTimingBufferFull())
236 dispatchEvent(Event::create(EventTypeNames::webkitresourcetimingbufferfull));
237 }
238
isResourceTimingBufferFull()239 bool Performance::isResourceTimingBufferFull()
240 {
241 return m_resourceTimingBuffer.size() >= m_resourceTimingBufferSize;
242 }
243
mark(const String & markName,ExceptionState & exceptionState)244 void Performance::mark(const String& markName, ExceptionState& exceptionState)
245 {
246 if (!m_userTiming)
247 m_userTiming = UserTiming::create(this);
248 m_userTiming->mark(markName, exceptionState);
249 }
250
clearMarks(const String & markName)251 void Performance::clearMarks(const String& markName)
252 {
253 if (!m_userTiming)
254 m_userTiming = UserTiming::create(this);
255 m_userTiming->clearMarks(markName);
256 }
257
measure(const String & measureName,const String & startMark,const String & endMark,ExceptionState & exceptionState)258 void Performance::measure(const String& measureName, const String& startMark, const String& endMark, ExceptionState& exceptionState)
259 {
260 if (!m_userTiming)
261 m_userTiming = UserTiming::create(this);
262 m_userTiming->measure(measureName, startMark, endMark, exceptionState);
263 }
264
clearMeasures(const String & measureName)265 void Performance::clearMeasures(const String& measureName)
266 {
267 if (!m_userTiming)
268 m_userTiming = UserTiming::create(this);
269 m_userTiming->clearMeasures(measureName);
270 }
271
now() const272 double Performance::now() const
273 {
274 return 1000.0 * (monotonicallyIncreasingTime() - m_referenceTime);
275 }
276
trace(Visitor * visitor)277 void Performance::trace(Visitor* visitor)
278 {
279 visitor->trace(m_navigation);
280 visitor->trace(m_timing);
281 visitor->trace(m_resourceTimingBuffer);
282 visitor->trace(m_userTiming);
283 EventTargetWithInlineData::trace(visitor);
284 DOMWindowProperty::trace(visitor);
285 }
286
287 } // namespace blink
288