Commit 7d5f0ee8 authored by erikchen's avatar erikchen Committed by Commit Bot

Add interface and Mac implementation for responsiveness::NativeEventObserver.

NativeEventObserver forwards will_run and did_run callbacks from the native
event processor to responsiveness::Watcher.

This CL also adds the macOS implementation. This requires modifying
BrowserCrApplication and ShellCrApplication to support a new protocol:
NativeEventProcessor, which allows observers to register for event will_run and
did_run callbacks.

Bug: 859155
Change-Id: Ie2db48efb4a93377ad54e91cbdb376d990b25f11
Reviewed-on: https://chromium-review.googlesource.com/1157235
Commit-Queue: Erik Chen <erikchen@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Reviewed-by: default avatarTimothy Dresser <tdresser@chromium.org>
Cr-Commit-Position: refs/heads/master@{#580345}
parent b1a0e716
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/call_with_eh_frame.h" #include "base/mac/call_with_eh_frame.h"
#include "base/observer_list.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
...@@ -18,6 +19,8 @@ ...@@ -18,6 +19,8 @@
#include "components/crash/core/common/crash_key.h" #include "components/crash/core/common/crash_key.h"
#import "components/crash/core/common/objc_zombie.h" #import "components/crash/core/common/objc_zombie.h"
#include "content/public/browser/browser_accessibility_state.h" #include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/native_event_processor_mac.h"
#include "content/public/browser/native_event_processor_observer_mac.h"
namespace chrome_browser_application_mac { namespace chrome_browser_application_mac {
...@@ -97,6 +100,11 @@ std::string DescriptionForNSEvent(NSEvent* event) { ...@@ -97,6 +100,11 @@ std::string DescriptionForNSEvent(NSEvent* event) {
- (void)_cycleWindowsReversed:(BOOL)arg1; - (void)_cycleWindowsReversed:(BOOL)arg1;
@end @end
@interface BrowserCrApplication ()<NativeEventProcessor> {
base::ObserverList<content::NativeEventProcessorObserver> observers_;
}
@end
@implementation BrowserCrApplication @implementation BrowserCrApplication
+ (void)initialize { + (void)initialize {
...@@ -325,6 +333,8 @@ std::string DescriptionForNSEvent(NSEvent* event) { ...@@ -325,6 +333,8 @@ std::string DescriptionForNSEvent(NSEvent* event) {
default: { default: {
base::mac::ScopedSendingEvent sendingEventScoper; base::mac::ScopedSendingEvent sendingEventScoper;
content::ScopedNotifyNativeEventProcessorObserver
scopedObserverNotifier(&observers_, event);
[super sendEvent:event]; [super sendEvent:event];
} }
} }
...@@ -353,4 +363,14 @@ std::string DescriptionForNSEvent(NSEvent* event) { ...@@ -353,4 +363,14 @@ std::string DescriptionForNSEvent(NSEvent* event) {
return cyclingWindows_; return cyclingWindows_;
} }
- (void)addNativeEventProcessorObserver:
(content::NativeEventProcessorObserver*)observer {
observers_.AddObserver(observer);
}
- (void)removeNativeEventProcessorObserver:
(content::NativeEventProcessorObserver*)observer {
observers_.RemoveObserver(observer);
}
@end @end
...@@ -1522,6 +1522,9 @@ jumbo_source_set("browser") { ...@@ -1522,6 +1522,9 @@ jumbo_source_set("browser") {
"scheduler/responsiveness/calculator.h", "scheduler/responsiveness/calculator.h",
"scheduler/responsiveness/message_loop_observer.cc", "scheduler/responsiveness/message_loop_observer.cc",
"scheduler/responsiveness/message_loop_observer.h", "scheduler/responsiveness/message_loop_observer.h",
"scheduler/responsiveness/native_event_observer.cc",
"scheduler/responsiveness/native_event_observer.h",
"scheduler/responsiveness/native_event_observer_mac.mm",
"scheduler/responsiveness/watcher.cc", "scheduler/responsiveness/watcher.cc",
"scheduler/responsiveness/watcher.h", "scheduler/responsiveness/watcher.h",
"scoped_active_url.cc", "scoped_active_url.cc",
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
namespace content {
namespace responsiveness { namespace responsiveness {
namespace { namespace {
...@@ -189,28 +190,25 @@ Calculator::JankList& Calculator::GetJanksOnIOThread() { ...@@ -189,28 +190,25 @@ Calculator::JankList& Calculator::GetJanksOnIOThread() {
Calculator::JankList Calculator::TakeJanksOlderThanTime( Calculator::JankList Calculator::TakeJanksOlderThanTime(
JankList* janks, JankList* janks,
base::TimeTicks end_time) { base::TimeTicks end_time) {
// Copy all janks with Jank.start_time < |end_time|. // Find all janks with Jank.start_time < |end_time|.
auto it = auto it = std::partition(
std::lower_bound(janks->begin(), janks->end(), end_time, janks->begin(), janks->end(),
[](const Jank& jank, const base::TimeTicks& end_time) { [&end_time](const Jank& jank) { return jank.start_time < end_time; });
return jank.start_time < end_time;
}); // Early exit. We don't need to remove any Janks either, since Jank.end_time
// >= Jank.start_time.
// We don't need to remove any Janks either, since Jank.end_time >=
// Jank.start_time.
if (it == janks->begin()) if (it == janks->begin())
return JankList(); return JankList();
JankList janks_to_return(janks->begin(), it); JankList janks_to_return(janks->begin(), it);
// Remove all janks with Jank.end_time < |end_time|. // Remove all janks with Jank.end_time < |end_time|.
auto first_jank_to_keep = auto first_jank_to_keep = std::partition(
std::lower_bound(janks->begin(), janks->end(), end_time, janks->begin(), janks->end(),
[](const Jank& jank, const base::TimeTicks& end_time) { [&end_time](const Jank& jank) { return jank.end_time < end_time; });
return jank.end_time < end_time;
});
janks->erase(janks->begin(), first_jank_to_keep); janks->erase(janks->begin(), first_jank_to_keep);
return janks_to_return; return janks_to_return;
} }
} // namespace responsiveness } // namespace responsiveness
} // namespace content
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
namespace content {
namespace responsiveness { namespace responsiveness {
// This class receives execution latency on events and tasks, and uses that to // This class receives execution latency on events and tasks, and uses that to
...@@ -26,6 +27,10 @@ class CONTENT_EXPORT Calculator { ...@@ -26,6 +27,10 @@ class CONTENT_EXPORT Calculator {
// Must be called from the UI thread. // Must be called from the UI thread.
// virtual for testing. // virtual for testing.
// The implementation will gracefully handle calls where finish_time <
// schedule_time.
// The implementation will gracefully handle successive calls with
// |schedule_times| that are out of order.
virtual void TaskOrEventFinishedOnUIThread(base::TimeTicks schedule_time, virtual void TaskOrEventFinishedOnUIThread(base::TimeTicks schedule_time,
base::TimeTicks finish_time); base::TimeTicks finish_time);
...@@ -84,8 +89,9 @@ class CONTENT_EXPORT Calculator { ...@@ -84,8 +89,9 @@ class CONTENT_EXPORT Calculator {
// already been taken. May be called from any thread. // already been taken. May be called from any thread.
JankList& GetJanksOnIOThread(); JankList& GetJanksOnIOThread();
// |janks| must be sorted by Jank.end_time. This method modifies |janks| to // This method:
// remove all janks older than |end_time|, and returns those. // 1) Removes all Janks with Jank.end_time < |end_time| from |janks|.
// 2) Returns all Janks with Jank.start_time < |end_time|.
JankList TakeJanksOlderThanTime(JankList* janks, base::TimeTicks end_time); JankList TakeJanksOlderThanTime(JankList* janks, base::TimeTicks end_time);
// This should only be accessed via the accessor, which checks that the caller // This should only be accessed via the accessor, which checks that the caller
...@@ -119,5 +125,6 @@ class CONTENT_EXPORT Calculator { ...@@ -119,5 +125,6 @@ class CONTENT_EXPORT Calculator {
}; };
} // namespace responsiveness } // namespace responsiveness
} // namespace content
#endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_CALCULATOR_H_ #endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_CALCULATOR_H_
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace responsiveness { namespace responsiveness {
namespace { namespace {
...@@ -211,4 +212,24 @@ TEST_F(ResponsivenessCalculatorTest, EventCrossesBoundary) { ...@@ -211,4 +212,24 @@ TEST_F(ResponsivenessCalculatorTest, EventCrossesBoundary) {
EXPECT_EQ(2, calculator_->Emissions()[1]); EXPECT_EQ(2, calculator_->Emissions()[1]);
} }
// Events may not be ordered by start or end time.
TEST_F(ResponsivenessCalculatorTest, UnorderedEvents) {
// We add the following events:
// [100, 250]
// [150, 300]
// [50, 200]
// [50, 390]
// The event [50, 400] subsumes all previous events.
AddEventUI(kJankThresholdInMs, 2.5 * kJankThresholdInMs);
AddEventUI(1.5 * kJankThresholdInMs, 3 * kJankThresholdInMs);
AddEventUI(0.5 * kJankThresholdInMs, 2 * kJankThresholdInMs);
AddEventUI(0.5 * kJankThresholdInMs, 3.9 * kJankThresholdInMs);
TriggerCalculation();
ASSERT_EQ(1u, calculator_->Emissions().size());
EXPECT_EQ(3, calculator_->Emissions()[0]);
}
} // namespace responsiveness } // namespace responsiveness
} // namespace content
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "content/browser/scheduler/responsiveness/message_loop_observer.h" #include "content/browser/scheduler/responsiveness/message_loop_observer.h"
namespace content {
namespace responsiveness { namespace responsiveness {
MessageLoopObserver::MessageLoopObserver(TaskCallback will_run_task_callback, MessageLoopObserver::MessageLoopObserver(TaskCallback will_run_task_callback,
...@@ -30,3 +31,4 @@ void MessageLoopObserver::DidProcessTask( ...@@ -30,3 +31,4 @@ void MessageLoopObserver::DidProcessTask(
} }
} // namespace responsiveness } // namespace responsiveness
} // namespace content
...@@ -13,6 +13,7 @@ namespace base { ...@@ -13,6 +13,7 @@ namespace base {
struct PendingTask; struct PendingTask;
} // namespace base } // namespace base
namespace content {
namespace responsiveness { namespace responsiveness {
// This object is not thread safe. It must be constructed and destroyed on the // This object is not thread safe. It must be constructed and destroyed on the
...@@ -41,5 +42,6 @@ class CONTENT_EXPORT MessageLoopObserver ...@@ -41,5 +42,6 @@ class CONTENT_EXPORT MessageLoopObserver
}; };
} // namespace responsiveness } // namespace responsiveness
} // namespace content
#endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_MESSAGE_LOOP_OBSERVER_H_ #endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_MESSAGE_LOOP_OBSERVER_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/scheduler/responsiveness/native_event_observer.h"
namespace content {
namespace responsiveness {
NativeEventObserver::NativeEventObserver(
WillRunEventCallback will_run_event_callback,
DidRunEventCallback did_run_event_callback)
: will_run_event_callback_(will_run_event_callback),
did_run_event_callback_(did_run_event_callback) {
RegisterObserver();
}
NativeEventObserver::~NativeEventObserver() {
DeregisterObserver();
}
#if !defined(OS_MACOSX)
// TODO(erikchen): Implement this for non-macOS platforms.
// https://crbug.com/859155.
void NativeEventObserver::RegisterObserver() {}
void NativeEventObserver::DeregisterObserver() {}
#endif
} // namespace responsiveness
} // namespace content
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_NATIVE_EVENT_OBSERVER_H_
#define CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_NATIVE_EVENT_OBSERVER_H_
#include "base/callback.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "content/common/content_export.h"
#if defined(OS_MACOSX)
#include "content/public/browser/native_event_processor_observer_mac.h"
#endif
namespace content {
namespace responsiveness {
// This class must only be used from the UI thread.
//
// This class hooks itself into the native event processor for the platform and
// forwards will_run/did_run callbacks to Watcher. Native events are processed
// at different layers for each platform, so the interface for this class is
// necessarily messy.
//
// On macOS, the hook should be in -[BrowserCrApplication sendEvent:].
// On Linux, the hook should be in ui::PlatformEventSource::DispatchEvent.
// On Windows, the hook should be in MessagePumpForUI::ProcessMessageHelper.
// On Android, the hook should be in <TBD>.
class CONTENT_EXPORT NativeEventObserver
#if defined(OS_MACOSX)
: public NativeEventProcessorObserver
#endif
{
public:
using WillRunEventCallback =
base::RepeatingCallback<void(const void* opaque_identifier)>;
// |creation_time| refers to the time at which the native event was created.
using DidRunEventCallback =
base::RepeatingCallback<void(const void* opaque_identifier,
base::TimeTicks creation_time)>;
// The constructor will register the object as an observer of the native event
// processor. The destructor will unregister the object.
NativeEventObserver(WillRunEventCallback will_run_event_callback,
DidRunEventCallback did_run_event_callback);
virtual ~NativeEventObserver();
protected:
#if defined(OS_MACOSX)
// NativeEventProcessorObserver overrides:
// Exposed for tests.
void WillRunNativeEvent(const void* opaque_identifier) override;
void DidRunNativeEvent(const void* opaque_identifier,
base::TimeTicks creation_time) override;
#endif
private:
void RegisterObserver();
void DeregisterObserver();
WillRunEventCallback will_run_event_callback_;
DidRunEventCallback did_run_event_callback_;
DISALLOW_COPY_AND_ASSIGN(NativeEventObserver);
};
} // namespace responsiveness
} // namespace content
#endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_NATIVE_EVENT_OBSERVER_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/scheduler/responsiveness/native_event_observer.h"
#include "base/bind_helpers.h"
#include "content/public/test/content_browser_test.h"
#include "ui/events/test/cocoa_test_event_utils.h"
#import <Carbon/Carbon.h>
namespace content {
namespace responsiveness {
namespace {
class FakeNativeEventObserver : public NativeEventObserver {
public:
FakeNativeEventObserver()
: NativeEventObserver(base::DoNothing(), base::DoNothing()) {}
~FakeNativeEventObserver() override = default;
void WillRunNativeEvent(const void* opaque_identifier) override {
ASSERT_FALSE(will_run_id_);
will_run_id_ = opaque_identifier;
}
void DidRunNativeEvent(const void* opaque_identifier,
base::TimeTicks creation_time) override {
ASSERT_FALSE(did_run_id_);
did_run_id_ = opaque_identifier;
creation_time_ = creation_time;
}
const void* will_run_id() { return will_run_id_; }
const void* did_run_id() { return did_run_id_; }
base::TimeTicks creation_time() { return creation_time_; }
private:
const void* will_run_id_ = nullptr;
const void* did_run_id_ = nullptr;
base::TimeTicks creation_time_;
};
} // namespace
class ResponsivenessNativeEventObserverBrowserTest : public ContentBrowserTest {
};
IN_PROC_BROWSER_TEST_F(ResponsivenessNativeEventObserverBrowserTest,
EventForwarding) {
FakeNativeEventObserver observer;
EXPECT_FALSE(observer.will_run_id());
EXPECT_FALSE(observer.did_run_id());
base::TimeTicks time_at_creation = base::TimeTicks::Now();
NSEvent* event = cocoa_test_event_utils::KeyEventWithKeyCode(kVK_Return, '\r',
NSKeyDown, 0);
[NSApp sendEvent:event];
EXPECT_EQ(observer.will_run_id(), event);
EXPECT_EQ(observer.did_run_id(), event);
// time_at_creation should be really similar to creation_time. As a sanity
// check, make sure they're within a second of each other.
EXPECT_LT(
fabs((observer.creation_time() - time_at_creation).InMilliseconds()),
1000);
}
} // namespace responsiveness
} // namespace content
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/scheduler/responsiveness/native_event_observer.h"
#import <AppKit/AppKit.h>
#import "content/public/browser/native_event_processor_mac.h"
namespace content {
namespace responsiveness {
void NativeEventObserver::RegisterObserver() {
DCHECK([NSApp conformsToProtocol:@protocol(NativeEventProcessor)]);
id<NativeEventProcessor> processor =
static_cast<id<NativeEventProcessor>>(NSApp);
[processor addNativeEventProcessorObserver:this];
}
void NativeEventObserver::DeregisterObserver() {
DCHECK([NSApp conformsToProtocol:@protocol(NativeEventProcessor)]);
id<NativeEventProcessor> processor =
static_cast<id<NativeEventProcessor>>(NSApp);
[processor removeNativeEventProcessorObserver:this];
}
void NativeEventObserver::WillRunNativeEvent(const void* opaque_identifier) {
will_run_event_callback_.Run(opaque_identifier);
}
void NativeEventObserver::DidRunNativeEvent(const void* opaque_identifier,
base::TimeTicks creation_time) {
did_run_event_callback_.Run(opaque_identifier, creation_time);
}
} // namespace responsiveness
} // namespace content
...@@ -7,8 +7,10 @@ ...@@ -7,8 +7,10 @@
#include "base/pending_task.h" #include "base/pending_task.h"
#include "content/browser/scheduler/responsiveness/calculator.h" #include "content/browser/scheduler/responsiveness/calculator.h"
#include "content/browser/scheduler/responsiveness/message_loop_observer.h" #include "content/browser/scheduler/responsiveness/message_loop_observer.h"
#include "content/browser/scheduler/responsiveness/native_event_observer.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
namespace content {
namespace responsiveness { namespace responsiveness {
Watcher::Metadata::Metadata(const void* identifier) : identifier(identifier) {} Watcher::Metadata::Metadata(const void* identifier) : identifier(identifier) {}
...@@ -26,7 +28,8 @@ void Watcher::SetUp() { ...@@ -26,7 +28,8 @@ void Watcher::SetUp() {
// and destruction. // and destruction.
AddRef(); AddRef();
calculator_ = MakeCalculator(); calculator_ = CreateCalculator();
native_event_observer_ui_ = CreateNativeEventObserver();
RegisterMessageLoopObserverUI(); RegisterMessageLoopObserverUI();
...@@ -43,16 +46,28 @@ void Watcher::Destroy() { ...@@ -43,16 +46,28 @@ void Watcher::Destroy() {
destroy_was_called_ = true; destroy_was_called_ = true;
message_loop_observer_ui_.reset(); message_loop_observer_ui_.reset();
native_event_observer_ui_.reset();
content::BrowserThread::PostTask( content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE, content::BrowserThread::IO, FROM_HERE,
base::BindOnce(&Watcher::TearDownOnIOThread, base::Unretained(this))); base::BindOnce(&Watcher::TearDownOnIOThread, base::Unretained(this)));
} }
std::unique_ptr<Calculator> Watcher::MakeCalculator() { std::unique_ptr<Calculator> Watcher::CreateCalculator() {
return std::make_unique<Calculator>(); return std::make_unique<Calculator>();
} }
std::unique_ptr<NativeEventObserver> Watcher::CreateNativeEventObserver() {
NativeEventObserver::WillRunEventCallback will_run_callback =
base::BindRepeating(&Watcher::WillRunEventOnUIThread,
base::Unretained(this));
NativeEventObserver::DidRunEventCallback did_run_callback =
base::BindRepeating(&Watcher::DidRunEventOnUIThread,
base::Unretained(this));
return std::make_unique<NativeEventObserver>(std::move(will_run_callback),
std::move(did_run_callback));
}
Watcher::~Watcher() { Watcher::~Watcher() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(destroy_was_called_); DCHECK(destroy_was_called_);
...@@ -162,8 +177,8 @@ void Watcher::DidRunTask(const base::PendingTask* task, ...@@ -162,8 +177,8 @@ void Watcher::DidRunTask(const base::PendingTask* task,
TaskOrEventFinishedCallback callback) { TaskOrEventFinishedCallback callback) {
// Calls to DidRunTask should always be paired with WillRunTask. The only time // Calls to DidRunTask should always be paired with WillRunTask. The only time
// the identifier should differ is when Watcher is first constructed. The // the identifier should differ is when Watcher is first constructed. The
// TaskRunner Observers are added while a task is being run, which means that // TaskRunner Observers may be added while a task is being run, which means
// there was no corresponding WillRunTask. // that there was no corresponding WillRunTask.
if (UNLIKELY(currently_running_metadata->empty() || if (UNLIKELY(currently_running_metadata->empty() ||
(task != currently_running_metadata->top().identifier))) { (task != currently_running_metadata->top().identifier))) {
*mismatched_task_identifiers += 1; *mismatched_task_identifiers += 1;
...@@ -199,4 +214,44 @@ void Watcher::DidRunTask(const base::PendingTask* task, ...@@ -199,4 +214,44 @@ void Watcher::DidRunTask(const base::PendingTask* task,
std::move(callback).Run(schedule_time, base::TimeTicks::Now()); std::move(callback).Run(schedule_time, base::TimeTicks::Now());
} }
void Watcher::WillRunEventOnUIThread(const void* opaque_identifier) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Reentrancy should be rare.
if (UNLIKELY(!currently_running_metadata_ui_.empty())) {
currently_running_metadata_ui_.top().caused_reentrancy = true;
}
currently_running_metadata_ui_.emplace(opaque_identifier);
}
void Watcher::DidRunEventOnUIThread(const void* opaque_identifier,
base::TimeTicks creation_time) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Calls to DidRunEventOnUIThread should always be paired with
// WillRunEventOnUIThread. The only time the identifier should differ is when
// Watcher is first constructed. The TaskRunner Observers may be added while a
// task is being run, which means that there was no corresponding WillRunTask.
if (UNLIKELY(currently_running_metadata_ui_.empty() ||
(opaque_identifier !=
currently_running_metadata_ui_.top().identifier))) {
mismatched_event_identifiers_ui_ += 1;
DCHECK_LE(mismatched_event_identifiers_ui_, 1);
return;
}
bool caused_reentrancy =
currently_running_metadata_ui_.top().caused_reentrancy;
currently_running_metadata_ui_.pop();
// Ignore events that caused reentrancy, since their execution latency will
// be very large, but Chrome was still responsive.
if (UNLIKELY(caused_reentrancy))
return;
calculator_->TaskOrEventFinishedOnUIThread(creation_time,
base::TimeTicks::Now());
}
} // namespace responsiveness } // namespace responsiveness
} // namespace content
...@@ -18,10 +18,12 @@ namespace base { ...@@ -18,10 +18,12 @@ namespace base {
struct PendingTask; struct PendingTask;
} // namespace base } // namespace base
namespace content {
namespace responsiveness { namespace responsiveness {
class Calculator; class Calculator;
class MessageLoopObserver; class MessageLoopObserver;
class NativeEventObserver;
// This class watches events and tasks processed on the UI and IO threads of the // This class watches events and tasks processed on the UI and IO threads of the
// browser process. It forwards stats on execution latency to Calculator, which // browser process. It forwards stats on execution latency to Calculator, which
...@@ -50,7 +52,8 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> { ...@@ -50,7 +52,8 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> {
protected: protected:
// Exposed for tests. // Exposed for tests.
virtual std::unique_ptr<Calculator> MakeCalculator(); virtual std::unique_ptr<Calculator> CreateCalculator();
virtual std::unique_ptr<NativeEventObserver> CreateNativeEventObserver();
virtual ~Watcher(); virtual ~Watcher();
virtual void RegisterMessageLoopObserverUI(); virtual void RegisterMessageLoopObserverUI();
virtual void RegisterMessageLoopObserverIO(); virtual void RegisterMessageLoopObserverIO();
...@@ -105,18 +108,30 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> { ...@@ -105,18 +108,30 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> {
int* mismatched_task_identifiers, int* mismatched_task_identifiers,
TaskOrEventFinishedCallback callback); TaskOrEventFinishedCallback callback);
// These methods are called by the NativeEventObserver of the UI thread to
// allow Watcher to collect metadata about the events being run.
void WillRunEventOnUIThread(const void* opaque_identifier);
void DidRunEventOnUIThread(const void* opaque_identifier,
base::TimeTicks creation_time);
// The following members are all affine to the UI thread. // The following members are all affine to the UI thread.
std::unique_ptr<Calculator> calculator_; std::unique_ptr<Calculator> calculator_;
std::unique_ptr<MessageLoopObserver> message_loop_observer_ui_; std::unique_ptr<MessageLoopObserver> message_loop_observer_ui_;
std::unique_ptr<NativeEventObserver> native_event_observer_ui_;
// Metadata for currently running tasks and events on the UI thread. // Metadata for currently running tasks and events on the UI thread.
std::stack<Metadata> currently_running_metadata_ui_; std::stack<Metadata> currently_running_metadata_ui_;
// Task identifiers should only be mismatched once, since the Watcher // Task identifiers should only be mismatched once, since the Watcher may
// registers itself during a Task execution, and thus doesn't capture the // register itself during a Task execution, and thus doesn't capture the
// initial WillRunTask() callback. // initial WillRunTask() callback.
int mismatched_task_identifiers_ui_ = 0; int mismatched_task_identifiers_ui_ = 0;
// Event identifiers should be mismatched at most once, since the Watcher may
// register itself during an event execution, and thus doesn't capture the
// initial WillRunEventOnUIThread callback.
int mismatched_event_identifiers_ui_ = 0;
// The following members are all affine to the IO thread. // The following members are all affine to the IO thread.
std::stack<Metadata> currently_running_metadata_io_; std::stack<Metadata> currently_running_metadata_io_;
int mismatched_task_identifiers_io_ = 0; int mismatched_task_identifiers_io_ = 0;
...@@ -136,5 +151,6 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> { ...@@ -136,5 +151,6 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> {
}; };
} // namespace responsiveness } // namespace responsiveness
} // namespace content
#endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_WATCHER_H_ #endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_WATCHER_H_
...@@ -9,10 +9,12 @@ ...@@ -9,10 +9,12 @@
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "content/browser/scheduler/responsiveness/calculator.h" #include "content/browser/scheduler/responsiveness/calculator.h"
#include "content/browser/scheduler/responsiveness/native_event_observer.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace responsiveness { namespace responsiveness {
namespace { namespace {
...@@ -49,13 +51,17 @@ class FakeCalculator : public Calculator { ...@@ -49,13 +51,17 @@ class FakeCalculator : public Calculator {
class FakeWatcher : public Watcher { class FakeWatcher : public Watcher {
public: public:
std::unique_ptr<Calculator> MakeCalculator() override { std::unique_ptr<Calculator> CreateCalculator() override {
std::unique_ptr<FakeCalculator> calculator = std::unique_ptr<FakeCalculator> calculator =
std::make_unique<FakeCalculator>(); std::make_unique<FakeCalculator>();
calculator_ = calculator.get(); calculator_ = calculator.get();
return calculator; return calculator;
} }
std::unique_ptr<NativeEventObserver> CreateNativeEventObserver() override {
return nullptr;
}
void RegisterMessageLoopObserverUI() override { void RegisterMessageLoopObserverUI() override {
if (register_message_loop_observer_) if (register_message_loop_observer_)
Watcher::RegisterMessageLoopObserverUI(); Watcher::RegisterMessageLoopObserverUI();
...@@ -220,3 +226,4 @@ TEST_F(ResponsivenessWatcherRealIOThreadTest, MessageLoopObserver) { ...@@ -220,3 +226,4 @@ TEST_F(ResponsivenessWatcherRealIOThreadTest, MessageLoopObserver) {
} }
} // namespace responsiveness } // namespace responsiveness
} // namespace content
...@@ -168,6 +168,9 @@ jumbo_source_set("browser_sources") { ...@@ -168,6 +168,9 @@ jumbo_source_set("browser_sources") {
"memory_coordinator_delegate.h", "memory_coordinator_delegate.h",
"message_port_provider.h", "message_port_provider.h",
"mhtml_extra_parts.h", "mhtml_extra_parts.h",
"native_event_processor_mac.h",
"native_event_processor_observer_mac.h",
"native_event_processor_observer_mac.mm",
"native_web_keyboard_event.h", "native_web_keyboard_event.h",
"navigation_controller.cc", "navigation_controller.cc",
"navigation_controller.h", "navigation_controller.h",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_PUBLIC_BROWSER_NATIVE_EVENT_PROCESSOR_MAC_H_
#define CONTENT_PUBLIC_BROWSER_NATIVE_EVENT_PROCESSOR_MAC_H_
namespace content {
class NativeEventProcessorObserver;
} // namespace content
// The application's NSApplication subclass should implement this protocol to
// give observers additional information about the native events being run in
// -[NSApplication sendEvent:].
@protocol NativeEventProcessor
- (void)addNativeEventProcessorObserver:
(content::NativeEventProcessorObserver*)observer;
- (void)removeNativeEventProcessorObserver:
(content::NativeEventProcessorObserver*)observer;
@end
#endif // CONTENT_PUBLIC_BROWSER_NATIVE_EVENT_PROCESSOR_MAC_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_PUBLIC_BROWSER_NATIVE_EVENT_PROCESSOR_OBSERVER_MAC_H_
#define CONTENT_PUBLIC_BROWSER_NATIVE_EVENT_PROCESSOR_OBSERVER_MAC_H_
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "content/common/content_export.h"
#if defined(__OBJC__)
@class NSEvent;
#else // __OBJC__
class NSEvent;
#endif // __OBJC__
namespace content {
class NativeEventProcessorObserver {
public:
// Called right before a native event is run.
virtual void WillRunNativeEvent(const void* opaque_identifier) = 0;
// Called right after a native event is run.
// |creation_time| refers to the time at which the native event was created.
virtual void DidRunNativeEvent(const void* opaque_identifier,
base::TimeTicks creation_time) = 0;
};
// The constructor sends a WillRunNativeEvent callback to each observer.
// The destructor sends a DidRunNativeEvent callback to each observer.
class CONTENT_EXPORT ScopedNotifyNativeEventProcessorObserver {
public:
ScopedNotifyNativeEventProcessorObserver(
base::ObserverList<NativeEventProcessorObserver>* observer_list,
NSEvent* event);
~ScopedNotifyNativeEventProcessorObserver();
private:
base::ObserverList<NativeEventProcessorObserver>* observer_list_;
NSEvent* event_;
DISALLOW_COPY_AND_ASSIGN(ScopedNotifyNativeEventProcessorObserver);
};
} // namespace content
#endif // CONTENT_PUBLIC_BROWSER_NATIVE_EVENT_PROCESSOR_OBSERVER_MAC_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/public/browser/native_event_processor_observer_mac.h"
#import <AppKit/AppKit.h>
#include "base/observer_list.h"
#include "base/time/time.h"
namespace content {
namespace {
base::TimeTicks TimeTicksForEvent(NSEvent* event) {
// NSEvent.timestamp gives the creation time of the event in seconds
// since system startup. The baseline it should be compared agaainst is
// NSProcessInfo.systemUptime. To convert to base::TimeTicks, we take
// the difference and subtract from base::TimeTicks::Now().
// Observations:
// 1) This implementation is fast, since both systemUptime and
// base::TimeTicks::Now() use the commpage [no syscalls].
// 2) systemUptime's implementation uses mach_absolute_time() -- see
// CoreFoundation.framework. Presumably, so does NSEvent.timestamp.
// mach_absolute_time() does not advance while the machine is asleep.
NSTimeInterval current_system_uptime =
[[NSProcessInfo processInfo] systemUptime];
return base::TimeTicks::Now() +
base::TimeDelta::FromSecondsD(event.timestamp - current_system_uptime);
}
} // namespace
ScopedNotifyNativeEventProcessorObserver::
ScopedNotifyNativeEventProcessorObserver(
base::ObserverList<NativeEventProcessorObserver>* observer_list,
NSEvent* event)
: observer_list_(observer_list), event_(event) {
for (auto& observer : *observer_list_)
observer.WillRunNativeEvent(event_);
}
ScopedNotifyNativeEventProcessorObserver::
~ScopedNotifyNativeEventProcessorObserver() {
base::TimeTicks event_creation_time;
for (auto& obs : *observer_list_) {
// Compute the value in the loop to avoid doing work if there are no
// observers.
if (event_creation_time.is_null())
event_creation_time = TimeTicksForEvent(event_);
obs.DidRunNativeEvent(event_, event_creation_time);
}
}
} // namespace content
...@@ -5,12 +5,20 @@ ...@@ -5,12 +5,20 @@
#include "content/shell/browser/shell_application_mac.h" #include "content/shell/browser/shell_application_mac.h"
#include "base/auto_reset.h" #include "base/auto_reset.h"
#include "base/observer_list.h"
#include "content/public/browser/native_event_processor_mac.h"
#include "content/public/browser/native_event_processor_observer_mac.h"
#include "content/public/common/url_constants.h" #include "content/public/common/url_constants.h"
#include "content/shell/browser/shell.h" #include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_browser_context.h" #include "content/shell/browser/shell_browser_context.h"
#include "content/shell/browser/shell_content_browser_client.h" #include "content/shell/browser/shell_content_browser_client.h"
#include "url/gurl.h" #include "url/gurl.h"
@interface ShellCrApplication ()<NativeEventProcessor> {
base::ObserverList<content::NativeEventProcessorObserver> observers_;
}
@end
@implementation ShellCrApplication @implementation ShellCrApplication
- (BOOL)isHandlingSendEvent { - (BOOL)isHandlingSendEvent {
...@@ -19,6 +27,9 @@ ...@@ -19,6 +27,9 @@
- (void)sendEvent:(NSEvent*)event { - (void)sendEvent:(NSEvent*)event {
base::AutoReset<BOOL> scoper(&handlingSendEvent_, YES); base::AutoReset<BOOL> scoper(&handlingSendEvent_, YES);
content::ScopedNotifyNativeEventProcessorObserver scopedObserverNotifier(
&observers_, event);
[super sendEvent:event]; [super sendEvent:event];
} }
...@@ -35,4 +46,14 @@ ...@@ -35,4 +46,14 @@
gfx::Size()); gfx::Size());
} }
- (void)addNativeEventProcessorObserver:
(content::NativeEventProcessorObserver*)observer {
observers_.AddObserver(observer);
}
- (void)removeNativeEventProcessorObserver:
(content::NativeEventProcessorObserver*)observer {
observers_.RemoveObserver(observer);
}
@end @end
...@@ -823,6 +823,7 @@ test("content_browsertests") { ...@@ -823,6 +823,7 @@ test("content_browsertests") {
"../browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc", "../browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc",
"../browser/renderer_host/render_widget_host_view_mac_browsertest.mm", "../browser/renderer_host/render_widget_host_view_mac_browsertest.mm",
"../browser/resource_loading_browsertest.cc", "../browser/resource_loading_browsertest.cc",
"../browser/scheduler/responsiveness/native_event_observer_browsertest.mm",
"../browser/screen_orientation/screen_orientation_browsertest.cc", "../browser/screen_orientation/screen_orientation_browsertest.cc",
"../browser/security_exploit_browsertest.cc", "../browser/security_exploit_browsertest.cc",
"../browser/service_manager/service_manager_context_browsertest.cc", "../browser/service_manager/service_manager_context_browsertest.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment