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/loader/DocumentLoader.h"
37 #include "core/timing/ResourceTimingInfo.h"
38 #include "core/timing/PerformanceResourceTiming.h"
39 #include "core/timing/PerformanceUserTiming.h"
40 #include "platform/weborigin/SecurityOrigin.h"
41 #include "wtf/CurrentTime.h"
42
43 #include "core/frame/Frame.h"
44
45 namespace WebCore {
46
47 static const size_t defaultResourceTimingBufferSize = 150;
48
Performance(Frame * frame)49 Performance::Performance(Frame* frame)
50 : DOMWindowProperty(frame)
51 , m_resourceTimingBufferSize(defaultResourceTimingBufferSize)
52 , m_referenceTime(frame->document()->loader()->timing()->referenceMonotonicTime())
53 , m_userTiming(0)
54 {
55 ASSERT(m_referenceTime);
56 ScriptWrappable::init(this);
57 }
58
~Performance()59 Performance::~Performance()
60 {
61 }
62
interfaceName() const63 const AtomicString& Performance::interfaceName() const
64 {
65 return EventTargetNames::Performance;
66 }
67
executionContext() const68 ExecutionContext* Performance::executionContext() const
69 {
70 if (!frame())
71 return 0;
72 return frame()->document();
73 }
74
memory() const75 PassRefPtr<MemoryInfo> Performance::memory() const
76 {
77 return MemoryInfo::create(m_frame);
78 }
79
navigation() const80 PerformanceNavigation* Performance::navigation() const
81 {
82 if (!m_navigation)
83 m_navigation = PerformanceNavigation::create(m_frame);
84
85 return m_navigation.get();
86 }
87
timing() const88 PerformanceTiming* Performance::timing() const
89 {
90 if (!m_timing)
91 m_timing = PerformanceTiming::create(m_frame);
92
93 return m_timing.get();
94 }
95
getEntries() const96 Vector<RefPtr<PerformanceEntry> > Performance::getEntries() const
97 {
98 Vector<RefPtr<PerformanceEntry> > entries;
99
100 entries.append(m_resourceTimingBuffer);
101
102 if (m_userTiming) {
103 entries.append(m_userTiming->getMarks());
104 entries.append(m_userTiming->getMeasures());
105 }
106
107 std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan);
108 return entries;
109 }
110
getEntriesByType(const String & entryType)111 Vector<RefPtr<PerformanceEntry> > Performance::getEntriesByType(const String& entryType)
112 {
113 Vector<RefPtr<PerformanceEntry> > entries;
114
115 if (equalIgnoringCase(entryType, "resource"))
116 for (Vector<RefPtr<PerformanceEntry> >::const_iterator resource = m_resourceTimingBuffer.begin(); resource != m_resourceTimingBuffer.end(); ++resource)
117 entries.append(*resource);
118
119 if (m_userTiming) {
120 if (equalIgnoringCase(entryType, "mark"))
121 entries.append(m_userTiming->getMarks());
122 else if (equalIgnoringCase(entryType, "measure"))
123 entries.append(m_userTiming->getMeasures());
124 }
125
126 std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan);
127 return entries;
128 }
129
getEntriesByName(const String & name,const String & entryType)130 Vector<RefPtr<PerformanceEntry> > Performance::getEntriesByName(const String& name, const String& entryType)
131 {
132 Vector<RefPtr<PerformanceEntry> > entries;
133
134 if (entryType.isNull() || equalIgnoringCase(entryType, "resource"))
135 for (Vector<RefPtr<PerformanceEntry> >::const_iterator resource = m_resourceTimingBuffer.begin(); resource != m_resourceTimingBuffer.end(); ++resource)
136 if ((*resource)->name() == name)
137 entries.append(*resource);
138
139 if (m_userTiming) {
140 if (entryType.isNull() || equalIgnoringCase(entryType, "mark"))
141 entries.append(m_userTiming->getMarks(name));
142 if (entryType.isNull() || equalIgnoringCase(entryType, "measure"))
143 entries.append(m_userTiming->getMeasures(name));
144 }
145
146 std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan);
147 return entries;
148 }
149
webkitClearResourceTimings()150 void Performance::webkitClearResourceTimings()
151 {
152 m_resourceTimingBuffer.clear();
153 }
154
webkitSetResourceTimingBufferSize(unsigned size)155 void Performance::webkitSetResourceTimingBufferSize(unsigned size)
156 {
157 m_resourceTimingBufferSize = size;
158 if (isResourceTimingBufferFull())
159 dispatchEvent(Event::create(EventTypeNames::webkitresourcetimingbufferfull));
160 }
161
passesTimingAllowCheck(const ResourceResponse & response,Document * requestingDocument)162 static bool passesTimingAllowCheck(const ResourceResponse& response, Document* requestingDocument)
163 {
164 AtomicallyInitializedStatic(AtomicString&, timingAllowOrigin = *new AtomicString("timing-allow-origin"));
165
166 RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::create(response.url());
167 if (resourceOrigin->isSameSchemeHostPort(requestingDocument->securityOrigin()))
168 return true;
169
170 const AtomicString& timingAllowOriginString = response.httpHeaderField(timingAllowOrigin);
171 if (timingAllowOriginString.isEmpty() || equalIgnoringCase(timingAllowOriginString, "null"))
172 return false;
173
174 if (timingAllowOriginString == starAtom)
175 return true;
176
177 const String& securityOrigin = requestingDocument->securityOrigin()->toString();
178 Vector<String> timingAllowOrigins;
179 timingAllowOriginString.string().split(" ", timingAllowOrigins);
180 for (size_t i = 0; i < timingAllowOrigins.size(); ++i) {
181 if (timingAllowOrigins[i] == securityOrigin)
182 return true;
183 }
184
185 return false;
186 }
187
allowsTimingRedirect(const Vector<ResourceResponse> & redirectChain,const ResourceResponse & finalResponse,Document * initiatorDocument)188 static bool allowsTimingRedirect(const Vector<ResourceResponse>& redirectChain, const ResourceResponse& finalResponse, Document* initiatorDocument)
189 {
190 if (!passesTimingAllowCheck(finalResponse, initiatorDocument))
191 return false;
192
193 for (size_t i = 0; i < redirectChain.size(); i++) {
194 if (!passesTimingAllowCheck(redirectChain[i], initiatorDocument))
195 return false;
196 }
197
198 return true;
199 }
200
addResourceTiming(const ResourceTimingInfo & info,Document * initiatorDocument)201 void Performance::addResourceTiming(const ResourceTimingInfo& info, Document* initiatorDocument)
202 {
203 if (isResourceTimingBufferFull())
204 return;
205
206 const ResourceResponse& finalResponse = info.finalResponse();
207 bool allowTimingDetails = passesTimingAllowCheck(finalResponse, initiatorDocument);
208 double startTime = info.initialTime();
209
210 if (info.redirectChain().isEmpty()) {
211 RefPtr<PerformanceEntry> entry = PerformanceResourceTiming::create(info, initiatorDocument, startTime, allowTimingDetails);
212 addResourceTimingBuffer(entry);
213 return;
214 }
215
216 const Vector<ResourceResponse>& redirectChain = info.redirectChain();
217 bool allowRedirectDetails = allowsTimingRedirect(redirectChain, finalResponse, initiatorDocument);
218
219 if (!allowRedirectDetails) {
220 ResourceLoadTiming* finalTiming = finalResponse.resourceLoadTiming();
221 ASSERT(finalTiming);
222 if (finalTiming)
223 startTime = finalTiming->requestTime;
224 }
225
226 ResourceLoadTiming* lastRedirectTiming = redirectChain.last().resourceLoadTiming();
227 ASSERT(lastRedirectTiming);
228 double lastRedirectEndTime = lastRedirectTiming->receiveHeadersEnd;
229
230 RefPtr<PerformanceEntry> entry = PerformanceResourceTiming::create(info, initiatorDocument, startTime, lastRedirectEndTime, allowTimingDetails, allowRedirectDetails);
231 addResourceTimingBuffer(entry);
232 }
233
addResourceTimingBuffer(PassRefPtr<PerformanceEntry> entry)234 void Performance::addResourceTimingBuffer(PassRefPtr<PerformanceEntry> entry)
235 {
236 m_resourceTimingBuffer.append(entry);
237
238 if (isResourceTimingBufferFull())
239 dispatchEvent(Event::create(EventTypeNames::webkitresourcetimingbufferfull));
240 }
241
isResourceTimingBufferFull()242 bool Performance::isResourceTimingBufferFull()
243 {
244 return m_resourceTimingBuffer.size() >= m_resourceTimingBufferSize;
245 }
246
mark(const String & markName,ExceptionState & exceptionState)247 void Performance::mark(const String& markName, ExceptionState& exceptionState)
248 {
249 if (!m_userTiming)
250 m_userTiming = UserTiming::create(this);
251 m_userTiming->mark(markName, exceptionState);
252 }
253
clearMarks(const String & markName)254 void Performance::clearMarks(const String& markName)
255 {
256 if (!m_userTiming)
257 m_userTiming = UserTiming::create(this);
258 m_userTiming->clearMarks(markName);
259 }
260
measure(const String & measureName,const String & startMark,const String & endMark,ExceptionState & exceptionState)261 void Performance::measure(const String& measureName, const String& startMark, const String& endMark, ExceptionState& exceptionState)
262 {
263 if (!m_userTiming)
264 m_userTiming = UserTiming::create(this);
265 m_userTiming->measure(measureName, startMark, endMark, exceptionState);
266 }
267
clearMeasures(const String & measureName)268 void Performance::clearMeasures(const String& measureName)
269 {
270 if (!m_userTiming)
271 m_userTiming = UserTiming::create(this);
272 m_userTiming->clearMeasures(measureName);
273 }
274
now() const275 double Performance::now() const
276 {
277 return 1000.0 * (monotonicallyIncreasingTime() - m_referenceTime);
278 }
279
280 } // namespace WebCore
281