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/page/PerformanceTiming.h" 33 34 #include "core/dom/Document.h" 35 #include "core/dom/DocumentTiming.h" 36 #include "core/loader/DocumentLoadTiming.h" 37 #include "core/loader/DocumentLoader.h" 38 #include "core/loader/FrameLoader.h" 39 #include "core/page/Frame.h" 40 #include "core/platform/network/ResourceLoadTiming.h" 41 #include "core/platform/network/ResourceResponse.h" 42 43 namespace WebCore { 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(Frame* frame) 52 : DOMWindowProperty(frame) 53 { 54 ScriptWrappable::init(this); 55 } 56 57 unsigned long long PerformanceTiming::navigationStart() const 58 { 59 DocumentLoadTiming* timing = documentLoadTiming(); 60 if (!timing) 61 return 0; 62 63 return monotonicTimeToIntegerMilliseconds(timing->navigationStart()); 64 } 65 66 unsigned long long PerformanceTiming::unloadEventStart() const 67 { 68 DocumentLoadTiming* timing = documentLoadTiming(); 69 if (!timing) 70 return 0; 71 72 if (timing->hasCrossOriginRedirect() || !timing->hasSameOriginAsPreviousDocument()) 73 return 0; 74 75 return monotonicTimeToIntegerMilliseconds(timing->unloadEventStart()); 76 } 77 78 unsigned long long PerformanceTiming::unloadEventEnd() const 79 { 80 DocumentLoadTiming* timing = documentLoadTiming(); 81 if (!timing) 82 return 0; 83 84 if (timing->hasCrossOriginRedirect() || !timing->hasSameOriginAsPreviousDocument()) 85 return 0; 86 87 return monotonicTimeToIntegerMilliseconds(timing->unloadEventEnd()); 88 } 89 90 unsigned long long PerformanceTiming::redirectStart() const 91 { 92 DocumentLoadTiming* timing = documentLoadTiming(); 93 if (!timing) 94 return 0; 95 96 if (timing->hasCrossOriginRedirect()) 97 return 0; 98 99 return monotonicTimeToIntegerMilliseconds(timing->redirectStart()); 100 } 101 102 unsigned long long PerformanceTiming::redirectEnd() const 103 { 104 DocumentLoadTiming* timing = documentLoadTiming(); 105 if (!timing) 106 return 0; 107 108 if (timing->hasCrossOriginRedirect()) 109 return 0; 110 111 return monotonicTimeToIntegerMilliseconds(timing->redirectEnd()); 112 } 113 114 unsigned long long PerformanceTiming::fetchStart() const 115 { 116 DocumentLoadTiming* timing = documentLoadTiming(); 117 if (!timing) 118 return 0; 119 120 return monotonicTimeToIntegerMilliseconds(timing->fetchStart()); 121 } 122 123 unsigned long long PerformanceTiming::domainLookupStart() const 124 { 125 ResourceLoadTiming* timing = resourceLoadTiming(); 126 if (!timing) 127 return fetchStart(); 128 129 // This will be zero when a DNS request is not performed. 130 // Rather than exposing a special value that indicates no DNS, we "backfill" with fetchStart. 131 double dnsStart = timing->dnsStart; 132 if (dnsStart == 0.0) 133 return fetchStart(); 134 135 return monotonicTimeToIntegerMilliseconds(dnsStart); 136 } 137 138 unsigned long long PerformanceTiming::domainLookupEnd() const 139 { 140 ResourceLoadTiming* timing = resourceLoadTiming(); 141 if (!timing) 142 return domainLookupStart(); 143 144 // This will be zero when a DNS request is not performed. 145 // Rather than exposing a special value that indicates no DNS, we "backfill" with domainLookupStart. 146 double dnsEnd = timing->dnsEnd; 147 if (dnsEnd == 0.0) 148 return domainLookupStart(); 149 150 return monotonicTimeToIntegerMilliseconds(dnsEnd); 151 } 152 153 unsigned long long PerformanceTiming::connectStart() const 154 { 155 DocumentLoader* loader = documentLoader(); 156 if (!loader) 157 return domainLookupEnd(); 158 159 ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); 160 if (!timing) 161 return domainLookupEnd(); 162 163 // connectStart will be zero when a network request is not made. 164 // Rather than exposing a special value that indicates no new connection, we "backfill" with domainLookupEnd. 165 double connectStart = timing->connectStart; 166 if (connectStart == 0.0 || loader->response().connectionReused()) 167 return domainLookupEnd(); 168 169 // ResourceLoadTiming's connect phase includes DNS, however Navigation Timing's 170 // connect phase should not. So if there is DNS time, trim it from the start. 171 if (timing->dnsEnd > 0.0 && timing->dnsEnd > connectStart) 172 connectStart = timing->dnsEnd; 173 174 return monotonicTimeToIntegerMilliseconds(connectStart); 175 } 176 177 unsigned long long PerformanceTiming::connectEnd() const 178 { 179 DocumentLoader* loader = documentLoader(); 180 if (!loader) 181 return connectStart(); 182 183 ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); 184 if (!timing) 185 return connectStart(); 186 187 // connectEnd will be zero when a network request is not made. 188 // Rather than exposing a special value that indicates no new connection, we "backfill" with connectStart. 189 double connectEnd = timing->connectEnd; 190 if (connectEnd == 0.0 || loader->response().connectionReused()) 191 return connectStart(); 192 193 return monotonicTimeToIntegerMilliseconds(connectEnd); 194 } 195 196 unsigned long long PerformanceTiming::secureConnectionStart() const 197 { 198 DocumentLoader* loader = documentLoader(); 199 if (!loader) 200 return 0; 201 202 ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); 203 if (!timing) 204 return 0; 205 206 double sslStart = timing->sslStart; 207 if (sslStart == 0.0) 208 return 0; 209 210 return monotonicTimeToIntegerMilliseconds(sslStart); 211 } 212 213 unsigned long long PerformanceTiming::requestStart() const 214 { 215 ResourceLoadTiming* timing = resourceLoadTiming(); 216 217 if (!timing || timing->sendStart == 0.0) 218 return connectEnd(); 219 220 return monotonicTimeToIntegerMilliseconds(timing->sendStart); 221 } 222 223 unsigned long long PerformanceTiming::responseStart() const 224 { 225 ResourceLoadTiming* timing = resourceLoadTiming(); 226 if (!timing || timing->receiveHeadersEnd == 0.0) 227 return requestStart(); 228 229 // FIXME: Response start needs to be the time of the first received byte. 230 // However, the ResourceLoadTiming API currently only supports the time 231 // the last header byte was received. For many responses with reasonable 232 // sized cookies, the HTTP headers fit into a single packet so this time 233 // is basically equivalent. But for some responses, particularly those with 234 // headers larger than a single packet, this time will be too late. 235 return monotonicTimeToIntegerMilliseconds(timing->receiveHeadersEnd); 236 } 237 238 unsigned long long PerformanceTiming::responseEnd() const 239 { 240 DocumentLoadTiming* timing = documentLoadTiming(); 241 if (!timing) 242 return 0; 243 244 return monotonicTimeToIntegerMilliseconds(timing->responseEnd()); 245 } 246 247 unsigned long long PerformanceTiming::domLoading() const 248 { 249 const DocumentTiming* timing = documentTiming(); 250 if (!timing) 251 return fetchStart(); 252 253 return monotonicTimeToIntegerMilliseconds(timing->domLoading); 254 } 255 256 unsigned long long PerformanceTiming::domInteractive() const 257 { 258 const DocumentTiming* timing = documentTiming(); 259 if (!timing) 260 return 0; 261 262 return monotonicTimeToIntegerMilliseconds(timing->domInteractive); 263 } 264 265 unsigned long long PerformanceTiming::domContentLoadedEventStart() const 266 { 267 const DocumentTiming* timing = documentTiming(); 268 if (!timing) 269 return 0; 270 271 return monotonicTimeToIntegerMilliseconds(timing->domContentLoadedEventStart); 272 } 273 274 unsigned long long PerformanceTiming::domContentLoadedEventEnd() const 275 { 276 const DocumentTiming* timing = documentTiming(); 277 if (!timing) 278 return 0; 279 280 return monotonicTimeToIntegerMilliseconds(timing->domContentLoadedEventEnd); 281 } 282 283 unsigned long long PerformanceTiming::domComplete() const 284 { 285 const DocumentTiming* timing = documentTiming(); 286 if (!timing) 287 return 0; 288 289 return monotonicTimeToIntegerMilliseconds(timing->domComplete); 290 } 291 292 unsigned long long PerformanceTiming::loadEventStart() const 293 { 294 DocumentLoadTiming* timing = documentLoadTiming(); 295 if (!timing) 296 return 0; 297 298 return monotonicTimeToIntegerMilliseconds(timing->loadEventStart()); 299 } 300 301 unsigned long long PerformanceTiming::loadEventEnd() const 302 { 303 DocumentLoadTiming* timing = documentLoadTiming(); 304 if (!timing) 305 return 0; 306 307 return monotonicTimeToIntegerMilliseconds(timing->loadEventEnd()); 308 } 309 310 DocumentLoader* PerformanceTiming::documentLoader() const 311 { 312 if (!m_frame) 313 return 0; 314 315 return m_frame->loader()->documentLoader(); 316 } 317 318 const DocumentTiming* PerformanceTiming::documentTiming() const 319 { 320 if (!m_frame) 321 return 0; 322 323 Document* document = m_frame->document(); 324 if (!document) 325 return 0; 326 327 return document->timing(); 328 } 329 330 DocumentLoadTiming* PerformanceTiming::documentLoadTiming() const 331 { 332 DocumentLoader* loader = documentLoader(); 333 if (!loader) 334 return 0; 335 336 return loader->timing(); 337 } 338 339 ResourceLoadTiming* PerformanceTiming::resourceLoadTiming() const 340 { 341 DocumentLoader* loader = documentLoader(); 342 if (!loader) 343 return 0; 344 345 return loader->response().resourceLoadTiming(); 346 } 347 348 unsigned long long PerformanceTiming::monotonicTimeToIntegerMilliseconds(double monotonicSeconds) const 349 { 350 ASSERT(monotonicSeconds >= 0); 351 const DocumentLoadTiming* timing = documentLoadTiming(); 352 ASSERT(timing); 353 return toIntegerMilliseconds(timing->monotonicTimeToPseudoWallTime(monotonicSeconds)); 354 } 355 356 } // namespace WebCore 357