diff --git a/DEPS b/DEPS
index 0ed7776c..bebcddb 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '445b5573613179c10d5d9c28f82aa8ed94390aea',
+  'skia_revision': 'cd25f63484badeac976b91506ce67a7dbd3c904c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -52,7 +52,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '2c1183bb07b934f53a0d40f19be4a340519445be',
+  'angle_revision': 'ef66151703318c4c5c975c01e8d1b1efbaf97b0b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
diff --git a/chrome/browser/profiling_host/profiling_process_host.cc b/chrome/browser/profiling_host/profiling_process_host.cc
index 5a839a3..187a0423 100644
--- a/chrome/browser/profiling_host/profiling_process_host.cc
+++ b/chrome/browser/profiling_host/profiling_process_host.cc
@@ -10,6 +10,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/sys_info.h"
 #include "base/task_scheduler/post_task.h"
+#include "base/trace_event/memory_dump_manager.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_content_client.h"
 #include "chrome/common/chrome_switches.h"
@@ -30,16 +31,34 @@
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 
+namespace {
+
+// A wrapper classes that allows a string to be exported as JSON in a trace
+// event.
+class StringWrapper : public base::trace_event::ConvertableToTraceFormat {
+ public:
+  explicit StringWrapper(std::string string) : json_(std::move(string)) {}
+
+  void AppendAsTraceFormat(std::string* out) const override {
+    out->append(json_);
+  }
+
+  std::string json_;
+};
+
+}  // namespace
+
 namespace profiling {
 
 namespace {
+
 const size_t kMaxTraceSizeUploadInBytes = 10 * 1024 * 1024;
 
 void UploadTraceToCrashServer(base::FilePath file_path) {
   std::string file_contents;
   if (!base::ReadFileToStringWithMaxSize(file_path, &file_contents,
                                          kMaxTraceSizeUploadInBytes)) {
-    LOG(ERROR) << "Cannot read trace file contents.";
+    DLOG(ERROR) << "Cannot read trace file contents.";
     return;
   }
 
@@ -119,6 +138,51 @@
   SendPipeToProfilingService(std::move(memlog_client), pid);
 }
 
+bool ProfilingProcessHost::OnMemoryDump(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* pmd) {
+  DCHECK_NE(GetCurrentMode(), Mode::kNone);
+  // TODO: Support dumping all processes for --memlog=all mode.
+  // https://crbug.com/758437.
+  memlog_->DumpProcessForTracing(
+      base::Process::Current().Pid(),
+      base::BindOnce(&ProfilingProcessHost::OnDumpProcessForTracingCallback,
+                     base::Unretained(this)));
+  return true;
+}
+
+void ProfilingProcessHost::OnDumpProcessForTracingCallback(
+    mojo::ScopedSharedBufferHandle buffer,
+    uint32_t size) {
+  if (!buffer->is_valid()) {
+    DLOG(ERROR) << "Failed to dump process for tracing";
+    return;
+  }
+
+  mojo::ScopedSharedBufferMapping mapping = buffer->Map(size);
+  if (!mapping) {
+    DLOG(ERROR) << "Failed to map buffer";
+    return;
+  }
+
+  const char* char_buffer = static_cast<const char*>(mapping.get());
+  std::string json(char_buffer, char_buffer + size);
+
+  const int kTraceEventNumArgs = 1;
+  const char* const kTraceEventArgNames[] = {"dumps"};
+  const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
+  std::unique_ptr<base::trace_event::ConvertableToTraceFormat> wrapper(
+      new StringWrapper(std::move(json)));
+
+  TRACE_EVENT_API_ADD_TRACE_EVENT(
+      TRACE_EVENT_PHASE_MEMORY_DUMP,
+      base::trace_event::TraceLog::GetCategoryGroupEnabled(
+          base::trace_event::MemoryDumpManager::kTraceCategory),
+      "periodic_interval", trace_event_internal::kGlobalScope, 0x0,
+      kTraceEventNumArgs, kTraceEventArgNames, kTraceEventArgTypes,
+      nullptr /* arg_values */, &wrapper, TRACE_EVENT_FLAG_HAS_ID);
+}
+
 void ProfilingProcessHost::SendPipeToProfilingService(
     profiling::mojom::MemlogClientPtr memlog_client,
     base::ProcessId pid) {
@@ -153,8 +217,8 @@
     if (mode == switches::kMemlogModeBrowser)
       return Mode::kBrowser;
 
-    LOG(ERROR) << "Unsupported value: \"" << mode << "\" passed to --"
-               << switches::kMemlog;
+    DLOG(ERROR) << "Unsupported value: \"" << mode << "\" passed to --"
+                << switches::kMemlog;
   }
   return Mode::kNone;
 #else
@@ -189,7 +253,7 @@
 void ProfilingProcessHost::RequestProcessDump(base::ProcessId pid,
                                               const base::FilePath& dest) {
   if (!connector_) {
-    LOG(ERROR)
+    DLOG(ERROR)
         << "Requesting process dump when profiling process hasn't started.";
     return;
   }
@@ -203,14 +267,14 @@
 
 void ProfilingProcessHost::RequestProcessReport(base::ProcessId pid) {
   if (!connector_) {
-    LOG(ERROR)
+    DLOG(ERROR)
         << "Requesting process dump when profiling process hasn't started.";
     return;
   }
 
   base::FilePath output_path;
   if (!CreateTemporaryFile(&output_path)) {
-    LOG(ERROR) << "Cannot create temporary file for memory dump.";
+    DLOG(ERROR) << "Cannot create temporary file for memory dump.";
     return;
   }
 
@@ -237,6 +301,12 @@
     return;
   }
 
+  // No need to unregister since ProfilingProcessHost is never destroyed.
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      this, "OutOfProcessHeapProfilingDumpProvider",
+      content::BrowserThread::GetTaskRunnerForThread(
+          content::BrowserThread::IO));
+
   // Bind to the memlog service. This will start it if it hasn't started
   // already.
   connector_->BindInterface(mojom::kServiceName, &memlog_);
@@ -279,7 +349,7 @@
                                                  bool upload,
                                                  bool success) {
   if (!success) {
-    LOG(ERROR) << "Cannot dump process.";
+    DLOG(ERROR) << "Cannot dump process.";
     // On any errors, the requested trace output file is deleted.
     base::DeleteFile(file_path, false);
     return;
diff --git a/chrome/browser/profiling_host/profiling_process_host.h b/chrome/browser/profiling_host/profiling_process_host.h
index f72fc7e1..4a45f34 100644
--- a/chrome/browser/profiling_host/profiling_process_host.h
+++ b/chrome/browser/profiling_host/profiling_process_host.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/memory/singleton.h"
 #include "base/process/process.h"
+#include "base/trace_event/memory_dump_provider.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/profiling/memlog.mojom.h"
@@ -43,7 +44,8 @@
 // TODO(ajwong): This host class seems over kill at this point. Can this be
 // fully subsumed by the ProfilingService class?
 class ProfilingProcessHost : public content::BrowserChildProcessObserver,
-                             content::NotificationObserver {
+                             content::NotificationObserver,
+                             base::trace_event::MemoryDumpProvider {
  public:
   enum class Mode {
     // No profiling enabled.
@@ -94,6 +96,13 @@
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
+  // base::trace_event::MemoryDumpProvider
+  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+                    base::trace_event::ProcessMemoryDump* pmd) override;
+
+  void OnDumpProcessForTracingCallback(mojo::ScopedSharedBufferHandle buffer,
+                                       uint32_t size);
+
   // Starts the profiling process.
   void LaunchAsService();
 
diff --git a/chrome/common/profiling/memlog.mojom b/chrome/common/profiling/memlog.mojom
index 4b43892..8b8a7e60 100644
--- a/chrome/common/profiling/memlog.mojom
+++ b/chrome/common/profiling/memlog.mojom
@@ -22,4 +22,11 @@
   // under the "metadata" key.
   DumpProcess(mojo.common.mojom.ProcessId pid, handle output_file,
               mojo.common.mojom.DictionaryValue metadata) => (bool result);
+
+  // Dumps the memory log of the process with the given |pid| into a data
+  // pipe. The format is a JSON string compatible with TRACE_EVENT* macros.
+  // On success, returns a valid shared_buffer handle and the size of the
+  // data written. On failure, returns an invalid shared_buffer handle.
+  DumpProcessForTracing(mojo.common.mojom.ProcessId pid) =>
+      (handle<shared_buffer> consumer, uint32 size);
 };
diff --git a/chrome/profiling/json_exporter.cc b/chrome/profiling/json_exporter.cc
index f2a38ded..ee76a2bc 100644
--- a/chrome/profiling/json_exporter.cc
+++ b/chrome/profiling/json_exporter.cc
@@ -74,11 +74,11 @@
   out << "\"ph\":\"v\",";
   out << "\"name\":\"periodic_interval\",";
   out << "\"args\":{";
-  out << "\"dumps\":{\n";
+  out << "\"dumps\":";
 }
 
 void WriteDumpsFooter(std::ostream& out) {
-  out << "}}}";  // dumps, args, event
+  out << "}}";  // args, event
 }
 
 // Writes the dictionary keys to preceed a "heaps_v2" trace argument inside a
@@ -260,10 +260,57 @@
   WriteProcessName(pid, out);
   out << ",\n";
   WriteDumpsHeader(pid, out);
+  ExportMemoryMapsAndV2StackTraceToJSON(event_set, maps, out);
+  WriteDumpsFooter(out);
+  out << "]";
+
+  // Append metadata.
+  if (metadata_dict) {
+    std::string metadata;
+    base::JSONWriter::Write(*metadata_dict, &metadata);
+    out << ",\"metadata\": " << metadata;
+  }
+
+  out << "}\n";
+}
+
+void ExportMemoryMapsAndV2StackTraceToJSON(
+    const AllocationEventSet& event_set,
+    const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
+    std::ostream& out) {
+  // Start dictionary.
+  out << "{\n";
 
   WriteMemoryMaps(maps, out);
   out << ",\n";
 
+  // Write level of detail.
+  out << R"("level_of_detail": "detailed")"
+      << ",\n";
+
+  // Write the top-level allocators section. This section is used by the tracing
+  // UI to show a small summary for each allocator. It's necessary as a
+  // placeholder to allow the stack-viewing UI to be shown.
+  // TODO: Fill in placeholders for "value". https://crbug.com/758434.
+  out << R"(
+  "allocators": {
+    "malloc": {
+      "attrs": {
+        "virtual_size": {
+          "type": "scalar",
+          "units": "bytes",
+          "value": "1234"
+        },
+        "size": {
+          "type": "scalar",
+          "units": "bytes",
+          "value": "1234"
+        }
+      }
+    }
+  },
+  )";
+
   WriteHeapsV2Header(out);
 
   // Output Heaps_V2 format version. Currently "1" is the only valid value.
@@ -319,16 +366,8 @@
   out << "}}\n";  // End of allocators section.
 
   WriteHeapsV2Footer(out);
-  WriteDumpsFooter(out);
-  out << "]";
 
-  // Append metadata.
-  if (metadata_dict) {
-    std::string metadata;
-    base::JSONWriter::Write(*metadata_dict, &metadata);
-    out << ",\"metadata\": " << metadata;
-  }
-
+  // End dictionary.
   out << "}\n";
 }
 
diff --git a/chrome/profiling/json_exporter.h b/chrome/profiling/json_exporter.h
index 6bbc76d..f518d91 100644
--- a/chrome/profiling/json_exporter.h
+++ b/chrome/profiling/json_exporter.h
@@ -14,6 +14,8 @@
 
 namespace profiling {
 
+// Creates a JSON-encoded string that is similar in form to traces created by
+// TracingControllerImpl.
 void ExportAllocationEventSetToJSON(
     int pid,
     const AllocationEventSet& set,
@@ -21,6 +23,13 @@
     std::ostream& out,
     std::unique_ptr<base::DictionaryValue> metadata);
 
+// Creates a JSON string representing a JSON dictionary that contains memory
+// maps and v2 format stack traces.
+void ExportMemoryMapsAndV2StackTraceToJSON(
+    const AllocationEventSet& set,
+    const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
+    std::ostream& out);
+
 }  // namespace profiling
 
 #endif  // CHROME_PROFILING_JSON_EXPORTER_H_
diff --git a/chrome/profiling/json_exporter_unittest.cc b/chrome/profiling/json_exporter_unittest.cc
index 111266a4..696dec24 100644
--- a/chrome/profiling/json_exporter_unittest.cc
+++ b/chrome/profiling/json_exporter_unittest.cc
@@ -114,49 +114,67 @@
   events.insert(AllocationEvent(Address(0x2), 32, bt2));
   events.insert(AllocationEvent(Address(0x3), 16, bt1));
 
-  std::ostringstream stream;
-  ExportAllocationEventSetToJSON(1234, events, MemoryMap(), stream, nullptr);
-  std::string json = stream.str();
+  {
+    std::ostringstream stream;
+    ExportAllocationEventSetToJSON(1234, events, MemoryMap(), stream, nullptr);
+    std::string json = stream.str();
 
-  // JSON should parse.
-  base::JSONReader reader(base::JSON_PARSE_RFC);
-  std::unique_ptr<base::Value> root = reader.ReadToValue(stream.str());
-  ASSERT_EQ(base::JSONReader::JSON_NO_ERROR, reader.error_code())
-      << reader.GetErrorMessage();
-  ASSERT_TRUE(root);
+    // JSON should parse.
+    base::JSONReader reader(base::JSON_PARSE_RFC);
+    std::unique_ptr<base::Value> root = reader.ReadToValue(stream.str());
+    ASSERT_EQ(base::JSONReader::JSON_NO_ERROR, reader.error_code())
+        << reader.GetErrorMessage();
+    ASSERT_TRUE(root);
 
-  // The trace array contains two items, a process_name one and a
-  // periodic_interval one. Find the latter.
-  const base::Value* periodic_interval = FindFirstPeriodicInterval(*root);
-  ASSERT_TRUE(periodic_interval) << "Array contains no periodic_interval";
+    // The trace array contains two items, a process_name one and a
+    // periodic_interval one. Find the latter.
+    const base::Value* periodic_interval = FindFirstPeriodicInterval(*root);
+    ASSERT_TRUE(periodic_interval) << "Array contains no periodic_interval";
 
-  const base::Value* heaps_v2 =
-      periodic_interval->FindPath({"args", "dumps", "heaps_v2"});
-  ASSERT_TRUE(heaps_v2);
+    const base::Value* heaps_v2 =
+        periodic_interval->FindPath({"args", "dumps", "heaps_v2"});
+    ASSERT_TRUE(heaps_v2);
 
-  // Counts should be a list of two items, a 1 and a 2 (in either order). The
-  // two matching 16-byte allocations should be coalesced to produce the 2.
-  const base::Value* counts =
-      heaps_v2->FindPath({"allocators", "malloc", "counts"});
-  ASSERT_TRUE(counts);
-  EXPECT_EQ(2u, counts->GetList().size());
-  EXPECT_TRUE((counts->GetList()[0].GetInt() == 1 &&
-               counts->GetList()[1].GetInt() == 2) ||
-              (counts->GetList()[0].GetInt() == 2 &&
-               counts->GetList()[1].GetInt() == 1));
+    // Counts should be a list of two items, a 1 and a 2 (in either order). The
+    // two matching 16-byte allocations should be coalesced to produce the 2.
+    const base::Value* counts =
+        heaps_v2->FindPath({"allocators", "malloc", "counts"});
+    ASSERT_TRUE(counts);
+    EXPECT_EQ(2u, counts->GetList().size());
+    EXPECT_TRUE((counts->GetList()[0].GetInt() == 1 &&
+                 counts->GetList()[1].GetInt() == 2) ||
+                (counts->GetList()[0].GetInt() == 2 &&
+                 counts->GetList()[1].GetInt() == 1));
 
-  // Nodes should be a list with 4 items.
-  //   [0] => address: 1234  parent: none
-  //   [1] => address: 5678  parent: 0
-  //   [2] => address: 9012  parent: 0
-  //   [3] => address: 9013  parent: 2
-  const base::Value* nodes = heaps_v2->FindPath({"maps", "nodes"});
-  ASSERT_TRUE(nodes);
-  EXPECT_EQ(4u, nodes->GetList().size());
-  EXPECT_TRUE(IsBacktraceInList(nodes, 0, kNoParent));
-  EXPECT_TRUE(IsBacktraceInList(nodes, 1, 0));
-  EXPECT_TRUE(IsBacktraceInList(nodes, 2, 0));
-  EXPECT_TRUE(IsBacktraceInList(nodes, 3, 2));
+    // Nodes should be a list with 4 items.
+    //   [0] => address: 1234  parent: none
+    //   [1] => address: 5678  parent: 0
+    //   [2] => address: 9012  parent: 0
+    //   [3] => address: 9013  parent: 2
+    const base::Value* nodes = heaps_v2->FindPath({"maps", "nodes"});
+    ASSERT_TRUE(nodes);
+    EXPECT_EQ(4u, nodes->GetList().size());
+    EXPECT_TRUE(IsBacktraceInList(nodes, 0, kNoParent));
+    EXPECT_TRUE(IsBacktraceInList(nodes, 1, 0));
+    EXPECT_TRUE(IsBacktraceInList(nodes, 2, 0));
+    EXPECT_TRUE(IsBacktraceInList(nodes, 3, 2));
+  }
+
+  // Check that ExportMemoryMapsAndV2StackTraceToJSON parses. Assume the
+  // contents is reasonable, given that it's nested in
+  // ExportAllocationEventSetToJSON.
+  {
+    std::ostringstream stream;
+    ExportMemoryMapsAndV2StackTraceToJSON(events, MemoryMap(), stream);
+    std::string json = stream.str();
+
+    // JSON should parse.
+    base::JSONReader reader(base::JSON_PARSE_RFC);
+    std::unique_ptr<base::Value> root = reader.ReadToValue(stream.str());
+    ASSERT_EQ(base::JSONReader::JSON_NO_ERROR, reader.error_code())
+        << reader.GetErrorMessage();
+    ASSERT_TRUE(root);
+  }
 }
 
 TEST(ProfilingJsonExporterTest, MemoryMaps) {
diff --git a/chrome/profiling/memlog_connection_manager.cc b/chrome/profiling/memlog_connection_manager.cc
index 745eba12..360f1c81 100644
--- a/chrome/profiling/memlog_connection_manager.cc
+++ b/chrome/profiling/memlog_connection_manager.cc
@@ -13,6 +13,7 @@
 #include "chrome/profiling/json_exporter.h"
 #include "chrome/profiling/memlog_receiver_pipe.h"
 #include "chrome/profiling/memlog_stream_parser.h"
+#include "mojo/public/cpp/system/buffer.h"
 #include "third_party/zlib/zlib.h"
 
 #if defined(OS_WIN)
@@ -110,7 +111,7 @@
 
   auto it = connections_.find(pid);
   if (it == connections_.end()) {
-    LOG(ERROR) << "No connections found for memory dump for pid:" << pid;
+    DLOG(ERROR) << "No connections found for memory dump for pid:" << pid;
     return false;
   }
 
@@ -131,7 +132,7 @@
 #endif
   gzFile gz_file = gzdopen(fd, "w");
   if (!gz_file) {
-    LOG(ERROR) << "Cannot compress trace file";
+    DLOG(ERROR) << "Cannot compress trace file";
     return false;
   }
 
@@ -141,4 +142,53 @@
   return written_bytes == reply.size();
 }
 
