Eliminates roundtrips between browser and renderer
This commit is contained in:
@@ -33,7 +33,7 @@ deps = {
|
||||
'condition': 'checkout_android',
|
||||
'dep_type': 'cipd',
|
||||
},
|
||||
"third_party/playlist_component/src": "https://github.com/brave/playlist-component.git@565d3ef2b31d8c7eb0d19b5d046f56b1d9e1fe01",
|
||||
"third_party/playlist_component/src": "https://github.com/brave/playlist-component.git@673d40f017a1559bb685a15cf608ad1d4a94f8fb",
|
||||
"third_party/rust/challenge_bypass_ristretto/v1/crate": "https://github.com/brave-intl/challenge-bypass-ristretto.git@48910ab1e0c6a52cd757abdc234aa4d6db214c09",
|
||||
"third_party/rust/futures_retry/v0_5/crate": "https://github.com/brave-intl/futures-retry.git@2aaaafbc3d394661534d4dbd14159d164243c20e",
|
||||
"third_party/macholib": {
|
||||
|
||||
@@ -136,6 +136,7 @@ public class PlaylistServiceObserverImpl implements PlaylistServiceObserver {
|
||||
|
||||
@Override
|
||||
public void onMediaFilesUpdated(Url pageUrl, PlaylistItem[] items) {
|
||||
if (items.length == 0) return;
|
||||
if (mDelegate == null) return;
|
||||
mDelegate.onMediaFilesUpdated(pageUrl, items);
|
||||
}
|
||||
|
||||
@@ -1638,6 +1638,7 @@ public abstract class BraveToolbarLayoutImpl extends ToolbarLayout
|
||||
|
||||
@Override
|
||||
public void onMediaFilesUpdated(Url pageUrl, PlaylistItem[] items) {
|
||||
if (items.length == 0) return;
|
||||
Tab currentTab = getToolbarDataProvider().getTab();
|
||||
if (currentTab == null || !pageUrl.url.equals(currentTab.getUrl().getSpec())) {
|
||||
return;
|
||||
|
||||
@@ -267,10 +267,7 @@ void PlaylistServiceFactory::RegisterProfilePrefs(
|
||||
PlaylistServiceFactory::PlaylistServiceFactory()
|
||||
: BrowserContextKeyedServiceFactory(
|
||||
"PlaylistService",
|
||||
BrowserContextDependencyManager::GetInstance()) {
|
||||
PlaylistDownloadRequestManager::SetPlaylistJavaScriptWorldId(
|
||||
ISOLATED_WORLD_ID_BRAVE_INTERNAL);
|
||||
}
|
||||
BrowserContextDependencyManager::GetInstance()) {}
|
||||
|
||||
PlaylistServiceFactory::~PlaylistServiceFactory() = default;
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "chrome/test/base/chrome_test_utils.h"
|
||||
#include "content/public/test/browser_test.h"
|
||||
#include "content/public/test/content_mock_cert_verifier.h"
|
||||
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
|
||||
#include "net/base/schemeful_site.h"
|
||||
#include "net/dns/mock_host_resolver.h"
|
||||
#include "net/test/embedded_test_server/embedded_test_server.h"
|
||||
@@ -41,8 +40,6 @@ class PlaylistDownloadRequestManagerBrowserTest : public PlatformBrowserTest {
|
||||
};
|
||||
|
||||
PlaylistDownloadRequestManagerBrowserTest() {
|
||||
playlist::PlaylistDownloadRequestManager::SetPlaylistJavaScriptWorldId(
|
||||
ISOLATED_WORLD_ID_BRAVE_INTERNAL);
|
||||
scoped_feature_list_.InitAndEnableFeature(playlist::features::kPlaylist);
|
||||
}
|
||||
|
||||
@@ -62,38 +59,25 @@ class PlaylistDownloadRequestManagerBrowserTest : public PlatformBrowserTest {
|
||||
return item;
|
||||
}
|
||||
|
||||
GURL LoadHTML(const std::string& html, GURL url = GURL()) {
|
||||
GURL set_up_https_server(const std::string& html, GURL url = GURL()) {
|
||||
const auto* test_info =
|
||||
testing::UnitTest::GetInstance()->current_test_info();
|
||||
VLOG(2) << __FUNCTION__ << test_info->name() << ": " << __func__;
|
||||
VLOG(2) << test_info->name() << ": " << __FUNCTION__;
|
||||
|
||||
// Start server: ASSER_TRUE() has its own return type internally, thus
|
||||
// encloses with lambda.
|
||||
([&]() {
|
||||
// ASSERT_*() only work in void functions
|
||||
([&] {
|
||||
if (https_server()->Started()) {
|
||||
ASSERT_TRUE(https_server()->ShutdownAndWaitUntilComplete());
|
||||
}
|
||||
|
||||
https_server()->RegisterRequestHandler(
|
||||
base::BindRepeating(&PlaylistDownloadRequestManagerBrowserTest::Serve,
|
||||
https_server(), html));
|
||||
ASSERT_TRUE(https_server()->Start());
|
||||
})();
|
||||
|
||||
// Load given |html| contents: ASSER_TRUE() has its own return type
|
||||
// internally, thus encloesg with lambda.
|
||||
GURL destination_url = url;
|
||||
([&]() {
|
||||
auto* active_web_contents = chrome_test_utils::GetActiveWebContents(this);
|
||||
if (destination_url.is_valid()) {
|
||||
destination_url = https_server()->GetURL(destination_url.host(),
|
||||
destination_url.path());
|
||||
} else {
|
||||
destination_url = https_server()->GetURL("/test");
|
||||
}
|
||||
ASSERT_TRUE(content::NavigateToURL(active_web_contents, destination_url));
|
||||
})();
|
||||
|
||||
return destination_url;
|
||||
return url.is_valid() ? https_server()->GetURL(url.host(), url.path())
|
||||
: https_server()->GetURL("/test");
|
||||
}
|
||||
|
||||
void LoadHTMLAndCheckResult(const std::string& html,
|
||||
@@ -103,20 +87,29 @@ class PlaylistDownloadRequestManagerBrowserTest : public PlatformBrowserTest {
|
||||
testing::UnitTest::GetInstance()->current_test_info();
|
||||
VLOG(2) << __FUNCTION__ << test_info->name() << ": " << __func__;
|
||||
|
||||
auto destination_url = LoadHTML(html, url);
|
||||
auto* playlist_service =
|
||||
playlist::PlaylistServiceFactory::GetForBrowserContext(
|
||||
chrome_test_utils::GetProfile(this));
|
||||
ASSERT_TRUE(playlist_service);
|
||||
testing::NiceMock<MockPlaylistServiceObserver> observer;
|
||||
playlist_service->AddObserver(observer.GetRemote());
|
||||
|
||||
const auto destination_url = set_up_https_server(html, url);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
EXPECT_CALL(observer, OnMediaFilesUpdated(testing::_, testing::_))
|
||||
.WillOnce(
|
||||
[&](const GURL&,
|
||||
std::vector<playlist::mojom::PlaylistItemPtr> actual_items) {
|
||||
OnGetMedia(test_info->name(), items,
|
||||
url.is_valid() ? url.host() : destination_url.host(),
|
||||
std::move(actual_items));
|
||||
run_loop.Quit();
|
||||
});
|
||||
|
||||
auto* active_web_contents = chrome_test_utils::GetActiveWebContents(this);
|
||||
// Run script and find media files
|
||||
ASSERT_FALSE(component_manager_->GetMediaDetectorScript({}).empty());
|
||||
request_manager_->GetMedia(
|
||||
active_web_contents,
|
||||
base::BindOnce(&PlaylistDownloadRequestManagerBrowserTest::OnGetMedia,
|
||||
base::Unretained(this), test_info->name(), items,
|
||||
url.is_valid() ? url.host() : destination_url.host()));
|
||||
|
||||
// Block until result is received from OnGetMedia().
|
||||
run_loop_ = std::make_unique<base::RunLoop>();
|
||||
run_loop_->Run();
|
||||
ASSERT_TRUE(content::NavigateToURL(active_web_contents, destination_url));
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -214,9 +207,6 @@ class PlaylistDownloadRequestManagerBrowserTest : public PlatformBrowserTest {
|
||||
a->thumbnail_path == b->thumbnail_path;
|
||||
};
|
||||
EXPECT_TRUE(base::ranges::equal(actual_items, expected_items, equal));
|
||||
|
||||
ASSERT_TRUE(run_loop_);
|
||||
run_loop_->Quit();
|
||||
}
|
||||
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
@@ -224,8 +214,6 @@ class PlaylistDownloadRequestManagerBrowserTest : public PlatformBrowserTest {
|
||||
raw_ptr<playlist::MediaDetectorComponentManager> component_manager_;
|
||||
raw_ptr<playlist::PlaylistDownloadRequestManager> request_manager_;
|
||||
|
||||
std::unique_ptr<base::RunLoop> run_loop_;
|
||||
|
||||
content::ContentMockCertVerifier mock_cert_verifier_;
|
||||
std::unique_ptr<net::EmbeddedTestServer> https_server_;
|
||||
};
|
||||
@@ -289,8 +277,14 @@ IN_PROC_BROWSER_TEST_F(PlaylistDownloadRequestManagerBrowserTest,
|
||||
{});
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
#define MAYBE_YouTubeSpecificRetriever YouTubeSpecificRetriever
|
||||
#else
|
||||
#define MAYBE_YouTubeSpecificRetriever DISABLED_YouTubeSpecificRetriever
|
||||
#endif
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(PlaylistDownloadRequestManagerBrowserTest,
|
||||
YouTubeSpecificRetriever) {
|
||||
MAYBE_YouTubeSpecificRetriever) {
|
||||
// Pre-conditions to decide site specific script
|
||||
ASSERT_EQ(net::SchemefulSite(GURL("https://m.youtube.com")),
|
||||
net::SchemefulSite(GURL("https://youtube.com")));
|
||||
@@ -299,9 +293,6 @@ IN_PROC_BROWSER_TEST_F(PlaylistDownloadRequestManagerBrowserTest,
|
||||
ASSERT_NE(net::SchemefulSite(GURL("http://m.youtube.com")),
|
||||
net::SchemefulSite(GURL("https://m.youtube.com")));
|
||||
|
||||
// Getting JavaScript object requires to access the main world.
|
||||
request_manager()->SetRunScriptOnMainWorldForTest();
|
||||
|
||||
// Check if we can retrieve metadata from youtube specific script.
|
||||
LoadHTMLAndCheckResult(
|
||||
R"html(
|
||||
@@ -419,14 +410,14 @@ IN_PROC_BROWSER_TEST_F(PlaylistDownloadRequestManagerBrowserTest,
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(PlaylistDownloadRequestManagerBrowserTest,
|
||||
DynamicallyAddedMedia) {
|
||||
testing::NiceMock<MockPlaylistServiceObserver> observer;
|
||||
auto* playlist_service =
|
||||
playlist::PlaylistServiceFactory::GetForBrowserContext(
|
||||
chrome_test_utils::GetProfile(this));
|
||||
ASSERT_TRUE(playlist_service);
|
||||
testing::NiceMock<MockPlaylistServiceObserver> observer;
|
||||
playlist_service->AddObserver(observer.GetRemote());
|
||||
|
||||
const auto& url = LoadHTML(
|
||||
const auto url = set_up_https_server(
|
||||
R"html(
|
||||
<html>
|
||||
<meta property="og:image" content="/img.jpg">
|
||||
@@ -445,42 +436,20 @@ IN_PROC_BROWSER_TEST_F(PlaylistDownloadRequestManagerBrowserTest,
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
)html",
|
||||
GURL("https://youtube.com") // For now, detecting dynamically added media
|
||||
// files is done only for Background web
|
||||
// contents. So we should pretend to be
|
||||
// Youtube.
|
||||
);
|
||||
|
||||
// At the first execution, we don't get any media files.
|
||||
// And after that, we should find dynamically added media files and
|
||||
// notify observers.
|
||||
using testing::_;
|
||||
base::MockOnceCallback<void(const GURL&,
|
||||
std::vector<playlist::mojom::PlaylistItemPtr>)>
|
||||
callback;
|
||||
|
||||
// Unfortunately, testing::ElementsAreArray does't seem to work with
|
||||
// non-copyable When we we write
|
||||
// EXPECT_CALL(callback, Run(url,
|
||||
// testing::ElementsAreArray(
|
||||
// empty_result.begin(), empty_result.empty())));
|
||||
// It fails to compile. So as a workaround, checks it in an Action.
|
||||
EXPECT_CALL(callback, Run(url, _))
|
||||
.WillOnce([](const GURL& url,
|
||||
std::vector<playlist::mojom::PlaylistItemPtr> items) {
|
||||
EXPECT_TRUE(items.empty());
|
||||
});
|
||||
// This returns cached items that are found so far. They should be empty.
|
||||
playlist_service->FindMediaFilesFromActiveTab(callback.Get());
|
||||
)html");
|
||||
|
||||
base::RunLoop run_loop;
|
||||
EXPECT_CALL(observer, OnMediaFilesUpdated(url, _))
|
||||
.WillOnce([&](const GURL& page_url,
|
||||
std::vector<playlist::mojom::PlaylistItemPtr> items) {
|
||||
EXPECT_FALSE(items.empty());
|
||||
run_loop.Quit();
|
||||
});
|
||||
testing::InSequence in_sequence;
|
||||
EXPECT_CALL(observer, OnMediaFilesUpdated(url, testing::IsEmpty()))
|
||||
.Times(testing::AtLeast(1));
|
||||
EXPECT_CALL(observer,
|
||||
OnMediaFilesUpdated(url, testing::Not(testing::IsEmpty())))
|
||||
.WillOnce(
|
||||
[&](const GURL&, std::vector<playlist::mojom::PlaylistItemPtr>) {
|
||||
run_loop.Quit();
|
||||
});
|
||||
|
||||
auto* active_web_contents = chrome_test_utils::GetActiveWebContents(this);
|
||||
ASSERT_TRUE(content::NavigateToURL(active_web_contents, url));
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ class PlaylistDownloadRequestManagerUnitTest : public testing::Test {
|
||||
return *download_request_manager_;
|
||||
}
|
||||
|
||||
std::vector<mojom::PlaylistItemPtr> ProcessFoundMedia(base::Value value) {
|
||||
return manager().ProcessFoundMedia(std::move(value),
|
||||
GURL("https://example.com"));
|
||||
std::vector<mojom::PlaylistItemPtr> GetPlaylistItems(base::Value value) {
|
||||
return manager().GetPlaylistItems(std::move(value),
|
||||
GURL("https://example.com"));
|
||||
}
|
||||
|
||||
// testing::Test:
|
||||
@@ -100,20 +100,20 @@ TEST_F(PlaylistDownloadRequestManagerUnitTest,
|
||||
}
|
||||
|
||||
TEST_F(PlaylistDownloadRequestManagerUnitTest,
|
||||
ProcessFoundMedia_NoMediaDetected) {
|
||||
GetPlaylistItems_NoMediaDetected) {
|
||||
// When media detection script doesn't find any media, it returns an empty
|
||||
// dict.
|
||||
EXPECT_TRUE(ProcessFoundMedia(base::Value(base::Value::Type::DICT)).empty());
|
||||
EXPECT_TRUE(GetPlaylistItems(base::Value(base::Value::Type::DICT)).empty());
|
||||
}
|
||||
|
||||
TEST_F(PlaylistDownloadRequestManagerUnitTest, ProcessFoundMedia_InvalidValue) {
|
||||
// ProcessFoundMedia only takes either list or dict
|
||||
BASE_EXPECT_DEATH(ProcessFoundMedia(base::Value(base::Value::Type::BOOLEAN)),
|
||||
TEST_F(PlaylistDownloadRequestManagerUnitTest, GetPlaylistItems_InvalidValue) {
|
||||
// GetPlaylistItems only takes either list or dict
|
||||
BASE_EXPECT_DEATH(GetPlaylistItems(base::Value(base::Value::Type::BOOLEAN)),
|
||||
CHECK_WILL_STREAM() ? "Check failed: value\\.is_list" : "");
|
||||
}
|
||||
|
||||
TEST_F(PlaylistDownloadRequestManagerUnitTest,
|
||||
ProcessFoundMedia_RequiredProperties) {
|
||||
GetPlaylistItems_RequiredProperties) {
|
||||
base::Value value(base::Value::Dict()
|
||||
.Set("name", "Video 1")
|
||||
.Set("pageTitle", "Example page")
|
||||
@@ -123,7 +123,7 @@ TEST_F(PlaylistDownloadRequestManagerUnitTest,
|
||||
.Set("srcIsMediaSourceObjectURL", false));
|
||||
|
||||
std::vector<mojom::PlaylistItemPtr> result =
|
||||
ProcessFoundMedia(base::Value(base::Value::List().Append(value.Clone())));
|
||||
GetPlaylistItems(base::Value(base::Value::List().Append(value.Clone())));
|
||||
EXPECT_EQ(result.size(), 1u);
|
||||
EXPECT_FALSE(result[0]->id.empty());
|
||||
EXPECT_EQ(result[0]->name, "Video 1");
|
||||
@@ -139,14 +139,14 @@ TEST_F(PlaylistDownloadRequestManagerUnitTest,
|
||||
"src", "srcIsMediaSourceObjectURL"}) {
|
||||
auto invalid_value = value.Clone();
|
||||
invalid_value.GetDict().Set(required_property, base::Value());
|
||||
EXPECT_TRUE(ProcessFoundMedia(base::Value(base::Value::List().Append(
|
||||
std::move(invalid_value))))
|
||||
EXPECT_TRUE(GetPlaylistItems(base::Value(base::Value::List().Append(
|
||||
std::move(invalid_value))))
|
||||
.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PlaylistDownloadRequestManagerUnitTest,
|
||||
ProcessFoundMedia_OptionalProperties) {
|
||||
GetPlaylistItems_OptionalProperties) {
|
||||
base::Value value(base::Value::Dict()
|
||||
.Set("name", "Video 1")
|
||||
.Set("pageTitle", "Example page")
|
||||
@@ -159,7 +159,7 @@ TEST_F(PlaylistDownloadRequestManagerUnitTest,
|
||||
.Set("duration", 1234.0));
|
||||
|
||||
std::vector<mojom::PlaylistItemPtr> result =
|
||||
ProcessFoundMedia(base::Value(base::Value::List().Append(value.Clone())));
|
||||
GetPlaylistItems(base::Value(base::Value::List().Append(value.Clone())));
|
||||
EXPECT_EQ(result.size(), 1u);
|
||||
EXPECT_FALSE(result[0]->id.empty());
|
||||
EXPECT_EQ(result[0]->author, "Me");
|
||||
@@ -172,7 +172,7 @@ TEST_F(PlaylistDownloadRequestManagerUnitTest,
|
||||
}
|
||||
|
||||
TEST_F(PlaylistDownloadRequestManagerUnitTest,
|
||||
ProcessFoundMedia_MediaSourceScheme) {
|
||||
GetPlaylistItems_MediaSourceScheme) {
|
||||
base::Value value(base::Value::Dict()
|
||||
.Set("name", "Video 1")
|
||||
.Set("pageTitle", "Example page")
|
||||
@@ -182,32 +182,32 @@ TEST_F(PlaylistDownloadRequestManagerUnitTest,
|
||||
.Set("srcIsMediaSourceObjectURL", false));
|
||||
|
||||
EXPECT_FALSE(
|
||||
ProcessFoundMedia(base::Value(base::Value::List().Append(value.Clone())))
|
||||
GetPlaylistItems(base::Value(base::Value::List().Append(value.Clone())))
|
||||
.empty());
|
||||
|
||||
// http:// scheme is not allowed.
|
||||
value.GetDict().Set("src", "http://example.com/12345");
|
||||
EXPECT_TRUE(
|
||||
ProcessFoundMedia(base::Value(base::Value::List().Append(value.Clone())))
|
||||
GetPlaylistItems(base::Value(base::Value::List().Append(value.Clone())))
|
||||
.empty());
|
||||
|
||||
// blob: that's not backed by MediaSource is not allowed
|
||||
value.GetDict().Set("src", "blob:https://example.com/12345");
|
||||
EXPECT_TRUE(
|
||||
ProcessFoundMedia(base::Value(base::Value::List().Append(value.Clone())))
|
||||
GetPlaylistItems(base::Value(base::Value::List().Append(value.Clone())))
|
||||
.empty());
|
||||
|
||||
// blob: that's backed by MediaSource but from unknown source is not allowed
|
||||
value.GetDict().Set("src", "blob:https://example.com/12345");
|
||||
value.GetDict().Set("srcIsMediaSourceObjectURL", true);
|
||||
EXPECT_TRUE(
|
||||
ProcessFoundMedia(base::Value(base::Value::List().Append(value.Clone())))
|
||||
GetPlaylistItems(base::Value(base::Value::List().Append(value.Clone())))
|
||||
.empty());
|
||||
|
||||
// blob: that's backed by MediaSource and from known source is allowed
|
||||
value.GetDict().Set("src", "blob:https://youtube.com/12345");
|
||||
EXPECT_FALSE(
|
||||
ProcessFoundMedia(base::Value(base::Value::List().Append(value.Clone())))
|
||||
GetPlaylistItems(base::Value(base::Value::List().Append(value.Clone())))
|
||||
.empty());
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -23,8 +23,6 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
return false;
|
||||
}
|
||||
out->force_cosmetic_filtering = data.force_cosmetic_filtering();
|
||||
out->hide_media_src_api = data.hide_media_src_api();
|
||||
out->should_detect_media_files = data.should_detect_media_files();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -26,8 +26,6 @@ struct BLINK_COMMON_EXPORT WebPreferences : public WebPreferences_ChromiumImpl {
|
||||
WebPreferences& operator=(WebPreferences&& other);
|
||||
|
||||
bool force_cosmetic_filtering = false;
|
||||
bool hide_media_src_api = false;
|
||||
bool should_detect_media_files = false;
|
||||
};
|
||||
|
||||
} // namespace web_pref
|
||||
|
||||
Vendored
-9
@@ -27,15 +27,6 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
return r.force_cosmetic_filtering;
|
||||
}
|
||||
|
||||
static bool hide_media_src_api(const blink::web_pref::WebPreferences& r) {
|
||||
return r.hide_media_src_api;
|
||||
}
|
||||
|
||||
static bool should_detect_media_files(
|
||||
const blink::web_pref::WebPreferences& r) {
|
||||
return r.should_detect_media_files;
|
||||
}
|
||||
|
||||
static bool Read(blink::mojom::WebPreferencesDataView r,
|
||||
blink::web_pref::WebPreferences* out);
|
||||
};
|
||||
|
||||
-10
@@ -11,14 +11,4 @@ struct WebPreferences {
|
||||
// regardless of contents settings. This is used for Playlist so that it can
|
||||
// remove ads from the page.
|
||||
bool force_cosmetic_filtering;
|
||||
|
||||
// If |hide_media_src_api| is true, PlaylistRenderFrameObserver will hide
|
||||
// the media source API from the page. This is used by Playlist so that
|
||||
// it can get downloadable media urls instead of blob url.
|
||||
bool hide_media_src_api;
|
||||
|
||||
// If |should_detect_media_files| is true, PlaylistRenderFrameObserver will
|
||||
// try to inject JavaScript to detect <video> and <audio> tag, that are
|
||||
// dynamically attached.
|
||||
bool should_detect_media_files;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,8 @@ static_library("browser") {
|
||||
"media_detector_component_installer.h",
|
||||
"media_detector_component_manager.cc",
|
||||
"media_detector_component_manager.h",
|
||||
"playlist_background_webcontents_helper.cc",
|
||||
"playlist_background_webcontents_helper.h",
|
||||
"playlist_constants.h",
|
||||
"playlist_download_request_manager.cc",
|
||||
"playlist_download_request_manager.h",
|
||||
@@ -50,7 +52,6 @@ static_library("browser") {
|
||||
"//brave/components/p3a_utils",
|
||||
"//brave/components/resources:static_resources",
|
||||
"//brave/components/resources:strings",
|
||||
"//brave/components/script_injector/common/mojom",
|
||||
"//components/component_updater",
|
||||
"//components/download/public/common:public",
|
||||
"//components/keyed_service/core",
|
||||
|
||||
@@ -25,24 +25,33 @@ using ScriptName = base::FilePath::StringType;
|
||||
using ScriptToSchemefulSiteMap = base::flat_map<ScriptName, net::SchemefulSite>;
|
||||
using ScriptToResourceIdMap = base::flat_map<ScriptName, int>;
|
||||
|
||||
const base::FilePath::StringType& GetMediaSourceAPISuppressorScriptName() {
|
||||
static const base::NoDestructor kMediaSourceApiSuppressor(
|
||||
ScriptName(FILE_PATH_LITERAL("media_source_api_suppressor.js")));
|
||||
return *kMediaSourceApiSuppressor;
|
||||
}
|
||||
|
||||
const base::FilePath::StringType& GetBaseScriptName() {
|
||||
static const base::NoDestructor base_script(
|
||||
static const base::NoDestructor kBaseScript(
|
||||
ScriptName(FILE_PATH_LITERAL("index.js")));
|
||||
return *base_script;
|
||||
return *kBaseScript;
|
||||
}
|
||||
|
||||
const ScriptToSchemefulSiteMap& GetScriptNameToSchemefulSiteMap() {
|
||||
static const base::NoDestructor script_name_to_schemeful_sites(
|
||||
static const base::NoDestructor kScriptNameToSchemefulSites(
|
||||
ScriptToSchemefulSiteMap{
|
||||
{FILE_PATH_LITERAL("youtube.com.js"),
|
||||
net::SchemefulSite(GURL("https://youtube.com"))}});
|
||||
|
||||
return *script_name_to_schemeful_sites;
|
||||
return *kScriptNameToSchemefulSites;
|
||||
}
|
||||
|
||||
base::flat_map<ScriptName, std::string> GetLocalScriptMap() {
|
||||
const auto& rb = ui::ResourceBundle::GetSharedInstance();
|
||||
return {
|
||||
{GetMediaSourceAPISuppressorScriptName(),
|
||||
std::string(rb.LoadDataResourceString(
|
||||
IDR_PLAYLIST_MEDIA_SOURCE_API_SUPPRESSOR_JS))},
|
||||
{GetBaseScriptName(),
|
||||
std::string(rb.LoadDataResourceString(IDR_PLAYLIST_MEDIA_DETECTOR_JS))},
|
||||
{FILE_PATH_LITERAL("youtube.com.js"),
|
||||
@@ -119,7 +128,8 @@ void MediaDetectorComponentManager::RegisterIfNeeded() {
|
||||
void MediaDetectorComponentManager::OnComponentReady(
|
||||
const base::FilePath& install_path) {
|
||||
base::flat_set<base::FilePath> files(
|
||||
{install_path.Append(GetBaseScriptName())});
|
||||
{install_path.Append(GetMediaSourceAPISuppressorScriptName()),
|
||||
install_path.Append(GetBaseScriptName())});
|
||||
for (const auto& [file, _] : GetScriptNameToSchemefulSiteMap()) {
|
||||
files.insert(install_path.Append(file));
|
||||
}
|
||||
@@ -138,7 +148,11 @@ void MediaDetectorComponentManager::OnGetScripts(
|
||||
return;
|
||||
}
|
||||
|
||||
DCHECK(script_map.count(GetBaseScriptName()));
|
||||
CHECK(script_map.contains(GetMediaSourceAPISuppressorScriptName()));
|
||||
media_source_api_suppressor_ =
|
||||
script_map.at(GetMediaSourceAPISuppressorScriptName());
|
||||
|
||||
CHECK(script_map.contains(GetBaseScriptName()));
|
||||
base_script_ = script_map.at(GetBaseScriptName());
|
||||
|
||||
// This could have been filled when we've used media detector script before
|
||||
@@ -147,7 +161,7 @@ void MediaDetectorComponentManager::OnGetScripts(
|
||||
|
||||
const auto& schemeful_site_map = GetScriptNameToSchemefulSiteMap();
|
||||
for (const auto& [script_name, script] : script_map) {
|
||||
if (schemeful_site_map.count(script_name)) {
|
||||
if (schemeful_site_map.contains(script_name)) {
|
||||
site_specific_detectors_[schemeful_site_map.at(script_name)] = script;
|
||||
}
|
||||
}
|
||||
@@ -172,6 +186,13 @@ bool MediaDetectorComponentManager::ShouldHideMediaSrcAPI(
|
||||
});
|
||||
}
|
||||
|
||||
const std::string&
|
||||
MediaDetectorComponentManager::GetMediaSourceAPISuppressorScript() {
|
||||
MaybeInitScripts();
|
||||
CHECK(!media_source_api_suppressor_.empty());
|
||||
return media_source_api_suppressor_;
|
||||
}
|
||||
|
||||
std::string MediaDetectorComponentManager::GetMediaDetectorScript(
|
||||
const GURL& url) {
|
||||
MaybeInitScripts();
|
||||
|
||||
@@ -45,6 +45,8 @@ class MediaDetectorComponentManager {
|
||||
void AddObserver(Observer* observer);
|
||||
void RemoveObserver(Observer* observer);
|
||||
|
||||
const std::string& GetMediaSourceAPISuppressorScript();
|
||||
|
||||
// Returns a script to get media from page. If the script isn't fetched
|
||||
// from component yet, will return a local script.
|
||||
std::string GetMediaDetectorScript(const GURL& url);
|
||||
@@ -72,6 +74,7 @@ class MediaDetectorComponentManager {
|
||||
bool register_requested_ = false;
|
||||
raw_ptr<component_updater::ComponentUpdateService> component_update_service_;
|
||||
|
||||
std::string media_source_api_suppressor_;
|
||||
std::string base_script_;
|
||||
|
||||
std::vector<net::SchemefulSite> sites_to_hide_media_src_api_;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "brave/components/playlist/browser/playlist_background_webcontents_helper.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "brave/components/playlist/common/mojom/playlist.mojom.h"
|
||||
#include "content/public/browser/navigation_handle.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
|
||||
|
||||
namespace playlist {
|
||||
|
||||
PlaylistBackgroundWebContentsHelper::~PlaylistBackgroundWebContentsHelper() =
|
||||
default;
|
||||
|
||||
void PlaylistBackgroundWebContentsHelper::ReadyToCommitNavigation(
|
||||
content::NavigationHandle* navigation_handle) {
|
||||
DVLOG(2) << __FUNCTION__;
|
||||
|
||||
DCHECK(navigation_handle);
|
||||
if (!navigation_handle->IsInPrimaryMainFrame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const GURL url = navigation_handle->GetURL();
|
||||
if (!url.SchemeIsHTTPOrHTTPS()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mojo::AssociatedRemote<mojom::PlaylistRenderFrameObserverConfigurator>
|
||||
frame_observer_config;
|
||||
navigation_handle->GetRenderFrameHost()
|
||||
->GetRemoteAssociatedInterfaces()
|
||||
->GetInterface(&frame_observer_config);
|
||||
frame_observer_config->AddMediaSourceAPISuppressor(
|
||||
media_source_api_suppressor_);
|
||||
frame_observer_config->AddMediaDetector(media_detector_);
|
||||
}
|
||||
|
||||
PlaylistBackgroundWebContentsHelper::PlaylistBackgroundWebContentsHelper(
|
||||
content::WebContents* web_contents,
|
||||
const std::string& media_source_api_suppressor,
|
||||
const std::string& media_detector)
|
||||
: content::WebContentsUserData<PlaylistBackgroundWebContentsHelper>(
|
||||
*web_contents),
|
||||
content::WebContentsObserver(web_contents),
|
||||
media_source_api_suppressor_(media_source_api_suppressor),
|
||||
media_detector_(media_detector) {}
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_IMPL(PlaylistBackgroundWebContentsHelper);
|
||||
|
||||
} // namespace playlist
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BRAVE_COMPONENTS_PLAYLIST_BROWSER_PLAYLIST_BACKGROUND_WEBCONTENTS_HELPER_H_
|
||||
#define BRAVE_COMPONENTS_PLAYLIST_BROWSER_PLAYLIST_BACKGROUND_WEBCONTENTS_HELPER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "content/public/browser/web_contents_user_data.h"
|
||||
|
||||
namespace content {
|
||||
class NavigationHandle;
|
||||
class WebContents;
|
||||
} // namespace content
|
||||
|
||||
namespace playlist {
|
||||
|
||||
class PlaylistBackgroundWebContentsHelper final
|
||||
: public content::WebContentsUserData<PlaylistBackgroundWebContentsHelper>,
|
||||
public content::WebContentsObserver {
|
||||
public:
|
||||
PlaylistBackgroundWebContentsHelper(
|
||||
const PlaylistBackgroundWebContentsHelper&) = delete;
|
||||
PlaylistBackgroundWebContentsHelper& operator=(
|
||||
const PlaylistBackgroundWebContentsHelper&) = delete;
|
||||
~PlaylistBackgroundWebContentsHelper() override;
|
||||
|
||||
// content::WebContentsObserver:
|
||||
void ReadyToCommitNavigation(
|
||||
content::NavigationHandle* navigation_handle) override;
|
||||
|
||||
private:
|
||||
friend class content::WebContentsUserData<
|
||||
PlaylistBackgroundWebContentsHelper>;
|
||||
|
||||
PlaylistBackgroundWebContentsHelper(
|
||||
content::WebContents* web_contents,
|
||||
const std::string& media_source_api_suppressor,
|
||||
const std::string& media_detector);
|
||||
|
||||
std::string media_source_api_suppressor_;
|
||||
std::string media_detector_;
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_DECL();
|
||||
};
|
||||
|
||||
} // namespace playlist
|
||||
|
||||
#endif // BRAVE_COMPONENTS_PLAYLIST_BROWSER_PLAYLIST_BACKGROUND_WEBCONTENTS_HELPER_H_
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/timer.h"
|
||||
#include "brave/components/playlist/browser/playlist_background_webcontents_helper.h"
|
||||
#include "brave/components/playlist/browser/playlist_service.h"
|
||||
#include "brave/components/playlist/browser/playlist_tab_helper.h"
|
||||
#include "brave/components/playlist/common/features.h"
|
||||
@@ -31,19 +32,6 @@
|
||||
|
||||
namespace playlist {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int32_t kInvalidWorldID = -1;
|
||||
|
||||
int32_t g_playlist_javascript_world_id = kInvalidWorldID;
|
||||
bool g_run_script_on_main_world = false;
|
||||
|
||||
bool PlaylistJavaScriptWorldIdIsSet() {
|
||||
return g_playlist_javascript_world_id != kInvalidWorldID;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PlaylistDownloadRequestManager::Request::Request() = default;
|
||||
PlaylistDownloadRequestManager::Request&
|
||||
PlaylistDownloadRequestManager::Request::operator=(
|
||||
@@ -52,24 +40,6 @@ PlaylistDownloadRequestManager::Request::Request(
|
||||
PlaylistDownloadRequestManager::Request&&) noexcept = default;
|
||||
PlaylistDownloadRequestManager::Request::~Request() = default;
|
||||
|
||||
// static
|
||||
void PlaylistDownloadRequestManager::SetPlaylistJavaScriptWorldId(
|
||||
const int32_t id) {
|
||||
// Never allow running in main world (0).
|
||||
CHECK(id > content::ISOLATED_WORLD_ID_CONTENT_END);
|
||||
// Only allow ID to be set once.
|
||||
if (PlaylistJavaScriptWorldIdIsSet()) {
|
||||
CHECK_IS_TEST();
|
||||
}
|
||||
g_playlist_javascript_world_id = id;
|
||||
}
|
||||
|
||||
// static
|
||||
void PlaylistDownloadRequestManager::SetRunScriptOnMainWorldForTest() {
|
||||
CHECK_IS_TEST();
|
||||
g_run_script_on_main_world = true;
|
||||
}
|
||||
|
||||
PlaylistDownloadRequestManager::PlaylistDownloadRequestManager(
|
||||
PlaylistService* service,
|
||||
content::BrowserContext* context,
|
||||
@@ -80,15 +50,16 @@ PlaylistDownloadRequestManager::PlaylistDownloadRequestManager(
|
||||
|
||||
PlaylistDownloadRequestManager::~PlaylistDownloadRequestManager() = default;
|
||||
|
||||
void PlaylistDownloadRequestManager::CreateWebContents(
|
||||
bool should_force_fake_ua) {
|
||||
void PlaylistDownloadRequestManager::CreateWebContents(const Request& request) {
|
||||
content::WebContents::CreateParams create_params(context_, nullptr);
|
||||
create_params.is_never_visible = true;
|
||||
web_contents_ = content::WebContents::Create(create_params);
|
||||
web_contents_->SetAudioMuted(true);
|
||||
PlaylistTabHelper::MaybeCreateForWebContents(web_contents_.get(),
|
||||
service_.get());
|
||||
if (should_force_fake_ua ||
|
||||
PlaylistBackgroundWebContentsHelper::CreateForWebContents(
|
||||
web_contents_.get(),
|
||||
media_detector_component_manager_->GetMediaSourceAPISuppressorScript(),
|
||||
media_detector_component_manager_->GetMediaDetectorScript(request.url));
|
||||
if (request.should_force_fake_ua ||
|
||||
base::FeatureList::IsEnabled(features::kPlaylistFakeUA)) {
|
||||
DVLOG(2) << __func__ << " Faked UA to detect media files";
|
||||
blink::UserAgentOverride user_agent(
|
||||
@@ -136,7 +107,6 @@ void PlaylistDownloadRequestManager::FetchPendingRequest() {
|
||||
|
||||
void PlaylistDownloadRequestManager::RunMediaDetector(Request request) {
|
||||
DVLOG(2) << __func__;
|
||||
CHECK(PlaylistJavaScriptWorldIdIsSet());
|
||||
|
||||
DCHECK_GE(in_progress_urls_count_, 0);
|
||||
in_progress_urls_count_++;
|
||||
@@ -150,7 +120,7 @@ void PlaylistDownloadRequestManager::RunMediaDetector(Request request) {
|
||||
|
||||
// Start to request on clean slate, so that result won't be affected by
|
||||
// previous page.
|
||||
CreateWebContents(request.should_force_fake_ua);
|
||||
CreateWebContents(request);
|
||||
|
||||
DCHECK(request.url.is_valid());
|
||||
DCHECK(web_contents_);
|
||||
@@ -177,54 +147,14 @@ bool PlaylistDownloadRequestManager::ReadyToRunMediaDetectorScript() const {
|
||||
return in_progress_urls_count_ == 0;
|
||||
}
|
||||
|
||||
void PlaylistDownloadRequestManager::GetMedia(
|
||||
content::WebContents* contents,
|
||||
base::OnceCallback<void(std::vector<mojom::PlaylistItemPtr>)> cb) {
|
||||
void PlaylistDownloadRequestManager::
|
||||
MaybeResetBackgroundWebContentsAndFetchNextRequest(
|
||||
const std::vector<mojom::PlaylistItemPtr>& items,
|
||||
content::WebContents* contents) {
|
||||
DVLOG(2) << __func__;
|
||||
CHECK(contents && contents->GetPrimaryMainFrame());
|
||||
CHECK(contents);
|
||||
|
||||
const auto& media_detector_script =
|
||||
media_detector_component_manager_->GetMediaDetectorScript(
|
||||
contents->GetLastCommittedURL());
|
||||
DCHECK(!media_detector_script.empty());
|
||||
|
||||
auto callback = base::BindOnce(
|
||||
&PlaylistDownloadRequestManager::OnGetMedia, weak_factory_.GetWeakPtr(),
|
||||
contents->GetWeakPtr(), contents->GetLastCommittedURL(), std::move(cb));
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
content::RenderFrameHost::AllowInjectingJavaScript();
|
||||
PlaylistTabHelper::FromWebContents(contents)->RequestAsyncExecuteScript(
|
||||
content::ISOLATED_WORLD_ID_GLOBAL /* main_world*/,
|
||||
base::UTF8ToUTF16(media_detector_script), std::move(callback));
|
||||
#else
|
||||
if (g_run_script_on_main_world) {
|
||||
PlaylistTabHelper::FromWebContents(contents)->RequestAsyncExecuteScript(
|
||||
content::ISOLATED_WORLD_ID_GLOBAL /* main_world*/,
|
||||
base::UTF8ToUTF16(media_detector_script), std::move(callback));
|
||||
} else {
|
||||
CHECK(PlaylistJavaScriptWorldIdIsSet());
|
||||
PlaylistTabHelper::FromWebContents(contents)->RequestAsyncExecuteScript(
|
||||
g_playlist_javascript_world_id,
|
||||
base::UTF8ToUTF16(media_detector_script), std::move(callback));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PlaylistDownloadRequestManager::OnGetMedia(
|
||||
base::WeakPtr<content::WebContents> contents,
|
||||
GURL url,
|
||||
base::OnceCallback<void(std::vector<mojom::PlaylistItemPtr>)> cb,
|
||||
base::Value value) {
|
||||
if (!contents) {
|
||||
return;
|
||||
}
|
||||
|
||||
DVLOG(2) << __func__;
|
||||
|
||||
auto items = ProcessFoundMedia(std::move(value), url);
|
||||
|
||||
if (contents.get() == background_contents() && items.size()) {
|
||||
if (contents == background_contents() && items.size()) {
|
||||
CHECK(!callback_for_current_request_.is_null()) << " callback already ran";
|
||||
auto callback = std::move(callback_for_current_request_);
|
||||
|
||||
@@ -239,14 +169,12 @@ void PlaylistDownloadRequestManager::OnGetMedia(
|
||||
web_contents_.reset();
|
||||
}
|
||||
|
||||
std::move(cb).Run(std::move(items));
|
||||
|
||||
FetchPendingRequest();
|
||||
}
|
||||
|
||||
std::vector<mojom::PlaylistItemPtr>
|
||||
PlaylistDownloadRequestManager::ProcessFoundMedia(base::Value value,
|
||||
GURL page_url) {
|
||||
PlaylistDownloadRequestManager::GetPlaylistItems(base::Value value,
|
||||
GURL page_url) {
|
||||
/* Expected output:
|
||||
[
|
||||
{
|
||||
@@ -395,22 +323,6 @@ bool PlaylistDownloadRequestManager::
|
||||
<< "CanCacheMedia() should be true when this method is called";
|
||||
}
|
||||
|
||||
void PlaylistDownloadRequestManager::ConfigureWebPrefsForBackgroundWebContents(
|
||||
content::WebContents* web_contents,
|
||||
blink::web_pref::WebPreferences* web_prefs) {
|
||||
if (!service_->playlist_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
web_prefs->should_detect_media_files = true;
|
||||
|
||||
if (web_contents_ && web_contents_.get() == web_contents) {
|
||||
// Background web contents.
|
||||
web_prefs->force_cosmetic_filtering = true;
|
||||
web_prefs->hide_media_src_api = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PlaylistDownloadRequestManager::ResetRequests() {
|
||||
if (web_contents_) {
|
||||
web_contents_.reset();
|
||||
@@ -425,7 +337,7 @@ void PlaylistDownloadRequestManager::ResetRequests() {
|
||||
content::WebContents*
|
||||
PlaylistDownloadRequestManager::GetBackgroundWebContentsForTesting() {
|
||||
if (!web_contents_) {
|
||||
CreateWebContents(false);
|
||||
CreateWebContents();
|
||||
}
|
||||
|
||||
return web_contents_.get();
|
||||
|
||||
@@ -23,10 +23,6 @@ namespace base {
|
||||
class Value;
|
||||
} // namespace base
|
||||
|
||||
namespace blink::web_pref {
|
||||
struct WebPreferences;
|
||||
} // namespace blink::web_pref
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
} // namespace content
|
||||
@@ -39,9 +35,6 @@ class PlaylistService;
|
||||
// by injecting media detector script to dedicated WebContents.
|
||||
class PlaylistDownloadRequestManager {
|
||||
public:
|
||||
static void SetRunScriptOnMainWorldForTest();
|
||||
static void SetPlaylistJavaScriptWorldId(const int32_t id);
|
||||
|
||||
struct Request {
|
||||
using Callback =
|
||||
base::OnceCallback<void(std::vector<mojom::PlaylistItemPtr>)>;
|
||||
@@ -72,10 +65,6 @@ class PlaylistDownloadRequestManager {
|
||||
// Request::callback will be called with generated param.
|
||||
virtual void GetMediaFilesFromPage(Request request);
|
||||
|
||||
// Update |web_prefs| if we want for |web_contents|.
|
||||
void ConfigureWebPrefsForBackgroundWebContents(
|
||||
content::WebContents* web_contents,
|
||||
blink::web_pref::WebPreferences* web_prefs);
|
||||
void ResetRequests();
|
||||
|
||||
const content::WebContents* background_contents() const {
|
||||
@@ -93,9 +82,11 @@ class PlaylistDownloadRequestManager {
|
||||
return media_detector_component_manager_;
|
||||
}
|
||||
|
||||
void GetMedia(
|
||||
content::WebContents* contents,
|
||||
base::OnceCallback<void(std::vector<mojom::PlaylistItemPtr>)> cb);
|
||||
std::vector<mojom::PlaylistItemPtr> GetPlaylistItems(base::Value value,
|
||||
GURL page_url);
|
||||
void MaybeResetBackgroundWebContentsAndFetchNextRequest(
|
||||
const std::vector<mojom::PlaylistItemPtr>& items,
|
||||
content::WebContents* contents);
|
||||
|
||||
bool CanCacheMedia(const mojom::PlaylistItemPtr& item) const;
|
||||
bool ShouldExtractMediaFromBackgroundWebContents(
|
||||
@@ -110,14 +101,7 @@ class PlaylistDownloadRequestManager {
|
||||
void RunMediaDetector(Request request);
|
||||
|
||||
bool ReadyToRunMediaDetectorScript() const;
|
||||
void CreateWebContents(bool should_force_fake_ua);
|
||||
void OnGetMedia(
|
||||
base::WeakPtr<content::WebContents> contents,
|
||||
GURL url,
|
||||
base::OnceCallback<void(std::vector<mojom::PlaylistItemPtr>)> cb,
|
||||
base::Value value);
|
||||
std::vector<mojom::PlaylistItemPtr> ProcessFoundMedia(base::Value value,
|
||||
GURL page_url);
|
||||
void CreateWebContents(const Request& request = {});
|
||||
|
||||
// Pop a task from queue and detect media from the page if any.
|
||||
void FetchPendingRequest();
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "brave/components/playlist/browser/playlist_media_handler.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "brave/components/playlist/browser/playlist_service.h"
|
||||
|
||||
namespace playlist {
|
||||
@@ -20,8 +22,13 @@ PlaylistMediaHandler::~PlaylistMediaHandler() {
|
||||
DVLOG(2) << __FUNCTION__ << " " << frame_id_;
|
||||
}
|
||||
|
||||
void PlaylistMediaHandler::OnMediaUpdatedFromRenderFrame() {
|
||||
void PlaylistMediaHandler::OnMediaDetected(base::Value media) {
|
||||
DVLOG(2) << __FUNCTION__ << " " << frame_id_;
|
||||
|
||||
if (!service_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* render_frame_host = content::RenderFrameHost::FromID(frame_id_);
|
||||
if (!render_frame_host) {
|
||||
return;
|
||||
@@ -33,9 +40,7 @@ void PlaylistMediaHandler::OnMediaUpdatedFromRenderFrame() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (service_) {
|
||||
service_->OnMediaUpdatedFromContents(web_contents);
|
||||
}
|
||||
service_->OnMediaDetected(std::move(media), web_contents);
|
||||
}
|
||||
|
||||
} // namespace playlist
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define BRAVE_COMPONENTS_PLAYLIST_BROWSER_PLAYLIST_MEDIA_HANDLER_H_
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/values.h"
|
||||
#include "brave/components/playlist/common/mojom/playlist.mojom.h"
|
||||
#include "content/public/browser/global_routing_id.h"
|
||||
|
||||
@@ -20,7 +21,7 @@ class PlaylistMediaHandler : public mojom::PlaylistMediaHandler {
|
||||
~PlaylistMediaHandler() override;
|
||||
|
||||
// mojom::PlaylistMediaHandler:
|
||||
void OnMediaUpdatedFromRenderFrame() override;
|
||||
void OnMediaDetected(base::Value media) override;
|
||||
|
||||
private:
|
||||
content::GlobalRenderFrameHostId frame_id_;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/strings/string_split.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "brave/components/playlist/browser/playlist_background_webcontents_helper.h"
|
||||
#include "brave/components/playlist/browser/playlist_constants.h"
|
||||
#include "brave/components/playlist/browser/playlist_tab_helper.h"
|
||||
#include "brave/components/playlist/browser/pref_names.h"
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "components/user_prefs/user_prefs.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "net/base/filename_util.h"
|
||||
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
|
||||
|
||||
namespace playlist {
|
||||
namespace {
|
||||
@@ -364,11 +366,7 @@ void PlaylistService::NotifyPlaylistChanged(mojom::PlaylistEvent playlist_event,
|
||||
|
||||
void PlaylistService::NotifyMediaFilesUpdated(
|
||||
const GURL& url,
|
||||
std::vector<mojom::PlaylistItemPtr> items) {
|
||||
if (items.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<mojom::PlaylistItemPtr>& items) {
|
||||
DVLOG(2) << __FUNCTION__ << " Media files from " << url.spec()
|
||||
<< " were updated: count =>" << items.size();
|
||||
|
||||
@@ -417,12 +415,11 @@ void PlaylistService::ConfigureWebPrefsForBackgroundWebContents(
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PlaylistTabHelper::FromWebContents(web_contents)) {
|
||||
if (!PlaylistBackgroundWebContentsHelper::FromWebContents(web_contents)) {
|
||||
return;
|
||||
}
|
||||
|
||||
download_request_manager_->ConfigureWebPrefsForBackgroundWebContents(
|
||||
web_contents, web_prefs);
|
||||
web_prefs->force_cosmetic_filtering = true;
|
||||
}
|
||||
|
||||
base::WeakPtr<PlaylistService> PlaylistService::GetWeakPtr() {
|
||||
@@ -532,6 +529,11 @@ bool PlaylistService::ShouldExtractMediaFromBackgroundWebContents(
|
||||
});
|
||||
}
|
||||
|
||||
std::string PlaylistService::GetMediaDetectorScript(const GURL& url) const {
|
||||
return download_request_manager_->media_detector_component_manager()
|
||||
->GetMediaDetectorScript(url);
|
||||
}
|
||||
|
||||
void PlaylistService::AddMediaFilesFromActiveTabToPlaylist(
|
||||
const std::string& playlist_id,
|
||||
bool can_cache,
|
||||
@@ -1198,16 +1200,20 @@ void PlaylistService::AddObserver(
|
||||
observers_.Add(std::move(observer));
|
||||
}
|
||||
|
||||
void PlaylistService::OnMediaUpdatedFromContents(
|
||||
content::WebContents* contents) {
|
||||
void PlaylistService::OnMediaDetected(base::Value media,
|
||||
content::WebContents* contents) {
|
||||
if (!*enabled_pref_) {
|
||||
return;
|
||||
}
|
||||
|
||||
download_request_manager_->GetMedia(
|
||||
contents, base::BindOnce(&PlaylistService::NotifyMediaFilesUpdated,
|
||||
weak_factory_.GetWeakPtr(),
|
||||
contents->GetLastCommittedURL()));
|
||||
CHECK(contents);
|
||||
const GURL url = contents->GetLastCommittedURL();
|
||||
const auto items =
|
||||
download_request_manager_->GetPlaylistItems(std::move(media), url);
|
||||
NotifyMediaFilesUpdated(url, items);
|
||||
|
||||
download_request_manager_->MaybeResetBackgroundWebContentsAndFetchNextRequest(
|
||||
items, contents);
|
||||
}
|
||||
|
||||
void PlaylistService::OnMediaFileDownloadProgressed(
|
||||
|
||||
@@ -217,7 +217,7 @@ class PlaylistService : public KeyedService,
|
||||
void AddObserver(
|
||||
mojo::PendingRemote<mojom::PlaylistServiceObserver> observer) override;
|
||||
|
||||
void OnMediaUpdatedFromContents(content::WebContents* contents);
|
||||
void OnMediaDetected(base::Value media, content::WebContents* contents);
|
||||
|
||||
bool HasPlaylistItem(const std::string& id) const;
|
||||
|
||||
@@ -239,6 +239,8 @@ class PlaylistService : public KeyedService,
|
||||
|
||||
bool playlist_enabled() const { return *enabled_pref_; }
|
||||
|
||||
std::string GetMediaDetectorScript(const GURL& url) const;
|
||||
|
||||
private:
|
||||
friend class ::CosmeticFilteringPlaylistFlagEnabledTest;
|
||||
friend class ::PlaylistBrowserTest;
|
||||
@@ -309,8 +311,9 @@ class PlaylistService : public KeyedService,
|
||||
// https://github.com/brave/brave-browser/issues/30735
|
||||
void NotifyPlaylistChanged(mojom::PlaylistEvent playlist_event,
|
||||
const std::string& playlist_id);
|
||||
void NotifyMediaFilesUpdated(const GURL& url,
|
||||
std::vector<mojom::PlaylistItemPtr> items);
|
||||
void NotifyMediaFilesUpdated(
|
||||
const GURL& url,
|
||||
const std::vector<mojom::PlaylistItemPtr>& items);
|
||||
|
||||
void UpdatePlaylistItemValue(const std::string& id, base::Value value);
|
||||
void RemovePlaylistItemValue(const std::string& id);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "components/user_prefs/user_prefs.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/navigation_handle.h"
|
||||
#include "mojo/public/cpp/bindings/associated_remote.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
@@ -182,14 +183,26 @@ void PlaylistTabHelper::ExtractMediaFromBackgroundWebContents(
|
||||
ExtractMediaFromBackgroundContents();
|
||||
}
|
||||
|
||||
void PlaylistTabHelper::RequestAsyncExecuteScript(
|
||||
int32_t world_id,
|
||||
const std::u16string& script,
|
||||
base::OnceCallback<void(base::Value)> cb) {
|
||||
GetRemote(web_contents()->GetPrimaryMainFrame())
|
||||
->RequestAsyncExecuteScript(
|
||||
world_id, script, blink::mojom::UserActivationOption::kActivate,
|
||||
blink::mojom::PromiseResultOption::kAwait, std::move(cb));
|
||||
void PlaylistTabHelper::ReadyToCommitNavigation(
|
||||
content::NavigationHandle* navigation_handle) {
|
||||
DVLOG(2) << __FUNCTION__;
|
||||
|
||||
if (!navigation_handle->IsInPrimaryMainFrame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const GURL url = navigation_handle->GetURL();
|
||||
if (!url.SchemeIsHTTPOrHTTPS()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mojo::AssociatedRemote<mojom::PlaylistRenderFrameObserverConfigurator>
|
||||
frame_observer_config;
|
||||
navigation_handle->GetRenderFrameHost()
|
||||
->GetRemoteAssociatedInterfaces()
|
||||
->GetInterface(&frame_observer_config);
|
||||
frame_observer_config->AddMediaDetector(
|
||||
service_->GetMediaDetectorScript(url));
|
||||
}
|
||||
|
||||
void PlaylistTabHelper::PrimaryPageChanged(content::Page& page) {
|
||||
@@ -279,6 +292,10 @@ void PlaylistTabHelper::OnItemLocalDataDeleted(const std::string& id) {
|
||||
void PlaylistTabHelper::OnMediaFilesUpdated(
|
||||
const GURL& url,
|
||||
std::vector<mojom::PlaylistItemPtr> items) {
|
||||
if (items.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
OnFoundMediaFromContents(url, std::move(items));
|
||||
}
|
||||
|
||||
@@ -480,19 +497,6 @@ void PlaylistTabHelper::OnPlaylistEnabledPrefChanged() {
|
||||
}
|
||||
}
|
||||
|
||||
mojo::AssociatedRemote<script_injector::mojom::ScriptInjector>&
|
||||
PlaylistTabHelper::GetRemote(content::RenderFrameHost* rfh) {
|
||||
if (rfh != script_injector_rfh_ || !script_injector_remote_.is_bound()) {
|
||||
script_injector_rfh_ = rfh;
|
||||
if (script_injector_remote_.is_bound()) {
|
||||
script_injector_remote_.reset();
|
||||
}
|
||||
rfh->GetRemoteAssociatedInterfaces()->GetInterface(
|
||||
&script_injector_remote_);
|
||||
}
|
||||
return script_injector_remote_;
|
||||
}
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_IMPL(PlaylistTabHelper);
|
||||
|
||||
} // namespace playlist
|
||||
|
||||
@@ -12,11 +12,9 @@
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/observer_list.h"
|
||||
#include "brave/components/playlist/common/mojom/playlist.mojom.h"
|
||||
#include "brave/components/script_injector/common/mojom/script_injector.mojom.h"
|
||||
#include "components/prefs/pref_member.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "content/public/browser/web_contents_user_data.h"
|
||||
#include "mojo/public/cpp/bindings/associated_remote.h"
|
||||
#include "mojo/public/cpp/bindings/receiver.h"
|
||||
|
||||
namespace playlist {
|
||||
@@ -69,11 +67,9 @@ class PlaylistTabHelper
|
||||
void ExtractMediaFromBackgroundWebContents(
|
||||
base::OnceCallback<void(bool)> extracted_callback);
|
||||
|
||||
void RequestAsyncExecuteScript(int32_t world_id,
|
||||
const std::u16string& script,
|
||||
base::OnceCallback<void(base::Value)> cb);
|
||||
|
||||
// content::WebContentsObserver:
|
||||
void ReadyToCommitNavigation(
|
||||
content::NavigationHandle* navigation_handle) override;
|
||||
void PrimaryPageChanged(content::Page& page) override;
|
||||
|
||||
// mojom::PlaylistServiceObserver:
|
||||
@@ -119,9 +115,6 @@ class PlaylistTabHelper
|
||||
|
||||
void OnPlaylistEnabledPrefChanged();
|
||||
|
||||
mojo::AssociatedRemote<script_injector::mojom::ScriptInjector>& GetRemote(
|
||||
content::RenderFrameHost* rfh);
|
||||
|
||||
raw_ptr<PlaylistService> service_;
|
||||
|
||||
GURL target_url_;
|
||||
@@ -143,10 +136,6 @@ class PlaylistTabHelper
|
||||
|
||||
BooleanPrefMember playlist_enabled_pref_;
|
||||
|
||||
mojo::AssociatedRemote<script_injector::mojom::ScriptInjector>
|
||||
script_injector_remote_;
|
||||
raw_ptr<content::RenderFrameHost> script_injector_rfh_ = nullptr;
|
||||
|
||||
base::WeakPtrFactory<PlaylistTabHelper> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<include name="IDR_PLAYLIST_HTML" file="../playlist/browser/resources/playlist.html" type="BINDATA" />
|
||||
<include name="IDR_PLAYLIST_PLAYER_HTML" file="../playlist/browser/resources/video_frame_contents.html" type="BINDATA" />
|
||||
</if>
|
||||
<include name="IDR_PLAYLIST_MEDIA_SOURCE_API_SUPPRESSOR_JS" file="../../third_party/playlist_component/src/media_source_api_suppressor.js" type="BINDATA" />
|
||||
<include name="IDR_PLAYLIST_MEDIA_DETECTOR_JS" file="../../third_party/playlist_component/src/index.js" type="BINDATA" />
|
||||
<include name="IDR_PLAYLIST_MEDIA_DETECTOR_YOUTUBE_JS" file="../../third_party/playlist_component/src/youtube.com.js" type="BINDATA" />
|
||||
</if>
|
||||
|
||||
@@ -192,7 +192,7 @@ interface PlaylistServiceObserver {
|
||||
// Interface that is used to notify that found media from renderer process to
|
||||
// browser process.
|
||||
interface PlaylistMediaHandler {
|
||||
OnMediaUpdatedFromRenderFrame();
|
||||
OnMediaDetected(mojo_base.mojom.Value media);
|
||||
};
|
||||
|
||||
interface PlaylistStreamingObserver {
|
||||
@@ -200,3 +200,8 @@ interface PlaylistStreamingObserver {
|
||||
OnDataReceived(array<uint8> data_received);
|
||||
OnDataCompleted();
|
||||
};
|
||||
|
||||
interface PlaylistRenderFrameObserverConfigurator {
|
||||
AddMediaSourceAPISuppressor(string media_source_api_suppressor);
|
||||
AddMediaDetector(string media_detector);
|
||||
};
|
||||
|
||||
@@ -5,28 +5,59 @@
|
||||
|
||||
#include "brave/components/playlist/renderer/playlist_render_frame_observer.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "content/public/renderer/v8_value_converter.h"
|
||||
#include "gin/converter.h"
|
||||
#include "gin/function_template.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
|
||||
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
|
||||
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
|
||||
#include "third_party/blink/public/platform/web_string.h"
|
||||
#include "third_party/blink/public/web/blink.h"
|
||||
#include "third_party/blink/public/web/web_local_frame.h"
|
||||
#include "third_party/blink/public/web/web_script_source.h"
|
||||
#include "url/gurl.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace gin {
|
||||
|
||||
template <>
|
||||
struct Converter<base::Value> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> v8_value,
|
||||
base::Value* out) {
|
||||
if (v8_value.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<base::Value> base_value =
|
||||
content::V8ValueConverter::Create()->FromV8Value(
|
||||
v8_value, isolate->GetCurrentContext());
|
||||
if (!base_value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = std::move(*base_value);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gin
|
||||
|
||||
namespace playlist {
|
||||
|
||||
PlaylistRenderFrameObserver::PlaylistRenderFrameObserver(
|
||||
content::RenderFrame* render_frame,
|
||||
content::RenderFrame* frame,
|
||||
int32_t isolated_world_id)
|
||||
: RenderFrameObserver(render_frame),
|
||||
RenderFrameObserverTracker<PlaylistRenderFrameObserver>(render_frame),
|
||||
: RenderFrameObserver(frame),
|
||||
RenderFrameObserverTracker<PlaylistRenderFrameObserver>(frame),
|
||||
isolated_world_id_(isolated_world_id) {
|
||||
EnsureConnectedToMediaHandler();
|
||||
render_frame()
|
||||
->GetAssociatedInterfaceRegistry()
|
||||
->AddInterface<mojom::PlaylistRenderFrameObserverConfigurator>(
|
||||
base::BindRepeating(&PlaylistRenderFrameObserver::BindConfigurator,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
PlaylistRenderFrameObserver::~PlaylistRenderFrameObserver() = default;
|
||||
@@ -35,137 +66,100 @@ void PlaylistRenderFrameObserver::OnDestruct() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool PlaylistRenderFrameObserver::EnsureConnectedToMediaHandler() {
|
||||
if (!media_handler_.is_bound()) {
|
||||
render_frame()->GetBrowserInterfaceBroker()->GetInterface(
|
||||
media_handler_.BindNewPipeAndPassReceiver());
|
||||
media_handler_.set_disconnect_handler(
|
||||
base::BindOnce(&PlaylistRenderFrameObserver::OnMediaHandlerDisconnect,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
}
|
||||
void PlaylistRenderFrameObserver::AddMediaSourceAPISuppressor(
|
||||
const std::string& media_source_api_suppressor) {
|
||||
DVLOG(2) << __FUNCTION__;
|
||||
|
||||
return media_handler_.is_bound();
|
||||
media_source_api_suppressor_ = media_source_api_suppressor;
|
||||
CHECK(!media_source_api_suppressor_->empty());
|
||||
}
|
||||
|
||||
void PlaylistRenderFrameObserver::OnMediaHandlerDisconnect() {
|
||||
media_handler_.reset();
|
||||
EnsureConnectedToMediaHandler();
|
||||
void PlaylistRenderFrameObserver::AddMediaDetector(
|
||||
const std::string& media_detector) {
|
||||
DVLOG(2) << __FUNCTION__;
|
||||
|
||||
media_detector_ = media_detector;
|
||||
CHECK(!media_detector_->empty());
|
||||
}
|
||||
|
||||
void PlaylistRenderFrameObserver::BindConfigurator(
|
||||
mojo::PendingAssociatedReceiver<
|
||||
mojom::PlaylistRenderFrameObserverConfigurator> receiver) {
|
||||
configurator_receiver_.reset();
|
||||
configurator_receiver_.Bind(std::move(receiver));
|
||||
}
|
||||
|
||||
const mojo::Remote<playlist::mojom::PlaylistMediaHandler>&
|
||||
PlaylistRenderFrameObserver::GetMediaHandler() {
|
||||
if (!media_handler_) {
|
||||
render_frame()->GetBrowserInterfaceBroker()->GetInterface(
|
||||
media_handler_.BindNewPipeAndPassReceiver());
|
||||
media_handler_.reset_on_disconnect();
|
||||
}
|
||||
|
||||
return media_handler_;
|
||||
}
|
||||
|
||||
void PlaylistRenderFrameObserver::RunScriptsAtDocumentStart() {
|
||||
if (render_frame()->GetWebFrame()->IsProvisional()) {
|
||||
return;
|
||||
}
|
||||
if (media_source_api_suppressor_) {
|
||||
v8::Isolate* isolate = blink::MainThreadIsolate();
|
||||
v8::Isolate::Scope isolate_scope(isolate);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
const auto& blink_preferences = render_frame()->GetBlinkPreferences();
|
||||
if (blink_preferences.hide_media_src_api) {
|
||||
HideMediaSourceAPI();
|
||||
}
|
||||
|
||||
if (blink_preferences.should_detect_media_files) {
|
||||
InstallMediaDetector();
|
||||
Inject(*media_source_api_suppressor_,
|
||||
render_frame()->GetWebFrame()->MainWorldScriptContext());
|
||||
}
|
||||
}
|
||||
|
||||
// Disables the MediaSource API in hope of the page switching to
|
||||
// network-fetchable HTTPS URLs. This script is from
|
||||
// https://github.com/brave/brave-ios/blob/development/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Paged/PlaylistSwizzlerScript.js
|
||||
void PlaylistRenderFrameObserver::HideMediaSourceAPI() const {
|
||||
DVLOG(2) << __FUNCTION__;
|
||||
void PlaylistRenderFrameObserver::RunScriptsAtDocumentEnd() {
|
||||
if (media_detector_) {
|
||||
v8::Isolate* isolate = blink::MainThreadIsolate();
|
||||
v8::Isolate::Scope isolate_scope(isolate);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
render_frame()->GetWebFrame()->ExecuteScript(
|
||||
blink::WebScriptSource(blink::WebString::FromASCII(R"(
|
||||
(function() {
|
||||
if (
|
||||
window.MediaSource ||
|
||||
window.WebKitMediaSource ||
|
||||
window.HTMLMediaElement && HTMLMediaElement.prototype.webkitSourceAddId
|
||||
) {
|
||||
delete window.MediaSource;
|
||||
delete window.WebKitMediaSource;
|
||||
}
|
||||
})();)")));
|
||||
v8::Local<v8::Context> context =
|
||||
#if !BUILDFLAG(IS_ANDROID)
|
||||
render_frame()->GetWebFrame()->GetScriptContextFromWorldId(
|
||||
isolate, isolated_world_id_);
|
||||
#else
|
||||
render_frame()->GetWebFrame()->MainWorldScriptContext();
|
||||
#endif
|
||||
v8::Local<v8::Function> on_media_detected =
|
||||
gin::CreateFunctionTemplate(
|
||||
isolate,
|
||||
base::BindRepeating(&PlaylistRenderFrameObserver::OnMediaDetected,
|
||||
weak_ptr_factory_.GetWeakPtr()))
|
||||
->GetFunction(context)
|
||||
.ToLocalChecked();
|
||||
Inject(*media_detector_, context, {on_media_detected.As<v8::Value>()});
|
||||
}
|
||||
}
|
||||
|
||||
void PlaylistRenderFrameObserver::InstallMediaDetector() {
|
||||
void PlaylistRenderFrameObserver::Inject(
|
||||
const std::string& script_text,
|
||||
v8::Local<v8::Context> context,
|
||||
std::vector<v8::Local<v8::Value>> args) const {
|
||||
DVLOG(2) << __FUNCTION__;
|
||||
|
||||
static const char kScript[] = R"(
|
||||
(function(onMediaUpdated) {
|
||||
// Firstly, we try to get find all <video> or <audio> tags periodically,
|
||||
// for a a while from the start up. If we find them, then we attach
|
||||
// MutationObservers to them to detect source URL.
|
||||
// After a given amount of time, we do this in requestIdleCallback().
|
||||
// Note that there's a global object named |pl_worker|. This worker is
|
||||
// created and bound by PlaylistJSHandler.
|
||||
|
||||
const mutationSources = new Set();
|
||||
const mutationObserver = new MutationObserver(mutations => {
|
||||
mutations.forEach(mutation => { onMediaUpdated(window.location.href); })
|
||||
});
|
||||
const findNewMediaAndObserveMutation = () => {
|
||||
return document.querySelectorAll('video, audio').forEach(
|
||||
(mediaNode) => {
|
||||
if (mutationSources.has(mediaNode)) return
|
||||
|
||||
mutationSources.add(mediaNode)
|
||||
onMediaUpdated(window.location.href)
|
||||
mutationObserver.observe(mediaNode, { attributeFilter: ['src'] })
|
||||
});
|
||||
}
|
||||
|
||||
const pollingIntervalId = window.setInterval(
|
||||
findNewMediaAndObserveMutation, 1000);
|
||||
window.setTimeout(() => {
|
||||
window.clearInterval(pollingIntervalId)
|
||||
window.requestIdleCallback(findNewMediaAndObserveMutation)
|
||||
// TODO(sko) We might want to check if idle callback is waiting too
|
||||
// long. In that case, we should get back to the polling style. And
|
||||
// also, this time could be too long for production.
|
||||
}, 20000)
|
||||
|
||||
// Try getting media after page was restored or navigated back.
|
||||
window.addEventListener(
|
||||
'pageshow', () => { onMediaUpdated(window.location.href); });
|
||||
})
|
||||
)";
|
||||
|
||||
v8::Isolate* isolate = blink::MainThreadIsolate();
|
||||
v8::Isolate::Scope isolate_scope(isolate);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> context =
|
||||
render_frame()->GetWebFrame()->GetScriptContextFromWorldId(
|
||||
isolate, isolated_world_id_);
|
||||
v8::Context::Scope context_scope(context);
|
||||
v8::MicrotasksScope microtasks_scope(
|
||||
context, v8::MicrotasksScope::kDoNotRunMicrotasks);
|
||||
|
||||
v8::Local<v8::Script> script =
|
||||
v8::Script::Compile(context, gin::StringToV8(isolate, kScript))
|
||||
v8::Script::Compile(context,
|
||||
gin::StringToV8(context->GetIsolate(), script_text))
|
||||
.ToLocalChecked();
|
||||
v8::Local<v8::Function> function =
|
||||
v8::Local<v8::Function>::Cast(script->Run(context).ToLocalChecked());
|
||||
|
||||
v8::Local<v8::Function> on_media_updated =
|
||||
gin::CreateFunctionTemplate(
|
||||
context->GetIsolate(),
|
||||
base::BindRepeating(&PlaylistRenderFrameObserver::OnMediaUpdated,
|
||||
weak_ptr_factory_.GetWeakPtr()))
|
||||
->GetFunction(context)
|
||||
.ToLocalChecked();
|
||||
v8::Local<v8::Value> arg = on_media_updated.As<v8::Value>();
|
||||
|
||||
std::ignore = function->Call(context, context->Global(), 1, &arg);
|
||||
std::ignore = function->Call(context, context->Global(), args.size(),
|
||||
args.empty() ? nullptr : args.data());
|
||||
}
|
||||
|
||||
void PlaylistRenderFrameObserver::OnMediaUpdated(const std::string& page_url) {
|
||||
if (!GURL(page_url).SchemeIsHTTPOrHTTPS()) {
|
||||
return;
|
||||
}
|
||||
void PlaylistRenderFrameObserver::OnMediaDetected(base::Value media) {
|
||||
DVLOG(2) << __FUNCTION__;
|
||||
|
||||
DVLOG(2) << __FUNCTION__ << " " << page_url;
|
||||
|
||||
media_handler_->OnMediaUpdatedFromRenderFrame();
|
||||
GetMediaHandler()->OnMediaDetected(std::move(media));
|
||||
}
|
||||
|
||||
} // namespace playlist
|
||||
|
||||
@@ -6,41 +6,64 @@
|
||||
#ifndef BRAVE_COMPONENTS_PLAYLIST_RENDERER_PLAYLIST_RENDER_FRAME_OBSERVER_H_
|
||||
#define BRAVE_COMPONENTS_PLAYLIST_RENDERER_PLAYLIST_RENDER_FRAME_OBSERVER_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/values.h"
|
||||
#include "brave/components/playlist/common/mojom/playlist.mojom.h"
|
||||
#include "content/public/renderer/render_frame_observer.h"
|
||||
#include "content/public/renderer/render_frame_observer_tracker.h"
|
||||
#include "mojo/public/cpp/bindings/associated_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
|
||||
namespace playlist {
|
||||
|
||||
class PlaylistRenderFrameObserver final
|
||||
: public content::RenderFrameObserver,
|
||||
public content::RenderFrameObserverTracker<PlaylistRenderFrameObserver> {
|
||||
public content::RenderFrameObserverTracker<PlaylistRenderFrameObserver>,
|
||||
public mojom::PlaylistRenderFrameObserverConfigurator {
|
||||
public:
|
||||
PlaylistRenderFrameObserver(content::RenderFrame* render_frame,
|
||||
PlaylistRenderFrameObserver(content::RenderFrame* frame,
|
||||
int32_t isolated_world_id);
|
||||
|
||||
PlaylistRenderFrameObserver(const PlaylistRenderFrameObserver&) = delete;
|
||||
PlaylistRenderFrameObserver& operator=(const PlaylistRenderFrameObserver&) =
|
||||
delete;
|
||||
|
||||
void RunScriptsAtDocumentStart();
|
||||
void RunScriptsAtDocumentEnd();
|
||||
|
||||
private:
|
||||
// RenderFrameObserver:
|
||||
~PlaylistRenderFrameObserver() override;
|
||||
|
||||
// RenderFrameObserver:
|
||||
void OnDestruct() override;
|
||||
|
||||
bool EnsureConnectedToMediaHandler();
|
||||
void OnMediaHandlerDisconnect();
|
||||
// mojom::PlaylistRenderFrameObserverConfigurator
|
||||
void AddMediaSourceAPISuppressor(
|
||||
const std::string& media_source_api_suppressor) override;
|
||||
void AddMediaDetector(const std::string& media_detector) override;
|
||||
|
||||
void HideMediaSourceAPI() const;
|
||||
void InstallMediaDetector();
|
||||
void BindConfigurator(
|
||||
mojo::PendingAssociatedReceiver<
|
||||
mojom::PlaylistRenderFrameObserverConfigurator> receiver);
|
||||
|
||||
void OnMediaUpdated(const std::string& page_url);
|
||||
const mojo::Remote<playlist::mojom::PlaylistMediaHandler>& GetMediaHandler();
|
||||
|
||||
void Inject(const std::string& script_text,
|
||||
v8::Local<v8::Context> context,
|
||||
std::vector<v8::Local<v8::Value>> args = {}) const;
|
||||
void OnMediaDetected(base::Value media);
|
||||
|
||||
private:
|
||||
int32_t isolated_world_id_;
|
||||
mojo::AssociatedReceiver<mojom::PlaylistRenderFrameObserverConfigurator>
|
||||
configurator_receiver_{this};
|
||||
mojo::Remote<playlist::mojom::PlaylistMediaHandler> media_handler_;
|
||||
std::optional<std::string> media_source_api_suppressor_;
|
||||
std::optional<std::string> media_detector_;
|
||||
base::WeakPtrFactory<PlaylistRenderFrameObserver> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
|
||||
@@ -215,6 +215,20 @@ void BraveContentRendererClient::RunScriptsAtDocumentStart(
|
||||
ChromeContentRendererClient::RunScriptsAtDocumentStart(render_frame);
|
||||
}
|
||||
|
||||
void BraveContentRendererClient::RunScriptsAtDocumentEnd(
|
||||
content::RenderFrame* render_frame) {
|
||||
#if BUILDFLAG(ENABLE_PLAYLIST)
|
||||
if (base::FeatureList::IsEnabled(playlist::features::kPlaylist)) {
|
||||
if (auto* playlist_observer =
|
||||
playlist::PlaylistRenderFrameObserver::Get(render_frame)) {
|
||||
playlist_observer->RunScriptsAtDocumentEnd();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ChromeContentRendererClient::RunScriptsAtDocumentEnd(render_frame);
|
||||
}
|
||||
|
||||
void BraveContentRendererClient::WillEvaluateServiceWorkerOnWorkerThread(
|
||||
blink::WebServiceWorkerContextProxy* context_proxy,
|
||||
v8::Local<v8::Context> v8_context,
|
||||
|
||||
@@ -32,6 +32,7 @@ class BraveContentRendererClient : public ChromeContentRendererClient {
|
||||
void RenderFrameCreated(content::RenderFrame* render_frame) override;
|
||||
void GetSupportedKeySystems(media::GetSupportedKeySystemsCB cb) override;
|
||||
void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override;
|
||||
void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override;
|
||||
void WillEvaluateServiceWorkerOnWorkerThread(
|
||||
blink::WebServiceWorkerContextProxy* context_proxy,
|
||||
v8::Local<v8::Context> v8_context,
|
||||
|
||||
Reference in New Issue
Block a user