1/*
2 * Copyright (C) 2013 Apple 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 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "core/frame/FrameConsole.h"
31
32#include "bindings/core/v8/ScriptCallStackFactory.h"
33#include "core/frame/FrameHost.h"
34#include "core/inspector/ConsoleAPITypes.h"
35#include "core/inspector/ConsoleMessage.h"
36#include "core/inspector/ConsoleMessageStorage.h"
37#include "core/inspector/InspectorConsoleInstrumentation.h"
38#include "core/inspector/ScriptCallStack.h"
39#include "core/page/Chrome.h"
40#include "core/page/ChromeClient.h"
41#include "core/page/Page.h"
42#include "core/workers/WorkerGlobalScopeProxy.h"
43#include "platform/network/ResourceResponse.h"
44#include "wtf/text/StringBuilder.h"
45
46namespace blink {
47
48static const HashSet<int>& allClientReportingMessageTypes()
49{
50    DEFINE_STATIC_LOCAL(HashSet<int>, types, ());
51    if (types.isEmpty()) {
52        types.add(LogMessageType);
53        types.add(DirMessageType);
54        types.add(DirXMLMessageType);
55        types.add(TableMessageType);
56        types.add(TraceMessageType);
57        types.add(ClearMessageType);
58        types.add(AssertMessageType);
59    }
60    return types;
61}
62
63namespace {
64
65int muteCount = 0;
66
67}
68
69FrameConsole::FrameConsole(LocalFrame& frame)
70    : m_frame(&frame)
71{
72}
73
74DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(FrameConsole);
75
76void FrameConsole::addMessage(PassRefPtrWillBeRawPtr<ConsoleMessage> prpConsoleMessage)
77{
78    RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = prpConsoleMessage;
79    if (muteCount && consoleMessage->source() != ConsoleAPIMessageSource)
80        return;
81
82    // FIXME: This should not need to reach for the main-frame.
83    // Inspector code should just take the current frame and know how to walk itself.
84    ExecutionContext* context = frame().document();
85    if (!context)
86        return;
87
88    String messageURL;
89    unsigned lineNumber = 0;
90    if (consoleMessage->callStack() && consoleMessage->callStack()->size()) {
91        lineNumber = consoleMessage->callStack()->at(0).lineNumber();
92        messageURL = consoleMessage->callStack()->at(0).sourceURL();
93    } else {
94        lineNumber = consoleMessage->lineNumber();
95        messageURL = consoleMessage->url();
96    }
97
98    messageStorage()->reportMessage(consoleMessage);
99
100    if (consoleMessage->source() == CSSMessageSource || consoleMessage->source() == NetworkMessageSource)
101        return;
102
103    RefPtrWillBeRawPtr<ScriptCallStack> reportedCallStack = nullptr;
104    if (consoleMessage->source() != ConsoleAPIMessageSource) {
105        if (consoleMessage->callStack() && frame().chromeClient().shouldReportDetailedMessageForSource(messageURL))
106            reportedCallStack = consoleMessage->callStack();
107    } else {
108        if (!frame().host() || (consoleMessage->scriptArguments() && !consoleMessage->scriptArguments()->argumentCount()))
109            return;
110
111        if (!allClientReportingMessageTypes().contains(consoleMessage->type()))
112            return;
113
114        if (frame().chromeClient().shouldReportDetailedMessageForSource(messageURL))
115            reportedCallStack = createScriptCallStack(ScriptCallStack::maxCallStackSizeToCapture);
116    }
117
118    String stackTrace;
119    if (reportedCallStack)
120        stackTrace = FrameConsole::formatStackTraceString(consoleMessage->message(), reportedCallStack);
121    frame().chromeClient().addMessageToConsole(m_frame, consoleMessage->source(), consoleMessage->level(), consoleMessage->message(), lineNumber, messageURL, stackTrace);
122}
123
124void FrameConsole::reportResourceResponseReceived(DocumentLoader* loader, unsigned long requestIdentifier, const ResourceResponse& response)
125{
126    if (!loader)
127        return;
128    if (response.httpStatusCode() < 400)
129        return;
130    String message = "Failed to load resource: the server responded with a status of " + String::number(response.httpStatusCode()) + " (" + response.httpStatusText() + ')';
131    RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = ConsoleMessage::create(NetworkMessageSource, ErrorMessageLevel, message, response.url().string());
132    consoleMessage->setRequestIdentifier(requestIdentifier);
133    addMessage(consoleMessage.release());
134}
135
136String FrameConsole::formatStackTraceString(const String& originalMessage, PassRefPtrWillBeRawPtr<ScriptCallStack> callStack)
137{
138    StringBuilder stackTrace;
139    for (size_t i = 0; i < callStack->size(); ++i) {
140        const ScriptCallFrame& frame = callStack->at(i);
141        stackTrace.append("\n    at " + (frame.functionName().length() ? frame.functionName() : "(anonymous function)"));
142        stackTrace.appendLiteral(" (");
143        stackTrace.append(frame.sourceURL());
144        stackTrace.append(':');
145        stackTrace.appendNumber(frame.lineNumber());
146        stackTrace.append(':');
147        stackTrace.appendNumber(frame.columnNumber());
148        stackTrace.append(')');
149    }
150
151    return stackTrace.toString();
152}
153
154void FrameConsole::mute()
155{
156    muteCount++;
157}
158
159void FrameConsole::unmute()
160{
161    ASSERT(muteCount > 0);
162    muteCount--;
163}
164
165ConsoleMessageStorage* FrameConsole::messageStorage()
166{
167    LocalFrame* curFrame = m_frame;
168    Frame* topFrame = curFrame->tree().top();
169    ASSERT(topFrame->isLocalFrame());
170    LocalFrame* localTopFrame = toLocalFrame(topFrame);
171    if (localTopFrame != curFrame)
172        return localTopFrame->console().messageStorage();
173    if (!m_consoleMessageStorage)
174        m_consoleMessageStorage = ConsoleMessageStorage::createForFrame(m_frame);
175    return m_consoleMessageStorage.get();
176}
177
178void FrameConsole::clearMessages()
179{
180    messageStorage()->clear();
181}
182
183void FrameConsole::adoptWorkerMessagesAfterTermination(WorkerGlobalScopeProxy* proxy)
184{
185    messageStorage()->adoptWorkerMessagesAfterTermination(proxy);
186}
187
188void FrameConsole::trace(Visitor* visitor)
189{
190    visitor->trace(m_frame);
191    visitor->trace(m_consoleMessageStorage);
192}
193
194} // namespace blink
195