+void MemlogConnectionManager::DumpProcessForTracing(
+    base::ProcessId pid,
+    mojom::Memlog::DumpProcessForTracingCallback callback,
+    const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps) {
+  base::AutoLock lock(connections_lock_);
+
+  // Lock all connections to prevent deallocations of atoms from
+  // BacktraceStorage. This only works if no new connections are made, which
+  // connections_lock_ guarantees.
+  std::vector<std::unique_ptr<base::AutoLock>> locks;
+  for (auto& it : connections_) {
+    Connection* connection = it.second.get();
+    locks.push_back(
+        base::MakeUnique<base::AutoLock>(*connection->parser->GetLock()));
+  }
+
+  auto it = connections_.find(pid);
+  if (it == connections_.end()) {
+    DLOG(ERROR) << "No connections found for memory dump for pid:" << pid;
+    std::move(callback).Run(mojo::ScopedSharedBufferHandle(), 0);
+    return;
+  }
+
+  Connection* connection = it->second.get();
+  std::ostringstream oss;
+  ExportMemoryMapsAndV2StackTraceToJSON(connection->tracker.live_allocs(), maps,
+                                        oss);
+  std::string reply = oss.str();
+
+  mojo::ScopedSharedBufferHandle buffer =
+      mojo::SharedBufferHandle::Create(reply.size());
+  if (!buffer.is_valid()) {
+    DLOG(ERROR) << "Could not create Mojo shared buffer";
+    std::move(callback).Run(std::move(buffer), 0);
+    return;
+  }
+
+  mojo::ScopedSharedBufferMapping mapping = buffer->Map(reply.size());
+  if (!mapping) {
+    DLOG(ERROR) << "Could not map Mojo shared buffer";
+    std::move(callback).Run(mojo::ScopedSharedBufferHandle(), 0);
+    return;
+  }
+
+  memcpy(mapping.get(), reply.c_str(), reply.size());
+
+  std::move(callback).Run(std::move(buffer), reply.size());
+}
+
 }  // namespace profiling
diff --git a/chrome/profiling/memlog_connection_manager.h b/chrome/profiling/memlog_connection_manager.h
index c923ff73..beca553 100644
--- a/chrome/profiling/memlog_connection_manager.h
+++ b/chrome/profiling/memlog_connection_manager.h
@@ -17,6 +17,7 @@
 #include "base/synchronization/lock.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "chrome/common/profiling/memlog.mojom.h"
 #include "chrome/profiling/backtrace_storage.h"
 #include "services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom.h"
 
@@ -49,6 +50,10 @@
       std::unique_ptr<base::DictionaryValue> metadata,
       const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
       base::File output_file);
+  void DumpProcessForTracing(
+      base::ProcessId pid,
+      mojom::Memlog::DumpProcessForTracingCallback callback,
+      const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps);
 
   void OnNewConnection(base::ScopedPlatformFile file, base::ProcessId pid);
 
diff --git a/chrome/profiling/memlog_impl.cc b/chrome/profiling/memlog_impl.cc
index d93b955..55255cf 100644
--- a/chrome/profiling/memlog_impl.cc
+++ b/chrome/profiling/memlog_impl.cc
@@ -38,7 +38,7 @@
   MojoResult result =
       UnwrapPlatformFile(std::move(output_file), &platform_file);
   if (result != MOJO_RESULT_OK) {
-    LOG(ERROR) << "Failed to unwrap output file " << result;
+    DLOG(ERROR) << "Failed to unwrap output file " << result;
     std::move(callback).Run(false);
     return;
   }
@@ -48,13 +48,24 @@
   // in the memory map global dump callback.
   // TODO(brettw) this should be a OnceCallback to avoid base::Passed.
   memory_instrumentation::MemoryInstrumentation::GetInstance()
-      ->GetVmRegionsForHeapProfiler(base::Bind(
-          &MemlogImpl::OnGetVmRegionsComplete, weak_factory_.GetWeakPtr(), pid,
-          base::Passed(std::move(metadata)), base::Passed(std::move(file)),
-          base::Passed(std::move(callback))));
+      ->GetVmRegionsForHeapProfiler(
+          base::Bind(&MemlogImpl::OnGetVmRegionsCompleteForDumpProcess,
+                     weak_factory_.GetWeakPtr(), pid, base::Passed(&metadata),
+                     base::Passed(&file), base::Passed(&callback)));
 }
 
-void MemlogImpl::OnGetVmRegionsComplete(
+void MemlogImpl::DumpProcessForTracing(base::ProcessId pid,
+                                       DumpProcessForTracingCallback callback) {
+  // Need a memory map to make sense of the dump. The dump will be triggered
+  // in the memory map global dump callback.
+  // TODO(brettw) this should be a OnceCallback to avoid base::Passed.
+  memory_instrumentation::MemoryInstrumentation::GetInstance()
+      ->GetVmRegionsForHeapProfiler(base::Bind(
+          &MemlogImpl::OnGetVmRegionsCompleteForDumpProcessForTracing,
+          weak_factory_.GetWeakPtr(), pid, base::Passed(&callback)));
+}
+
+void MemlogImpl::OnGetVmRegionsCompleteForDumpProcess(
     base::ProcessId pid,
     std::unique_ptr<base::DictionaryValue> metadata,
     base::File file,
@@ -62,7 +73,7 @@
     bool success,
     memory_instrumentation::mojom::GlobalMemoryDumpPtr dump) {
   if (!success) {
-    LOG(ERROR) << "Global dump failed";
+    DLOG(ERROR) << "Global dump failed";
     std::move(callback).Run(false);
     return;
   }
@@ -78,7 +89,7 @@
     }
   }
   if (!process_dump) {
-    LOG(ERROR) << "Don't have a memory dump for PID " << pid;
+    DLOG(ERROR) << "Don't have a memory dump for PID " << pid;
     std::move(callback).Run(false);
     return;
   }
