1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "extensions/renderer/extensions_render_frame_observer.h"
6
7#include "base/strings/string_split.h"
8#include "base/strings/utf_string_conversions.h"
9#include "content/public/renderer/render_frame.h"
10#include "extensions/common/extension_messages.h"
11#include "extensions/common/stack_frame.h"
12#include "third_party/WebKit/public/web/WebLocalFrame.h"
13
14namespace extensions {
15
16namespace {
17
18// The delimiter for a stack trace provided by WebKit.
19const char kStackFrameDelimiter[] = "\n    at ";
20
21// Get a stack trace from a WebKit console message.
22// There are three possible scenarios:
23// 1. WebKit gives us a stack trace in |stack_trace|.
24// 2. The stack trace is embedded in the error |message| by an internal
25//    script. This will be more useful than |stack_trace|, since |stack_trace|
26//    will include the internal bindings trace, instead of a developer's code.
27// 3. No stack trace is included. In this case, we should mock one up from
28//    the given line number and source.
29// |message| will be populated with the error message only (i.e., will not
30// include any stack trace).
31StackTrace GetStackTraceFromMessage(
32    base::string16* message,
33    const base::string16& source,
34    const base::string16& stack_trace,
35    int32 line_number) {
36  StackTrace result;
37  std::vector<base::string16> pieces;
38  size_t index = 0;
39
40  if (message->find(base::UTF8ToUTF16(kStackFrameDelimiter)) !=
41          base::string16::npos) {
42    base::SplitStringUsingSubstr(*message,
43                                 base::UTF8ToUTF16(kStackFrameDelimiter),
44                                 &pieces);
45    *message = pieces[0];
46    index = 1;
47  } else if (!stack_trace.empty()) {
48    base::SplitStringUsingSubstr(stack_trace,
49                                 base::UTF8ToUTF16(kStackFrameDelimiter),
50                                 &pieces);
51  }
52
53  // If we got a stack trace, parse each frame from the text.
54  if (index < pieces.size()) {
55    for (; index < pieces.size(); ++index) {
56      scoped_ptr<StackFrame> frame = StackFrame::CreateFromText(pieces[index]);
57      if (frame.get())
58        result.push_back(*frame);
59    }
60  }
61
62  if (result.empty()) {  // If we don't have a stack trace, mock one up.
63    result.push_back(
64        StackFrame(line_number,
65                   1u,  // column number
66                   source,
67                   base::string16() /* no function name */ ));
68  }
69
70  return result;
71}
72
73}  // namespace
74
75ExtensionsRenderFrameObserver::ExtensionsRenderFrameObserver(
76    content::RenderFrame* render_frame)
77    : content::RenderFrameObserver(render_frame) {
78}
79
80ExtensionsRenderFrameObserver::~ExtensionsRenderFrameObserver() {
81}
82
83void ExtensionsRenderFrameObserver::DetailedConsoleMessageAdded(
84    const base::string16& message,
85    const base::string16& source,
86    const base::string16& stack_trace_string,
87    int32 line_number,
88    int32 severity_level) {
89  base::string16 trimmed_message = message;
90  StackTrace stack_trace = GetStackTraceFromMessage(
91      &trimmed_message,
92      source,
93      stack_trace_string,
94      line_number);
95  Send(new ExtensionHostMsg_DetailedConsoleMessageAdded(
96      routing_id(), trimmed_message, source, stack_trace, severity_level));
97}
98
99void ExtensionsRenderFrameObserver::DidChangeName(
100    const base::string16& name) {
101  Send(new ExtensionHostMsg_FrameNameChanged(
102      routing_id(),
103      !render_frame()->GetWebFrame()->parent(),
104      base::UTF16ToUTF8(name)));
105}
106
107}  // namespace extensions
108