diff --git a/DEPS b/DEPS
index eeb43696..82fcb58 100644
--- a/DEPS
+++ b/DEPS
@@ -43,7 +43,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'e86c7faf2af172045cdf379e9c3c78433071c425',
+  'v8_revision': '54afc6c29a30fa0fb0683dca33214861d40a18ba',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc
index fc06a2f..f6f62a54 100644
--- a/content/browser/frame_host/frame_tree_node.cc
+++ b/content/browser/frame_host/frame_tree_node.cc
@@ -16,7 +16,6 @@
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
-#include "content/browser/frame_host/traced_frame_tree_node.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/common/frame_messages.h"
 #include "content/common/site_isolation_policy.h"
@@ -107,18 +106,17 @@
           false /* is a potentially trustworthy unique origin */),
       pending_sandbox_flags_(blink::WebSandboxFlags::None),
       frame_owner_properties_(frame_owner_properties),
-      loading_progress_(kLoadingProgressNotStarted) {
+      loading_progress_(kLoadingProgressNotStarted),
+      blame_context_(frame_tree_node_id_, parent) {
   std::pair<FrameTreeNodeIdMap::iterator, bool> result =
       g_frame_tree_node_id_map.Get().insert(
           std::make_pair(frame_tree_node_id_, this));
   CHECK(result.second);
 
   RecordUniqueNameLength(unique_name.size());
-  TRACE_EVENT_OBJECT_CREATED_WITH_ID(
-      "navigation", "FrameTreeNode",
-      TRACE_ID_WITH_SCOPE("FrameTreeNode", frame_tree_node_id_));
-  // Don't TraceSnapshot() until the RenderFrameHostManager is initialized and
-  // calls SetCurrentURL().
+
+  // Note: this should always be done last in the constructor.
+  blame_context_.Initialize();
 }
 
 FrameTreeNode::~FrameTreeNode() {
@@ -130,10 +128,6 @@
     opener_->RemoveObserver(opener_observer_.get());
 
   g_frame_tree_node_id_map.Get().erase(frame_tree_node_id_);
-
-  TRACE_EVENT_OBJECT_DELETED_WITH_ID(
-      "navigation", "FrameTreeNode",
-      TRACE_ID_WITH_SCOPE("FrameTreeNode", frame_tree_node_id_));
 }
 
 void FrameTreeNode::AddObserver(Observer* observer) {
@@ -189,7 +183,7 @@
 
 void FrameTreeNode::ResetForNewProcess() {
   current_frame_host()->set_last_committed_url(GURL());
-  TraceSnapshot();
+  blame_context_.TakeSnapshot();
 
   // Remove child nodes from the tree, then delete them. This destruction
   // operation will notify observers.
@@ -215,7 +209,7 @@
   if (!has_committed_real_load_ && url != GURL(url::kAboutBlankURL))
     has_committed_real_load_ = true;
   current_frame_host()->set_last_committed_url(url);
-  TraceSnapshot();
+  blame_context_.TakeSnapshot();
 }
 
 void FrameTreeNode::SetCurrentOrigin(
@@ -491,15 +485,6 @@
   }
 }
 
-void FrameTreeNode::TraceSnapshot() const {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
-      "navigation", "FrameTreeNode",
-      TRACE_ID_WITH_SCOPE("FrameTreeNode", frame_tree_node_id_),
-      std::unique_ptr<base::trace_event::ConvertableToTraceFormat>(
-          new TracedFrameTreeNode(*this)));
-}
-
 FrameTreeNode* FrameTreeNode::GetSibling(int relative_offset) const {
   if (!parent_ || !parent_->child_count())
     return nullptr;
diff --git a/content/browser/frame_host/frame_tree_node.h b/content/browser/frame_host/frame_tree_node.h
index c6ab2b3..32e56234 100644
--- a/content/browser/frame_host/frame_tree_node.h
+++ b/content/browser/frame_host/frame_tree_node.h
@@ -13,6 +13,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "content/browser/frame_host/frame_tree_node_blame_context.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/frame_host/render_frame_host_manager.h"
 #include "content/common/content_export.h"
@@ -283,11 +284,12 @@
   // FrameTreeNode.
   void BeforeUnloadCanceled();
 
+  // Returns the BlameContext associated with this node.
+  FrameTreeNodeBlameContext& blame_context() { return blame_context_; }
+
  private:
   class OpenerDestroyedObserver;
 
-  void TraceSnapshot() const;
-
   FrameTreeNode* GetSibling(int relative_offset) const;
 
   // The next available browser-global FrameTreeNode ID.
@@ -364,6 +366,11 @@
 
   base::TimeTicks last_focus_time_;
 
+  // A helper for tracing the snapshots of this FrameTreeNode and attributing
+  // browser process activities to this node (when possible).  It is unrelated
+  // to the core logic of FrameTreeNode.
+  FrameTreeNodeBlameContext blame_context_;
+
   DISALLOW_COPY_AND_ASSIGN(FrameTreeNode);
 };
 