@@ -87,7 +98,7 @@
           pid, std::move(metadata),
           std::move(process_dump->os_dump->memory_maps_for_heap_profiler),
           std::move(file))) {
-    LOG(ERROR) << "Can't dump process to file";
+    DLOG(ERROR) << "Can't dump process to file";
     std::move(callback).Run(false);
     return;
   }
@@ -96,4 +107,34 @@
   std::move(callback).Run(true);
 }
 
+void MemlogImpl::OnGetVmRegionsCompleteForDumpProcessForTracing(
+    base::ProcessId pid,
+    mojom::Memlog::DumpProcessForTracingCallback callback,
+    bool success,
+    memory_instrumentation::mojom::GlobalMemoryDumpPtr dump) {
+  if (!success) {
+    DLOG(ERROR) << "GetVMRegions failed";
+    std::move(callback).Run(mojo::ScopedSharedBufferHandle(), 0);
+    return;
+  }
+  // Find the process's memory dump we want.
+  // TODO(bug 752621) we should be asking and getting the memory map of only
+  // the process we want rather than querying all processes and filtering.
+  memory_instrumentation::mojom::ProcessMemoryDump* process_dump = nullptr;
+  for (const auto& proc : dump->process_dumps) {
+    if (proc->pid == pid) {
+      process_dump = &*proc;
+      break;
+    }
+  }
+  if (!process_dump) {
+    DLOG(ERROR) << "Don't have a memory dump for PID " << pid;
+    std::move(callback).Run(mojo::ScopedSharedBufferHandle(), 0);
+    return;
+  }
+  connection_manager_->DumpProcessForTracing(
+      pid, std::move(callback),
+      std::move(process_dump->os_dump->memory_maps_for_heap_profiler));
+}
+
 }  // namespace profiling
diff --git a/chrome/profiling/memlog_impl.h b/chrome/profiling/memlog_impl.h
index 2c3c9a7..193de38f 100644
--- a/chrome/profiling/memlog_impl.h
+++ b/chrome/profiling/memlog_impl.h
@@ -36,15 +36,22 @@
                    mojo::ScopedHandle output_file,
                    std::unique_ptr<base::DictionaryValue> metadata,
                    DumpProcessCallback callback) override;
+  void DumpProcessForTracing(base::ProcessId pid,
+                             DumpProcessForTracingCallback callback) override;
 
  private:
-  void OnGetVmRegionsComplete(
+  void OnGetVmRegionsCompleteForDumpProcess(
       base::ProcessId pid,
       std::unique_ptr<base::DictionaryValue> metadata,
       base::File file,
       DumpProcessCallback callback,
       bool success,
       memory_instrumentation::mojom::GlobalMemoryDumpPtr dump);
+  void OnGetVmRegionsCompleteForDumpProcessForTracing(
+      base::ProcessId pid,
+      DumpProcessForTracingCallback callback,
+      bool success,
+      memory_instrumentation::mojom::GlobalMemoryDumpPtr dump);
 
   std::unique_ptr<MemlogConnectionManager> connection_manager_;
 
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index 28b352a..2832a75 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -206,7 +206,8 @@
   if (!disabled_types.Has(syncer::BOOKMARKS)) {
     if (FeatureList::IsEnabled(switches::kSyncUSSBookmarks)) {
       sync_service->RegisterDataTypeController(
-          std::make_unique<sync_bookmarks::BookmarkModelTypeController>());
+          std::make_unique<sync_bookmarks::BookmarkModelTypeController>(
+              sync_client_));
     } else {
       sync_service->RegisterDataTypeController(
           std::make_unique<BookmarkDataTypeController>(error_callback,
diff --git a/components/sync_bookmarks/BUILD.gn b/components/sync_bookmarks/BUILD.gn
index 2cc8268..09110f9 100644
--- a/components/sync_bookmarks/BUILD.gn
+++ b/components/sync_bookmarks/BUILD.gn
@@ -34,6 +34,7 @@
 
   sources = [
     "bookmark_data_type_controller_unittest.cc",
+    "bookmark_model_type_controller_unittest.cc",
     "bookmark_model_type_processor_unittest.cc",
   ]
 
@@ -46,6 +47,7 @@
     "//components/prefs:test_support",
     "//components/sync",
     "//components/sync:test_support_driver",
+    "//components/sync:test_support_engine",
     "//components/sync:test_support_model",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/components/sync_bookmarks/bookmark_model_type_controller.cc b/components/sync_bookmarks/bookmark_model_type_controller.cc
index 832fe9a..ccc49f8 100644
--- a/components/sync_bookmarks/bookmark_model_type_controller.cc
+++ b/components/sync_bookmarks/bookmark_model_type_controller.cc
@@ -4,68 +4,166 @@
 
 #include "components/sync_bookmarks/bookmark_model_type_controller.h"
 
+#include <utility>
+
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/sync/driver/sync_client.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/sync/engine/model_type_configurer.h"
+#include "components/sync/engine/model_type_processor_proxy.h"
+#include "components/sync/model/sync_error.h"
+#include "components/sync/protocol/model_type_state.pb.h"
+#include "components/sync/protocol/sync.pb.h"
+#include "components/sync/syncable/directory.h"
+#include "components/sync/syncable/user_share.h"
+#include "components/sync_bookmarks/bookmark_model_type_processor.h"
+
+using syncer::SyncError;
+
 namespace sync_bookmarks {
 
-BookmarkModelTypeController::BookmarkModelTypeController()
-    : DataTypeController(syncer::BOOKMARKS) {}
+BookmarkModelTypeController::BookmarkModelTypeController(
+    syncer::SyncClient* sync_client)
+    : DataTypeController(syncer::BOOKMARKS),
+      sync_client_(sync_client),
+      state_(NOT_RUNNING) {}
+
+BookmarkModelTypeController::~BookmarkModelTypeController() = default;
 
 bool BookmarkModelTypeController::ShouldLoadModelBeforeConfigure() const {
-  NOTIMPLEMENTED();
-  return false;
+  DCHECK(CalledOnValidThread());
+  return true;
 }
 
 void BookmarkModelTypeController::BeforeLoadModels(
     syncer::ModelTypeConfigurer* configurer) {
-  NOTIMPLEMENTED();
+  DCHECK(CalledOnValidThread());
 }
 
 void BookmarkModelTypeController::LoadModels(
     const ModelLoadCallback& model_load_callback) {
-  NOTIMPLEMENTED();
+  DCHECK(CalledOnValidThread());
+  if (state() != NOT_RUNNING) {
+    model_load_callback.Run(type(),
+                            SyncError(FROM_HERE, SyncError::DATATYPE_ERROR,
+                                      "Model already running", type()));
+    return;
+  }
+
+  state_ = MODEL_STARTING;
+
+  if (DependenciesLoaded()) {
+    state_ = MODEL_LOADED;
+    model_load_callback.Run(type(), SyncError());
+  } else {
+    // TODO(pavely): Subscribe for BookmarkModel and HistoryService
+    // notifications.
+    NOTIMPLEMENTED();
+  }
 }
 
 void BookmarkModelTypeController::RegisterWithBackend(
     base::Callback<void(bool)> set_downloaded,
     syncer::ModelTypeConfigurer* configurer) {
-  NOTIMPLEMENTED();
+  DCHECK(CalledOnValidThread());
+  DCHECK(configurer);
+  std::unique_ptr<syncer::ActivationContext> activation_context =
+      PrepareActivationContext();
+  set_downloaded.Run(activation_context->model_type_state.initial_sync_done());
+  configurer->ActivateNonBlockingDataType(type(),
+                                          std::move(activation_context));
 }
 
 void BookmarkModelTypeController::StartAssociating(
     const StartCallback& start_callback) {
-  NOTIMPLEMENTED();
+  DCHECK(CalledOnValidThread());
+  DCHECK(!start_callback.is_null());
+  DCHECK_EQ(MODEL_LOADED, state_);
+
+  state_ = RUNNING;
+
+  // There is no association, just call back promptly.
+  syncer::SyncMergeResult merge_result(type());
+  start_callback.Run(OK, merge_result, merge_result);
 }
 
 void BookmarkModelTypeController::ActivateDataType(
     syncer::ModelTypeConfigurer* configurer) {
+  DCHECK(CalledOnValidThread());
   NOTIMPLEMENTED();
 }
 
 void BookmarkModelTypeController::DeactivateDataType(
     syncer::ModelTypeConfigurer* configurer) {
+  DCHECK(CalledOnValidThread());
   NOTIMPLEMENTED();
 }
 
 void BookmarkModelTypeController::Stop() {
+  DCHECK(CalledOnValidThread());
   NOTIMPLEMENTED();
 }
 
 syncer::DataTypeController::State BookmarkModelTypeController::state() const {
-  NOTIMPLEMENTED();
-  return NOT_RUNNING;
+  DCHECK(CalledOnValidThread());
+  return state_;
 }
 
 void BookmarkModelTypeController::GetAllNodes(
     const AllNodesCallback& callback) {
+  DCHECK(CalledOnValidThread());
   NOTIMPLEMENTED();
 }
 
 void BookmarkModelTypeController::GetStatusCounters(
     const StatusCountersCallback& callback) {
+  DCHECK(CalledOnValidThread());
   NOTIMPLEMENTED();
 }
 
 void BookmarkModelTypeController::RecordMemoryUsageHistogram() {
+  DCHECK(CalledOnValidThread());
   NOTIMPLEMENTED();
 }
 
+bool BookmarkModelTypeController::DependenciesLoaded() {
+  DCHECK(CalledOnValidThread());
+  bookmarks::BookmarkModel* bookmark_model = sync_client_->GetBookmarkModel();
+  if (!bookmark_model || !bookmark_model->loaded())
+    return false;
+
+  history::HistoryService* history_service = sync_client_->GetHistoryService();
+  if (!history_service || !history_service->BackendLoaded())
+    return false;
+
+  return true;
+}
+
+std::unique_ptr<syncer::ActivationContext>
+BookmarkModelTypeController::PrepareActivationContext() {
+  DCHECK(!model_type_processor_);
+
+  syncer::UserShare* user_share =
+      sync_client_->GetSyncService()->GetUserShare();
+  syncer::syncable::Directory* directory = user_share->directory.get();
+
+  std::unique_ptr<syncer::ActivationContext> activation_context =
+      std::make_unique<syncer::ActivationContext>();
+
+  directory->GetDownloadProgress(
+      type(), activation_context->model_type_state.mutable_progress_marker());
+  activation_context->model_type_state.set_initial_sync_done(
+      directory->InitialSyncEndedForType(type()));
+  // TODO(pavely): Populate model_type_state.type_context.
+
+  model_type_processor_ = std::make_unique<BookmarkModelTypeProcessor>();
+  activation_context->type_processor =
+      std::make_unique<syncer::ModelTypeProcessorProxy>(
+          model_type_processor_->GetWeakPtr(),
+          base::ThreadTaskRunnerHandle::Get());
+  return activation_context;
+}
+
 }  // namespace sync_bookmarks
diff --git a/components/sync_bookmarks/bookmark_model_type_controller.h b/components/sync_bookmarks/bookmark_model_type_controller.h
index 49f69745..daf3b1a 100644
--- a/components/sync_bookmarks/bookmark_model_type_controller.h
+++ b/components/sync_bookmarks/bookmark_model_type_controller.h
@@ -5,16 +5,26 @@
 #ifndef COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_CONTROLLER_H_
 #define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_CONTROLLER_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "components/sync/driver/data_type_controller.h"
+#include "components/sync/engine/activation_context.h"
+
+namespace syncer {
+class SyncClient;
+}  // namespace syncer
 
 namespace sync_bookmarks {
 
+class BookmarkModelTypeProcessor;
+
 // A class that manages the startup and shutdown of bookmark sync implemented
 // through USS APIs.
 class BookmarkModelTypeController : public syncer::DataTypeController {
  public:
-  BookmarkModelTypeController();
+  explicit BookmarkModelTypeController(syncer::SyncClient* sync_client);
+  ~BookmarkModelTypeController() override;
 
   // syncer::DataTypeController implementation.
   bool ShouldLoadModelBeforeConfigure() const override;
@@ -32,6 +42,25 @@
   void RecordMemoryUsageHistogram() override;
 
  private:
+  friend class BookmarkModelTypeControllerTest;
+
+  // Returns true if both BookmarkModel and HistoryService are loaded.
+  bool DependenciesLoaded();
+
+  // Reads ModelTypeState from storage and creates BookmarkModelTypeProcessor.
+  std::unique_ptr<syncer::ActivationContext> PrepareActivationContext();
+
+  // SyncClient provides access to BookmarkModel, HistoryService and
+  // SyncService.
+  syncer::SyncClient* sync_client_;
+
+  // State of this datatype controller.
+  State state_;
+
+  // BookmarkModelTypeProcessor handles communications between sync engine and
+  // BookmarkModel/HistoryService.
+  std::unique_ptr<BookmarkModelTypeProcessor> model_type_processor_;
+
   DISALLOW_COPY_AND_ASSIGN(BookmarkModelTypeController);
 };
 
diff --git a/components/sync_bookmarks/bookmark_model_type_controller_unittest.cc b/components/sync_bookmarks/bookmark_model_type_controller_unittest.cc
new file mode 100644
index 0000000..8d63337a
--- /dev/null
+++ b/components/sync_bookmarks/bookmark_model_type_controller_unittest.cc
@@ -0,0 +1,244 @@
+// Copyright 2017 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 "components/sync_bookmarks/bookmark_model_type_controller.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/test/test_bookmark_client.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/driver/data_type_controller.h"
+#include "components/sync/driver/data_type_controller_mock.h"
+#include "components/sync/driver/fake_sync_client.h"
+#include "components/sync/driver/fake_sync_service.h"
+#include "components/sync/engine/model_type_configurer.h"
+#include "components/sync/model/sync_error.h"
+#include "components/sync/model/sync_merge_result.h"
+#include "components/sync/syncable/directory.h"
+#include "components/sync/syncable/test_user_share.h"
+#include "components/sync/test/engine/test_syncable_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using syncer::DataTypeController;
+using syncer::ModelType;
+using syncer::SyncService;
+using syncer::UserShare;
+using testing::_;
+
+namespace sync_bookmarks {
+
+namespace {
+
+// Fake specializations for BookmarModelTypeController's external dependencies.
+
+class TestHistoryService : public history::HistoryService {
+ public:
+  bool BackendLoaded() override { return true; }
+};
+
+class TestSyncClient : public syncer::FakeSyncClient {
+ public:
+  TestSyncClient(bookmarks::BookmarkModel* bookmark_model,
+                 history::HistoryService* history_service,
+                 SyncService* sync_service)
+      : bookmark_model_(bookmark_model),
+        history_service_(history_service),
+        sync_service_(sync_service) {}
+
+  bookmarks::BookmarkModel* GetBookmarkModel() override {
+    return bookmark_model_;
+  }
+
+  history::HistoryService* GetHistoryService() override {
+    return history_service_;
+  }
+
+  SyncService* GetSyncService() override { return sync_service_; }
+
+ private:
+  bookmarks::BookmarkModel* bookmark_model_;
+  history::HistoryService* history_service_;
+  SyncService* sync_service_;
+};
+
+class TestSyncService : public syncer::FakeSyncService {
+ public:
+  explicit TestSyncService(UserShare* user_share) : user_share_(user_share) {}
+
+  UserShare* GetUserShare() const override { return user_share_; }
+
+ private:
+  UserShare* user_share_;
+};
+
+class TestModelTypeConfigurer : public syncer::ModelTypeConfigurer {
+ public:
+  TestModelTypeConfigurer() {}
+  ~TestModelTypeConfigurer() override {}
+
+  void ConfigureDataTypes(ConfigureParams params) override {}
+
+  void RegisterDirectoryDataType(ModelType type,
+                                 syncer::ModelSafeGroup group) override {}
+
+  void UnregisterDirectoryDataType(ModelType type) override {}
+
+  void ActivateDirectoryDataType(
+      ModelType type,
+      syncer::ModelSafeGroup group,
+      syncer::ChangeProcessor* change_processor) override {}
+
+  void DeactivateDirectoryDataType(ModelType type) override {}
+
+  void ActivateNonBlockingDataType(
+      ModelType type,
+      std::unique_ptr<syncer::ActivationContext> activation_context) override {
+    activation_context_ = std::move(activation_context);
+  }
+
+  void DeactivateNonBlockingDataType(ModelType type) override {}
+
+  syncer::ActivationContext* activation_context() {
+    return activation_context_.get();
+  }
+
+ private:
+  // ActivationContext captured in ActivateNonBlockingDataType call.
+  std::unique_ptr<syncer::ActivationContext> activation_context_;
+};
+
+}  // namespace
+
+class BookmarkModelTypeControllerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    bookmark_model_ = bookmarks::TestBookmarkClient::CreateModel();
+    history_service_ = std::make_unique<TestHistoryService>();
+    test_user_share_.SetUp();
+    sync_service_ =
+        std::make_unique<TestSyncService>(test_user_share_.user_share());
+    sync_client_ = std::make_unique<TestSyncClient>(
+        bookmark_model_.get(), history_service_.get(), sync_service_.get());
+    controller_ =
+        std::make_unique<BookmarkModelTypeController>(sync_client_.get());
+  }
+
+  void TearDown() override { test_user_share_.TearDown(); }
+
+ protected:
+  BookmarkModelTypeController* controller() { return controller_.get(); }
+
+  syncer::UserShare* user_share() { return test_user_share_.user_share(); }
+
+  syncer::ModelLoadCallbackMock& model_load_callback() {
+    return model_load_callback_;
+  }
+
+  syncer::StartCallbackMock& start_callback() { return start_callback_; }
+
+  syncer::ActivationContext* activation_context() {
+    return model_type_configurer_.activation_context();
+  }
+
+  void LoadModels() {
+    controller()->LoadModels(
+        base::Bind(&syncer::ModelLoadCallbackMock::Run,
+                   base::Unretained(&model_load_callback_)));
+  }
+
+  static void CaptureBoolean(bool* value_dest, bool value) {
+    *value_dest = value;
+  }
+
+  void CallRegisterWithBackend(bool* initial_sync_done) {
+    controller()->RegisterWithBackend(
+        base::Bind(&BookmarkModelTypeControllerTest::CaptureBoolean,
+                   initial_sync_done),
+        &model_type_configurer_);
+  }
+
+  void StartAssociating() {
+    controller()->StartAssociating(base::Bind(
+        &syncer::StartCallbackMock::Run, base::Unretained(&start_callback_)));
+  }
+
+ private:
+  base::MessageLoop message_loop_;
+
+  std::unique_ptr<bookmarks::BookmarkModel> bookmark_model_;
+  std::unique_ptr<history::HistoryService> history_service_;
+  syncer::TestUserShare test_user_share_;
+  std::unique_ptr<SyncService> sync_service_;
+  std::unique_ptr<TestSyncClient> sync_client_;
+  TestModelTypeConfigurer model_type_configurer_;
+
+  syncer::ModelLoadCallbackMock model_load_callback_;
+  syncer::StartCallbackMock start_callback_;
+
+  std::unique_ptr<BookmarkModelTypeController> controller_;
+};
+
+// Tests model type and initial state of bookmarks controller.
+TEST_F(BookmarkModelTypeControllerTest, InitialState) {
+  EXPECT_EQ(syncer::BOOKMARKS, controller()->type());
+  EXPECT_EQ(DataTypeController::NOT_RUNNING, controller()->state());
+  EXPECT_TRUE(controller()->ShouldLoadModelBeforeConfigure());
+}
+
+// Tests that call to LoadModels triggers ModelLoadCallback and advances DTC
+// state.
+TEST_F(BookmarkModelTypeControllerTest, LoadModels) {
+  EXPECT_CALL(model_load_callback(), Run(_, _));
+  LoadModels();
+  EXPECT_EQ(DataTypeController::MODEL_LOADED, controller()->state());
+}
+
+// Tests that registering with backend from clean state reports that initial
+// sync is not done and progress marker is empty.
+TEST_F(BookmarkModelTypeControllerTest, RegisterWithBackend_CleanState) {
+  LoadModels();
+  bool initial_sync_done = false;
+  CallRegisterWithBackend(&initial_sync_done);
+  EXPECT_FALSE(initial_sync_done);
+  EXPECT_FALSE(activation_context()->model_type_state.initial_sync_done());
+  EXPECT_TRUE(
+      activation_context()->model_type_state.progress_marker().token().empty());
+  EXPECT_NE(nullptr, activation_context()->type_processor);
+}
+
+// Tests that registering with backend from valid state returns non-empty
+// progress marker.
+TEST_F(BookmarkModelTypeControllerTest, RegisterWithBackend) {
+  syncer::TestUserShare::CreateRoot(syncer::BOOKMARKS, user_share());
+  sync_pb::DataTypeProgressMarker progress_marker =
+      syncer::syncable::BuildProgress(syncer::BOOKMARKS);
+  user_share()->directory->SetDownloadProgress(syncer::BOOKMARKS,
+                                               progress_marker);
+  LoadModels();
+  bool initial_sync_done = false;
+  CallRegisterWithBackend(&initial_sync_done);
+  EXPECT_TRUE(initial_sync_done);
+  EXPECT_TRUE(activation_context()->model_type_state.initial_sync_done());
+  EXPECT_EQ(progress_marker.SerializeAsString(),
+            activation_context()
+                ->model_type_state.progress_marker()
+                .SerializeAsString());
+  EXPECT_NE(nullptr, activation_context()->type_processor);
+}
+
+// Tests that call to StartAssociating triggers StartCallback and adjusts DTC
+// state.
+TEST_F(BookmarkModelTypeControllerTest, StartAssociating) {
+  LoadModels();
+  EXPECT_CALL(start_callback(), Run(_, _, _));
+  StartAssociating();
+  EXPECT_EQ(DataTypeController::RUNNING, controller()->state());
+}
+
+}  // namespace sync_bookmarks
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.cc b/components/sync_bookmarks/bookmark_model_type_processor.cc
index db7ecbe..ccac311 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor.cc
+++ b/components/sync_bookmarks/bookmark_model_type_processor.cc
@@ -4,40 +4,58 @@
 
 #include "components/sync_bookmarks/bookmark_model_type_processor.h"
 
+#include <utility>
+
+#include "base/callback.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/engine/commit_queue.h"
 
 namespace sync_bookmarks {
 
-BookmarkModelTypeProcessor::BookmarkModelTypeProcessor() = default;
+BookmarkModelTypeProcessor::BookmarkModelTypeProcessor()
+    : weak_ptr_factory_(this) {}
 
 BookmarkModelTypeProcessor::~BookmarkModelTypeProcessor() = default;
 
 void BookmarkModelTypeProcessor::ConnectSync(
     std::unique_ptr<syncer::CommitQueue> worker) {
-  NOTIMPLEMENTED();
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!worker_);
+  worker_ = std::move(worker);
 }
 
 void BookmarkModelTypeProcessor::DisconnectSync() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   NOTIMPLEMENTED();
 }
 
 void BookmarkModelTypeProcessor::GetLocalChanges(
     size_t max_entries,
     const GetLocalChangesCallback& callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  syncer::CommitRequestDataList local_changes;
+  callback.Run(std::move(local_changes));
   NOTIMPLEMENTED();
 }
 
 void BookmarkModelTypeProcessor::OnCommitCompleted(
     const sync_pb::ModelTypeState& type_state,
     const syncer::CommitResponseDataList& response_list) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   NOTIMPLEMENTED();
 }
 
 void BookmarkModelTypeProcessor::OnUpdateReceived(
     const sync_pb::ModelTypeState& model_type_state,
     const syncer::UpdateResponseDataList& updates) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   NOTIMPLEMENTED();
 }
 
