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 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 59 Performance::~Performance() 60 { 61 } 62 63 const AtomicString& Performance::interfaceName() const 64 { 65 return EventTargetNames::Performance; 66 } 67 68 ExecutionContext* Performance::executionContext() const 69 { 70 if (!frame()) 71 return 0; 72 return frame()->document(); 73 } 74 75 PassRefPtr<MemoryInfo> Performance::memory() const 76 { 77 return MemoryInfo::create(m_frame); 78 } 79 80 PerformanceNavigation* Performance::navigation() const 81 { 82 if (!m_navigation) 83 m_navigation = PerformanceNavigation::create(m_frame); 84 85 return m_navigation.get(); 86 } 87 88 PerformanceTiming* Performance::timing() const 89 { 90 if (!m_timing) 91 m_timing = PerformanceTiming::create(m_frame); 92 93 return m_timing.get(); 94 } 95 96 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 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 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 150 void Performance::webkitClearResourceTimings() 151 { 152 m_resourceTimingBuffer.clear(); 153 } 154 155 void Performance::webkitSetResourceTimingBufferSize(unsigned size) 156 { 157 m_resourceTimingBufferSize = size; 158 if (isResourceTimingBufferFull()) 159 dispatchEvent(Event::create(EventTypeNames::webkitresourcetimingbufferfull)); 160 } 161 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 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 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 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 242 bool Performance::isResourceTimingBufferFull() 243 { 244 return m_resourceTimingBuffer.size() >= m_resourceTimingBufferSize; 245 } 246 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 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 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 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 275 double Performance::now() const 276 { 277 return 1000.0 * (monotonicallyIncreasingTime() - m_referenceTime); 278 } 279 280 } // namespace WebCore 281