diff --git a/content/browser/frame_host/frame_tree_node_blame_context.cc b/content/browser/frame_host/frame_tree_node_blame_context.cc
new file mode 100644
index 0000000..fb993be
--- /dev/null
+++ b/content/browser/frame_host/frame_tree_node_blame_context.cc
@@ -0,0 +1,71 @@
+// 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 "content/browser/frame_host/frame_tree_node_blame_context.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "content/browser/frame_host/frame_tree.h"
+#include "url/gurl.h"
+
+namespace content {
+
+namespace {
+
+const char kFrameTreeNodeBlameContextCategory[] = "navigation";
+const char kFrameTreeNodeBlameContextName[] = "FrameTreeNodeBlameContext";
+const char kFrameTreeNodeBlameContextType[] = "FrameTreeNode";
+const char kFrameTreeNodeBlameContextScope[] = "FrameTreeNode";
+const char kRenderFrameBlameContextScope[] = "RenderFrame";
+
+}  // namespace
+
+FrameTreeNodeBlameContext::FrameTreeNodeBlameContext(int node_id,
+                                                     FrameTreeNode* parent)
+    : base::trace_event::BlameContext(kFrameTreeNodeBlameContextCategory,
+                                      kFrameTreeNodeBlameContextName,
+                                      kFrameTreeNodeBlameContextType,
+                                      kFrameTreeNodeBlameContextScope,
+                                      node_id,
+                                      parent ? &parent->blame_context()
+                                             : nullptr) {}
+
+FrameTreeNodeBlameContext::~FrameTreeNodeBlameContext() {}
+
+void FrameTreeNodeBlameContext::AsValueInto(
+    base::trace_event::TracedValue* value) {
+  BlameContext::AsValueInto(value);
+
+  // id() is equal to the owner FrameTreeNode's id, as set in the constructor.
+  FrameTreeNode* owner = FrameTreeNode::GloballyFindByID(id());
+  DCHECK(owner);
+
+  RenderFrameHostImpl* current_frame_host = owner->current_frame_host();
+  if (!current_frame_host)
+    return;
+
+  // On Windows, |rph->GetHandle()| does not duplicate ownership of the
+  // process handle and the render host still retains it. Therefore, we
+  // cannot create a base::Process object, which provides a proper way to get
+  // a process id, from the handle. For a stopgap, we use this deprecated
+  // function that does not require the ownership (http://crbug.com/417532).
+  int process_id = base::GetProcId(
+      current_frame_host->GetProcess()->GetHandle());
+  if (process_id >= 0) {
+    int routing_id = current_frame_host->GetRoutingID();
+    DCHECK_NE(routing_id, MSG_ROUTING_NONE);
+
+    value->BeginDictionary(kRenderFrameBlameContextScope);
+    value->SetInteger("pid_ref", process_id);
+    value->SetString("id_ref", base::StringPrintf("0x%x", routing_id));
+    value->SetString("scope", kRenderFrameBlameContextScope);
+    value->EndDictionary();
+  }
+
+  GURL url = current_frame_host->last_committed_url();
+  if (url.is_valid())
+    value->SetString("url", url.spec());
+}
+
+}  // namespace content
diff --git a/content/browser/frame_host/frame_tree_node_blame_context.h b/content/browser/frame_host/frame_tree_node_blame_context.h
new file mode 100644
index 0000000..6573f7c
--- /dev/null
+++ b/content/browser/frame_host/frame_tree_node_blame_context.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_FRAME_HOST_FRAME_TREE_NODE_BLAME_CONTEXT_H
+#define CONTENT_BROWSER_FRAME_HOST_FRAME_TREE_NODE_BLAME_CONTEXT_H
+
+#include "base/macros.h"
+#include "base/trace_event/blame_context.h"
+#include "url/gurl.h"
+
+namespace base {
+namespace trace_event {
+class TracedValue;
+}
+}
+
+namespace content {
+
+class FrameTreeNode;
+
+// FrameTreeNodeBlameContext is a helper class for tracing snapshots of each
+// FrameTreeNode and attributing browser activities to frames (when possible),
+// in the framework of FrameBlamer (crbug.com/546021).  This class is unrelated
+// to the core logic of FrameTreeNode.
+class FrameTreeNodeBlameContext : public base::trace_event::BlameContext {
+ public:
+  FrameTreeNodeBlameContext(int node_id, FrameTreeNode* parent);
+  ~FrameTreeNodeBlameContext() override;
+
+ private:
+  void AsValueInto(base::trace_event::TracedValue* value) override;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameTreeNodeBlameContext);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_FRAME_HOST_FRAME_TREE_NODE_BLAME_CONTEXT_H
diff --git a/content/browser/frame_host/frame_tree_node_blame_context_unittest.cc b/content/browser/frame_host/frame_tree_node_blame_context_unittest.cc
new file mode 100644
index 0000000..6f76b92
--- /dev/null
+++ b/content/browser/frame_host/frame_tree_node_blame_context_unittest.cc
@@ -0,0 +1,263 @@
+// 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 "content/browser/frame_host/frame_tree_node_blame_context.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/test/trace_event_analyzer.h"
+#include "base/trace_event/trace_buffer.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "content/browser/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/test/test_render_view_host.h"
+#include "content/test/test_web_contents.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/web/WebSandboxFlags.h"
+
+namespace content {
+
+namespace {
+
+bool EventPointerCompare(const trace_analyzer::TraceEvent* lhs,
+                         const trace_analyzer::TraceEvent* rhs) {
+  CHECK(lhs);
+  CHECK(rhs);
+  return *lhs < *rhs;
+}
+
+void OnTraceDataCollected(base::Closure quit_closure,
+                          base::trace_event::TraceResultBuffer* buffer,
+                          const scoped_refptr<base::RefCountedString>& json,
+                          bool has_more_events) {
+  buffer->AddFragment(json->data());
+  if (!has_more_events)
+    quit_closure.Run();
+}
+
+void ExpectFrameTreeNodeObject(const trace_analyzer::TraceEvent* event) {
+  EXPECT_EQ("navigation", event->category);
+  EXPECT_EQ("FrameTreeNode", event->name);
+}
+
+void ExpectFrameTreeNodeSnapshot(const trace_analyzer::TraceEvent* event) {
+  ExpectFrameTreeNodeObject(event);
+  EXPECT_TRUE(event->HasArg("snapshot"));
+  EXPECT_TRUE(event->arg_values.at("snapshot")
+                  ->IsType(base::Value::Type::TYPE_DICTIONARY));
+}
+
+std::string GetParentNodeID(const trace_analyzer::TraceEvent* event) {
+  const base::Value* arg_snapshot = event->arg_values.at("snapshot").get();
+  const base::DictionaryValue* snapshot;
+  EXPECT_TRUE(arg_snapshot->GetAsDictionary(&snapshot));
+  if (!snapshot->HasKey("parent"))
+    return std::string();
+  const base::DictionaryValue* parent;
+  EXPECT_TRUE(snapshot->GetDictionary("parent", &parent));
+  std::string parent_id;
+  EXPECT_TRUE(parent->GetString("id_ref", &parent_id));
+  return parent_id;
+}
+
+std::string GetSnapshotURL(const trace_analyzer::TraceEvent* event) {
+  const base::Value* arg_snapshot = event->arg_values.at("snapshot").get();
+  const base::DictionaryValue* snapshot;
+  EXPECT_TRUE(arg_snapshot->GetAsDictionary(&snapshot));
+  if (!snapshot->HasKey("url"))
+    return std::string();
+  std::string url;
+  EXPECT_TRUE(snapshot->GetString("url", &url));
+  return url;
+}
+
+}  // namespace
+
+class FrameTreeNodeBlameContextTest : public RenderViewHostImplTestHarness {
+ public:
+  FrameTree* tree() { return contents()->GetFrameTree(); }
+  FrameTreeNode* root() { return tree()->root(); }
+  int process_id() {
+    return root()->current_frame_host()->GetProcess()->GetID();
+  }
+
+  // Creates a frame tree specified by |shape|, which is a string of paired
+  // parentheses. Each pair of parentheses represents a FrameTreeNode, and the
+  // nesting of parentheses represents the parent-child relation between nodes.
+  // Nodes represented by outer-most parentheses are children of the root node.
+  // NOTE: Each node can have at most 9 child nodes, and the tree height (i.e.,
+  // max # of edges in any root-to-leaf path) must be at most 9.
+  // See the test cases for sample usage.
+  void CreateFrameTree(const char* shape) {
+    main_test_rfh()->InitializeRenderFrameIfNeeded();
+    CreateSubframes(root(), 1, shape);
+  }
+
+  void RemoveAllNonRootFrames() {
+    while (root()->child_count())
+      tree()->RemoveFrame(root()->child_at(0));
+  }
+
+  void StartTracing() {
+    base::trace_event::TraceLog::GetInstance()->SetEnabled(
+        base::trace_event::TraceConfig("*"),
+        base::trace_event::TraceLog::RECORDING_MODE);
+  }
+
+  void StopTracing() {
+    base::trace_event::TraceLog::GetInstance()->SetDisabled();
+  }
+
+  std::unique_ptr<trace_analyzer::TraceAnalyzer> CreateTraceAnalyzer() {
+    base::trace_event::TraceResultBuffer buffer;
+    base::trace_event::TraceResultBuffer::SimpleOutput trace_output;
+    buffer.SetOutputCallback(trace_output.GetCallback());
+    base::RunLoop run_loop;
+    buffer.Start();
+    base::trace_event::TraceLog::GetInstance()->Flush(
+        base::Bind(&OnTraceDataCollected, run_loop.QuitClosure(),
+                   base::Unretained(&buffer)));
+    run_loop.Run();
+    buffer.Finish();
+
+    return base::WrapUnique(
+        trace_analyzer::TraceAnalyzer::Create(trace_output.json_output));
+  }
+
+ private:
+  int CreateSubframes(FrameTreeNode* node, int self_id, const char* shape) {
+    int consumption = 0;
+    for (int child_num = 1; shape[consumption++] == '('; ++child_num) {
+      int child_id = self_id * 10 + child_num;
+      tree()->AddFrame(
+          node, process_id(), child_id, blink::WebTreeScopeType::Document,
+          std::string(), base::StringPrintf("uniqueName%d", child_id),
+          blink::WebSandboxFlags::None, blink::WebFrameOwnerProperties());
+      FrameTreeNode* child = node->child_at(child_num - 1);
+      consumption += CreateSubframes(child, child_id, shape + consumption);
+    }
+    return consumption;
+  }
+};
+
+// Creates a frame tree, tests if (i) the creation of each new frame is
+// correctly traced, and (ii) the topology given by the snapshots is correct.
+TEST_F(FrameTreeNodeBlameContextTest, FrameCreation) {
+  /* Shape of the frame tree to be created:
+   *        ()
+   *      /    \
+   *     ()    ()
+   *    /  \   |
+   *   ()  ()  ()
+   *           |
+   *           ()
+   */
+  const char* tree_shape = "(()())((()))";
+
+  StartTracing();
+  CreateFrameTree(tree_shape);
+  StopTracing();
+
+  std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer =
+      CreateTraceAnalyzer();
+  trace_analyzer::TraceEventVector events;
+  trace_analyzer::Query q =
+      trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_CREATE_OBJECT) ||
+      trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_SNAPSHOT_OBJECT);
+  analyzer->FindEvents(q, &events);
+
+  // Two events for each new node: creation and snapshot.
+  EXPECT_EQ(12u, events.size());
+
+  std::set<FrameTreeNode*> creation_traced;
+  std::set<FrameTreeNode*> snapshot_traced;
+  for (auto event : events) {
+    ExpectFrameTreeNodeObject(event);
+    FrameTreeNode* node =
+        tree()->FindByID(strtol(event->id.c_str(), nullptr, 16));
+    EXPECT_NE(nullptr, node);
+    if (event->HasArg("snapshot")) {
+      ExpectFrameTreeNodeSnapshot(event);
+      EXPECT_FALSE(ContainsValue(snapshot_traced, node));
+      snapshot_traced.insert(node);
+      std::string parent_id = GetParentNodeID(event);
+      EXPECT_FALSE(parent_id.empty());
+      EXPECT_EQ(node->parent(),
+                tree()->FindByID(strtol(parent_id.c_str(), nullptr, 16)));
+    } else {
+      EXPECT_EQ(TRACE_EVENT_PHASE_CREATE_OBJECT, event->phase);
+      EXPECT_FALSE(ContainsValue(creation_traced, node));
+      creation_traced.insert(node);
+    }
+  }
+}
+
+// Deletes frames from a frame tree, tests if the destruction of each frame is
+// correctly traced.
+TEST_F(FrameTreeNodeBlameContextTest, FrameDeletion) {
+  /* Shape of the frame tree to be created:
+   *        ()
+   *      /    \
+   *     ()    ()
+   *    /  \   |
+   *   ()  ()  ()
+   *           |
+   *           ()
+   */
+  const char* tree_shape = "(()())((()))";
+
+  CreateFrameTree(tree_shape);
+  std::set<int> node_ids;
+  for (FrameTreeNode* node : tree()->Nodes())
+    node_ids.insert(node->frame_tree_node_id());
+
+  StartTracing();
+  RemoveAllNonRootFrames();
+  StopTracing();
+
+  std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer =
+      CreateTraceAnalyzer();
+  trace_analyzer::TraceEventVector events;
+  trace_analyzer::Query q =
+      trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_DELETE_OBJECT);
+  analyzer->FindEvents(q, &events);
+
+  // The removal of all non-root nodes should be traced.
+  EXPECT_EQ(6u, events.size());
+  for (auto event : events) {
+    ExpectFrameTreeNodeObject(event);
+    int id = strtol(event->id.c_str(), nullptr, 16);
+    EXPECT_TRUE(ContainsValue(node_ids, id));
+    node_ids.erase(id);
+  }
+}
+
+// Changes URL of the root node. Tests if URL change is correctly traced.
+TEST_F(FrameTreeNodeBlameContextTest, URLChange) {
+  main_test_rfh()->InitializeRenderFrameIfNeeded();
+  GURL url1("http://a.com/");
+  GURL url2("https://b.net/");
+
+  StartTracing();
+  root()->SetCurrentURL(url1);
+  root()->SetCurrentURL(url2);
+  root()->ResetForNewProcess();
+  StopTracing();
+
+  std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer =
+      CreateTraceAnalyzer();
+  trace_analyzer::TraceEventVector events;
+  trace_analyzer::Query q =
+      trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_SNAPSHOT_OBJECT);
+  analyzer->FindEvents(q, &events);
+  std::sort(events.begin(), events.end(), EventPointerCompare);
+
+  // Three snapshots are traced, one for each URL change.
+  EXPECT_EQ(3u, events.size());
+  EXPECT_EQ(url1.spec(), GetSnapshotURL(events[0]));
+  EXPECT_EQ(url2.spec(), GetSnapshotURL(events[1]));
+  EXPECT_EQ("", GetSnapshotURL(events[2]));
+}
+
+}  // namespace content
diff --git a/content/browser/frame_host/traced_frame_tree_node.cc b/content/browser/frame_host/traced_frame_tree_node.cc
deleted file mode 100644
index 7026011..0000000
--- a/content/browser/frame_host/traced_frame_tree_node.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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 "content/browser/frame_host/traced_frame_tree_node.h"
-
-#include "base/command_line.h"
-#include "base/json/json_writer.h"
-#include "base/strings/stringprintf.h"
-#include "content/browser/frame_host/frame_tree.h"
-#include "content/public/common/content_switches.h"
-#include "url/gurl.h"
-
-namespace content {
-
-TracedFrameTreeNode::TracedFrameTreeNode(const FrameTreeNode& node)
-    : parent_node_id_(-1),
-      process_id_(-1),
-      routing_id_(-1) {
-  FrameTreeNode* parent = node.parent();
-  if (parent)
-    parent_node_id_ = parent->frame_tree_node_id();
-
-  RenderFrameHostImpl* current_frame_host = node.current_frame_host();
-
-  if (current_frame_host->last_committed_url().is_valid())
-    url_ = current_frame_host->last_committed_url().spec();
-
-  // On Windows, |rph->GetHandle()| does not duplicate ownership
-  // of the process handle and the render host still retains it. Therefore, we
-  // cannot create a base::Process object, which provides a proper way to get a
-  // process id, from the handle. For a stopgap, we use this deprecated
-  // function that does not require the ownership (http://crbug.com/417532).
-  process_id_ = base::GetProcId(current_frame_host->GetProcess()->GetHandle());
-
-  routing_id_ = current_frame_host->GetRoutingID();
-  DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
-}
-
-TracedFrameTreeNode::~TracedFrameTreeNode() {
-}
-
-void TracedFrameTreeNode::AppendAsTraceFormat(std::string* out) const {
-  std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue());
-
-  if (parent_node_id_ >= 0) {
-    std::unique_ptr<base::DictionaryValue> ref(new base::DictionaryValue());
-    ref->SetString("id_ref", base::StringPrintf("0x%x", parent_node_id_));
-    ref->SetString("scope", "FrameTreeNode");
-    value->Set("parent", std::move(ref));
-  }
-
-  if (process_id_ >= 0) {
-    std::unique_ptr<base::DictionaryValue> ref(new base::DictionaryValue());
-    ref->SetInteger("pid_ref", process_id_);
-    ref->SetString("id_ref", base::StringPrintf("0x%x", routing_id_));
-    ref->SetString("scope", "RenderFrame");
-    value->Set("RenderFrame", std::move(ref));
-  }
-
-  if (!url_.empty())
-    value->SetString("url", url_);
-
-  std::string tmp;
-  base::JSONWriter::Write(*value, &tmp);
-  *out += tmp;
-}
-
-}  // content
diff --git a/content/browser/frame_host/traced_frame_tree_node.h b/content/browser/frame_host/traced_frame_tree_node.h
deleted file mode 100644
index 79c2a80..0000000
--- a/content/browser/frame_host/traced_frame_tree_node.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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 "base/macros.h"
-#include "base/memory/scoped_vector.h"
-#include "base/trace_event/trace_event_impl.h"
-#include "base/values.h"
-#include "content/common/content_export.h"
-
-namespace content {
-
-class FrameTree;
-class FrameTreeNode;
-
-// This is a temporary container used when tracing snapshots of FrameTree
-// objects. When a snapshot of a FrameTree is taken, a TracedFrameTreeNode is
-// created and stored by the tracing system until the trace is dumped.
-class CONTENT_EXPORT TracedFrameTreeNode :
-  public base::trace_event::ConvertableToTraceFormat {
- public:
-  TracedFrameTreeNode(const FrameTreeNode& node);
-  void AppendAsTraceFormat(std::string* out) const override;
-
- private:
-  ~TracedFrameTreeNode() override;
-
-  int parent_node_id_;
-  std::string url_;
-  int process_id_;
-  int routing_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(TracedFrameTreeNode);
-};
-
-}  // content
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 150367d..71d766a 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -759,6 +759,8 @@
       'browser/frame_host/frame_tree.h',
       'browser/frame_host/frame_tree_node.cc',
       'browser/frame_host/frame_tree_node.h',
