Skip to content

Commit

Permalink
Record whether AudioContext was audible for Autoplay UKM
Browse files Browse the repository at this point in the history
Implements AudioContextManager mojo interface that can be obtained from a frame so that
audible playback from WebAudio can be recorded by the browser.  Whenever audible playback
starts or stops, a message is sent to the browser indicating such.

Bug: 855069
Change-Id: Ib236a9506a2a6d9b53e95948eb5519145805a2cb
Reviewed-on: https://chromium-review.googlesource.com/1114147
Reviewed-by: Mounir Lamouri <mlamouri@chromium.org>
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Reviewed-by: Nasko Oskov <nasko@chromium.org>
Reviewed-by: Hongchan Choi <hongchan@chromium.org>
Commit-Queue: Raymond Toy <rtoy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#587867}
  • Loading branch information
Raymond Toy authored and Commit Bot committed Aug 31, 2018
1 parent d78a0ce commit e413650
Show file tree
Hide file tree
Showing 22 changed files with 315 additions and 17 deletions.
2 changes: 2 additions & 0 deletions content/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,8 @@ jumbo_source_set("browser") {
"media/url_provision_fetcher.h",
"media/video_decoder_proxy.cc",
"media/video_decoder_proxy.h",
"media/webaudio/audio_context_manager_impl.cc",
"media/webaudio/audio_context_manager_impl.h",
"memory/memory_condition_observer.cc",
"memory/memory_condition_observer.h",
"memory/memory_coordinator_default_policy.cc",
Expand Down
12 changes: 12 additions & 0 deletions content/browser/frame_host/interstitial_page_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1090,4 +1090,16 @@ InterstitialPageImpl::GetOrCreateRootBrowserAccessibilityManager() {
return web_contents_impl->GetOrCreateRootBrowserAccessibilityManager();
}

void InterstitialPageImpl::AudioContextPlaybackStarted(RenderFrameHost* host,
int context_id) {
// Interstitial pages should not be playing any sound via WebAudio
NOTREACHED();
}

void InterstitialPageImpl::AudioContextPlaybackStopped(RenderFrameHost* host,
int context_id) {
// Interstitial pages should not be playing any sound via WebAudio.
NOTREACHED();
}

} // namespace content
4 changes: 4 additions & 0 deletions content/browser/frame_host/interstitial_page_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ class CONTENT_EXPORT InterstitialPageImpl : public InterstitialPage,
bool user_gesture) override;
void SetFocusedFrame(FrameTreeNode* node, SiteInstance* source) override;
Visibility GetVisibility() const override;
void AudioContextPlaybackStarted(RenderFrameHost* host,
int context_id) override;
void AudioContextPlaybackStopped(RenderFrameHost* host,
int context_id) override;

// RenderViewHostDelegate implementation:
RenderViewHostDelegateView* GetDelegateView() override;
Expand Down
7 changes: 7 additions & 0 deletions content/browser/frame_host/render_frame_host_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,13 @@ class CONTENT_EXPORT RenderFrameHostDelegate {
// Note: This is also exposed by the RenderWidgetHostDelegate.
virtual ukm::SourceId GetUkmSourceIdForLastCommittedSource() const;

// Notify observers if WebAudio AudioContext has started (or stopped) playing
// audible sounds.
virtual void AudioContextPlaybackStarted(RenderFrameHost* host,
int context_id){};
virtual void AudioContextPlaybackStopped(RenderFrameHost* host,
int context_id){};

protected:
virtual ~RenderFrameHostDelegate() {}
};
Expand Down
12 changes: 12 additions & 0 deletions content/browser/frame_host/render_frame_host_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
#include "content/browser/media/capture/audio_mirroring_manager.h"
#include "content/browser/media/media_interface_proxy.h"
#include "content/browser/media/session/media_session_service_impl.h"
#include "content/browser/media/webaudio/audio_context_manager_impl.h"
#include "content/browser/payments/payment_app_context_impl.h"
#include "content/browser/permissions/permission_controller_impl.h"
#include "content/browser/permissions/permission_service_context.h"
Expand Down Expand Up @@ -782,6 +783,14 @@ void RenderFrameHostImpl::DidCommitProvisionalLoadForTesting(
std::move(interface_provider_request));
}

