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 @@
#include "base/command_line.h"
#include "base/logging.h"
#include "base/mac/call_with_eh_frame.h"
#include "base/observer_list.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/trace_event/trace_event.h"
......@@ -18,6 +19,8 @@
#include "components/crash/core/common/crash_key.h"
#import "components/crash/core/common/objc_zombie.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 {
......@@ -97,6 +100,11 @@ std::string DescriptionForNSEvent(NSEvent* event) {
- (void)_cycleWindowsReversed:(BOOL)arg1;
@end
@interface BrowserCrApplication ()<NativeEventProcessor> {
base::ObserverList<content::NativeEventProcessorObserver> observers_;
}
@end
@implementation BrowserCrApplication
+ (void)initialize {
......@@ -325,6 +333,8 @@ std::string DescriptionForNSEvent(NSEvent* event) {
default: {
base::mac::ScopedSendingEvent sendingEventScoper;
content::ScopedNotifyNativeEventProcessorObserver
scopedObserverNotifier(&observers_, event);
[super sendEvent:event];
}
}
......@@ -353,4 +363,14 @@ std::string DescriptionForNSEvent(NSEvent* event) {
return cyclingWindows_;
}
- (void)addNativeEventProcessorObserver:
(content::NativeEventProcessorObserver*)observer {
observers_.AddObserver(observer);
}
- (void)removeNativeEventProcessorObserver:
(content::NativeEventProcessorObserver*)observer {
observers_.RemoveObserver(observer);
}
@end
......@@ -1522,6 +1522,9 @@ jumbo_source_set("browser") {
"scheduler/responsiveness/calculator.h",
"scheduler/responsiveness/message_loop_observer.cc",
"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.h",
"scoped_active_url.cc",
......
......@@ -9,6 +9,7 @@
#include "content/public/browser/browser_thread.h"
namespace content {
namespace responsiveness {
namespace {
......@@ -189,28 +190,25 @@ Calculator::JankList& Calculator::GetJanksOnIOThread() {
Calculator::JankList Calculator::TakeJanksOlderThanTime(
JankList* janks,
base::TimeTicks end_time) {
// Copy all janks with Jank.start_time < |end_time|.
auto it =
std::lower_bound(janks->begin(), janks->end(), end_time,
[](const Jank& jank, const base::TimeTicks& end_time) {
return jank.start_time < end_time;
});
// We don't need to remove any Janks either, since Jank.end_time >=
// Jank.start_time.
// Find all janks with Jank.start_time < |end_time|.
auto it = std::partition(
janks->begin(), janks->end(),
[&end_time](const Jank& jank) { return jank.start_time < end_time; });
// Early exit. We don't need to remove any Janks either, since Jank.end_time
// >= Jank.start_time.
if (it == janks->begin())
return JankList();
JankList janks_to_return(janks->begin(), it);
// Remove all janks with Jank.end_time < |end_time|.
auto first_jank_to_keep =
std::lower_bound(janks->begin(), janks->end(), end_time,
[](const Jank& jank, const base::TimeTicks& end_time) {
return jank.end_time < end_time;
});
auto first_jank_to_keep = std::partition(
janks->begin(), janks->end(),
[&end_time](const Jank& jank) { return jank.end_time < end_time; });
janks->erase(janks->begin(), first_jank_to_keep);
return janks_to_return;
}
} // namespace responsiveness
} // namespace content
......@@ -12,6 +12,7 @@
#include "base/time/time.h"
#include "content/common/content_export.h"
namespace content {
namespace responsiveness {
// This class receives execution latency on events and tasks, and uses that to
......@@ -26,6 +27,10 @@ class CONTENT_EXPORT Calculator {
// Must be called from the UI thread.
// 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,
base::TimeTicks finish_time);
......@@ -84,8 +89,9 @@ class CONTENT_EXPORT Calculator {
// already been taken. May be called from any thread.
JankList& GetJanksOnIOThread();
// |janks| must be sorted by Jank.end_time. This method modifies |janks| to
// remove all janks older than |end_time|, and returns those.
// This method:
// 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);
// This should only be accessed via the accessor, which checks that the caller
......@@ -119,5 +125,6 @@ class CONTENT_EXPORT Calculator {
};
} // namespace responsiveness
} // namespace content
#endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_CALCULATOR_H_
......@@ -7,6 +7,7 @@
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace responsiveness {
namespace {
......@@ -211,4 +212,24 @@ TEST_F(ResponsivenessCalculatorTest, EventCrossesBoundary) {
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 content
......@@ -4,6 +4,7 @@
#include "content/browser/scheduler/responsiveness/message_loop_observer.h"
namespace content {
namespace responsiveness {
MessageLoopObserver::MessageLoopObserver(TaskCallback will_run_task_callback,
......@@ -30,3 +31,4 @@ void MessageLoopObserver::DidProcessTask(
}
} // namespace responsiveness
} // namespace content
......@@ -13,6 +13,7 @@ namespace base {
struct PendingTask;
} // namespace base
namespace content {
namespace responsiveness {
// This object is not thread safe. It must be constructed and destroyed on the
......@@ -41,5 +42,6 @@ class CONTENT_EXPORT MessageLoopObserver
};
} // namespace responsiveness
} // namespace content
#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 @@
#include "base/pending_task.h"
#include "content/browser/scheduler/responsiveness/calculator.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"
namespace content {
namespace responsiveness {
Watcher::Metadata::Metadata(const void* identifier) : identifier(identifier) {}
......@@ -26,7 +28,8 @@ void Watcher::SetUp() {
// and destruction.
AddRef();
calculator_ = MakeCalculator();
calculator_ = CreateCalculator();
native_event_observer_ui_ = CreateNativeEventObserver();
RegisterMessageLoopObserverUI();
......@@ -43,16 +46,28 @@ void Watcher::Destroy() {
destroy_was_called_ = true;
message_loop_observer_ui_.reset();
native_event_observer_ui_.reset();
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::BindOnce(&Watcher::TearDownOnIOThread, base::Unretained(this)));
}
std::unique_ptr<Calculator> Watcher::MakeCalculator() {
std::unique_ptr<Calculator> Watcher::CreateCalculator() {
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() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(destroy_was_called_);
......@@ -162,8 +177,8 @@ void Watcher::DidRunTask(const base::PendingTask* task,
TaskOrEventFinishedCallback callback) {
// Calls to DidRunTask should always be paired with WillRunTask. The only time
// the identifier should differ is when Watcher is first constructed. The
// TaskRunner Observers are added while a task is being run, which means that
// there was no corresponding WillRunTask.
// TaskRunner Observers may be added while a task is being run, which means
// that there was no corresponding WillRunTask.
if (UNLIKELY(currently_running_metadata->empty() ||
(task != currently_running_metadata->top().identifier))) {
*mismatched_task_identifiers += 1;
......@@ -199,4 +214,44 @@ void Watcher::DidRunTask(const base::PendingTask* task,
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 content
......@@ -18,10 +18,12 @@ namespace base {
struct PendingTask;
} // namespace base
namespace content {
namespace responsiveness {
class Calculator;
class MessageLoopObserver;
class NativeEventObserver;
// 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
......@@ -50,7 +52,8 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> {
protected:
// Exposed for tests.
virtual std::unique_ptr<Calculator> MakeCalculator();
virtual std::unique_ptr<Calculator> CreateCalculator();
virtual std::unique_ptr<NativeEventObserver> CreateNativeEventObserver();
virtual ~Watcher();
virtual void RegisterMessageLoopObserverUI();
virtual void RegisterMessageLoopObserverIO();
......@@ -105,18 +108,30 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> {
int* mismatched_task_identifiers,
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.
std::unique_ptr<Calculator> calculator_;
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.
std::stack<Metadata> currently_running_metadata_ui_;
// Task identifiers should only be mismatched once, since the Watcher
// registers itself during a Task execution, and thus doesn't capture the
// Task identifiers should only be mismatched once, since the Watcher may
// register itself during a Task execution, and thus doesn't capture the
// initial WillRunTask() callback.
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.
std::stack<Metadata> currently_running_metadata_io_;
int mismatched_task_identifiers_io_ = 0;
......@@ -136,5 +151,6 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> {
};
} // namespace responsiveness
} // namespace content
#endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_WATCHER_H_
......@@ -9,10 +9,12 @@
#include "base/run_loop.h"
#include "base/synchronization/lock.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/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace responsiveness {
namespace {
......@@ -49,13 +51,17 @@ class FakeCalculator : public Calculator {
class FakeWatcher : public Watcher {
public:
std::unique_ptr<Calculator> MakeCalculator() override {
std::unique_ptr<Calculator> CreateCalculator() override {
std::unique_ptr<FakeCalculator> calculator =
std::make_unique<FakeCalculator>();
calculator_ = calculator.get();
return calculator;
}
std::unique_ptr<NativeEventObserver> CreateNativeEventObserver() override {
return nullptr;
}
void RegisterMessageLoopObserverUI() override {
if (register_message_loop_observer_)
Watcher::RegisterMessageLoopObserverUI();
......@@ -220,3 +226,4 @@ TEST_F(ResponsivenessWatcherRealIOThreadTest, MessageLoopObserver) {
}
} // namespace responsiveness
} // namespace content
......@@ -168,6 +168,9 @@ jumbo_source_set("browser_sources") {
"memory_coordinator_delegate.h",
"message_port_provider.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",
"navigation_controller.cc",
"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 @@
#include "content/shell/browser/shell_application_mac.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/shell/browser/shell.h"
#include "content/shell/browser/shell_browser_context.h"
#include "content/shell/browser/shell_content_browser_client.h"
#include "url/gurl.h"
@interface ShellCrApplication ()<NativeEventProcessor> {
base::ObserverList<content::NativeEventProcessorObserver> observers_;
}
@end
@implementation ShellCrApplication
- (BOOL)isHandlingSendEvent {
......@@ -19,6 +27,9 @@
- (void)sendEvent:(NSEvent*)event {
base::AutoReset<BOOL> scoper(&handlingSendEvent_, YES);
content::ScopedNotifyNativeEventProcessorObserver scopedObserverNotifier(
&observers_, event);
[super sendEvent:event];
}
......@@ -35,4 +46,14 @@
gfx::Size());
}
- (void)addNativeEventProcessorObserver:
(content::NativeEventProcessorObserver*)observer {
observers_.AddObserver(observer);
}
- (void)removeNativeEventProcessorObserver:
(content::NativeEventProcessorObserver*)observer {
observers_.RemoveObserver(observer);
}
@end
......@@ -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_mac_browsertest.mm",
"../browser/resource_loading_browsertest.cc",
"../browser/scheduler/responsiveness/native_event_observer_browsertest.mm",
"../browser/screen_orientation/screen_orientation_browsertest.cc",
"../browser/security_exploit_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