+      'browser/frame_host/frame_tree_node_blame_context.cc',
+      'browser/frame_host/frame_tree_node_blame_context.h',
       'browser/frame_host/interstitial_page_impl.cc',
       'browser/frame_host/interstitial_page_impl.h',
       'browser/frame_host/interstitial_page_navigator_impl.cc',
@@ -802,8 +804,6 @@
       'browser/frame_host/render_widget_host_view_child_frame.h',
       'browser/frame_host/render_widget_host_view_guest.cc',
       'browser/frame_host/render_widget_host_view_guest.h',
-      'browser/frame_host/traced_frame_tree_node.cc',
-      'browser/frame_host/traced_frame_tree_node.h',
       'browser/gamepad/gamepad_consumer.h',
       'browser/gamepad/gamepad_data_fetcher.cc',
       'browser/gamepad/gamepad_data_fetcher.h',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 061b6476..4b773ca 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -461,6 +461,7 @@
       'browser/fileapi/transient_file_util_unittest.cc',
       'browser/fileapi/upload_file_system_file_element_reader_unittest.cc',
       'browser/frame_host/frame_tree_unittest.cc',
+      'browser/frame_host/frame_tree_node_blame_context_unittest.cc',
       'browser/frame_host/navigation_controller_impl_unittest.cc',
       'browser/frame_host/navigation_entry_impl_unittest.cc',
       'browser/frame_host/navigation_handle_impl_unittest.cc',
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 84b3a098..1320b72 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -3238,6 +3238,7 @@
     if (LayoutViewItem layoutView = LayoutViewItem(document->layoutView())) {
         if (layoutView.usesCompositing())
             layoutView.compositor()->frameViewDidScroll();
+        layoutView.clearHitTestCache();
     }
 
     if (m_didScrollTimer.isActive())
