1 /* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "core/timing/PerformanceTiming.h" 33 34 #include "core/dom/Document.h" 35 #include "core/dom/DocumentTiming.h" 36 #include "core/frame/LocalFrame.h" 37 #include "core/loader/DocumentLoadTiming.h" 38 #include "core/loader/DocumentLoader.h" 39 #include "core/loader/FrameLoader.h" 40 #include "platform/network/ResourceLoadTiming.h" 41 #include "platform/network/ResourceResponse.h" 42 43 namespace blink { 44 45 static unsigned long long toIntegerMilliseconds(double seconds) 46 { 47 ASSERT(seconds >= 0); 48 return static_cast<unsigned long long>(seconds * 1000.0); 49 } 50 51 PerformanceTiming::PerformanceTiming(LocalFrame* frame) 52 : DOMWindowProperty(frame) 53 { 54 } 55 56 unsigned long long PerformanceTiming::navigationStart() const 57 { 58 DocumentLoadTiming* timing = documentLoadTiming(); 59 if (!timing) 60 return 0; 61 62 return monotonicTimeToIntegerMilliseconds(timing->navigationStart()); 63 } 64 65 unsigned long long PerformanceTiming::unloadEventStart() const 66 { 67 DocumentLoadTiming* timing = documentLoadTiming(); 68 if (!timing) 69 return 0; 70 71 if (timing->hasCrossOriginRedirect() || !timing->hasSameOriginAsPreviousDocument()) 72 return 0; 73 74 return monotonicTimeToIntegerMilliseconds(timing->unloadEventStart()); 75 } 76 77 unsigned long long PerformanceTiming::unloadEventEnd() const 78 { 79 DocumentLoadTiming* timing = documentLoadTiming(); 80 if (!timing) 81 return 0; 82 83 if (timing->hasCrossOriginRedirect() || !timing->hasSameOriginAsPreviousDocument()) 84 return 0; 85 86 return monotonicTimeToIntegerMilliseconds(timing->unloadEventEnd()); 87 } 88 89 unsigned long long PerformanceTiming::redirectStart() const 90 { 91 DocumentLoadTiming* timing = documentLoadTiming(); 92 if (!timing) 93 return 0; 94 95 if (timing->hasCrossOriginRedirect()) 96 return 0; 97 98 return monotonicTimeToIntegerMilliseconds(timing->redirectStart()); 99 } 100 101 unsigned long long PerformanceTiming::redirectEnd() const 102 { 103 DocumentLoadTiming* timing = documentLoadTiming(); 104 if (!timing) 105 return 0; 106 107 if (timing->hasCrossOriginRedirect()) 108 return 0; 109 110 return monotonicTimeToIntegerMilliseconds(timing->redirectEnd()); 111 } 112 113 unsigned long long PerformanceTiming::fetchStart() const 114 { 115 DocumentLoadTiming* timing = documentLoadTiming(); 116 if (!timing) 117 return 0; 118 119 return monotonicTimeToIntegerMilliseconds(timing->fetchStart()); 120 } 121 122 unsigned long long PerformanceTiming::domainLookupStart() const 123 { 124 ResourceLoadTiming* timing = resourceLoadTiming(); 125 if (!timing) 126 return fetchStart(); 127 128 // This will be zero when a DNS request is not performed. 129 // Rather than exposing a special value that indicates no DNS, we "backfill" with fetchStart. 130 double dnsStart = timing->dnsStart; 131 if (dnsStart == 0.0) 132 return fetchStart(); 133 134 return monotonicTimeToIntegerMilliseconds(dnsStart); 135 } 136 137 unsigned long long PerformanceTiming::domainLookupEnd() const 138 { 139 ResourceLoadTiming* timing = resourceLoadTiming(); 140 if (!timing) 141 return domainLookupStart(); 142 143 // This will be zero when a DNS request is not performed. 144 // Rather than exposing a special value that indicates no DNS, we "backfill" with domainLookupStart. 145 double dnsEnd = timing->dnsEnd; 146 if (dnsEnd == 0.0) 147 return domainLookupStart(); 148 149 return monotonicTimeToIntegerMilliseconds(dnsEnd); 150 } 151 152 unsigned long long PerformanceTiming::connectStart() const 153 { 154 DocumentLoader* loader = documentLoader(); 155 if (!loader) 156 return domainLookupEnd(); 157 158 ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); 159 if (!timing) 160 return domainLookupEnd(); 161 162 // connectStart will be zero when a network request is not made. 163 // Rather than exposing a special value that indicates no new connection, we "backfill" with domainLookupEnd. 164 double connectStart = timing->connectStart; 165 if (connectStart == 0.0 || loader->response().connectionReused()) 166 return domainLookupEnd(); 167 168 // ResourceLoadTiming's connect phase includes DNS, however Navigation Timing's 169 // connect phase should not. So if there is DNS time, trim it from the start. 170 if (timing->dnsEnd > 0.0 && timing->dnsEnd > connectStart) 171 connectStart = timing->dnsEnd; 172 173 return monotonicTimeToIntegerMilliseconds(connectStart); 174 } 175 176 unsigned long long PerformanceTiming::connectEnd() const 177 { 178 DocumentLoader* loader = documentLoader(); 179 if (!loader) 180 return connectStart(); 181 182 ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); 183 if (!timing) 184 return connectStart(); 185 186 // connectEnd will be zero when a network request is not made. 187 // Rather than exposing a special value that indicates no new connection, we "backfill" with connectStart. 188 double connectEnd = timing->connectEnd; 189 if (connectEnd == 0.0 || loader->response().connectionReused()) 190 return connectStart(); 191 192 return monotonicTimeToIntegerMilliseconds(connectEnd); 193 } 194 195 unsigned long long PerformanceTiming::secureConnectionStart() const 196 { 197 DocumentLoader* loader = documentLoader(); 198 if (!loader) 199 return 0; 200 201 ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); 202 if (!timing) 203 return 0; 204 205 double sslStart = timing->sslStart; 206 if (sslStart == 0.0) 207 return 0; 208 209 return monotonicTimeToIntegerMilliseconds(sslStart); 210 } 211 212 unsigned long long PerformanceTiming::requestStart() const 213 { 214 ResourceLoadTiming* timing = resourceLoadTiming(); 215 216 if (!timing || timing->sendStart == 0.0) 217 return connectEnd(); 218 219 return monotonicTimeToIntegerMilliseconds(timing->sendStart); 220 } 221 222 unsigned long long PerformanceTiming::responseStart() const 223 { 224 ResourceLoadTiming* timing = resourceLoadTiming(); 225 if (!timing || timing->receiveHeadersEnd == 0.0) 226 return requestStart(); 227 228 // FIXME: Response start needs to be the time of the first received byte. 229 // However, the ResourceLoadTiming API currently only supports the time 230 // the last header byte was received. For many responses with reasonable 231 // sized cookies, the HTTP headers fit into a single packet so this time 232 // is basically equivalent. But for some responses, particularly those with 233 // headers larger than a single packet, this time will be too late. 234 return monotonicTimeToIntegerMilliseconds(timing->receiveHeadersEnd); 235 } 236 237 unsigned long long PerformanceTiming::responseEnd() const 238 { 239 DocumentLoadTiming* timing = documentLoadTiming(); 240 if (!timing) 241 return 0; 242 243 return monotonicTimeToIntegerMilliseconds(timing->responseEnd()); 244 } 245 246 unsigned long long PerformanceTiming::domLoading() const 247 { 248 const DocumentTiming* timing = documentTiming(); 249 if (!timing) 250 return fetchStart(); 251 252 return monotonicTimeToIntegerMilliseconds(timing->domLoading); 253 } 254 255 unsigned long long PerformanceTiming::domInteractive() const 256 { 257 const DocumentTiming* timing = documentTiming(); 258 if (!timing) 259 return 0; 260 261 return monotonicTimeToIntegerMilliseconds(timing->domInteractive); 262 } 263 264 unsigned long long PerformanceTiming::domContentLoadedEventStart() const 265 { 266 const DocumentTiming* timing = documentTiming(); 267 if (!timing) 268 return 0; 269 270 return monotonicTimeToIntegerMilliseconds(timing->domContentLoadedEventStart); 271 } 272 273 unsigned long long PerformanceTiming::domContentLoadedEventEnd() const 274 { 275 const DocumentTiming* timing = documentTiming(); 276 if (!timing) 277 return 0; 278 279 return monotonicTimeToIntegerMilliseconds(timing->domContentLoadedEventEnd); 280 } 281 282 unsigned long long PerformanceTiming::domComplete() const 283 { 284 const DocumentTiming* timing = documentTiming(); 285 if (!timing) 286 return 0; 287 288 return monotonicTimeToIntegerMilliseconds(timing->domComplete); 289 } 290 291 unsigned long long PerformanceTiming::loadEventStart() const 292 { 293 DocumentLoadTiming* timing = documentLoadTiming(); 294 if (!timing) 295 return 0; 296 297 return monotonicTimeToIntegerMilliseconds(timing->loadEventStart()); 298 } 299 300 unsigned long long PerformanceTiming::loadEventEnd() const 301 { 302 DocumentLoadTiming* timing = documentLoadTiming(); 303 if (!timing) 304 return 0; 305 306 return monotonicTimeToIntegerMilliseconds(timing->loadEventEnd()); 307 } 308 309 DocumentLoader* PerformanceTiming::documentLoader() const 310 { 311 if (!m_frame) 312 return 0; 313 314 return m_frame->loader().documentLoader(); 315 } 316 317 const DocumentTiming* PerformanceTiming::documentTiming() const 318 { 319 if (!m_frame) 320 return 0; 321 322 Document* document = m_frame->document(); 323 if (!document) 324 return 0; 325 326 return &document->timing(); 327 } 328 329 DocumentLoadTiming* PerformanceTiming::documentLoadTiming() const 330 { 331 DocumentLoader* loader = documentLoader(); 332 if (!loader) 333 return 0; 334 335 return loader->timing(); 336 } 337 338 ResourceLoadTiming* PerformanceTiming::resourceLoadTiming() const 339 { 340 DocumentLoader* loader = documentLoader(); 341 if (!loader) 342 return 0; 343 344 return loader->response().resourceLoadTiming(); 345 } 346 347 unsigned long long PerformanceTiming::monotonicTimeToIntegerMilliseconds(double monotonicSeconds) const 348 { 349 ASSERT(monotonicSeconds >= 0); 350 const DocumentLoadTiming* timing = documentLoadTiming(); 351 if (!timing) 352 return 0; 353 354 return toIntegerMilliseconds(timing->monotonicTimeToPseudoWallTime(monotonicSeconds)); 355 } 356 357 void PerformanceTiming::trace(Visitor* visitor) 358 { 359 DOMWindowProperty::trace(visitor); 360 } 361 362 } // namespace blink 363