| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "bindings/core/v8/SourceLocation.h" |
| |
| #include "bindings/core/v8/V8BindingMacros.h" |
| #include "bindings/core/v8/V8PerIsolateData.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/ExecutionContext.h" |
| #include "core/dom/ScriptableDocumentParser.h" |
| #include "core/html/HTMLFrameOwnerElement.h" |
| #include "core/inspector/ThreadDebugger.h" |
| #include "core/inspector/V8InspectorString.h" |
| #include "platform/ScriptForbiddenScope.h" |
| #include "platform/TracedValue.h" |
| #include "wtf/PtrUtil.h" |
| #include <memory> |
| |
| namespace blink { |
| |
| namespace { |
| |
| std::unique_ptr<v8_inspector::V8StackTrace> captureStackTrace(bool full) { |
| v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
| V8PerIsolateData* data = V8PerIsolateData::from(isolate); |
| if (!data->threadDebugger() || !isolate->InContext()) |
| return nullptr; |
| ScriptForbiddenScope::AllowUserAgentScript allowScripting; |
| return data->threadDebugger()->v8Inspector()->captureStackTrace(full); |
| } |
| } |
| |
| // static |
| std::unique_ptr<SourceLocation> SourceLocation::capture(const String& url, |
| unsigned lineNumber, |
| unsigned columnNumber) { |
| std::unique_ptr<v8_inspector::V8StackTrace> stackTrace = |
| captureStackTrace(false); |
| if (stackTrace && !stackTrace->isEmpty()) |
| return SourceLocation::createFromNonEmptyV8StackTrace(std::move(stackTrace), |
| 0); |
| return SourceLocation::create(url, lineNumber, columnNumber, |
| std::move(stackTrace)); |
| } |
| |
| // static |
| std::unique_ptr<SourceLocation> SourceLocation::capture( |
| ExecutionContext* executionContext) { |
| std::unique_ptr<v8_inspector::V8StackTrace> stackTrace = |
| captureStackTrace(false); |
| if (stackTrace && !stackTrace->isEmpty()) |
| return SourceLocation::createFromNonEmptyV8StackTrace(std::move(stackTrace), |
| 0); |
| |
| Document* document = executionContext && executionContext->isDocument() |
| ? toDocument(executionContext) |
| : nullptr; |
| if (document) { |
| unsigned lineNumber = 0; |
| if (document->scriptableDocumentParser() && |
| !document->isInDocumentWrite()) { |
| if (document->scriptableDocumentParser()->isParsingAtLineNumber()) |
| lineNumber = |
| document->scriptableDocumentParser()->lineNumber().oneBasedInt(); |
| } |
| return SourceLocation::create(document->url().getString(), lineNumber, 0, |
| std::move(stackTrace)); |
| } |
| |
| return SourceLocation::create( |
| executionContext ? executionContext->url().getString() : String(), 0, 0, |
| std::move(stackTrace)); |
| } |
| |
| // static |
| std::unique_ptr<SourceLocation> SourceLocation::fromMessage( |
| v8::Isolate* isolate, |
| v8::Local<v8::Message> message, |
| ExecutionContext* executionContext) { |
| v8::Local<v8::StackTrace> stack = message->GetStackTrace(); |
| std::unique_ptr<v8_inspector::V8StackTrace> stackTrace = nullptr; |
| V8PerIsolateData* data = V8PerIsolateData::from(isolate); |
| if (data && data->threadDebugger()) |
| stackTrace = data->threadDebugger()->v8Inspector()->createStackTrace(stack); |
| |
| int scriptId = message->GetScriptOrigin().ScriptID()->Value(); |
| if (!stack.IsEmpty() && stack->GetFrameCount() > 0) { |
| int topScriptId = stack->GetFrame(0)->GetScriptId(); |
| if (topScriptId == scriptId) |
| scriptId = 0; |
| } |
| |
| int lineNumber = 0; |
| int columnNumber = 0; |
| if (v8Call(message->GetLineNumber(isolate->GetCurrentContext()), |
| lineNumber) && |
| v8Call(message->GetStartColumn(isolate->GetCurrentContext()), |
| columnNumber)) |
| ++columnNumber; |
| |
| if ((!scriptId || !lineNumber) && stackTrace && !stackTrace->isEmpty()) |
| return SourceLocation::createFromNonEmptyV8StackTrace(std::move(stackTrace), |
| 0); |
| |
| String url = toCoreStringWithUndefinedOrNullCheck( |
| message->GetScriptOrigin().ResourceName()); |
| if (url.isNull()) |
| url = executionContext->url(); |
| return SourceLocation::create(url, lineNumber, columnNumber, |
| std::move(stackTrace), scriptId); |
| } |
| |
| // static |
| std::unique_ptr<SourceLocation> SourceLocation::create( |
| const String& url, |
| unsigned lineNumber, |
| unsigned columnNumber, |
| std::unique_ptr<v8_inspector::V8StackTrace> stackTrace, |
| int scriptId) { |
| return wrapUnique(new SourceLocation(url, lineNumber, columnNumber, |
| std::move(stackTrace), scriptId)); |
| } |
| |
| // static |
| std::unique_ptr<SourceLocation> SourceLocation::createFromNonEmptyV8StackTrace( |
| std::unique_ptr<v8_inspector::V8StackTrace> stackTrace, |
| int scriptId) { |
| // Retrieve the data before passing the ownership to SourceLocation. |
| String url = toCoreString(stackTrace->topSourceURL()); |
| unsigned lineNumber = stackTrace->topLineNumber(); |
| unsigned columnNumber = stackTrace->topColumnNumber(); |
| return wrapUnique(new SourceLocation(url, lineNumber, columnNumber, |
| std::move(stackTrace), scriptId)); |
| } |
| |
| // static |
| std::unique_ptr<SourceLocation> SourceLocation::fromFunction( |
| v8::Local<v8::Function> function) { |
| if (!function.IsEmpty()) |
| return SourceLocation::create( |
| toCoreStringWithUndefinedOrNullCheck( |
| function->GetScriptOrigin().ResourceName()), |
| function->GetScriptLineNumber() + 1, |
| function->GetScriptColumnNumber() + 1, nullptr, function->ScriptId()); |
| return SourceLocation::create(String(), 0, 0, nullptr, 0); |
| } |
| |
| // static |
| std::unique_ptr<SourceLocation> SourceLocation::captureWithFullStackTrace() { |
| std::unique_ptr<v8_inspector::V8StackTrace> stackTrace = |
| captureStackTrace(true); |
| if (stackTrace && !stackTrace->isEmpty()) |
| return SourceLocation::createFromNonEmptyV8StackTrace(std::move(stackTrace), |
| 0); |
| return SourceLocation::create(String(), 0, 0, nullptr, 0); |
| } |
| |
| SourceLocation::SourceLocation( |
| const String& url, |
| unsigned lineNumber, |
| unsigned columnNumber, |
| std::unique_ptr<v8_inspector::V8StackTrace> stackTrace, |
| int scriptId) |
| : m_url(url), |
| m_lineNumber(lineNumber), |
| m_columnNumber(columnNumber), |
| m_stackTrace(std::move(stackTrace)), |
| m_scriptId(scriptId) {} |
| |
| SourceLocation::~SourceLocation() {} |
| |
| void SourceLocation::toTracedValue(TracedValue* value, const char* name) const { |
| if (!m_stackTrace || m_stackTrace->isEmpty()) |
| return; |
| value->beginArray(name); |
| value->beginDictionary(); |
| value->setString("functionName", |
| toCoreString(m_stackTrace->topFunctionName())); |
| value->setString("scriptId", toCoreString(m_stackTrace->topScriptId())); |
| value->setString("url", toCoreString(m_stackTrace->topSourceURL())); |
| value->setInteger("lineNumber", m_stackTrace->topLineNumber()); |
| value->setInteger("columnNumber", m_stackTrace->topColumnNumber()); |
| value->endDictionary(); |
| value->endArray(); |
| } |
| |
| std::unique_ptr<SourceLocation> SourceLocation::clone() const { |
| return wrapUnique(new SourceLocation( |
| m_url.isolatedCopy(), m_lineNumber, m_columnNumber, |
| m_stackTrace ? m_stackTrace->clone() : nullptr, m_scriptId)); |
| } |
| |
| std::unique_ptr<v8_inspector::protocol::Runtime::API::StackTrace> |
| SourceLocation::buildInspectorObject() const { |
| return m_stackTrace ? m_stackTrace->buildInspectorObject() : nullptr; |
| } |
| |
| String SourceLocation::toString() const { |
| if (!m_stackTrace) |
| return String(); |
| return toCoreString(m_stackTrace->toString()); |
| } |
| |
| } // namespace blink |