Home | History | Annotate | Download | only in inspector
      1 /*
      2 * Copyright (C) 2013 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/inspector/InspectorTimelineAgent.h"
     33 
     34 #include "InspectorFrontend.h"
     35 #include "core/dom/Event.h"
     36 #include "core/inspector/IdentifiersFactory.h"
     37 #include "core/inspector/InspectorCounters.h"
     38 #include "core/inspector/InspectorDOMAgent.h"
     39 #include "core/inspector/InspectorInstrumentation.h"
     40 #include "core/inspector/InspectorMemoryAgent.h"
     41 #include "core/inspector/InspectorPageAgent.h"
     42 #include "core/inspector/InspectorState.h"
     43 #include "core/inspector/InstrumentingAgents.h"
     44 #include "core/inspector/ScriptCallStack.h"
     45 #include "core/inspector/TimelineRecordFactory.h"
     46 #include "core/inspector/TimelineTraceEventProcessor.h"
     47 #include "core/loader/DocumentLoader.h"
     48 #include "core/page/DOMWindow.h"
     49 #include "core/page/Frame.h"
     50 #include "core/page/FrameView.h"
     51 #include "core/platform/MemoryUsageSupport.h"
     52 #include "core/platform/chromium/TraceEvent.h"
     53 #include "core/platform/network/ResourceRequest.h"
     54 #include "core/rendering/RenderObject.h"
     55 #include "core/rendering/RenderView.h"
     56 #include "core/xml/XMLHttpRequest.h"
     57 
     58 #include "wtf/CurrentTime.h"
     59 
     60 namespace WebCore {
     61 
     62 namespace TimelineAgentState {
     63 static const char timelineAgentEnabled[] = "timelineAgentEnabled";
     64 static const char timelineMaxCallStackDepth[] = "timelineMaxCallStackDepth";
     65 static const char includeDomCounters[] = "includeDomCounters";
     66 static const char includeNativeMemoryStatistics[] = "includeNativeMemoryStatistics";
     67 }
     68 
     69 // Must be kept in sync with WebInspector.TimelineModel.RecordType in TimelineModel.js
     70 namespace TimelineRecordType {
     71 static const char Program[] = "Program";
     72 
     73 static const char EventDispatch[] = "EventDispatch";
     74 static const char BeginFrame[] = "BeginFrame";
     75 static const char ScheduleStyleRecalculation[] = "ScheduleStyleRecalculation";
     76 static const char RecalculateStyles[] = "RecalculateStyles";
     77 static const char InvalidateLayout[] = "InvalidateLayout";
     78 static const char Layout[] = "Layout";
     79 static const char Paint[] = "Paint";
     80 static const char ScrollLayer[] = "ScrollLayer";
     81 static const char ResizeImage[] = "ResizeImage";
     82 static const char CompositeLayers[] = "CompositeLayers";
     83 
     84 static const char ParseHTML[] = "ParseHTML";
     85 
     86 static const char TimerInstall[] = "TimerInstall";
     87 static const char TimerRemove[] = "TimerRemove";
     88 static const char TimerFire[] = "TimerFire";
     89 
     90 static const char EvaluateScript[] = "EvaluateScript";
     91 
     92 static const char MarkLoad[] = "MarkLoad";
     93 static const char MarkDOMContent[] = "MarkDOMContent";
     94 
     95 static const char TimeStamp[] = "TimeStamp";
     96 static const char Time[] = "Time";
     97 static const char TimeEnd[] = "TimeEnd";
     98 
     99 static const char ScheduleResourceRequest[] = "ScheduleResourceRequest";
    100 static const char ResourceSendRequest[] = "ResourceSendRequest";
    101 static const char ResourceReceiveResponse[] = "ResourceReceiveResponse";
    102 static const char ResourceReceivedData[] = "ResourceReceivedData";
    103 static const char ResourceFinish[] = "ResourceFinish";
    104 
    105 static const char XHRReadyStateChange[] = "XHRReadyStateChange";
    106 static const char XHRLoad[] = "XHRLoad";
    107 
    108 static const char FunctionCall[] = "FunctionCall";
    109 static const char GCEvent[] = "GCEvent";
    110 
    111 static const char RequestAnimationFrame[] = "RequestAnimationFrame";
    112 static const char CancelAnimationFrame[] = "CancelAnimationFrame";
    113 static const char FireAnimationFrame[] = "FireAnimationFrame";
    114 
    115 static const char WebSocketCreate[] = "WebSocketCreate";
    116 static const char WebSocketSendHandshakeRequest[] = "WebSocketSendHandshakeRequest";
    117 static const char WebSocketReceiveHandshakeResponse[] = "WebSocketReceiveHandshakeResponse";
    118 static const char WebSocketDestroy[] = "WebSocketDestroy";
    119 
    120 // Event names visible to other modules.
    121 const char DecodeImage[] = "DecodeImage";
    122 const char Rasterize[] = "Rasterize";
    123 const char PaintSetup[] = "PaintSetup";
    124 }
    125 
    126 namespace {
    127 const char BackendNodeIdGroup[] = "timeline";
    128 }
    129 
    130 static Frame* frameForScriptExecutionContext(ScriptExecutionContext* context)
    131 {
    132     Frame* frame = 0;
    133     if (context->isDocument())
    134         frame = toDocument(context)->frame();
    135     return frame;
    136 }
    137 
    138 static bool eventHasListeners(const AtomicString& eventType, DOMWindow* window, Node* node, const EventPath& eventPath)
    139 {
    140     if (window && window->hasEventListeners(eventType))
    141         return true;
    142 
    143     if (node->hasEventListeners(eventType))
    144         return true;
    145 
    146     for (size_t i = 0; i < eventPath.size(); i++) {
    147         if (eventPath[i]->node()->hasEventListeners(eventType))
    148             return true;
    149     }
    150 
    151     return false;
    152 }
    153 
    154 void TimelineTimeConverter::reset()
    155 {
    156     m_startTimeMs = currentTime() * 1000;
    157     m_timestampsBaseMs = monotonicallyIncreasingTime() * 1000;
    158 }
    159 
    160 void InspectorTimelineAgent::pushGCEventRecords()
    161 {
    162     if (!m_gcEvents.size())
    163         return;
    164 
    165     GCEvents events = m_gcEvents;
    166     m_gcEvents.clear();
    167     for (GCEvents::iterator i = events.begin(); i != events.end(); ++i) {
    168         RefPtr<JSONObject> record = TimelineRecordFactory::createGenericRecord(m_timeConverter.toProtocolTimestamp(i->startTime), m_maxCallStackDepth, TimelineRecordType::GCEvent);
    169         record->setObject("data", TimelineRecordFactory::createGCEventData(i->collectedBytes));
    170         record->setNumber("endTime", m_timeConverter.toProtocolTimestamp(i->endTime));
    171         addRecordToTimeline(record.release());
    172     }
    173 }
    174 
    175 void InspectorTimelineAgent::didGC(double startTime, double endTime, size_t collectedBytesCount)
    176 {
    177     m_gcEvents.append(GCEvent(startTime, endTime, collectedBytesCount));
    178 }
    179 
    180 InspectorTimelineAgent::~InspectorTimelineAgent()
    181 {
    182 }
    183 
    184 void InspectorTimelineAgent::setFrontend(InspectorFrontend* frontend)
    185 {
    186     m_frontend = frontend->timeline();
    187 }
    188 
    189 void InspectorTimelineAgent::clearFrontend()
    190 {
    191     ErrorString error;
    192     stop(&error);
    193     releaseNodeIds();
    194     m_frontend = 0;
    195 }
    196 
    197 void InspectorTimelineAgent::restore()
    198 {
    199     if (m_state->getBoolean(TimelineAgentState::timelineAgentEnabled)) {
    200         m_maxCallStackDepth = m_state->getLong(TimelineAgentState::timelineMaxCallStackDepth);
    201         ErrorString error;
    202         bool includeDomCounters = m_state->getBoolean(TimelineAgentState::includeDomCounters);
    203         bool includeNativeMemoryStatistics = m_state->getBoolean(TimelineAgentState::includeNativeMemoryStatistics);
    204         start(&error, &m_maxCallStackDepth, &includeDomCounters, &includeNativeMemoryStatistics);
    205     }
    206 }
    207 
    208 void InspectorTimelineAgent::start(ErrorString*, const int* maxCallStackDepth, const bool* includeDomCounters, const bool* includeNativeMemoryStatistics)
    209 {
    210     if (!m_frontend)
    211         return;
    212 
    213     releaseNodeIds();
    214     if (maxCallStackDepth && *maxCallStackDepth >= 0)
    215         m_maxCallStackDepth = *maxCallStackDepth;
    216     else
    217         m_maxCallStackDepth = 5;
    218     m_state->setLong(TimelineAgentState::timelineMaxCallStackDepth, m_maxCallStackDepth);
    219     m_state->setBoolean(TimelineAgentState::includeDomCounters, includeDomCounters && *includeDomCounters);
    220     m_state->setBoolean(TimelineAgentState::includeNativeMemoryStatistics, includeNativeMemoryStatistics && *includeNativeMemoryStatistics);
    221     m_timeConverter.reset();
    222     m_frontend->timelineStarted(m_timeConverter.timestampsBaseMs(), m_timeConverter.startTimeMs());
    223 
    224     m_instrumentingAgents->setInspectorTimelineAgent(this);
    225     ScriptGCEvent::addEventListener(this);
    226     m_state->setBoolean(TimelineAgentState::timelineAgentEnabled, true);
    227     if (m_client && m_pageAgent)
    228         m_traceEventProcessor = adoptRef(new TimelineTraceEventProcessor(m_weakFactory.createWeakPtr(), m_client));
    229 }
    230 
    231 void InspectorTimelineAgent::stop(ErrorString*)
    232 {
    233     if (!m_state->getBoolean(TimelineAgentState::timelineAgentEnabled))
    234         return;
    235 
    236     if (m_traceEventProcessor) {
    237         m_traceEventProcessor->shutdown();
    238         m_traceEventProcessor.clear();
    239     }
    240     m_weakFactory.revokeAll();
    241     m_instrumentingAgents->setInspectorTimelineAgent(0);
    242     ScriptGCEvent::removeEventListener(this);
    243 
    244     clearRecordStack();
    245     m_gcEvents.clear();
    246 
    247     m_state->setBoolean(TimelineAgentState::timelineAgentEnabled, false);
    248 }
    249 
    250 void InspectorTimelineAgent::didBeginFrame()
    251 {
    252     TRACE_EVENT_INSTANT0("webkit", InstrumentationEvents::BeginFrame);
    253     m_pendingFrameRecord = TimelineRecordFactory::createGenericRecord(timestamp(), 0, TimelineRecordType::BeginFrame);
    254 }
    255 
    256 void InspectorTimelineAgent::didCancelFrame()
    257 {
    258     m_pendingFrameRecord.clear();
    259 }
    260 
    261 bool InspectorTimelineAgent::willCallFunction(ScriptExecutionContext* context, const String& scriptName, int scriptLine)
    262 {
    263     pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(scriptName, scriptLine), TimelineRecordType::FunctionCall, true, frameForScriptExecutionContext(context));
    264     return true;
    265 }
    266 
    267 void InspectorTimelineAgent::didCallFunction()
    268 {
    269     didCompleteCurrentRecord(TimelineRecordType::FunctionCall);
    270 }
    271 
    272 bool InspectorTimelineAgent::willDispatchEvent(Document* document, const Event& event, DOMWindow* window, Node* node, const EventPath& eventPath)
    273 {
    274     if (!eventHasListeners(event.type(), window, node, eventPath))
    275        return false;
    276 
    277     pushCurrentRecord(TimelineRecordFactory::createEventDispatchData(event), TimelineRecordType::EventDispatch, false, document->frame());
    278     return true;
    279 }
    280 
    281 bool InspectorTimelineAgent::willDispatchEventOnWindow(const Event& event, DOMWindow* window)
    282 {
    283     if (!window->hasEventListeners(event.type()))
    284         return false;
    285     pushCurrentRecord(TimelineRecordFactory::createEventDispatchData(event), TimelineRecordType::EventDispatch, false, window->frame());
    286     return true;
    287 }
    288 
    289 void InspectorTimelineAgent::didDispatchEvent()
    290 {
    291     didCompleteCurrentRecord(TimelineRecordType::EventDispatch);
    292 }
    293 
    294 void InspectorTimelineAgent::didDispatchEventOnWindow()
    295 {
    296     didDispatchEvent();
    297 }
    298 
    299 void InspectorTimelineAgent::didInvalidateLayout(Frame* frame)
    300 {
    301     appendRecord(JSONObject::create(), TimelineRecordType::InvalidateLayout, true, frame);
    302 }
    303 
    304 bool InspectorTimelineAgent::willLayout(Frame* frame)
    305 {
    306     RenderObject* root = frame->view()->layoutRoot();
    307     bool partialLayout = !!root;
    308 
    309     if (!partialLayout)
    310         root = frame->contentRenderer();
    311 
    312     unsigned dirtyObjects = 0;
    313     unsigned totalObjects = 0;
    314     for (RenderObject* o = root; o; o = o->nextInPreOrder(root)) {
    315         ++totalObjects;
    316         if (o->needsLayout())
    317             ++dirtyObjects;
    318     }
    319     pushCurrentRecord(TimelineRecordFactory::createLayoutData(dirtyObjects, totalObjects, partialLayout), TimelineRecordType::Layout, true, frame);
    320     return true;
    321 }
    322 
    323 void InspectorTimelineAgent::didLayout(RenderObject* root)
    324 {
    325     if (m_recordStack.isEmpty())
    326         return;
    327     TimelineRecordEntry& entry = m_recordStack.last();
    328     ASSERT(entry.type == TimelineRecordType::Layout);
    329     Vector<FloatQuad> quads;
    330     root->absoluteQuads(quads);
    331     if (quads.size() >= 1)
    332         TimelineRecordFactory::appendLayoutRoot(entry.data.get(), quads[0], idForNode(root->generatingNode()));
    333     else
    334         ASSERT_NOT_REACHED();
    335     didCompleteCurrentRecord(TimelineRecordType::Layout);
    336 }
    337 
    338 void InspectorTimelineAgent::didScheduleStyleRecalculation(Document* document)
    339 {
    340     appendRecord(JSONObject::create(), TimelineRecordType::ScheduleStyleRecalculation, true, document->frame());
    341 }
    342 
    343 bool InspectorTimelineAgent::willRecalculateStyle(Document* document)
    344 {
    345     pushCurrentRecord(JSONObject::create(), TimelineRecordType::RecalculateStyles, true, document->frame());
    346     ASSERT(!m_styleRecalcElementCounter);
    347     return true;
    348 }
    349 
    350 void InspectorTimelineAgent::didRecalculateStyle()
    351 {
    352     if (m_recordStack.isEmpty())
    353         return;
    354     TimelineRecordEntry& entry = m_recordStack.last();
    355     ASSERT(entry.type == TimelineRecordType::RecalculateStyles);
    356     TimelineRecordFactory::appendStyleRecalcDetails(entry.data.get(), m_styleRecalcElementCounter);
    357     m_styleRecalcElementCounter = 0;
    358     didCompleteCurrentRecord(TimelineRecordType::RecalculateStyles);
    359 }
    360 
    361 void InspectorTimelineAgent::didRecalculateStyleForElement()
    362 {
    363     ++m_styleRecalcElementCounter;
    364 }
    365 
    366 void InspectorTimelineAgent::willPaint(RenderObject* renderer)
    367 {
    368     Frame* frame = renderer->frame();
    369     TRACE_EVENT_INSTANT2("instrumentation", InstrumentationEvents::Paint,
    370         InstrumentationEventArguments::PageId, reinterpret_cast<unsigned long long>(frame->page()),
    371         InstrumentationEventArguments::NodeId, idForNode(renderer->generatingNode()));
    372 
    373     pushCurrentRecord(JSONObject::create(), TimelineRecordType::Paint, true, frame, true);
    374 }
    375 
    376 void InspectorTimelineAgent::didPaint(RenderObject* renderer, GraphicsContext*, const LayoutRect& clipRect)
    377 {
    378     TimelineRecordEntry& entry = m_recordStack.last();
    379     ASSERT(entry.type == TimelineRecordType::Paint);
    380     FloatQuad quad;
    381     localToPageQuad(*renderer, clipRect, &quad);
    382     entry.data = TimelineRecordFactory::createPaintData(quad, idForNode(renderer->generatingNode()));
    383     didCompleteCurrentRecord(TimelineRecordType::Paint);
    384 }
    385 
    386 void InspectorTimelineAgent::willScrollLayer(RenderObject* renderer)
    387 {
    388     pushCurrentRecord(TimelineRecordFactory::createLayerData(idForNode(renderer->generatingNode())), TimelineRecordType::ScrollLayer, false, renderer->frame());
    389 }
    390 
    391 void InspectorTimelineAgent::didScrollLayer()
    392 {
    393     didCompleteCurrentRecord(TimelineRecordType::ScrollLayer);
    394 }
    395 
    396 void InspectorTimelineAgent::willDecodeImage(const String& imageType)
    397 {
    398     pushCurrentRecord(TimelineRecordFactory::createDecodeImageData(imageType), TimelineRecordType::DecodeImage, true, 0);
    399 }
    400 
    401 void InspectorTimelineAgent::didDecodeImage()
    402 {
    403     didCompleteCurrentRecord(TimelineRecordType::DecodeImage);
    404 }
    405 
    406 void InspectorTimelineAgent::willResizeImage(bool shouldCache)
    407 {
    408     pushCurrentRecord(TimelineRecordFactory::createResizeImageData(shouldCache), TimelineRecordType::ResizeImage, true, 0);
    409 }
    410 
    411 void InspectorTimelineAgent::didResizeImage()
    412 {
    413     didCompleteCurrentRecord(TimelineRecordType::ResizeImage);
    414 }
    415 
    416 void InspectorTimelineAgent::willComposite()
    417 {
    418     pushCurrentRecord(JSONObject::create(), TimelineRecordType::CompositeLayers, false, 0);
    419 }
    420 
    421 void InspectorTimelineAgent::didComposite()
    422 {
    423     didCompleteCurrentRecord(TimelineRecordType::CompositeLayers);
    424 }
    425 
    426 bool InspectorTimelineAgent::willWriteHTML(Document* document, unsigned startLine)
    427 {
    428     pushCurrentRecord(TimelineRecordFactory::createParseHTMLData(startLine), TimelineRecordType::ParseHTML, true, document->frame());
    429     return true;
    430 }
    431 
    432 void InspectorTimelineAgent::didWriteHTML(unsigned endLine)
    433 {
    434     if (!m_recordStack.isEmpty()) {
    435         TimelineRecordEntry entry = m_recordStack.last();
    436         entry.data->setNumber("endLine", endLine);
    437         didCompleteCurrentRecord(TimelineRecordType::ParseHTML);
    438     }
    439 }
    440 
    441 void InspectorTimelineAgent::didInstallTimer(ScriptExecutionContext* context, int timerId, int timeout, bool singleShot)
    442 {
    443     appendRecord(TimelineRecordFactory::createTimerInstallData(timerId, timeout, singleShot), TimelineRecordType::TimerInstall, true, frameForScriptExecutionContext(context));
    444 }
    445 
    446 void InspectorTimelineAgent::didRemoveTimer(ScriptExecutionContext* context, int timerId)
    447 {
    448     appendRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerRemove, true, frameForScriptExecutionContext(context));
    449 }
    450 
    451 bool InspectorTimelineAgent::willFireTimer(ScriptExecutionContext* context, int timerId)
    452 {
    453     pushCurrentRecord(TimelineRecordFactory::createGenericTimerData(timerId), TimelineRecordType::TimerFire, false, frameForScriptExecutionContext(context));
    454     return true;
    455 }
    456 
    457 void InspectorTimelineAgent::didFireTimer()
    458 {
    459     didCompleteCurrentRecord(TimelineRecordType::TimerFire);
    460 }
    461 
    462 bool InspectorTimelineAgent::willDispatchXHRReadyStateChangeEvent(ScriptExecutionContext* context, XMLHttpRequest* request)
    463 {
    464     if (!request->hasEventListeners(eventNames().readystatechangeEvent))
    465         return false;
    466     pushCurrentRecord(TimelineRecordFactory::createXHRReadyStateChangeData(request->url().string(), request->readyState()), TimelineRecordType::XHRReadyStateChange, false, frameForScriptExecutionContext(context));
    467     return true;
    468 }
    469 
    470 void InspectorTimelineAgent::didDispatchXHRReadyStateChangeEvent()
    471 {
    472     didCompleteCurrentRecord(TimelineRecordType::XHRReadyStateChange);
    473 }
    474 
    475 bool InspectorTimelineAgent::willDispatchXHRLoadEvent(ScriptExecutionContext* context, XMLHttpRequest* request)
    476 {
    477     if (!request->hasEventListeners(eventNames().loadEvent))
    478         return false;
    479     pushCurrentRecord(TimelineRecordFactory::createXHRLoadData(request->url().string()), TimelineRecordType::XHRLoad, true, frameForScriptExecutionContext(context));
    480     return true;
    481 }
    482 
    483 void InspectorTimelineAgent::didDispatchXHRLoadEvent()
    484 {
    485     didCompleteCurrentRecord(TimelineRecordType::XHRLoad);
    486 }
    487 
    488 bool InspectorTimelineAgent::willEvaluateScript(Frame* frame, const String& url, int lineNumber)
    489 {
    490     pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptData(url, lineNumber), TimelineRecordType::EvaluateScript, true, frame);
    491     return true;
    492 }
    493 
    494 void InspectorTimelineAgent::didEvaluateScript()
    495 {
    496     didCompleteCurrentRecord(TimelineRecordType::EvaluateScript);
    497 }
    498 
    499 void InspectorTimelineAgent::didScheduleResourceRequest(Document* document, const String& url)
    500 {
    501     appendRecord(TimelineRecordFactory::createScheduleResourceRequestData(url), TimelineRecordType::ScheduleResourceRequest, true, document->frame());
    502 }
    503 
    504 void InspectorTimelineAgent::willSendRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request, const ResourceResponse&, const FetchInitiatorInfo&)
    505 {
    506     String requestId = IdentifiersFactory::requestId(identifier);
    507     appendRecord(TimelineRecordFactory::createResourceSendRequestData(requestId, request), TimelineRecordType::ResourceSendRequest, true, loader->frame());
    508 }
    509 
    510 bool InspectorTimelineAgent::willReceiveResourceData(Frame* frame, unsigned long identifier, int length)
    511 {
    512     String requestId = IdentifiersFactory::requestId(identifier);
    513     pushCurrentRecord(TimelineRecordFactory::createReceiveResourceData(requestId, length), TimelineRecordType::ResourceReceivedData, false, frame);
    514     return true;
    515 }
    516 
    517 void InspectorTimelineAgent::didReceiveResourceData()
    518 {
    519     didCompleteCurrentRecord(TimelineRecordType::ResourceReceivedData);
    520 }
    521 
    522 bool InspectorTimelineAgent::willReceiveResourceResponse(Frame* frame, unsigned long identifier, const ResourceResponse& response)
    523 {
    524     String requestId = IdentifiersFactory::requestId(identifier);
    525     pushCurrentRecord(TimelineRecordFactory::createResourceReceiveResponseData(requestId, response), TimelineRecordType::ResourceReceiveResponse, false, frame);
    526     return true;
    527 }
    528 
    529 void InspectorTimelineAgent::didReceiveResourceResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response, ResourceLoader* resourceLoader)
    530 {
    531     didCompleteCurrentRecord(TimelineRecordType::ResourceReceiveResponse);
    532 }
    533 
    534 void InspectorTimelineAgent::didFinishLoadingResource(unsigned long identifier, bool didFail, double finishTime, Frame* frame)
    535 {
    536     appendRecord(TimelineRecordFactory::createResourceFinishData(IdentifiersFactory::requestId(identifier), didFail, finishTime * 1000), TimelineRecordType::ResourceFinish, false, frame);
    537 }
    538 
    539 void InspectorTimelineAgent::didFinishLoading(unsigned long identifier, DocumentLoader* loader, double monotonicFinishTime)
    540 {
    541     double finishTime = 0.0;
    542     // FIXME: Expose all of the timing details to inspector and have it calculate finishTime.
    543     if (monotonicFinishTime)
    544         finishTime = loader->timing()->monotonicTimeToPseudoWallTime(monotonicFinishTime);
    545 
    546     didFinishLoadingResource(identifier, false, finishTime, loader->frame());
    547 }
    548 
    549 void InspectorTimelineAgent::didFailLoading(unsigned long identifier, DocumentLoader* loader, const ResourceError& error)
    550 {
    551     didFinishLoadingResource(identifier, true, 0, loader->frame());
    552 }
    553 
    554 void InspectorTimelineAgent::consoleTimeStamp(Frame* frame, PassRefPtr<ScriptArguments> arguments)
    555 {
    556     String message;
    557     arguments->getFirstArgumentAsString(message);
    558     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeStamp, true, frame);
    559 }
    560 
    561 void InspectorTimelineAgent::startConsoleTiming(Frame* frame, const String& message)
    562 {
    563     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::Time, true, frame);
    564 }
    565 
    566 void InspectorTimelineAgent::stopConsoleTiming(Frame* frame, const String& message, PassRefPtr<ScriptCallStack>)
    567 {
    568     appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeEnd, true, frame);
    569 }
    570 
    571 void InspectorTimelineAgent::domContentLoadedEventFired(Frame* frame)
    572 {
    573     bool isMainFrame = frame && m_pageAgent && (frame == m_pageAgent->mainFrame());
    574     appendRecord(TimelineRecordFactory::createMarkData(isMainFrame), TimelineRecordType::MarkDOMContent, false, frame);
    575 }
    576 
    577 void InspectorTimelineAgent::loadEventFired(Frame* frame)
    578 {
    579     bool isMainFrame = frame && m_pageAgent && (frame == m_pageAgent->mainFrame());
    580     appendRecord(TimelineRecordFactory::createMarkData(isMainFrame), TimelineRecordType::MarkLoad, false, frame);
    581 }
    582 
    583 void InspectorTimelineAgent::didCommitLoad()
    584 {
    585     clearRecordStack();
    586 }
    587 
    588 void InspectorTimelineAgent::didRequestAnimationFrame(Document* document, int callbackId)
    589 {
    590     appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::RequestAnimationFrame, true, document->frame());
    591 }
    592 
    593 void InspectorTimelineAgent::didCancelAnimationFrame(Document* document, int callbackId)
    594 {
    595     appendRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::CancelAnimationFrame, true, document->frame());
    596 }
    597 
    598 bool InspectorTimelineAgent::willFireAnimationFrame(Document* document, int callbackId)
    599 {
    600     pushCurrentRecord(TimelineRecordFactory::createAnimationFrameData(callbackId), TimelineRecordType::FireAnimationFrame, false, document->frame());
    601     return true;
    602 }
    603 
    604 void InspectorTimelineAgent::didFireAnimationFrame()
    605 {
    606     didCompleteCurrentRecord(TimelineRecordType::FireAnimationFrame);
    607 }
    608 
    609 void InspectorTimelineAgent::willProcessTask()
    610 {
    611     pushCurrentRecord(JSONObject::create(), TimelineRecordType::Program, false, 0);
    612 }
    613 
    614 void InspectorTimelineAgent::didProcessTask()
    615 {
    616     didCompleteCurrentRecord(TimelineRecordType::Program);
    617 }
    618 
    619 void InspectorTimelineAgent::didCreateWebSocket(Document* document, unsigned long identifier, const KURL& url, const String& protocol)
    620 {
    621     appendRecord(TimelineRecordFactory::createWebSocketCreateData(identifier, url, protocol), TimelineRecordType::WebSocketCreate, true, document->frame());
    622 }
    623 
    624 void InspectorTimelineAgent::willSendWebSocketHandshakeRequest(Document* document, unsigned long identifier, const WebSocketHandshakeRequest&)
    625 {
    626     appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketSendHandshakeRequest, true, document->frame());
    627 }
    628 
    629 void InspectorTimelineAgent::didReceiveWebSocketHandshakeResponse(Document* document, unsigned long identifier, const WebSocketHandshakeResponse&)
    630 {
    631     appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketReceiveHandshakeResponse, false, document->frame());
    632 }
    633 
    634 void InspectorTimelineAgent::didCloseWebSocket(Document* document, unsigned long identifier)
    635 {
    636     appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketDestroy, true, document->frame());
    637 }
    638 
    639 void InspectorTimelineAgent::addRecordToTimeline(PassRefPtr<JSONObject> record)
    640 {
    641     commitFrameRecord();
    642     innerAddRecordToTimeline(record);
    643 }
    644 
    645 void InspectorTimelineAgent::innerAddRecordToTimeline(PassRefPtr<JSONObject> prpRecord)
    646 {
    647     RefPtr<TypeBuilder::Timeline::TimelineEvent> record = TypeBuilder::Timeline::TimelineEvent::runtimeCast(prpRecord);
    648 
    649     if (m_recordStack.isEmpty()) {
    650         sendEvent(record.release());
    651     } else {
    652         setDOMCounters(record.get());
    653         TimelineRecordEntry parent = m_recordStack.last();
    654         parent.children->pushObject(record.release());
    655     }
    656 }
    657 
    658 static size_t getUsedHeapSize()
    659 {
    660     HeapInfo info;
    661     ScriptGCEvent::getHeapSize(info);
    662     return info.usedJSHeapSize;
    663 }
    664 
    665 void InspectorTimelineAgent::setDOMCounters(TypeBuilder::Timeline::TimelineEvent* record)
    666 {
    667     record->setUsedHeapSize(getUsedHeapSize());
    668 
    669     if (m_state->getBoolean(TimelineAgentState::includeDomCounters)) {
    670         int documentCount = 0;
    671         int nodeCount = 0;
    672         if (m_inspectorType == PageInspector) {
    673             documentCount = InspectorCounters::counterValue(InspectorCounters::DocumentCounter);
    674             nodeCount = InspectorCounters::counterValue(InspectorCounters::NodeCounter);
    675         }
    676         int listenerCount = ThreadLocalInspectorCounters::current().counterValue(ThreadLocalInspectorCounters::JSEventListenerCounter);
    677         RefPtr<TypeBuilder::Timeline::DOMCounters> counters = TypeBuilder::Timeline::DOMCounters::create()
    678             .setDocuments(documentCount)
    679             .setNodes(nodeCount)
    680             .setJsEventListeners(listenerCount);
    681         record->setCounters(counters.release());
    682     }
    683 }
    684 
    685 void InspectorTimelineAgent::setFrameIdentifier(JSONObject* record, Frame* frame)
    686 {
    687     if (!frame || !m_pageAgent)
    688         return;
    689     String frameId;
    690     if (frame && m_pageAgent)
    691         frameId = m_pageAgent->frameId(frame);
    692     record->setString("frameId", frameId);
    693 }
    694 
    695 void InspectorTimelineAgent::didCompleteCurrentRecord(const String& type)
    696 {
    697     // An empty stack could merely mean that the timeline agent was turned on in the middle of
    698     // an event.  Don't treat as an error.
    699     if (!m_recordStack.isEmpty()) {
    700         if (m_platformInstrumentationClientInstalledAtStackDepth == m_recordStack.size()) {
    701             m_platformInstrumentationClientInstalledAtStackDepth = 0;
    702             PlatformInstrumentation::setClient(0);
    703         }
    704 
    705         pushGCEventRecords();
    706         TimelineRecordEntry entry = m_recordStack.last();
    707         m_recordStack.removeLast();
    708         ASSERT(entry.type == type);
    709         entry.record->setObject("data", entry.data);
    710         entry.record->setArray("children", entry.children);
    711         entry.record->setNumber("endTime", timestamp());
    712         ptrdiff_t usedHeapSizeDelta = getUsedHeapSize() - entry.usedHeapSizeAtStart;
    713         if (usedHeapSizeDelta)
    714             entry.record->setNumber("usedHeapSizeDelta", usedHeapSizeDelta);
    715         addRecordToTimeline(entry.record);
    716     }
    717 }
    718 
    719 InspectorTimelineAgent::InspectorTimelineAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorMemoryAgent* memoryAgent, InspectorDOMAgent* domAgent, InspectorCompositeState* state, InspectorType type, InspectorClient* client)
    720     : InspectorBaseAgent<InspectorTimelineAgent>("Timeline", instrumentingAgents, state)
    721     , m_pageAgent(pageAgent)
    722     , m_memoryAgent(memoryAgent)
    723     , m_domAgent(domAgent)
    724     , m_frontend(0)
    725     , m_id(1)
    726     , m_maxCallStackDepth(5)
    727     , m_platformInstrumentationClientInstalledAtStackDepth(0)
    728     , m_inspectorType(type)
    729     , m_client(client)
    730     , m_weakFactory(this)
    731     , m_styleRecalcElementCounter(0)
    732     , m_layerTreeId(0)
    733 {
    734 }
    735 
    736 void InspectorTimelineAgent::appendRecord(PassRefPtr<JSONObject> data, const String& type, bool captureCallStack, Frame* frame)
    737 {
    738     pushGCEventRecords();
    739     RefPtr<JSONObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0, type);
    740     record->setObject("data", data);
    741     setFrameIdentifier(record.get(), frame);
    742     addRecordToTimeline(record.release());
    743 }
    744 
    745 void InspectorTimelineAgent::sendEvent(PassRefPtr<JSONObject> event)
    746 {
    747     // FIXME: runtimeCast is a hack. We do it because we can't build TimelineEvent directly now.
    748     RefPtr<TypeBuilder::Timeline::TimelineEvent> recordChecked = TypeBuilder::Timeline::TimelineEvent::runtimeCast(event);
    749     m_frontend->eventRecorded(recordChecked.release());
    750 }
    751 
    752 void InspectorTimelineAgent::pushCurrentRecord(PassRefPtr<JSONObject> data, const String& type, bool captureCallStack, Frame* frame, bool hasLowLevelDetails)
    753 {
    754     pushGCEventRecords();
    755     commitFrameRecord();
    756     RefPtr<JSONObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0, type);
    757     setFrameIdentifier(record.get(), frame);
    758     m_recordStack.append(TimelineRecordEntry(record.release(), data, JSONArray::create(), type, getUsedHeapSize()));
    759     if (hasLowLevelDetails && !m_platformInstrumentationClientInstalledAtStackDepth && !PlatformInstrumentation::hasClient()) {
    760         m_platformInstrumentationClientInstalledAtStackDepth = m_recordStack.size();
    761         PlatformInstrumentation::setClient(this);
    762     }
    763 }
    764 
    765 void InspectorTimelineAgent::commitFrameRecord()
    766 {
    767     if (!m_pendingFrameRecord)
    768         return;
    769 
    770     m_pendingFrameRecord->setObject("data", JSONObject::create());
    771     innerAddRecordToTimeline(m_pendingFrameRecord.release());
    772 }
    773 
    774 void InspectorTimelineAgent::clearRecordStack()
    775 {
    776     if (m_platformInstrumentationClientInstalledAtStackDepth) {
    777         m_platformInstrumentationClientInstalledAtStackDepth = 0;
    778         PlatformInstrumentation::setClient(0);
    779     }
    780     m_pendingFrameRecord.clear();
    781     m_recordStack.clear();
    782     m_id++;
    783 }
    784 
    785 void InspectorTimelineAgent::localToPageQuad(const RenderObject& renderer, const LayoutRect& rect, FloatQuad* quad)
    786 {
    787     Frame* frame = renderer.frame();
    788     FrameView* view = frame->view();
    789     FloatQuad absolute = renderer.localToAbsoluteQuad(FloatQuad(rect));
    790     quad->setP1(view->contentsToRootView(roundedIntPoint(absolute.p1())));
    791     quad->setP2(view->contentsToRootView(roundedIntPoint(absolute.p2())));
    792     quad->setP3(view->contentsToRootView(roundedIntPoint(absolute.p3())));
    793     quad->setP4(view->contentsToRootView(roundedIntPoint(absolute.p4())));
    794 }
    795 
    796 long long InspectorTimelineAgent::idForNode(Node* node)
    797 {
    798     return m_domAgent && node ? m_domAgent->backendNodeIdForNode(node, BackendNodeIdGroup) : 0;
    799 }
    800 
    801 void InspectorTimelineAgent::releaseNodeIds()
    802 {
    803     ErrorString unused;
    804     if (m_domAgent)
    805         m_domAgent->releaseBackendNodeIds(&unused, BackendNodeIdGroup);
    806 }
    807 
    808 double InspectorTimelineAgent::timestamp() const
    809 {
    810     return m_timeConverter.toProtocolTimestamp(WTF::monotonicallyIncreasingTime());
    811 }
    812 
    813 Page* InspectorTimelineAgent::page()
    814 {
    815     return m_pageAgent ? m_pageAgent->page() : 0;
    816 }
    817 
    818 } // namespace WebCore
    819 
    820