@@ -3247,7 +3248,6 @@
     if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache())
         cache->handleScrollPositionChanged(this);
 
-    layoutView()->clearHitTestCache();
     frame().loader().saveScrollState();
     frame().loader().client()->didChangeScrollOffset();
 
diff --git a/third_party/WebKit/Source/modules/mediasource/MediaSource.cpp b/third_party/WebKit/Source/modules/mediasource/MediaSource.cpp
index 07c5d044..73fff60 100644
--- a/third_party/WebKit/Source/modules/mediasource/MediaSource.cpp
+++ b/third_party/WebKit/Source/modules/mediasource/MediaSource.cpp
@@ -51,6 +51,8 @@
 using blink::WebMediaSource;
 using blink::WebSourceBuffer;
 
+#define MEDIA_SOURCE_LOG_LEVEL 3
+
 namespace blink {
 
 static bool throwExceptionIfClosedOrUpdating(bool isOpen, bool isUpdating, ExceptionState& exceptionState)
@@ -102,24 +104,24 @@
     , m_activeSourceBuffers(SourceBufferList::create(getExecutionContext(), m_asyncEventQueue.get()))
     , m_isAddedToRegistry(false)
 {
-    WTF_LOG(Media, "MediaSource::MediaSource %p", this);
+    DVLOG(MEDIA_SOURCE_LOG_LEVEL) << __FUNCTION__ << "(" << this << ")";
 }
 
 MediaSource::~MediaSource()
 {
-    WTF_LOG(Media, "MediaSource::~MediaSource %p", this);
-    ASSERT(isClosed());
+    DVLOG(MEDIA_SOURCE_LOG_LEVEL) << __FUNCTION__ << "(" << this << ")";
+    DCHECK(isClosed());
 }
 
 void MediaSource::logAndThrowDOMException(ExceptionState& exceptionState, const ExceptionCode& error, const String& message)
 {
-    WTF_LOG(Media, "throwDOMException: error=%d msg=%s", error, message.utf8().data());
+    DVLOG(MEDIA_SOURCE_LOG_LEVEL) << __FUNCTION__ << "(error=" << error << ", message=" << message << ")";
     exceptionState.throwDOMException(error, message);
 }
 
 SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionState& exceptionState)
 {
-    WTF_LOG(Media, "MediaSource::addSourceBuffer(%s) %p", type.ascii().data(), this);
+    DVLOG(MEDIA_SOURCE_LOG_LEVEL) << __FUNCTION__ << "(" << type << ") " << this;
 
     // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
     // 1. If type is an empty string then throw an InvalidAccessError exception
@@ -149,7 +151,7 @@
     OwnPtr<WebSourceBuffer> webSourceBuffer = createWebSourceBuffer(contentType.type(), codecs, exceptionState);
 
     if (!webSourceBuffer) {
-        ASSERT(exceptionState.code() == NotSupportedError || exceptionState.code() == QuotaExceededError);
+        DCHECK(exceptionState.code() == NotSupportedError || exceptionState.code() == QuotaExceededError);
         // 2. If type contains a MIME type that is not supported ..., then throw a NotSupportedError exception and abort these steps.
         // 3. If the user agent can't handle any more SourceBuffer objects then throw a QuotaExceededError exception and abort these steps
         return 0;
@@ -160,13 +162,13 @@
     m_sourceBuffers->add(buffer);
 
     // 7. Return the new object to the caller.
-    WTF_LOG(Media, "MediaSource::addSourceBuffer(%s) %p -> %p", type.ascii().data(), this, buffer);
+    DVLOG(MEDIA_SOURCE_LOG_LEVEL) << __FUNCTION__ << "(" << type << ") " << buffer << " -> " << this;
     return buffer;
 }
 
 void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionState& exceptionState)
 {
-    WTF_LOG(Media, "MediaSource::removeSourceBuffer() %p", this);
+    DVLOG(MEDIA_SOURCE_LOG_LEVEL) << __FUNCTION__ << "(" << buffer << ") -> " << this;
 
     // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-removeSourceBuffer-void-SourceBuffer-sourceBuffer
 
@@ -203,7 +205,7 @@
         return;
     }
 