+base::WeakPtr<syncer::ModelTypeProcessor>
+BookmarkModelTypeProcessor::GetWeakPtr() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
 }  // namespace sync_bookmarks
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.h b/components/sync_bookmarks/bookmark_model_type_processor.h
index 5ced433..3a32a958 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor.h
+++ b/components/sync_bookmarks/bookmark_model_type_processor.h
@@ -8,6 +8,8 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
 #include "components/sync/engine/model_type_processor.h"
 
 namespace sync_bookmarks {
@@ -28,7 +30,15 @@
   void OnUpdateReceived(const sync_pb::ModelTypeState& type_state,
                         const syncer::UpdateResponseDataList& updates) override;
 
+  base::WeakPtr<syncer::ModelTypeProcessor> GetWeakPtr();
+
  private:
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  std::unique_ptr<syncer::CommitQueue> worker_;
+
+  base::WeakPtrFactory<BookmarkModelTypeProcessor> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(BookmarkModelTypeProcessor);
 };
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index f0862ec..1b41d55 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1596,11 +1596,11 @@
 void RenderWidgetHostViewAura::GetHitTestMask(gfx::Path* mask) const {
 }
 
-void RenderWidgetHostViewAura::OnWindowSurfaceChanged(
+void RenderWidgetHostViewAura::OnFirstSurfaceActivation(
     const viz::SurfaceInfo& surface_info) {
   if (!is_guest_view_hack_)
     return;
-  host_->GetView()->OnSurfaceChanged(surface_info);
+  host_->GetView()->OnFirstSurfaceActivation(surface_info);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index c0c8387..a6a4297 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -256,7 +256,7 @@
   void OnWindowTargetVisibilityChanged(bool visible) override;
   bool HasHitTestMask() const override;
   void GetHitTestMask(gfx::Path* mask) const override;
-  void OnWindowSurfaceChanged(const viz::SurfaceInfo& surface_info) override;
+  void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override;
 
   // Overridden from ui::EventHandler:
   void OnKeyEvent(ui::KeyEvent* event) override;
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index 8b1c651..b555878d 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -235,7 +235,7 @@
       cc::CompositorFrame frame) = 0;
 
   virtual void OnDidNotProduceFrame(const viz::BeginFrameAck& ack) {}
-  virtual void OnSurfaceChanged(const viz::SurfaceInfo& surface_info) {}
+  virtual void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) {}
 
   // This method exists to allow removing of displayed graphics, after a new
   // page has been loaded, to prevent the displayed URL from being out of sync
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index 2fef84d..dd2ad3e 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -538,12 +538,6 @@
   support_->DidNotProduceFrame(ack);
 }
 