void RenderFrameHostImpl::AudioContextPlaybackStarted(int audio_context_id) {
delegate_->AudioContextPlaybackStarted(this, audio_context_id);
}

void RenderFrameHostImpl::AudioContextPlaybackStopped(int audio_context_id) {
delegate_->AudioContextPlaybackStopped(this, audio_context_id);
}

SiteInstanceImpl* RenderFrameHostImpl::GetSiteInstance() {
return site_instance_.get();
}
Expand Down Expand Up @@ -3594,6 +3603,9 @@ void RenderFrameHostImpl::RegisterMojoInterfaces() {

registry_->AddInterface(base::BindRepeating(
&BackgroundFetchServiceImpl::CreateForFrame, GetProcess(), routing_id_));

registry_->AddInterface(base::BindRepeating(&AudioContextManagerImpl::Create,
base::Unretained(this)));
}

void RenderFrameHostImpl::ResetWaitingState() {
Expand Down
5 changes: 5 additions & 0 deletions content/browser/frame_host/render_frame_host_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,11 @@ class CONTENT_EXPORT RenderFrameHostImpl
return *registry_;
}

// Called when the WebAudio AudioContext given by |audio_context_id| has
// started (or stopped) playing audible audio.
void AudioContextPlaybackStarted(int audio_context_id);
void AudioContextPlaybackStopped(int audio_context_id);

protected:
friend class RenderFrameHostFactory;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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/web_contents_observer.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"

namespace content {

namespace {
// Test for audible playback message.
class WaitForAudioContextAudible : WebContentsObserver {
public:
explicit WaitForAudioContextAudible(WebContents* web_contents)
: WebContentsObserver(web_contents) {
run_loop_.Run();
}

void AudioContextPlaybackStarted(const AudioContextId&) final {
// Stop the run loop when we get the message
run_loop_.Quit();
}

private:
base::RunLoop run_loop_;

DISALLOW_COPY_AND_ASSIGN(WaitForAudioContextAudible);
};

// Test for silent playback started (audible playback stopped).
class WaitForAudioContextSilent : WebContentsObserver {
public:
explicit WaitForAudioContextSilent(WebContents* web_contents)
: WebContentsObserver(web_contents) {
run_loop_.Run();
}

void AudioContextPlaybackStopped(const AudioContextId&) final {
// Stop the run loop when we get the message
run_loop_.Quit();
}

private:
base::RunLoop run_loop_;

DISALLOW_COPY_AND_ASSIGN(WaitForAudioContextSilent);
};

} // namespace

class AudioContextManagerTest : public ContentBrowserTest {};

IN_PROC_BROWSER_TEST_F(AudioContextManagerTest, AudioContextPlaybackRecorded) {
NavigateToURL(shell(),
content::GetTestUrl("media/webaudio/", "playback-test.html"));

// Set gain to 1 to start audible audio and verify we got the
// playback started message.
{
ASSERT_TRUE(ExecuteScript(shell()->web_contents(), "gain.gain.value = 1;"));
WaitForAudioContextAudible wait(shell()->web_contents());
}

// Set gain to 0 to stop audible audio and verify we got the
// playback stopped message.
{
ASSERT_TRUE(ExecuteScript(shell()->web_contents(), "gain.gain.value = 0;"));
WaitForAudioContextSilent wait(shell()->web_contents());
}
}

} // namespace content
46 changes: 46 additions & 0 deletions content/browser/media/webaudio/audio_context_manager_impl.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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/media/webaudio/audio_context_manager_impl.h"

#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "mojo/public/cpp/bindings/strong_binding.h"