-    ASSERT(isClosed());
+    DCHECK(isClosed());
 
     m_activeSourceBuffers->clear();
 
@@ -234,7 +236,7 @@
     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-isTypeSupported-boolean-DOMString-type
     // 1. If type is an empty string, then return false.
     if (type.isEmpty()) {
-        WTF_LOG(Media, "MediaSource::isTypeSupported(%s) -> false (empty input)", type.ascii().data());
+        DVLOG(MEDIA_SOURCE_LOG_LEVEL) << __FUNCTION__ << "(" << type << ") -> false (empty input)";
         return false;
     }
 
@@ -243,14 +245,14 @@
 
     // 2. If type does not contain a valid MIME type string, then return false.
     if (contentType.type().isEmpty()) {
-        WTF_LOG(Media, "MediaSource::isTypeSupported(%s) -> false (invalid mime type)", type.ascii().data());
+        DVLOG(MEDIA_SOURCE_LOG_LEVEL) << __FUNCTION__ << "(" << type << ") -> false (invalid mime type)";
         return false;
     }
 
     // Note: MediaSource.isTypeSupported() returning true implies that HTMLMediaElement.canPlayType() will return "maybe" or "probably"
     // since it does not make sense for a MediaSource to support a type the HTMLMediaElement knows it cannot play.
     if (HTMLMediaElement::supportsType(contentType) == WebMimeRegistry::IsNotSupported) {
-        WTF_LOG(Media, "MediaSource::isTypeSupported(%s) -> false (not supported by HTMLMediaElement)", type.ascii().data());
+        DVLOG(MEDIA_SOURCE_LOG_LEVEL) << __FUNCTION__ << "(" << type << ") -> false (not supported by HTMLMediaElement)";
         return false;
     }
 