-void RenderWidgetHostViewChildFrame::OnSurfaceChanged(
-    const viz::SurfaceInfo& surface_info) {
-  viz::SurfaceSequence sequence(frame_sink_id_, next_surface_sequence_++);
-  SendSurfaceInfoToEmbedderImpl(surface_info, sequence);
-}
-
 void RenderWidgetHostViewChildFrame::ProcessFrameSwappedCallbacks() {
   // We only use callbacks once, therefore we make a new list for registration
   // before we start, and discard the old list entries when we are done.
@@ -786,8 +780,8 @@
 
 void RenderWidgetHostViewChildFrame::OnFirstSurfaceActivation(
     const viz::SurfaceInfo& surface_info) {
-  // TODO(fsamuel): Once surface synchronization is turned on, the fallback
-  // surface should be set here.
+  viz::SurfaceSequence sequence(frame_sink_id_, next_surface_sequence_++);
+  SendSurfaceInfoToEmbedderImpl(surface_info, sequence);
 }
 
 void RenderWidgetHostViewChildFrame::SetNeedsBeginFrames(
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.h b/content/browser/renderer_host/render_widget_host_view_child_frame.h
index 5af88f7..96e11aa 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.h
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.h
@@ -121,7 +121,6 @@
   void SubmitCompositorFrame(const viz::LocalSurfaceId& local_surface_id,
                              cc::CompositorFrame frame) override;
   void OnDidNotProduceFrame(const viz::BeginFrameAck& ack) override;
-  void OnSurfaceChanged(const viz::SurfaceInfo& surface_info) override;
   // Since the URL of content rendered by this class is not displayed in
   // the URL bar, this method does not need an implementation.
   void ClearCompositorFrame() override {}
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.cpp b/third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.cpp
index d415cd8..6a292fa 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.cpp
@@ -21,10 +21,6 @@
 #include "platform/instrumentation/tracing/TraceEvent.h"
 #include "v8/include/v8.h"
 
-#if defined(MEMORY_SANITIZER)
-#include <sanitizer/msan_interface.h>  // NOLINT
-#endif
-
 namespace blink {
 
 namespace {
@@ -327,12 +323,6 @@
   v8::StartupData blob =
       creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
 
-#if defined(MEMORY_SANITIZER)
-  // Tell MSan to ignore uninitialized padding in the blob.
-  // TODO(crbug.com/v8/3645): Remove this hack when the issue is resolved.
-  __msan_unpoison(blob.data, blob.raw_size);
-#endif
-
   return blob;
 }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/PerformanceMonitor.js b/third_party/WebKit/Source/devtools/front_end/timeline/PerformanceMonitor.js
index 1f619db..1703d2e5 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/PerformanceMonitor.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/PerformanceMonitor.js
@@ -274,10 +274,10 @@
    */
   onResize() {
     super.onResize();
-    this._width = this._canvas.offsetWidth * window.devicePixelRatio;
-    this._height = this._canvas.offsetHeight * window.devicePixelRatio;
-    this._canvas.width = this._width;
-    this._canvas.height = this._height;
+    this._width = this._canvas.offsetWidth;
+    this._height = this._canvas.offsetHeight;
+    this._canvas.width = this._width * window.devicePixelRatio;
+    this._canvas.height = this._height * window.devicePixelRatio;
     this._draw();
   }
 };
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index 73be29adb..cdf055e 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -337,6 +337,8 @@
 
   fallback_surface_info_ = surface_info;
   UpdateClientSurfaceEmbedder();
+  if (window_->delegate())
+    window_->delegate()->OnFirstSurfaceActivation(fallback_surface_info_);
 }
 
 void WindowPortMus::DestroyFromServer() {
@@ -591,8 +593,6 @@
       viz::SurfaceId(frame_sink_id_, local_surface_id_),
       ScaleFactorForDisplay(window_), last_surface_size_in_pixels_);
   UpdateClientSurfaceEmbedder();
-  if (window_->delegate())
-    window_->delegate()->OnWindowSurfaceChanged(primary_surface_info_);
 }
 
 void WindowPortMus::UpdateClientSurfaceEmbedder() {
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index 85f2444..9fb47c7 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -212,11 +212,35 @@
                         WindowTreeClientWmTestSurfaceSync,
                         ::testing::Bool());
 
