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/core/v8/ScriptCallStackFactory.h" 30 #include "bindings/core/v8/ScriptController.h" 31 #include "bindings/core/v8/ScriptProfiler.h" 32 #include "core/frame/LocalFrame.h" 33 #include "core/frame/UseCounter.h" 34 #include "core/inspector/ConsoleMessage.h" 35 #include "core/inspector/ConsoleMessageStorage.h" 36 #include "core/inspector/IdentifiersFactory.h" 37 #include "core/inspector/InjectedScript.h" 38 #include "core/inspector/InjectedScriptHost.h" 39 #include "core/inspector/InjectedScriptManager.h" 40 #include "core/inspector/InspectorState.h" 41 #include "core/inspector/InspectorTimelineAgent.h" 42 #include "core/inspector/InstrumentingAgents.h" 43 #include "core/inspector/ScriptArguments.h" 44 #include "core/inspector/ScriptAsyncCallStack.h" 45 #include "core/inspector/ScriptCallFrame.h" 46 #include "core/inspector/ScriptCallStack.h" 47 #include "core/loader/DocumentLoader.h" 48 #include "core/page/Page.h" 49 #include "core/xml/XMLHttpRequest.h" 50 #include "platform/network/ResourceError.h" 51 #include "platform/network/ResourceResponse.h" 52 #include "wtf/CurrentTime.h" 53 #include "wtf/OwnPtr.h" 54 #include "wtf/PassOwnPtr.h" 55 #include "wtf/text/StringBuilder.h" 56 #include "wtf/text/WTFString.h" 57 58 namespace blink { 59 60 namespace ConsoleAgentState { 61 static const char monitoringXHR[] = "monitoringXHR"; 62 static const char consoleMessagesEnabled[] = "consoleMessagesEnabled"; 63 static const char tracingBasedTimeline[] = "tracingBasedTimeline"; 64 } 65 66 int InspectorConsoleAgent::s_enabledAgentCount = 0; 67 68 InspectorConsoleAgent::InspectorConsoleAgent(InspectorTimelineAgent* timelineAgent, InjectedScriptManager* injectedScriptManager) 69 : InspectorBaseAgent<InspectorConsoleAgent>("Console") 70 , m_timelineAgent(timelineAgent) 71 , m_injectedScriptManager(injectedScriptManager) 72 , m_frontend(0) 73 , m_enabled(false) 74 { 75 } 76 77 InspectorConsoleAgent::~InspectorConsoleAgent() 78 { 79 #if !ENABLE(OILPAN) 80 m_instrumentingAgents->setInspectorConsoleAgent(0); 81 #endif 82 } 83 84 void InspectorConsoleAgent::trace(Visitor* visitor) 85 { 86 visitor->trace(m_timelineAgent); 87 visitor->trace(m_injectedScriptManager); 88 InspectorBaseAgent::trace(visitor); 89 } 90 91 void InspectorConsoleAgent::init() 92 { 93 m_instrumentingAgents->setInspectorConsoleAgent(this); 94 } 95 96 void InspectorConsoleAgent::enable(ErrorString*) 97 { 98 if (m_enabled) 99 return; 100 m_enabled = true; 101 if (!s_enabledAgentCount) 102 ScriptController::setCaptureCallStackForUncaughtExceptions(true); 103 ++s_enabledAgentCount; 104 105 m_state->setBoolean(ConsoleAgentState::consoleMessagesEnabled, true); 106 107 ConsoleMessageStorage* storage = messageStorage(); 108 if (storage->expiredCount()) { 109 RefPtrWillBeRawPtr<ConsoleMessage> expiredMessage = ConsoleMessage::create(OtherMessageSource, WarningMessageLevel, String::format("%d console messages are not shown.", storage->expiredCount())); 110 expiredMessage->setTimestamp(0); 111 sendConsoleMessageToFrontend(expiredMessage.get(), false); 112 } 113 114 size_t messageCount = storage->size(); 115 for (size_t i = 0; i < messageCount; ++i) 116 sendConsoleMessageToFrontend(storage->at(i), false); 117 } 118 119 void InspectorConsoleAgent::disable(ErrorString*) 120 { 121 if (!m_enabled) 122 return; 123 m_enabled = false; 124 if (!(--s_enabledAgentCount)) 125 ScriptController::setCaptureCallStackForUncaughtExceptions(false); 126 m_state->setBoolean(ConsoleAgentState::consoleMessagesEnabled, false); 127 m_state->setBoolean(ConsoleAgentState::tracingBasedTimeline, false); 128 } 129 130 void InspectorConsoleAgent::clearMessages(ErrorString*) 131 { 132 messageStorage()->clear(); 133 } 134 135 void InspectorConsoleAgent::restore() 136 { 137 if (m_state->getBoolean(ConsoleAgentState::consoleMessagesEnabled)) { 138 m_frontend->messagesCleared(); 139 ErrorString error; 140 enable(&error); 141 } 142 } 143 144 void InspectorConsoleAgent::setFrontend(InspectorFrontend* frontend) 145 { 146 m_frontend = frontend->console(); 147 } 148 149 void InspectorConsoleAgent::clearFrontend() 150 { 151 m_frontend = 0; 152 String errorString; 153 disable(&errorString); 154 } 155 156 void InspectorConsoleAgent::addMessageToConsole(ConsoleMessage* consoleMessage) 157 { 158 if (m_frontend && m_enabled) 159 sendConsoleMessageToFrontend(consoleMessage, true); 160 } 161 162 void InspectorConsoleAgent::consoleMessagesCleared() 163 { 164 m_injectedScriptManager->releaseObjectGroup("console"); 165 if (m_frontend && m_enabled) 166 m_frontend->messagesCleared(); 167 } 168 169 void InspectorConsoleAgent::setTracingBasedTimeline(ErrorString*, bool enabled) 170 { 171 m_state->setBoolean(ConsoleAgentState::tracingBasedTimeline, enabled); 172 } 173 174 void InspectorConsoleAgent::consoleTimeline(ExecutionContext* context, const String& title, ScriptState* scriptState) 175 { 176 UseCounter::count(context, UseCounter::DevToolsConsoleTimeline); 177 if (!m_state->getBoolean(ConsoleAgentState::tracingBasedTimeline)) 178 m_timelineAgent->consoleTimeline(context, title, scriptState); 179 } 180 181 void InspectorConsoleAgent::consoleTimelineEnd(ExecutionContext* context, const String& title, ScriptState* scriptState) 182 { 183 if (!m_state->getBoolean(ConsoleAgentState::tracingBasedTimeline)) 184 m_timelineAgent->consoleTimelineEnd(context, title, scriptState); 185 } 186 187 void InspectorConsoleAgent::frameWindowDiscarded(LocalDOMWindow* window) 188 { 189 m_injectedScriptManager->discardInjectedScriptsFor(window); 190 } 191 192 void InspectorConsoleAgent::didFinishXHRLoading(XMLHttpRequest*, ThreadableLoaderClient*, unsigned long requestIdentifier, ScriptString, const AtomicString& method, const String& url, const String& sendURL, unsigned sendLineNumber) 193 { 194 if (m_frontend && m_state->getBoolean(ConsoleAgentState::monitoringXHR)) { 195 String message = "XHR finished loading: " + method + " \"" + url + "\"."; 196 RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = ConsoleMessage::create(NetworkMessageSource, DebugMessageLevel, message, sendURL, sendLineNumber); 197 consoleMessage->setRequestIdentifier(requestIdentifier); 198 messageStorage()->reportMessage(consoleMessage.release()); 199 } 200 } 201 202 void InspectorConsoleAgent::didFailLoading(unsigned long requestIdentifier, const ResourceError& error) 203 { 204 if (error.isCancellation()) // Report failures only. 205 return; 206 StringBuilder message; 207 message.appendLiteral("Failed to load resource"); 208 if (!error.localizedDescription().isEmpty()) { 209 message.appendLiteral(": "); 210 message.append(error.localizedDescription()); 211 } 212 RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = ConsoleMessage::create(NetworkMessageSource, ErrorMessageLevel, message.toString(), error.failingURL()); 213 consoleMessage->setRequestIdentifier(requestIdentifier); 214 messageStorage()->reportMessage(consoleMessage.release()); 215 } 216 217 void InspectorConsoleAgent::setMonitoringXHREnabled(ErrorString*, bool enabled) 218 { 219 m_state->setBoolean(ConsoleAgentState::monitoringXHR, enabled); 220 } 221 222 static TypeBuilder::Console::ConsoleMessage::Source::Enum messageSourceValue(MessageSource source) 223 { 224 switch (source) { 225 case XMLMessageSource: return TypeBuilder::Console::ConsoleMessage::Source::Xml; 226 case JSMessageSource: return TypeBuilder::Console::ConsoleMessage::Source::Javascript; 227 case NetworkMessageSource: return TypeBuilder::Console::ConsoleMessage::Source::Network; 228 case ConsoleAPIMessageSource: return TypeBuilder::Console::ConsoleMessage::Source::Console_api; 229 case StorageMessageSource: return TypeBuilder::Console::ConsoleMessage::Source::Storage; 230 case AppCacheMessageSource: return TypeBuilder::Console::ConsoleMessage::Source::Appcache; 231 case RenderingMessageSource: return TypeBuilder::Console::ConsoleMessage::Source::Rendering; 232 case CSSMessageSource: return TypeBuilder::Console::ConsoleMessage::Source::Css; 233 case SecurityMessageSource: return TypeBuilder::Console::ConsoleMessage::Source::Security; 234 case OtherMessageSource: return TypeBuilder::Console::ConsoleMessage::Source::Other; 235 case DeprecationMessageSource: return TypeBuilder::Console::ConsoleMessage::Source::Deprecation; 236 } 237 return TypeBuilder::Console::ConsoleMessage::Source::Other; 238 } 239 240 241 static TypeBuilder::Console::ConsoleMessage::Type::Enum messageTypeValue(MessageType type) 242 { 243 switch (type) { 244 case LogMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Log; 245 case ClearMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Clear; 246 case DirMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Dir; 247 case DirXMLMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Dirxml; 248 case TableMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Table; 249 case TraceMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Trace; 250 case StartGroupMessageType: return TypeBuilder::Console::ConsoleMessage::Type::StartGroup; 251 case StartGroupCollapsedMessageType: return TypeBuilder::Console::ConsoleMessage::Type::StartGroupCollapsed; 252 case EndGroupMessageType: return TypeBuilder::Console::ConsoleMessage::Type::EndGroup; 253 case AssertMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Assert; 254 case TimeEndMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Log; 255 case CountMessageType: return TypeBuilder::Console::ConsoleMessage::Type::Log; 256 } 257 return TypeBuilder::Console::ConsoleMessage::Type::Log; 258 } 259 260 static TypeBuilder::Console::ConsoleMessage::Level::Enum messageLevelValue(MessageLevel level) 261 { 262 switch (level) { 263 case DebugMessageLevel: return TypeBuilder::Console::ConsoleMessage::Level::Debug; 264 case LogMessageLevel: return TypeBuilder::Console::ConsoleMessage::Level::Log; 265 case WarningMessageLevel: return TypeBuilder::Console::ConsoleMessage::Level::Warning; 266 case ErrorMessageLevel: return TypeBuilder::Console::ConsoleMessage::Level::Error; 267 case InfoMessageLevel: return TypeBuilder::Console::ConsoleMessage::Level::Info; 268 } 269 return TypeBuilder::Console::ConsoleMessage::Level::Log; 270 } 271 272 void InspectorConsoleAgent::sendConsoleMessageToFrontend(ConsoleMessage* consoleMessage, bool generatePreview) 273 { 274 if (consoleMessage->workerGlobalScopeProxy()) 275 return; 276 277 RefPtr<TypeBuilder::Console::ConsoleMessage> jsonObj = TypeBuilder::Console::ConsoleMessage::create() 278 .setSource(messageSourceValue(consoleMessage->source())) 279 .setLevel(messageLevelValue(consoleMessage->level())) 280 .setText(consoleMessage->message()) 281 .setTimestamp(consoleMessage->timestamp()); 282 // FIXME: only send out type for ConsoleAPI source messages. 283 jsonObj->setType(messageTypeValue(consoleMessage->type())); 284 jsonObj->setLine(static_cast<int>(consoleMessage->lineNumber())); 285 jsonObj->setColumn(static_cast<int>(consoleMessage->columnNumber())); 286 if (consoleMessage->scriptId()) 287 jsonObj->setScriptId(String::number(consoleMessage->scriptId())); 288 jsonObj->setUrl(consoleMessage->url()); 289 ScriptState* scriptState = consoleMessage->scriptState(); 290 if (scriptState) 291 jsonObj->setExecutionContextId(m_injectedScriptManager->injectedScriptIdFor(scriptState)); 292 if (consoleMessage->source() == NetworkMessageSource && consoleMessage->requestIdentifier()) 293 jsonObj->setNetworkRequestId(IdentifiersFactory::requestId(consoleMessage->requestIdentifier())); 294 RefPtrWillBeRawPtr<ScriptArguments> arguments = consoleMessage->scriptArguments(); 295 if (arguments && arguments->argumentCount()) { 296 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(arguments->scriptState()); 297 if (!injectedScript.isEmpty()) { 298 RefPtr<TypeBuilder::Array<TypeBuilder::Runtime::RemoteObject> > jsonArgs = TypeBuilder::Array<TypeBuilder::Runtime::RemoteObject>::create(); 299 if (consoleMessage->type() == TableMessageType && generatePreview && arguments->argumentCount()) { 300 ScriptValue table = arguments->argumentAt(0); 301 ScriptValue columns = arguments->argumentCount() > 1 ? arguments->argumentAt(1) : ScriptValue(); 302 RefPtr<TypeBuilder::Runtime::RemoteObject> inspectorValue = injectedScript.wrapTable(table, columns); 303 if (!inspectorValue) { 304 ASSERT_NOT_REACHED(); 305 return; 306 } 307 jsonArgs->addItem(inspectorValue); 308 } else { 309 for (unsigned i = 0; i < arguments->argumentCount(); ++i) { 310 RefPtr<TypeBuilder::Runtime::RemoteObject> inspectorValue = injectedScript.wrapObject(arguments->argumentAt(i), "console", generatePreview); 311 if (!inspectorValue) { 312 ASSERT_NOT_REACHED(); 313 return; 314 } 315 jsonArgs->addItem(inspectorValue); 316 } 317 } 318 jsonObj->setParameters(jsonArgs); 319 } 320 } 321 if (consoleMessage->callStack()) { 322 jsonObj->setStackTrace(consoleMessage->callStack()->buildInspectorArray()); 323 RefPtrWillBeRawPtr<ScriptAsyncCallStack> asyncCallStack = consoleMessage->callStack()->asyncCallStack(); 324 if (asyncCallStack) 325 jsonObj->setAsyncStackTrace(asyncCallStack->buildInspectorObject()); 326 } 327 m_frontend->messageAdded(jsonObj); 328 m_frontend->flush(); 329 } 330 331 class InspectableHeapObject FINAL : public InjectedScriptHost::InspectableObject { 332 public: 333 explicit InspectableHeapObject(int heapObjectId) : m_heapObjectId(heapObjectId) { } 334 virtual ScriptValue get(ScriptState*) OVERRIDE 335 { 336 return ScriptProfiler::objectByHeapObjectId(m_heapObjectId); 337 } 338 private: 339 int m_heapObjectId; 340 }; 341 342 void InspectorConsoleAgent::addInspectedHeapObject(ErrorString*, int inspectedHeapObjectId) 343 { 344 m_injectedScriptManager->injectedScriptHost()->addInspectedObject(adoptPtr(new InspectableHeapObject(inspectedHeapObjectId))); 345 } 346 347 } // namespace blink 348