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 "core/inspector/InspectorConsoleAgent.h"
     28 
     29 #include "bindings/v8/ScriptCallStackFactory.h"
     30 #include "bindings/v8/ScriptController.h"
     31 #include "bindings/v8/ScriptProfiler.h"
     32 #include "core/frame/LocalFrame.h"
     33 #include "core/inspector/ConsoleMessage.h"
     34 #include "core/inspector/InjectedScriptHost.h"
     35 #include "core/inspector/InjectedScriptManager.h"
     36 #include "core/inspector/InspectorState.h"
     37 #include "core/inspector/InspectorTimelineAgent.h"
     38 #include "core/inspector/InstrumentingAgents.h"
     39 #include "core/inspector/ScriptArguments.h"
     40 #include "core/inspector/ScriptCallFrame.h"
     41 #include "core/inspector/ScriptCallStack.h"
     42 #include "core/loader/DocumentLoader.h"
     43 #include "core/page/Page.h"
     44 #include "platform/network/ResourceError.h"
     45 #include "platform/network/ResourceResponse.h"
     46 #include "wtf/CurrentTime.h"
     47 #include "wtf/OwnPtr.h"
     48 #include "wtf/PassOwnPtr.h"
     49 #include "wtf/text/StringBuilder.h"
     50 #include "wtf/text/WTFString.h"
     51 
     52 namespace WebCore {
     53 
     54 static const unsigned maximumConsoleMessages = 1000;
     55 static const int expireConsoleMessagesStep = 100;
     56 
     57 namespace ConsoleAgentState {
     58 static const char monitoringXHR[] = "monitoringXHR";
     59 static const char consoleMessagesEnabled[] = "consoleMessagesEnabled";
     60 }
     61 
     62 int InspectorConsoleAgent::s_enabledAgentCount = 0;
     63 
     64 InspectorConsoleAgent::InspectorConsoleAgent(InspectorTimelineAgent* timelineAgent, InjectedScriptManager* injectedScriptManager)
     65     : InspectorBaseAgent<InspectorConsoleAgent>("Console")
     66     , m_timelineAgent(timelineAgent)
     67     , m_injectedScriptManager(injectedScriptManager)
     68     , m_frontend(0)
     69     , m_expiredConsoleMessageCount(0)
     70     , m_enabled(false)
     71 {
     72 }
     73 
     74 InspectorConsoleAgent::~InspectorConsoleAgent()
     75 {
     76     m_instrumentingAgents->setInspectorConsoleAgent(0);
     77     m_instrumentingAgents = 0;
     78     m_state = 0;
     79     m_injectedScriptManager = 0;
     80 }
     81 
     82 void InspectorConsoleAgent::init()
     83 {
     84     m_instrumentingAgents->setInspectorConsoleAgent(this);
     85 }
     86 
     87 void InspectorConsoleAgent::enable(ErrorString*)
     88 {
     89     if (m_enabled)
     90         return;
     91     m_enabled = true;
     92     if (!s_enabledAgentCount)
     93         ScriptController::setCaptureCallStackForUncaughtExceptions(true);
     94     ++s_enabledAgentCount;
     95 
     96     m_state->setBoolean(ConsoleAgentState::consoleMessagesEnabled, true);
     97 
     98     if (m_expiredConsoleMessageCount) {
     99         ConsoleMessage expiredMessage(!isWorkerAgent(), OtherMessageSource, LogMessageType, WarningMessageLevel, String::format("%d console messages are not shown.", m_expiredConsoleMessageCount));
    100         expiredMessage.setTimestamp(0);
    101         expiredMessage.addToFrontend(m_frontend, m_injectedScriptManager, false);
    102     }
    103 
    104     size_t messageCount = m_consoleMessages.size();
    105     for (size_t i = 0; i < messageCount; ++i)
    106         m_consoleMessages[i]->addToFrontend(m_frontend, m_injectedScriptManager, false);
    107 }
    108 
    109 void InspectorConsoleAgent::disable(ErrorString*)
    110 {
    111     if (!m_enabled)
    112         return;
    113     m_enabled = false;
    114     if (!(--s_enabledAgentCount))
    115         ScriptController::setCaptureCallStackForUncaughtExceptions(false);
    116     m_state->setBoolean(ConsoleAgentState::consoleMessagesEnabled, false);
    117 }
    118 
    119 void InspectorConsoleAgent::clearMessages(ErrorString*)
    120 {
    121     m_consoleMessages.clear();
    122     m_expiredConsoleMessageCount = 0;
    123     m_injectedScriptManager->releaseObjectGroup("console");
    124     if (m_frontend && m_enabled)
    125         m_frontend->messagesCleared();
    126 }
    127 
    128 void InspectorConsoleAgent::reset()
    129 {
    130     ErrorString error;
    131     clearMessages(&error);
    132     m_times.clear();
    133     m_counts.clear();
    134 }
    135 
    136 void InspectorConsoleAgent::restore()
    137 {
    138     if (m_state->getBoolean(ConsoleAgentState::consoleMessagesEnabled)) {
    139         m_frontend->messagesCleared();
    140         ErrorString error;
    141         enable(&error);
    142     }
    143 }
    144 
    145 void InspectorConsoleAgent::setFrontend(InspectorFrontend* frontend)
    146 {
    147     m_frontend = frontend->console();
    148 }
    149 
    150 void InspectorConsoleAgent::clearFrontend()
    151 {
    152     m_frontend = 0;
    153     String errorString;
    154     disable(&errorString);
    155 }
    156 
    157 void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtrWillBeRawPtr<ScriptCallStack> callStack, unsigned long requestIdentifier)
    158 {
    159     if (type == ClearMessageType) {
    160         ErrorString error;
    161         clearMessages(&error);
    162     }
    163 
    164     addConsoleMessage(adoptPtr(new ConsoleMessage(!isWorkerAgent(), source, type, level, message, callStack, requestIdentifier)));
    165 }
    166 
    167 void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, ScriptState* scriptState, PassRefPtrWillBeRawPtr<ScriptArguments> arguments, unsigned long requestIdentifier)
    168 {
    169     if (type == ClearMessageType) {
    170         ErrorString error;
    171         clearMessages(&error);
    172     }
    173 
    174     addConsoleMessage(adoptPtr(new ConsoleMessage(!isWorkerAgent(), source, type, level, message, arguments, scriptState, requestIdentifier)));
    175 }
    176 
    177 void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptId, unsigned lineNumber, unsigned columnNumber, ScriptState* scriptState, unsigned long requestIdentifier)
    178 {
    179     if (type == ClearMessageType) {
    180         ErrorString error;
    181         clearMessages(&error);
    182     }
    183 
    184     bool canGenerateCallStack = !isWorkerAgent() && m_frontend;
    185     addConsoleMessage(adoptPtr(new ConsoleMessage(canGenerateCallStack, source, type, level, message, scriptId, lineNumber, columnNumber, scriptState, requestIdentifier)));
    186 }
    187 
    188 Vector<unsigned> InspectorConsoleAgent::consoleMessageArgumentCounts()
    189 {
    190     Vector<unsigned> result(m_consoleMessages.size());
    191     for (size_t i = 0; i < m_consoleMessages.size(); i++)
    192         result[i] = m_consoleMessages[i]->argumentCount();
    193     return result;
    194 }
    195 
    196 void InspectorConsoleAgent::consoleTime(ExecutionContext*, const String& title)
    197 {
    198     // Follow Firebug's behavior of requiring a title that is not null or
    199     // undefined for timing functions
    200     if (title.isNull())
    201         return;
    202 
    203     m_times.add(title, monotonicallyIncreasingTime());
    204 }
    205 
    206 void InspectorConsoleAgent::consoleTimeEnd(ExecutionContext*, const String& title, ScriptState* scriptState)
    207 {
    208     // Follow Firebug's behavior of requiring a title that is not null or
    209     // undefined for timing functions
    210     if (title.isNull())
    211         return;
    212 
    213     HashMap<String, double>::iterator it = m_times.find(title);
    214     if (it == m_times.end())
    215         return;
    216 
    217     double startTime = it->value;
    218     m_times.remove(it);
    219 
    220     double elapsed = monotonicallyIncreasingTime() - startTime;
    221     String message = title + String::format(": %.3fms", elapsed * 1000);
    222     addMessageToConsole(ConsoleAPIMessageSource, LogMessageType, DebugMessageLevel, message, String(), 0, 0, scriptState);
    223 }
    224 
    225 void InspectorConsoleAgent::consoleTimeline(ExecutionContext* context, const String& title, ScriptState* scriptState)
    226 {
    227     m_timelineAgent->consoleTimeline(context, title, scriptState);
    228 }
    229 
    230 void InspectorConsoleAgent::consoleTimelineEnd(ExecutionContext* context, const String& title, ScriptState* scriptState)
    231 {
    232     m_timelineAgent->consoleTimelineEnd(context, title, scriptState);
    233 }
    234 
    235 void InspectorConsoleAgent::consoleCount(ScriptState* scriptState, PassRefPtrWillBeRawPtr<ScriptArguments> arguments)
    236 {
    237     RefPtrWillBeRawPtr<ScriptCallStack> callStack(createScriptCallStackForConsole(scriptState));
    238     const ScriptCallFrame& lastCaller = callStack->at(0);
    239     // Follow Firebug's behavior of counting with null and undefined title in
    240     // the same bucket as no argument
    241     String title;
    242     arguments->getFirstArgumentAsString(title);
    243     String identifier = title.isEmpty() ? String(lastCaller.sourceURL() + ':' + String::number(lastCaller.lineNumber()))
    244                                         : String(title + '@');
    245 
    246     HashCountedSet<String>::AddResult result = m_counts.add(identifier);
    247     String message = title + ": " + String::number(result.storedValue->value);
    248     addMessageToConsole(ConsoleAPIMessageSource, LogMessageType, DebugMessageLevel, message, callStack.get());
    249 }
    250 
    251 void InspectorConsoleAgent::frameWindowDiscarded(LocalDOMWindow* window)
    252 {
    253     size_t messageCount = m_consoleMessages.size();
    254     for (size_t i = 0; i < messageCount; ++i)
    255         m_consoleMessages[i]->windowCleared(window);
    256     m_injectedScriptManager->discardInjectedScriptsFor(window);
    257 }
    258 
    259 void InspectorConsoleAgent::didCommitLoad(LocalFrame* frame, DocumentLoader* loader)
    260 {
    261     if (loader->frame() != frame->page()->mainFrame())
    262         return;
    263     reset();
    264 }
    265 
    266 void InspectorConsoleAgent::didFinishXHRLoading(XMLHttpRequest*, ThreadableLoaderClient*, unsigned long requestIdentifier, ScriptString, const AtomicString& method, const String& url, const String& sendURL, unsigned sendLineNumber)
    267 {
    268     if (m_frontend && m_state->getBoolean(ConsoleAgentState::monitoringXHR)) {
    269         String message = "XHR finished loading: " + method + " \"" + url + "\".";
    270         addMessageToConsole(NetworkMessageSource, LogMessageType, DebugMessageLevel, message, sendURL, sendLineNumber, 0, 0, requestIdentifier);
    271     }
    272 }
    273 
    274 void InspectorConsoleAgent::didReceiveResourceResponse(LocalFrame*, unsigned long requestIdentifier, DocumentLoader* loader, const ResourceResponse& response, ResourceLoader* resourceLoader)
    275 {
    276     if (!loader)
    277         return;
    278     if (response.httpStatusCode() >= 400) {
    279         String message = "Failed to load resource: the server responded with a status of " + String::number(response.httpStatusCode()) + " (" + response.httpStatusText() + ')';
    280         addMessageToConsole(NetworkMessageSource, LogMessageType, ErrorMessageLevel, message, response.url().string(), 0, 0, 0, requestIdentifier);
    281     }
    282 }
    283 
    284 void InspectorConsoleAgent::didFailLoading(unsigned long requestIdentifier, const ResourceError& error)
    285 {
    286     if (error.isCancellation()) // Report failures only.
    287         return;
    288     StringBuilder message;
    289     message.appendLiteral("Failed to load resource");
    290     if (!error.localizedDescription().isEmpty()) {
    291         message.appendLiteral(": ");
    292         message.append(error.localizedDescription());
    293     }
    294     addMessageToConsole(NetworkMessageSource, LogMessageType, ErrorMessageLevel, message.toString(), error.failingURL(), 0, 0, 0, requestIdentifier);
    295 }
    296 
    297 void InspectorConsoleAgent::setMonitoringXHREnabled(ErrorString*, bool enabled)
    298 {
    299     m_state->setBoolean(ConsoleAgentState::monitoringXHR, enabled);
    300 }
    301 
    302 void InspectorConsoleAgent::addConsoleMessage(PassOwnPtr<ConsoleMessage> consoleMessage)
    303 {
    304     ASSERT_ARG(consoleMessage, consoleMessage);
    305 
    306     if (m_frontend && m_enabled)
    307         consoleMessage->addToFrontend(m_frontend, m_injectedScriptManager, true);
    308 
    309     m_consoleMessages.append(consoleMessage);
    310 
    311     if (!m_frontend && m_consoleMessages.size() >= maximumConsoleMessages) {
    312         m_expiredConsoleMessageCount += expireConsoleMessagesStep;
    313         m_consoleMessages.remove(0, expireConsoleMessagesStep);
    314     }
    315 }
    316 
    317 class InspectableHeapObject FINAL : public InjectedScriptHost::InspectableObject {
    318 public:
    319     explicit InspectableHeapObject(int heapObjectId) : m_heapObjectId(heapObjectId) { }
    320     virtual ScriptValue get(ScriptState*) OVERRIDE
    321     {
    322         return ScriptProfiler::objectByHeapObjectId(m_heapObjectId);
    323     }
    324 private:
    325     int m_heapObjectId;
    326 };
    327 
    328 void InspectorConsoleAgent::addInspectedHeapObject(ErrorString*, int inspectedHeapObjectId)
    329 {
    330     m_injectedScriptManager->injectedScriptHost()->addInspectedObject(adoptPtr(new InspectableHeapObject(inspectedHeapObjectId)));
    331 }
    332 
    333 } // namespace WebCore
    334 
    335