+namespace {
+
+class FirstSurfaceActivationWindowDelegate : public test::TestWindowDelegate {
+ public:
+  FirstSurfaceActivationWindowDelegate() = default;
+  ~FirstSurfaceActivationWindowDelegate() override = default;
+
+  const viz::SurfaceInfo& last_surface_info() const {
+    return last_surface_info_;
+  }
+
+  void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override {
+    last_surface_info_ = surface_info;
+  }
+
+ private:
+  viz::SurfaceInfo last_surface_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(FirstSurfaceActivationWindowDelegate);
+};
+
+}  // namespace
+
 // Verifies that a ClientSurfaceEmbedder is created for a window once it has
 // a bounds, and a valid FrameSinkId.
 TEST_P(WindowTreeClientWmTestSurfaceSync,
        ClientSurfaceEmbedderOnValidEmbedding) {
-  Window window(nullptr);
+  FirstSurfaceActivationWindowDelegate delegate;
+  Window window(&delegate);
   // TOP_LEVEL_IN_WM and EMBED_IN_OWNER windows allocate viz::LocalSurfaceIds
   // when their sizes change.
   window.SetProperty(aura::client::kEmbedType,
@@ -246,6 +270,7 @@
   ClientSurfaceEmbedder* client_surface_embedder =
       window_port_mus->client_surface_embedder();
   ASSERT_NE(nullptr, client_surface_embedder);
+  EXPECT_FALSE(delegate.last_surface_info().is_valid());
 
   // Until the fallback surface fills the window, we will have gutter.
   {
@@ -262,6 +287,9 @@
   // client lib. This should cause the gutter to go away, eliminating overdraw.
   window_tree_client()->OnWindowSurfaceChanged(
       server_id(&window), window_port_mus->PrimarySurfaceInfoForTesting());
+  EXPECT_TRUE(delegate.last_surface_info().is_valid());
+  EXPECT_EQ(delegate.last_surface_info(),
+            window_port_mus->PrimarySurfaceInfoForTesting());
 
   // The gutter is gone.
   ASSERT_EQ(nullptr, client_surface_embedder->BottomGutterForTesting());
diff --git a/ui/aura/window_delegate.h b/ui/aura/window_delegate.h
index 4ef1c7c..eac0589b 100644
--- a/ui/aura/window_delegate.h
+++ b/ui/aura/window_delegate.h
@@ -98,7 +98,9 @@
   // above returns true.
   virtual void GetHitTestMask(gfx::Path* mask) const = 0;
 
-  virtual void OnWindowSurfaceChanged(const viz::SurfaceInfo& surface_info) {}
+  // Called when a child submits a CompositorFrame to a surface with the given
+  // |surface_info| for the first time.
+  virtual void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) {}
 
  protected:
   ~WindowDelegate() override {}
diff --git a/ui/views/accessibility/ax_aura_obj_cache.cc b/ui/views/accessibility/ax_aura_obj_cache.cc
index 5128edc1..1a0cf7f 100644
--- a/ui/views/accessibility/ax_aura_obj_cache.cc
+++ b/ui/views/accessibility/ax_aura_obj_cache.cc
@@ -19,6 +19,12 @@
 
 namespace views {
 
+aura::client::FocusClient* GetFocusClient(aura::Window* root_window) {
+  if (!root_window)
+    return nullptr;
+  return aura::client::GetFocusClient(root_window);
+}
+
 // static
 AXAuraObjCache* AXAuraObjCache::GetInstance() {
   return base::Singleton<AXAuraObjCache>::get();
@@ -33,24 +39,6 @@
 }
 
 AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(aura::Window* window) {
-  if (!focus_client_) {
-    // Note: On Chrome OS, there's exactly one root window per screen,
-    // it's the same as ash::Shell::Get()->GetPrimaryRootWindow() when
-    // there's only one screen. Observing the root window allows us to
-    // detect any time a window is added or removed.
-    //
-    // TODO(dmazzoni): Explicitly observe each root window on Chrome OS
-    // and track as root windows are added and removed.
-    // http://crbug.com/713278
-    aura::Window* root_window = window->GetRootWindow();
-    if (root_window && root_window != root_window_) {
-      root_window_ = root_window;
-      focus_client_ = aura::client::GetFocusClient(root_window);
-      root_window->AddObserver(this);
-      if (focus_client_)
-        focus_client_->AddObserver(this);
-    }
-  }
   return CreateInternal<AXWindowObjWrapper>(window, window_to_id_map_);
 }
 
@@ -141,7 +129,6 @@
 
 AXAuraObjCache::AXAuraObjCache()
     : current_id_(1),
-      focus_client_(nullptr),
       is_destroying_(false),
       delegate_(nullptr),
       root_window_(nullptr) {}
@@ -152,10 +139,11 @@
 }
 
 View* AXAuraObjCache::GetFocusedView() {
-  if (!focus_client_)
+  aura::client::FocusClient* focus_client = GetFocusClient(root_window_);
+  if (!focus_client)
     return nullptr;
 
-  aura::Window* focused_window = focus_client_->GetFocusedWindow();
+  aura::Window* focused_window = focus_client->GetFocusedWindow();
   if (!focused_window)
     return nullptr;
 
@@ -193,19 +181,16 @@
   OnFocusedViewChanged();
 }
 
-void AXAuraObjCache::OnWindowDestroying(aura::Window* window) {
-  focus_client_ = nullptr;
-  root_window_ = nullptr;
-  window->RemoveObserver(this);
+void AXAuraObjCache::OnRootWindowObjCreated(aura::Window* window) {
+  root_window_ = window;
+  if (GetFocusClient(window))
+    GetFocusClient(window)->AddObserver(this);
 }
 
-void AXAuraObjCache::OnWindowHierarchyChanged(
-    const HierarchyChangeParams& params) {
-  aura::Window* window = params.target;
-  if (window->parent()) {
-    delegate_->OnEvent(GetOrCreate(window->parent()),
-                       ui::AX_EVENT_CHILDREN_CHANGED);
-  }
+void AXAuraObjCache::OnRootWindowObjDestroyed(aura::Window* window) {
+  if (GetFocusClient(window))
+    GetFocusClient(window)->RemoveObserver(this);
+  root_window_ = nullptr;
 }
 
 template <typename AuraViewWrapper, typename AuraView>
diff --git a/ui/views/accessibility/ax_aura_obj_cache.h b/ui/views/accessibility/ax_aura_obj_cache.h
index db617f0..bbd65e4 100644
--- a/ui/views/accessibility/ax_aura_obj_cache.h
+++ b/ui/views/accessibility/ax_aura_obj_cache.h
@@ -14,7 +14,6 @@
 #include "base/macros.h"
 #include "ui/accessibility/ax_enums.h"
 #include "ui/aura/client/focus_change_observer.h"
-#include "ui/aura/window_observer.h"
 #include "ui/views/views_export.h"
 
 namespace base {
@@ -22,9 +21,6 @@
 }
 
 namespace aura {
-namespace client {
-class FocusClient;
-}
 class Window;
 }  // namespace aura
 
@@ -34,9 +30,7 @@
 class Widget;
 
 // A cache responsible for assigning id's to a set of interesting Aura views.
-class VIEWS_EXPORT AXAuraObjCache
-    : public aura::client::FocusChangeObserver,
-      public aura::WindowObserver {
+class VIEWS_EXPORT AXAuraObjCache : public aura::client::FocusChangeObserver {
  public:
   // Get the single instance of this class.
   static AXAuraObjCache* GetInstance();
@@ -94,6 +88,12 @@
   // Indicates if this object's currently being destroyed.
   bool is_destroying() { return is_destroying_; }
 
+  // Notifies this cache of a change in root window.
+  void OnRootWindowObjCreated(aura::Window* window);
+
+  // Notifies this cache of a change in root window.
+  void OnRootWindowObjDestroyed(aura::Window* window);
+
   void SetDelegate(Delegate* delegate) { delegate_ = delegate; }
 
  private:
@@ -108,10 +108,6 @@
   void OnWindowFocused(aura::Window* gained_focus,
                        aura::Window* lost_focus) override;
 
-  // aura::WindowObserver override.
-  void OnWindowDestroying(aura::Window* window) override;
-  void OnWindowHierarchyChanged(const HierarchyChangeParams& params) override;
-
   template <typename AuraViewWrapper, typename AuraView>
   AXAuraObjWrapper* CreateInternal(
       AuraView* aura_view,
@@ -133,8 +129,6 @@
   std::map<int32_t, std::unique_ptr<AXAuraObjWrapper>> cache_;
   int32_t current_id_;
 
-  aura::client::FocusClient* focus_client_;
-
   // True immediately when entering this object's destructor.
   bool is_destroying_;
 
diff --git a/ui/views/accessibility/ax_window_obj_wrapper.cc b/ui/views/accessibility/ax_window_obj_wrapper.cc
index 73d3935..863046a 100644
--- a/ui/views/accessibility/ax_window_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_window_obj_wrapper.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/accessibility/platform/aura_window_properties.h"
+#include "ui/aura/client/focus_client.h"
 #include "ui/aura/window.h"
 #include "ui/views/accessibility/ax_aura_obj_cache.h"
 #include "ui/views/widget/widget.h"
@@ -19,15 +20,17 @@
     : window_(window),
       is_alert_(false),
       is_root_window_(window->IsRootWindow()) {
-  // Root windows get observed by AXAuraObjCache, so skip observing them here.
-  if (!is_root_window_)
-    window->AddObserver(this);
+  window->AddObserver(this);
+
+  if (is_root_window_)
+    AXAuraObjCache::GetInstance()->OnRootWindowObjCreated(window);
 }
 
 AXWindowObjWrapper::~AXWindowObjWrapper() {
-  // Root windows get observed by AXAuraObjCache, so skip them here.
-  if (!is_root_window_)
-    window_->RemoveObserver(this);
+  if (is_root_window_)
+    AXAuraObjCache::GetInstance()->OnRootWindowObjDestroyed(window_);
+
+  window_->RemoveObserver(this);
   window_ = NULL;
 }