@@ -259,7 +261,7 @@
     // 5. If the MediaSource does not support the specified combination of media type, media subtype, and codecs then return false.
     // 6. Return true.
     bool result = MIMETypeRegistry::isSupportedMediaSourceMIMEType(contentType.type(), codecs);
-    WTF_LOG(Media, "MediaSource::isTypeSupported(%s) -> %s", type.ascii().data(), result ? "true" : "false");
+    DVLOG(MEDIA_SOURCE_LOG_LEVEL) << __FUNCTION__ << "(" << type << ") -> " << (result ? "true" : "false");
     return result;
 }
 
@@ -286,22 +288,22 @@
 void MediaSource::setWebMediaSourceAndOpen(PassOwnPtr<WebMediaSource> webMediaSource)
 {
     TRACE_EVENT_ASYNC_END0("media", "MediaSource::attachToElement", this);
-    ASSERT(webMediaSource);
-    ASSERT(!m_webMediaSource);
-    ASSERT(m_attachedElement);
+    DCHECK(webMediaSource);
+    DCHECK(!m_webMediaSource);
+    DCHECK(m_attachedElement);
     m_webMediaSource = std::move(webMediaSource);
     setReadyState(openKeyword());
 }
 
 void MediaSource::addedToRegistry()
 {
-    ASSERT(!m_isAddedToRegistry);
+    DCHECK(!m_isAddedToRegistry);
     m_isAddedToRegistry = true;
 }
 
 void MediaSource::removedFromRegistry()
 {
-    ASSERT(m_isAddedToRegistry);
+    DCHECK(m_isAddedToRegistry);
     m_isAddedToRegistry = false;
 }
 
