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; }