namespace content {

void AudioContextManagerImpl::Create(
RenderFrameHost* render_frame_host,
blink::mojom::AudioContextManagerRequest request) {
mojo::MakeStrongBinding(
std::make_unique<AudioContextManagerImpl>(render_frame_host),
std::move(request));
}

AudioContextManagerImpl::AudioContextManagerImpl(
RenderFrameHost* render_frame_host)
: render_frame_host_impl_(
static_cast<RenderFrameHostImpl*>(render_frame_host)) {
DCHECK(render_frame_host);
}

AudioContextManagerImpl::~AudioContextManagerImpl() = default;

void AudioContextManagerImpl::AudioContextAudiblePlaybackStarted(
int32_t audio_context_id) {
// Notify observers that audible audio started playing from a WebAudio
// AudioContext.
render_frame_host_impl_->AudioContextPlaybackStarted(audio_context_id);
}

void AudioContextManagerImpl::AudioContextAudiblePlaybackStopped(
int32_t audio_context_id) {
// Notify observers that audible audio stopped playing from a WebAudio
// AudioContext.
render_frame_host_impl_->AudioContextPlaybackStopped(audio_context_id);
}

} // namespace content
41 changes: 41 additions & 0 deletions content/browser/media/webaudio/audio_context_manager_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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_MEDIA_WEBAUDIO_AUDIO_CONTEXT_MANAGER_IMPL_H_
#define CONTENT_BROWSER_MEDIA_WEBAUDIO_AUDIO_CONTEXT_MANAGER_IMPL_H_

#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "third_party/blink/public/mojom/webaudio/audio_context_manager.mojom.h"

namespace content {

class RenderFrameHost;
class RenderFrameHostImpl;

// Implements the mojo interface between WebAudio and the browser so that
// WebAudio can report when audible sounds from an AudioContext starts and
// stops.
class CONTENT_EXPORT AudioContextManagerImpl
: public blink::mojom::AudioContextManager {
public:
explicit AudioContextManagerImpl(RenderFrameHost* render_frame_host);
~AudioContextManagerImpl() override;

static void Create(RenderFrameHost* render_frame_host,
blink::mojom::AudioContextManagerRequest request);

// Called when AudioContext starts or stops playing audible audio.
void AudioContextAudiblePlaybackStarted(int32_t audio_context_id) final;
void AudioContextAudiblePlaybackStopped(int32_t audio_context_id) final;

private:
RenderFrameHostImpl* const render_frame_host_impl_;

DISALLOW_COPY_AND_ASSIGN(AudioContextManagerImpl);
};

} // namespace content

#endif // CONTENT_BROWSER_MEDIA_WEBAUDIO_AUDIO_CONTEXT_MANAGER_IMPL_H_
14 changes: 14 additions & 0 deletions content/browser/web_contents/web_contents_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6517,6 +6517,20 @@ int WebContentsImpl::GetCurrentlyPlayingVideoCount() {
return currently_playing_video_count_;
}

void WebContentsImpl::AudioContextPlaybackStarted(RenderFrameHost* host,
int context_id) {
WebContentsObserver::AudioContextId audio_context_id(host, context_id);
for (auto& observer : observers_)
observer.AudioContextPlaybackStarted(audio_context_id);
}

void WebContentsImpl::AudioContextPlaybackStopped(RenderFrameHost* host,
int context_id) {
WebContentsObserver::AudioContextId audio_context_id(host, context_id);
for (auto& observer : observers_)
observer.AudioContextPlaybackStopped(audio_context_id);
}

void WebContentsImpl::UpdateWebContentsVisibility(Visibility visibility) {
// Occlusion is disabled when |features::kWebContentsOcclusion| is disabled
// (for power and speed impact assessment) or when
Expand Down
8 changes: 8 additions & 0 deletions content/browser/web_contents/web_contents_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,14 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents,
RenderFrameHost* render_frame_host,
const GlobalRequestID& request_id,
mojom::ResourceLoadInfoPtr resource_load_information) override;

