Home | History | Annotate | Download | only in inspector
      1 /*
      2  * Copyright (C) 2011 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
      6  * are met:
      7  * 1.  Redistributions of source code must retain the above copyright
      8  *     notice, this list of conditions and the following disclaimer.
      9  * 2.  Redistributions in binary form must reproduce the above copyright
     10  *     notice, this list of conditions and the following disclaimer in the
     11  *     documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
     20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     23  */
     24 
     25 
     26 #include "config.h"
     27 #include "InspectorConsoleAgent.h"
     28 
     29 #if ENABLE(INSPECTOR)
     30 #include "InstrumentingAgents.h"
     31 #include "Console.h"
     32 #include "ConsoleMessage.h"
     33 #include "InjectedScriptHost.h"
     34 #include "InjectedScriptManager.h"
     35 #include "InspectorAgent.h"
     36 #include "InspectorDOMAgent.h"
     37 #include "InspectorFrontend.h"
     38 #include "InspectorState.h"
     39 #include "ResourceError.h"
     40 #include "ResourceResponse.h"
     41 #include "ScriptArguments.h"
     42 #include "ScriptCallFrame.h"
     43 #include "ScriptCallStack.h"
     44 #include <wtf/CurrentTime.h>
     45 #include <wtf/OwnPtr.h>
     46 #include <wtf/PassOwnPtr.h>
     47 #include <wtf/text/StringConcatenate.h>
     48 
     49 namespace WebCore {
     50 
     51 static const unsigned maximumConsoleMessages = 1000;
     52 static const int expireConsoleMessagesStep = 100;
     53 
     54 namespace ConsoleAgentState {
     55 static const char monitoringXHR[] = "monitoringXHR";
     56 static const char consoleMessagesEnabled[] = "consoleMessagesEnabled";
     57 }
     58 
     59 InspectorConsoleAgent::InspectorConsoleAgent(InstrumentingAgents* instrumentingAgents, InspectorAgent* inspectorAgent, InspectorState* state, InjectedScriptManager* injectedScriptManager, InspectorDOMAgent* domAgent)
     60     : m_instrumentingAgents(instrumentingAgents)
     61     , m_inspectorAgent(inspectorAgent)
     62     , m_inspectorState(state)
     63     , m_injectedScriptManager(injectedScriptManager)
     64     , m_inspectorDOMAgent(domAgent)
     65     , m_frontend(0)
     66     , m_previousMessage(0)
     67     , m_expiredConsoleMessageCount(0)
     68 {
     69     m_instrumentingAgents->setInspectorConsoleAgent(this);
     70 }
     71 
     72 InspectorConsoleAgent::~InspectorConsoleAgent()
     73 {
     74     m_instrumentingAgents->setInspectorConsoleAgent(0);
     75     m_instrumentingAgents = 0;
     76     m_inspectorAgent = 0;
     77     m_inspectorState = 0;
     78     m_injectedScriptManager = 0;
     79     m_inspectorDOMAgent = 0;
     80 }
     81 
     82 void InspectorConsoleAgent::enable(ErrorString*, int* consoleMessageExpireCount)
     83 {
     84     *consoleMessageExpireCount = m_expiredConsoleMessageCount;
     85 
     86     m_inspectorState->setBoolean(ConsoleAgentState::consoleMessagesEnabled, true);
     87 
     88     size_t messageCount = m_consoleMessages.size();
     89     for (size_t i = 0; i < messageCount; ++i)
     90         m_consoleMessages[i]->addToFrontend(m_frontend, m_injectedScriptManager);
     91 }
     92 
     93 void InspectorConsoleAgent::disable(ErrorString*)
     94 {
     95     m_inspectorState->setBoolean(ConsoleAgentState::consoleMessagesEnabled, false);
     96 }
     97 
     98 void InspectorConsoleAgent::clearConsoleMessages(ErrorString*)
     99 {
    100     m_consoleMessages.clear();
    101     m_expiredConsoleMessageCount = 0;
    102     m_previousMessage = 0;
    103     m_injectedScriptManager->releaseObjectGroup("console");
    104     m_inspectorDOMAgent->releaseDanglingNodes();
    105     if (m_frontend)
    106         m_frontend->messagesCleared();
    107 }
    108 
    109 void InspectorConsoleAgent::reset()
    110 {
    111     ErrorString error;
    112     clearConsoleMessages(&error);
    113     m_times.clear();
    114     m_counts.clear();
    115 }
    116 
    117 void InspectorConsoleAgent::setFrontend(InspectorFrontend* frontend)
    118 {
    119     m_frontend = frontend->console();
    120 }
    121 
    122 void InspectorConsoleAgent::clearFrontend()
    123 {
    124     m_frontend = 0;
    125 }
    126 
    127 void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
    128 {
    129     if (!m_inspectorAgent->enabled())
    130         return;
    131     addConsoleMessage(new ConsoleMessage(source, type, level, message, arguments, callStack));
    132 }
    133 
    134 void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID)
    135 {
    136     if (!m_inspectorAgent->enabled())
    137         return;
    138     addConsoleMessage(new ConsoleMessage(source, type, level, message, lineNumber, sourceID));
    139 }
    140 
    141 void InspectorConsoleAgent::startTiming(const String& title)
    142 {
    143     // Follow Firebug's behavior of requiring a title that is not null or
    144     // undefined for timing functions
    145     if (title.isNull())
    146         return;
    147 
    148     m_times.add(title, currentTime() * 1000);
    149 }
    150 
    151 void InspectorConsoleAgent::stopTiming(const String& title, PassRefPtr<ScriptCallStack> callStack)
    152 {
    153     // Follow Firebug's behavior of requiring a title that is not null or
    154     // undefined for timing functions
    155     if (title.isNull())
    156         return;
    157 
    158     HashMap<String, double>::iterator it = m_times.find(title);
    159     if (it == m_times.end())
    160         return;
    161 
    162     double startTime = it->second;
    163     m_times.remove(it);
    164 
    165     double elapsed = currentTime() * 1000 - startTime;
    166     String message = title + String::format(": %.0fms", elapsed);
    167     const ScriptCallFrame& lastCaller = callStack->at(0);
    168     addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lastCaller.lineNumber(), lastCaller.sourceURL());
    169 }
    170 
    171 void InspectorConsoleAgent::count(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
    172 {
    173     const ScriptCallFrame& lastCaller = callStack->at(0);
    174     // Follow Firebug's behavior of counting with null and undefined title in
    175     // the same bucket as no argument
    176     String title;
    177     arguments->getFirstArgumentAsString(title);
    178     String identifier = makeString(title, '@', lastCaller.sourceURL(), ':', String::number(lastCaller.lineNumber()));
    179 
    180     HashMap<String, unsigned>::iterator it = m_counts.find(identifier);
    181     int count;
    182     if (it == m_counts.end())
    183         count = 1;
    184     else {
    185         count = it->second + 1;
    186         m_counts.remove(it);
    187     }
    188 
    189     m_counts.add(identifier, count);
    190 
    191     String message = makeString(title, ": ", String::number(count));
    192     addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lastCaller.lineNumber(), lastCaller.sourceURL());
    193 }
    194 
    195 void InspectorConsoleAgent::resourceRetrievedByXMLHttpRequest(const String& url, const String& sendURL, unsigned sendLineNumber)
    196 {
    197     if (!m_inspectorAgent->enabled())
    198         return;
    199     if (m_inspectorState->getBoolean(ConsoleAgentState::monitoringXHR))
    200         addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, "XHR finished loading: \"" + url + "\".", sendLineNumber, sendURL);
    201 }
    202 
    203 void InspectorConsoleAgent::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
    204 {
    205     if (!m_inspectorAgent->enabled())
    206         return;
    207 
    208     if (response.httpStatusCode() >= 400) {
    209         String message = makeString("Failed to load resource: the server responded with a status of ", String::number(response.httpStatusCode()), " (", response.httpStatusText(), ')');
    210         addConsoleMessage(new ConsoleMessage(OtherMessageSource, NetworkErrorMessageType, ErrorMessageLevel, message, response.url().string(), identifier));
    211     }
    212 }
    213 
    214 void InspectorConsoleAgent::didFailLoading(unsigned long identifier, const ResourceError& error)
    215 {
    216     if (!m_inspectorAgent->enabled())
    217         return;
    218     if (error.isCancellation()) // Report failures only.
    219         return;
    220     String message = "Failed to load resource";
    221     if (!error.localizedDescription().isEmpty())
    222         message += ": " + error.localizedDescription();
    223     addConsoleMessage(new ConsoleMessage(OtherMessageSource, NetworkErrorMessageType, ErrorMessageLevel, message, error.failingURL(), identifier));
    224 }
    225 
    226 void InspectorConsoleAgent::setMonitoringXHREnabled(ErrorString*, bool enabled)
    227 {
    228     m_inspectorState->setBoolean(ConsoleAgentState::monitoringXHR, enabled);
    229 }
    230 
    231 void InspectorConsoleAgent::addInspectedNode(ErrorString*, int nodeId)
    232 {
    233     Node* node = m_inspectorDOMAgent->nodeForId(nodeId);
    234     if (!node)
    235         return;
    236     m_injectedScriptManager->injectedScriptHost()->addInspectedNode(node);
    237 }
    238 
    239 void InspectorConsoleAgent::addConsoleMessage(PassOwnPtr<ConsoleMessage> consoleMessage)
    240 {
    241     ASSERT(m_inspectorAgent->enabled());
    242     ASSERT_ARG(consoleMessage, consoleMessage);
    243 
    244     if (m_previousMessage && m_previousMessage->type() != EndGroupMessageType && m_previousMessage->isEqual(consoleMessage.get())) {
    245         m_previousMessage->incrementCount();
    246         if (m_inspectorState->getBoolean(ConsoleAgentState::consoleMessagesEnabled) && m_frontend)
    247             m_previousMessage->updateRepeatCountInConsole(m_frontend);
    248     } else {
    249         m_previousMessage = consoleMessage.get();
    250         m_consoleMessages.append(consoleMessage);
    251         if (m_inspectorState->getBoolean(ConsoleAgentState::consoleMessagesEnabled) && m_frontend)
    252             m_previousMessage->addToFrontend(m_frontend, m_injectedScriptManager);
    253     }
    254 
    255     if (!m_frontend && m_consoleMessages.size() >= maximumConsoleMessages) {
    256         m_expiredConsoleMessageCount += expireConsoleMessagesStep;
    257         m_consoleMessages.remove(0, expireConsoleMessagesStep);
    258     }
    259 }
    260 
    261 } // namespace WebCore
    262 
    263 #endif // ENABLE(INSPECTOR)
    264