Commit 74b7bd46 authored by Adrian Elder's avatar Adrian Elder Committed by Commit Bot

Support calling webrtcLoggingPrivate from an app for a webview.

Adds a flag to webrtcLoggingPrivate's RequestInfo that causes the API
functions to look up the render process id for a webview in the caller's
web contents.

There are 2 ways webrtcLoggingPrivate functions can be called.

  1. From a whitelisted component extension on behalf of a page with the
  appropriate origin via a message from that page. In this case, either the
  guest process id or the tab id is on the message received by the component
  extension, and the extension can pass that along in RequestInfo as
  |guest_process_id| or |tab_id|.

  2. From a whitelisted app that hosts a page in a webview. In this case,
  the app should call these API functions with the |target_webview| flag
  set, from a web contents that has exactly 1 webview .

Case 1 was already supported before. This change adds support for case 2.

Bug: 777933
Change-Id: I02f6d011fc7a6f1589c10aff19dc6e6e8f2e24a8
Reviewed-on: https://chromium-review.googlesource.com/763911
Commit-Queue: Adrian Elder <aelder@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#517859}
parent ec67cae3
......@@ -62,15 +62,55 @@ std::string HashIdWithOrigin(const std::string& security_origin,
// http://crbug.com/710371
content::RenderProcessHost* WebrtcLoggingPrivateFunction::RphFromRequest(
const RequestInfo& request, const std::string& security_origin) {
// There are 2 ways these API functions can get called.
//
// 1. From a whitelisted component extension on behalf of a page with the
// appropriate origin via a message from that page. In this case, either the
// guest process id or the tab id is on the message received by the component
// extension, and the extension can pass that along in RequestInfo as
// |guest_process_id| or |tab_id|.
//
// 2. From a whitelisted app that hosts a page in a webview. In this case,
// the app should call these API functions with the |target_webview| flag
// set, from a web contents that has exactly 1 webview .
// If |target_webview| is set, lookup the guest content's render process in
// the sender's web contents. There should be exactly 1 guest.
if (request.target_webview.get()) {
content::RenderProcessHost* target_host = nullptr;
int guests_found = 0;
auto get_guest = [](int* guests_found,
content::RenderProcessHost** target_host,
content::WebContents* guest_contents) {
*guests_found = *guests_found + 1;
*target_host = guest_contents->GetMainFrame()->GetProcess();
// Don't short-circuit, so we can count how many other guest contents
// there are.
return false;
};
guest_view::GuestViewManager::FromBrowserContext(browser_context())
->ForEachGuest(GetSenderWebContents(),
base::Bind(get_guest, &guests_found, &target_host));
if (!target_host) {
error_ = "No webview render process found";
return nullptr;
}
if (guests_found > 1) {
error_ = "Multiple webviews found";
return nullptr;
}
return target_host;
}
// If |guest_process_id| is defined, directly use this id to find the
// corresponding RenderProcessHost.
if (request.guest_process_id.get())
return content::RenderProcessHost::FromID(*request.guest_process_id);
// Otherwise, use the |tab_id|. If there's no |tab_id| and no
// |guest_process_id|, we can't look up the RenderProcessHost.
// Otherwise, use the |tab_id|. If there's no |target_viewview|, no |tab_id|,
// and no |guest_process_id|, we can't look up the RenderProcessHost.
if (!request.tab_id.get()) {
error_ = "No tab ID or guest process ID specified.";
error_ = "No webview, tab ID, or guest process ID specified.";
return nullptr;
}
......
......@@ -7,6 +7,7 @@
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/apps/app_browsertest_util.h"
#include "chrome/browser/media/webrtc/webrtc_log_list.h"
#include "chrome/common/chrome_switches.h"
class WebrtcLoggingPrivateApiBrowserTest
: public extensions::PlatformAppBrowserTest {
......@@ -54,3 +55,13 @@ IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiBrowserTest,
"api_test/webrtc_logging_private/no_get_logs_directory_permissions"))
<< message_;
}
IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiBrowserTest,
TestStartAudioDebugRecordingsForWebviewFromApp) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableAudioDebugRecordingsFromExtension);
ASSERT_TRUE(
RunPlatformAppTest("api_test/webrtc_logging_private/audio_debug/"
"start_audio_debug_recordings_for_webview_from_app"))
<< message_;
}
......@@ -26,6 +26,11 @@ namespace webrtcLoggingPrivate {
// The guest process id for the requester, if the request is from a
// webview.
long? guestProcessId;
// Use the render process of the webview in the current page. This allows an
// app to make a request for a webview it contains. If there are more or
// less than 1 webview, this will fail with a runtime error.
boolean? targetWebview;
};
// This contains information about the result of audio debug recordings or
......
// 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.
// Making it explicit that this is exposed for the background page to call.
window.attemptAudioDebugRecording = function(succeed, fail) {
// The API calls must be made in the window that hosts the webview.
chrome.webrtcLoggingPrivate.startAudioDebugRecordings(
{targetWebview: true}, '', 0,
function(startResult) {
if (chrome.runtime.lastError) {
fail('startAudioDebugRecordings: ' +
chrome.runtime.lastError.message);
return;
}
chrome.webrtcLoggingPrivate.stopAudioDebugRecordings(
{targetWebview: true}, '', function(stopResult) {
if (chrome.runtime.lastError) {
fail('stopAudioDebugRecordings: ' +
chrome.runtime.lastError.message);
return;
}
succeed();
});
});
};
{
// This app id is whitelisted for webrtcLoggingPrivate.
// Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB",
"name": "webrtcLoggingPrivate webview in RequestInfo test",
"version": "0.1",
"manifest_version": 2,
"description": "Tests initiation of WebRTC audio debug recordings on behalf of a webview with an app using webrtcLoggingPrivate",
"app": {
"background": {
"scripts": ["test.js"]
}
},
"permissions": ["webrtcLoggingPrivate", "webview"]
}
// 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.
function assertNoLastError(message) {
if (chrome.runtime.lastError) {
message += ': ' + chrome.runtime.lastError.message;
}
chrome.test.assertFalse(!!chrome.runtime.lastError, message);
}
function executeWindowTest(testBody) {
chrome.app.window.create(
'appwindow.html', {}, function(appWindow) {
assertNoLastError('window.create');
appWindow.contentWindow.onload = function() {
testBody(appWindow.contentWindow);
};
});
}
function createWebviews(doc, numWebviews, onWebviewsLoaded) {
var webviewStates = {};
for (var i = 0; i < numWebviews; i++) {
// Initialize object with mapping from id to isLoaded boolean.
webviewStates['webview' + i] = false;
}
function allLoaded() {
return Object.values(webviewStates).every(function(isLoaded) {
return isLoaded;
});
}
function onLoadStop(e) {
var webview = e.target;
e.target.removeEventListener('loadstop', onLoadStop);
var wasLoaded = allLoaded();
webviewStates[webview.id] = true;
var isLoaded = allLoaded();
if (!wasLoaded && isLoaded) {
onWebviewsLoaded();
}
}
Object.keys(webviewStates).forEach(function(webviewId) {
var webview = doc.createElement('webview');
webview.src = 'data:text/plain, test';
webview.addEventListener('loadstop', onLoadStop);
webview.id = webviewId;
doc.body.appendChild(webview);
});
}
function testStartStopWithOneWebview() {
executeWindowTest(function(win) {
createWebviews(win.document, 1, function() {
win.attemptAudioDebugRecording(
function() {
chrome.test.succeed('Started and stopped with 1 webview');
}, chrome.test.fail);
});
});
}
function testFailWithMoreThanOneWebview() {
executeWindowTest(function(win) {
createWebviews(win.document, 2, function() {
win.attemptAudioDebugRecording(
function() {
chrome.test.fail('Expected runtime error');
},
chrome.test.succeed);
});
});
}
function testFailWithZeroWebviews() {
executeWindowTest(function(win) {
win.attemptAudioDebugRecording(
function() {
chrome.test.fail('Expected runtime error');
},
chrome.test.succeed);
});
}
chrome.app.runtime.onLaunched.addListener(function() {
chrome.test.runTests([
testStartStopWithOneWebview,
testFailWithMoreThanOneWebview,
testFailWithZeroWebviews,
]);
});
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