// Called when WebAudio starts or stops playing audible audio in an
// AudioContext.
void AudioContextPlaybackStarted(RenderFrameHost* host,
int context_id) override;
void AudioContextPlaybackStopped(RenderFrameHost* host,
int context_id) override;

// RenderViewHostDelegate ----------------------------------------------------
RenderViewHostDelegateView* GetDelegateView() override;
bool OnMessageReceived(RenderViewHostImpl* render_view_host,
Expand Down
1 change: 1 addition & 0 deletions content/public/app/mojo/content_browser_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
"autofill.mojom.AutofillDriver",
"autofill.mojom.PasswordManagerDriver",
"blink.mojom.AnchorElementMetricsHost",
"blink.mojom.AudioContextManager",
"blink.mojom.Authenticator",
"blink.mojom.BackgroundFetchService",
"blink.mojom.CacheStorage",
Expand Down
7 changes: 7 additions & 0 deletions content/public/browser/web_contents_observer.h
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,13 @@ class CONTENT_EXPORT WebContentsObserver : public IPC::Listener {
// tab.
virtual void DidCommitAndDrawCompositorFrame() {}

// Called when "audible" playback starts or stops on a WebAudio AudioContext.
using AudioContextId = std::pair<RenderFrameHost*, int>;
virtual void AudioContextPlaybackStarted(
const AudioContextId& audio_context_id) {}
virtual void AudioContextPlaybackStopped(
const AudioContextId& audio_context_id) {}

// IPC::Listener implementation.
// DEPRECATED: Use (i.e. override) the other overload instead:
// virtual bool OnMessageReceived(const IPC::Message& message,
Expand Down
1 change: 1 addition & 0 deletions content/test/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ test("content_browsertests") {
"../browser/media/session/media_session_service_impl_browsertest.cc",
"../browser/media/session/mock_media_session_player_observer.cc",
"../browser/media/session/mock_media_session_player_observer.h",
"../browser/media/webaudio/audio_context_manager_browsertest.cc",
"../browser/memory/memory_coordinator_impl_browsertest.cc",
"../browser/message_port_provider_browsertest.cc",
"../browser/mojo_sandbox_browsertest.cc",
Expand Down
21 changes: 21 additions & 0 deletions content/test/data/media/webaudio/playback-test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html>
<head>
<title>Test AudioContextManager Audible Playback</title>
</head>

<body>
<script>
let context = new AudioContext();
let osc = new OscillatorNode(context);
// Set initial gain to 0 to make sure we start with silence.
let gain = new GainNode(context, {gain: 0});

// Connect everything and start the oscillator. The browser test will set the gain value as
// needed to test if the messages are being sent.
osc.connect(gain).connect(context.destination);
osc.start();

</script>
</body>
</html>
1 change: 1 addition & 0 deletions third_party/blink/public/mojom/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ mojom("mojom_platform") {
"speech/speech_recognizer.mojom",
"use_counter/css_property_id.mojom",
"web_package/web_package_internals.mojom",
"webaudio/audio_context_manager.mojom",
]

public_deps = [
Expand Down
6 changes: 6 additions & 0 deletions third_party/blink/public/mojom/webaudio/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
file://third_party/blink/renderer/modules/webaudio/OWNERS

# COMPONENT: Blink>WebAudio

per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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.

module blink.mojom;

// Interface for allowing a real-time AudioContext to send
// notifications to browser that the AudioContext has started (or
// stopped) playing audible sounds. Audibility is computed by the
// AudioContext.
interface AudioContextManager {
// AudioContext started producing audible sound. The id is the ID
// of the AudioContext.
AudioContextAudiblePlaybackStarted(int32 id);

// AudioContext stopped producing audible sound. The id is the ID
// of the AudioContext.
AudioContextAudiblePlaybackStopped(int32 id);
};
Loading

0 comments on commit e413650

Please sign in to comment.