@@ -442,10 +444,10 @@
 
 void MediaSource::setReadyState(const AtomicString& state)
 {
-    ASSERT(state == openKeyword() || state == closedKeyword() || state == endedKeyword());
+    DCHECK(state == openKeyword() || state == closedKeyword() || state == endedKeyword());
 
     AtomicString oldState = readyState();
-    WTF_LOG(Media, "MediaSource::setReadyState() %p : %s -> %s", this, oldState.ascii().data(), state.ascii().data());
+    DVLOG(MEDIA_SOURCE_LOG_LEVEL) << __FUNCTION__ << " : " << oldState << " -> " << state << " " << this;
 
     if (state == closedKeyword()) {
         m_webMediaSource.clear();
@@ -469,7 +471,7 @@
     } else if (error == decode) {
         endOfStreamInternal(WebMediaSource::EndOfStreamStatusDecodeError, exceptionState);
     } else {
-        ASSERT_NOT_REACHED(); // IDL enforcement should prevent this case.
+        NOTREACHED(); // IDL enforcement should prevent this case.
     }
 }
 
@@ -504,7 +506,7 @@
 
 void MediaSource::setSourceBufferActive(SourceBuffer* sourceBuffer)
 {
-    ASSERT(!m_activeSourceBuffers->contains(sourceBuffer));
+    DCHECK(!m_activeSourceBuffers->contains(sourceBuffer));
 
     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-activeSourceBuffers
     // SourceBuffer objects in SourceBuffer.activeSourceBuffers must appear in
@@ -513,7 +515,7 @@
     // same order as buffers in |m_sourceBuffers|, so this method needs to
     // insert |sourceBuffer| into |m_activeSourceBuffers|.
     size_t indexInSourceBuffers = m_sourceBuffers->find(sourceBuffer);
-    ASSERT(indexInSourceBuffers != kNotFound);
+    DCHECK(indexInSourceBuffers != kNotFound);
 
     size_t insertPosition = 0;
     while (insertPosition < m_activeSourceBuffers->length()
@@ -544,7 +546,7 @@
     if (m_attachedElement)
         return false;
 
-    ASSERT(isClosed());
+    DCHECK(isClosed());
 
     TRACE_EVENT_ASYNC_BEGIN0("media", "MediaSource::attachToElement", this);
     m_attachedElement = element;
@@ -583,7 +585,7 @@
     case WebMediaSource::AddStatusOk:
         return adoptPtr(webSourceBuffer);
     case WebMediaSource::AddStatusNotSupported:
-        ASSERT(!webSourceBuffer);
+        DCHECK(!webSourceBuffer);
         // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
         // Step 2: If type contains a MIME type ... that is not supported with the types
         // specified for the other SourceBuffer objects in sourceBuffers, then throw
@@ -591,7 +593,7 @@
         logAndThrowDOMException(exceptionState, NotSupportedError, "The type provided ('" + type + "') is not supported.");
         return nullptr;
     case WebMediaSource::AddStatusReachedIdLimit:
-        ASSERT(!webSourceBuffer);
+        DCHECK(!webSourceBuffer);
         // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
         // Step 3: If the user agent can't handle any more SourceBuffer objects then throw
         // a QuotaExceededError exception and abort these steps.
@@ -599,13 +601,13 @@
         return nullptr;
     }
 
-    ASSERT_NOT_REACHED();
+    NOTREACHED();
     return nullptr;
 }
 
 void MediaSource::scheduleEvent(const AtomicString& eventName)
 {
-    ASSERT(m_asyncEventQueue);
+    DCHECK(m_asyncEventQueue);
 
     Event* event = Event::create(eventName);
     event->setTarget(this);
diff --git a/third_party/WebKit/Source/platform/graphics/DecodingImageGenerator.cpp b/third_party/WebKit/Source/platform/graphics/DecodingImageGenerator.cpp
index 8ccf343..8e0eb43 100644
--- a/third_party/WebKit/Source/platform/graphics/DecodingImageGenerator.cpp
+++ b/third_party/WebKit/Source/platform/graphics/DecodingImageGenerator.cpp
@@ -114,6 +114,8 @@
     if (!decoder)
         return 0;
 
+    // Blink does not know Skia has already adopted |data|.
+    WTF::adopted(data);
     RefPtr<SegmentReader> segmentReader = SegmentReader::createFromSkData(data);
     decoder->setData(segmentReader.get(), true);
     if (!decoder->isSizeAvailable())