0

Enable ChromeScreenAI on Windows.

ChromeScreenAI is enabled on Windows.

Sandbox is set to allow read only access to installed component folder,
this will be removed before launch and replaced with opening required
files in the browser and sending their handles to the ScreenAI binary.

The component binary path is now stored in InstallState and passed to
the service after service is initialized.

Bug: 1278249
Change-Id: Ia86e3f6309e55195040ab1927d54ca4f9018ed6f
Ax-Relnotes: n/a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3876149
Reviewed-by: Sorin Jianu <sorin@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Reviewed-by: David Tseng <dtseng@chromium.org>
Commit-Queue: Ramin Halavati <rhalavati@chromium.org>
Reviewed-by: Matthew Denton <mpdenton@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1060869}
This commit is contained in:
Ramin Halavati
2022-10-19 05:11:27 +00:00
committed by Chromium LUCI CQ
parent c1c47ac43f
commit fd712d8e30
21 changed files with 182 additions and 92 deletions

@@ -55,7 +55,7 @@ void AXScreenAIAnnotator::AnnotateScreenshot(Browser* browser) {
// TODO(https://crbug.com/1278249): Add UMA for screenshot timing to ensure // TODO(https://crbug.com/1278249): Add UMA for screenshot timing to ensure
// the sync method is not blocking the browser process. // the sync method is not blocking the browser process.
#if BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
gfx::Image snapshot; gfx::Image snapshot;
if (!ui::GrabViewSnapshot(native_view, gfx::Rect(web_contents->GetSize()), if (!ui::GrabViewSnapshot(native_view, gfx::Rect(web_contents->GetSize()),
&snapshot)) { &snapshot)) {

@@ -4262,6 +4262,9 @@ std::wstring ChromeContentBrowserClient::GetAppContainerSidForSandboxType(
#endif #endif
case sandbox::mojom::Sandbox::kPrintCompositor: case sandbox::mojom::Sandbox::kPrintCompositor:
case sandbox::mojom::Sandbox::kAudio: case sandbox::mojom::Sandbox::kAudio:
#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
case sandbox::mojom::Sandbox::kScreenAI:
#endif
case sandbox::mojom::Sandbox::kSpeechRecognition: case sandbox::mojom::Sandbox::kSpeechRecognition:
case sandbox::mojom::Sandbox::kPdfConversion: case sandbox::mojom::Sandbox::kPdfConversion:
case sandbox::mojom::Sandbox::kService: case sandbox::mojom::Sandbox::kService:
@@ -4347,6 +4350,9 @@ bool ChromeContentBrowserClient::PreSpawnChild(
case sandbox::mojom::Sandbox::kPrintBackend: case sandbox::mojom::Sandbox::kPrintBackend:
#endif #endif
case sandbox::mojom::Sandbox::kPrintCompositor: case sandbox::mojom::Sandbox::kPrintCompositor:
#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
case sandbox::mojom::Sandbox::kScreenAI:
#endif
case sandbox::mojom::Sandbox::kAudio: case sandbox::mojom::Sandbox::kAudio:
case sandbox::mojom::Sandbox::kSpeechRecognition: case sandbox::mojom::Sandbox::kSpeechRecognition:
case sandbox::mojom::Sandbox::kPdfConversion: case sandbox::mojom::Sandbox::kPdfConversion:
@@ -6676,8 +6682,8 @@ bool ChromeContentBrowserClient::SetupEmbedderSandboxParameters(
CHECK(client->SetParameter(sandbox::policy::kParamSodaLanguagePackPath, CHECK(client->SetParameter(sandbox::policy::kParamSodaLanguagePackPath,
soda_language_pack_path.value())); soda_language_pack_path.value()));
return true; return true;
} else if (sandbox_type == sandbox::mojom::Sandbox::kScreenAI) {
#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
} else if (sandbox_type == sandbox::mojom::Sandbox::kScreenAI) {
// ScreenAI service needs read access to ScreenAI component path, so that it // ScreenAI service needs read access to ScreenAI component path, so that it
// would be able to find the latest downloaded version, and load its binary // would be able to find the latest downloaded version, and load its binary
// and all enclosed model files. // and all enclosed model files.

@@ -70,7 +70,8 @@ void ScreenAIComponentInstallerPolicy::ComponentReady(
const base::Version& version, const base::Version& version,
const base::FilePath& install_dir, const base::FilePath& install_dir,
base::Value manifest) { base::Value manifest) {
screen_ai::ScreenAIInstallState::GetInstance()->SetComponentReady(); screen_ai::ScreenAIInstallState::GetInstance()->SetComponentReady(
install_dir.Append(screen_ai::GetComponentBinaryFileName()));
VLOG(1) << "Screen AI Component ready, version " << version.GetString() VLOG(1) << "Screen AI Component ready, version " << version.GetString()
<< " in " << install_dir.value(); << " in " << install_dir.value();
} }

@@ -14,6 +14,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h" #include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/run_loop.h" #include "base/run_loop.h"
@@ -2495,8 +2496,10 @@ class PdfOcrContextMenuBrowserTest : public PdfPluginContextMenuBrowserTest,
scoped_feature_list_.InitAndDisableFeature(features::kPdfOcr); scoped_feature_list_.InitAndDisableFeature(features::kPdfOcr);
accessibility_state_utils::OverrideIsScreenReaderEnabledForTesting( accessibility_state_utils::OverrideIsScreenReaderEnabledForTesting(
IsScreenReaderEnabled()); IsScreenReaderEnabled());
screen_ai::ScreenAIInstallState::GetInstance()->SetComponentReadyForTesting( screen_ai::ScreenAIInstallState::GetInstance()
IsComponentReady()); ->set_component_ready_for_testing(
IsComponentReady() ? base::FilePath(FILE_PATH_LITERAL("tmp"))
: base::FilePath());
} }
PdfOcrContextMenuBrowserTest(const PdfOcrContextMenuBrowserTest&) = delete; PdfOcrContextMenuBrowserTest(const PdfOcrContextMenuBrowserTest&) = delete;

@@ -23,7 +23,7 @@ source_set("screen_ai") {
] ]
} }
if (!is_mac) { if (is_linux || is_chromeos) {
source_set("screen_ai_sandbox_hook") { source_set("screen_ai_sandbox_hook") {
sources = [ sources = [
"sandbox/screen_ai_sandbox_hook_linux.cc", "sandbox/screen_ai_sandbox_hook_linux.cc",
@@ -73,19 +73,26 @@ source_set("unit_tests") {
testonly = true testonly = true
sources = [ sources = [
"proto/proto_convertor_unittest.cc",
"public/cpp/screen_ai_install_state_unittest.cc", "public/cpp/screen_ai_install_state_unittest.cc",
"screen_ai_ax_tree_serializer_unittest.cc", "screen_ai_ax_tree_serializer_unittest.cc",
] ]
# TODO(https://crbug.com/1278249): Enable after the protobuf issue is fixed.
if (!is_win) {
sources += [ "proto/proto_convertor_unittest.cc" ]
}
data = [ "//components/test/data/screen_ai/" ] data = [ "//components/test/data/screen_ai/" ]
deps = [ deps = [
":screen_ai", ":screen_ai",
":test_support",
"//base/test:test_support", "//base/test:test_support",
"//components/services/screen_ai/proto", "//components/services/screen_ai/proto",
"//components/services/screen_ai/public/cpp:screen_ai_install_state", "//components/services/screen_ai/public/cpp:screen_ai_install_state",
"//testing/gtest", "//testing/gtest",
"//ui/accessibility:test_support", "//ui/accessibility:test_support",
] ]
if (!is_win) {
deps += [ ":test_support" ]
}
} }

@@ -6,6 +6,7 @@ import("//build/config/chromecast_build.gni")
import("//build/config/chromeos/ui_mode.gni") import("//build/config/chromeos/ui_mode.gni")
declare_args() { declare_args() {
# Screen AI service is still not supported on other platforms. # TODO(https://crbug.com/1278249): Remove this feature flag as now it's
enable_screen_ai_service = is_linux || is_mac || is_chromeos # supported on all platforms.
enable_screen_ai_service = is_linux || is_mac || is_chromeos || is_win
} }

@@ -4,6 +4,7 @@
#include "components/services/screen_ai/public/cpp/screen_ai_install_state.h" #include "components/services/screen_ai/public/cpp/screen_ai_install_state.h"
#include "base/files/file_path.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/ranges/algorithm.h" #include "base/ranges/algorithm.h"
@@ -21,7 +22,7 @@ ScreenAIInstallState::~ScreenAIInstallState() = default;
void ScreenAIInstallState::AddObserver( void ScreenAIInstallState::AddObserver(
ScreenAIInstallState::Observer* observer) { ScreenAIInstallState::Observer* observer) {
observers_.push_back(observer); observers_.push_back(observer);
if (component_ready_) if (!component_binary_path_.empty())
observer->ComponentReady(); observer->ComponentReady();
} }
@@ -32,11 +33,16 @@ void ScreenAIInstallState::RemoveObserver(
observers_.erase(pos); observers_.erase(pos);
} }
void ScreenAIInstallState::SetComponentReady() { void ScreenAIInstallState::SetComponentReady(
component_ready_ = true; const base::FilePath& component_binary_path) {
component_binary_path_ = component_binary_path;
for (ScreenAIInstallState::Observer* observer : observers_) for (ScreenAIInstallState::Observer* observer : observers_)
observer->ComponentReady(); observer->ComponentReady();
} }
bool ScreenAIInstallState::is_component_ready() {
return !component_binary_path_.empty();
}
} // namespace screen_ai } // namespace screen_ai

@@ -7,6 +7,7 @@
#include <vector> #include <vector>
#include "base/files/file_path.h"
#include "base/observer_list_types.h" #include "base/observer_list_types.h"
namespace component_updater { namespace component_updater {
@@ -34,17 +35,22 @@ class ScreenAIInstallState {
void AddObserver(Observer* observer); void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer); void RemoveObserver(Observer* observer);
bool is_component_ready() { return component_ready_; } bool is_component_ready();
void SetComponentReadyForTesting(bool ready) { component_ready_ = ready; } base::FilePath get_component_binary_path() { return component_binary_path_; }
void set_component_ready_for_testing(
const base::FilePath& component_binary_path) {
component_binary_path_ = component_binary_path;
}
private: private:
friend class component_updater::ScreenAIComponentInstallerPolicy; friend class component_updater::ScreenAIComponentInstallerPolicy;
friend class ScreenAIInstallStateTest; friend class ScreenAIInstallStateTest;
void SetComponentReady(); void SetComponentReady(const base::FilePath& component_binary_path);
bool component_ready_ = false; base::FilePath component_binary_path_;
std::vector<Observer*> observers_; std::vector<Observer*> observers_;
}; };

@@ -4,6 +4,7 @@
#include "components/services/screen_ai/public/cpp/screen_ai_install_state.h" #include "components/services/screen_ai/public/cpp/screen_ai_install_state.h"
#include "base/files/file_path.h"
#include "base/scoped_observation.h" #include "base/scoped_observation.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
@@ -17,7 +18,10 @@ class ScreenAIInstallStateTest : public testing::Test,
} }
void MakeComponentReady() { void MakeComponentReady() {
ScreenAIInstallState::GetInstance()->SetComponentReady(); // The passed file path is not used and just indicates that the component
// exists.
ScreenAIInstallState::GetInstance()->SetComponentReady(
base::FilePath(FILE_PATH_LITERAL("tmp")));
} }
void ComponentReady() override { component_ready_received_ = true; } void ComponentReady() override { component_ready_received_ = true; }

@@ -4,6 +4,7 @@
#include "components/services/screen_ai/public/cpp/screen_ai_service_router.h" #include "components/services/screen_ai/public/cpp/screen_ai_service_router.h"
#include "base/files/file_path.h"
#include "components/services/screen_ai/public/cpp/screen_ai_install_state.h" #include "components/services/screen_ai/public/cpp/screen_ai_install_state.h"
#include "content/public/browser/service_process_host.h" #include "content/public/browser/service_process_host.h"
@@ -46,11 +47,20 @@ void ScreenAIServiceRouter::LaunchIfNotRunning() {
return; return;
} }
base::FilePath library_path =
ScreenAIInstallState::GetInstance()->get_component_binary_path();
// TODO(https://crbug.com/1278249): Make sure the library is loaded from
// |library_path| and component updater doesn't download a new version
// during sandbox creation.
content::ServiceProcessHost::Launch( content::ServiceProcessHost::Launch(
screen_ai_service_.BindNewPipeAndPassReceiver(), screen_ai_service_.BindNewPipeAndPassReceiver(),
content::ServiceProcessHost::Options() content::ServiceProcessHost::Options()
.WithDisplayName("Screen AI Service") .WithDisplayName("Screen AI Service")
.Pass()); .Pass());
if (screen_ai_service_.is_bound())
screen_ai_service_->LoadLibrary(library_path);
} }
} // namespace screen_ai } // namespace screen_ai

@@ -5,8 +5,10 @@
#include "components/services/screen_ai/public/cpp/utilities.h" #include "components/services/screen_ai/public/cpp/utilities.h"
#include "base/files/file_enumerator.h" #include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "build/build_config.h"
#include "components/component_updater/component_updater_paths.h" #include "components/component_updater/component_updater_paths.h"
namespace screen_ai { namespace screen_ai {
@@ -16,17 +18,11 @@ const base::FilePath::CharType kScreenAISubDirName[] =
FILE_PATH_LITERAL("screen_ai"); FILE_PATH_LITERAL("screen_ai");
const base::FilePath::CharType kScreenAIComponentBinaryName[] = const base::FilePath::CharType kScreenAIComponentBinaryName[] =
FILE_PATH_LITERAL("libchrome_screen_ai.so"); #if BUILDFLAG(IS_WIN)
FILE_PATH_LITERAL("chrome_screen_ai.dll");
enum { #else
PATH_START = 13000, FILE_PATH_LITERAL("libchromescreenai.so");
#endif
// Note that this value is not kept between sessions or shared between
// processes.
PATH_SCREEN_AI_LIBRARY_BINARY,
PATH_END
};
} // namespace } // namespace
@@ -34,6 +30,10 @@ base::FilePath GetRelativeInstallDir() {
return base::FilePath(kScreenAISubDirName); return base::FilePath(kScreenAISubDirName);
} }
base::FilePath GetComponentBinaryFileName() {
return base::FilePath(kScreenAIComponentBinaryName);
}
base::FilePath GetComponentDir() { base::FilePath GetComponentDir() {
base::FilePath components_dir; base::FilePath components_dir;
base::PathService::Get(component_updater::DIR_COMPONENT_USER, base::PathService::Get(component_updater::DIR_COMPONENT_USER,
@@ -68,14 +68,4 @@ base::FilePath GetLatestComponentBinaryPath() {
return component_path; return component_path;
} }
void StoreComponentBinaryPath(const base::FilePath& path) {
base::PathService::Override(PATH_SCREEN_AI_LIBRARY_BINARY, path);
}
base::FilePath GetStoredComponentBinaryPath() {
base::FilePath path;
base::PathService::Get(PATH_SCREEN_AI_LIBRARY_BINARY, &path);
return path;
}
} // namespace screen_ai } // namespace screen_ai

@@ -18,13 +18,8 @@ base::FilePath GetRelativeInstallDir();
// Returns the folder in which ScreenAI component is installed. // Returns the folder in which ScreenAI component is installed.
base::FilePath GetComponentDir(); base::FilePath GetComponentDir();
// Stores the path to the component binary. This value is kept in memory and is // Returns the file name of component binary.
// not kept between sessions or shared between processes. base::FilePath GetComponentBinaryFileName();
void StoreComponentBinaryPath(const base::FilePath& path);
// Returns the component binary path if it is already stored by
// |StoreComponentBinaryPath|.
base::FilePath GetStoredComponentBinaryPath();
} // namespace screen_ai } // namespace screen_ai
#endif // COMPONENTS_SERVICES_SCREEN_AI_PUBLIC_CPP_UTILITIES_H_ #endif // COMPONENTS_SERVICES_SCREEN_AI_PUBLIC_CPP_UTILITIES_H_

@@ -4,6 +4,7 @@
module screen_ai.mojom; module screen_ai.mojom;
import "mojo/public/mojom/base/file_path.mojom";
import "sandbox/policy/mojom/sandbox.mojom"; import "sandbox/policy/mojom/sandbox.mojom";
import "skia/public/mojom/bitmap.mojom"; import "skia/public/mojom/bitmap.mojom";
import "ui/accessibility/mojom/ax_tree_id.mojom"; import "ui/accessibility/mojom/ax_tree_id.mojom";
@@ -53,6 +54,11 @@ interface Screen2xMainContentExtractor {
// returns the main content of the tree. // returns the main content of the tree.
[ServiceSandbox=sandbox.mojom.Sandbox.kScreenAI] [ServiceSandbox=sandbox.mojom.Sandbox.kScreenAI]
interface ScreenAIService { interface ScreenAIService {
// Triggers the service to load and initialize the Screen AI library at
// |library_path|. This should be called from the browser process and be
// eventually replaced by passing file handles.
LoadLibrary(mojo_base.mojom.FilePath library_path);
// Binds a new annotator to the service. // Binds a new annotator to the service.
BindAnnotator(pending_receiver<ScreenAIAnnotator> annotator); BindAnnotator(pending_receiver<ScreenAIAnnotator> annotator);

@@ -34,7 +34,6 @@ bool ScreenAIPreSandboxHook(sandbox::policy::SandboxLinux::Options options) {
VLOG(2) << "Screen AI library loaded pre-sandboxing:" << library_path; VLOG(2) << "Screen AI library loaded pre-sandboxing:" << library_path;
} }
} }
screen_ai::StoreComponentBinaryPath(library_path);
auto* instance = sandbox::policy::SandboxLinux::GetInstance(); auto* instance = sandbox::policy::SandboxLinux::GetInstance();

@@ -4,10 +4,16 @@
#include "components/services/screen_ai/screen_ai_service_impl.h" #include "components/services/screen_ai/screen_ai_service_impl.h"
#include <utility>
#include "base/check.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/notreached.h"
#include "base/process/process.h" #include "base/process/process.h"
#include "base/scoped_native_library.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "build/build_config.h"
#include "components/services/screen_ai/proto/proto_convertor.h" #include "components/services/screen_ai/proto/proto_convertor.h"
#include "components/services/screen_ai/public/cpp/utilities.h" #include "components/services/screen_ai/public/cpp/utilities.h"
#include "components/services/screen_ai/screen_ai_ax_tree_serializer.h" #include "components/services/screen_ai/screen_ai_ax_tree_serializer.h"
@@ -20,21 +26,6 @@ namespace screen_ai {
namespace { namespace {
base::FilePath GetLibraryFilePath() {
base::FilePath library_path = GetStoredComponentBinaryPath();
if (!library_path.empty())
return library_path;
// Binary file path is set while setting the sandbox on Linux, or the first
// time this function is called. So in all other cases we need to look for
// the library binary in its component folder.
library_path = GetLatestComponentBinaryPath();
StoreComponentBinaryPath(library_path);
return library_path;
}
std::string MakeString(const char* content, uint32_t length) { std::string MakeString(const char* content, uint32_t length) {
DCHECK(content); DCHECK(content);
DCHECK(length); DCHECK(length);
@@ -49,18 +40,24 @@ std::string MakeString(const char* content, uint32_t length) {
ScreenAIService::ScreenAIService( ScreenAIService::ScreenAIService(
mojo::PendingReceiver<mojom::ScreenAIService> receiver) mojo::PendingReceiver<mojom::ScreenAIService> receiver)
: library_(GetLibraryFilePath()), : receiver_(this, std::move(receiver)) {}
init_function_(
reinterpret_cast<InitFunction>(library_.GetFunctionPointer("Init"))), void ScreenAIService::LoadLibrary(const base::FilePath& library_path) {
annotate_function_(reinterpret_cast<AnnotateFunction>( library_ = base::ScopedNativeLibrary(library_path);
library_.GetFunctionPointer("Annotate"))), init_function_ =
extract_main_content_function_( reinterpret_cast<InitFunction>(library_.GetFunctionPointer("Init"));
reinterpret_cast<ExtractMainContentFunction>( extract_main_content_function_ = reinterpret_cast<ExtractMainContentFunction>(
library_.GetFunctionPointer("ExtractMainContent"))), library_.GetFunctionPointer("ExtractMainContent"));
receiver_(this, std::move(receiver)) { DCHECK(init_function_ && extract_main_content_function_);
DCHECK(init_function_ && annotate_function_ && // TODO(https://crbug.com/1278249): Enable when ScreenAI is supported on
extract_main_content_function_); // Windows.
if (!CallLibraryInitFunction()) { #if !BUILDFLAG(IS_WIN)
annotate_function_ = reinterpret_cast<AnnotateFunction>(
library_.GetFunctionPointer("Annotate"));
DCHECK(annotate_function_);
#endif
if (!CallLibraryInitFunction(library_path.DirName())) {
// TODO(https://crbug.com/1278249): Add UMA metrics to monitor failures. // TODO(https://crbug.com/1278249): Add UMA metrics to monitor failures.
VLOG(0) << "Screen AI library initialization failed."; VLOG(0) << "Screen AI library initialization failed.";
base::Process::TerminateCurrentProcessImmediately(-1); base::Process::TerminateCurrentProcessImmediately(-1);
@@ -68,16 +65,20 @@ ScreenAIService::ScreenAIService(
} }
NO_SANITIZE("cfi-icall") NO_SANITIZE("cfi-icall")
bool ScreenAIService::CallLibraryInitFunction() { bool ScreenAIService::CallLibraryInitFunction(
return init_function_( const base::FilePath& models_path) {
/*init_visual_annotations = */ features:: bool init_main_content_extraction =
IsScreenAIVisualAnnotationsEnabled() || features::IsReadAnythingWithScreen2xEnabled();
features::IsPdfOcrEnabled(), bool init_visual_annotations;
/*init_main_content_extraction = */ #if BUILDFLAG(IS_WIN)
features::IsReadAnythingWithScreen2xEnabled(), init_visual_annotations = false;
/*debug_mode = */ features::IsScreenAIDebugModeEnabled(), #else
/*models_path = */ init_visual_annotations = features::IsScreenAIVisualAnnotationsEnabled() ||
GetLibraryFilePath().DirName().MaybeAsASCII().c_str()); features::IsPdfOcrEnabled();
#endif
return init_function_(init_visual_annotations, init_main_content_extraction,
features::IsScreenAIDebugModeEnabled(),
models_path.MaybeAsASCII().c_str());
} }
ScreenAIService::~ScreenAIService() = default; ScreenAIService::~ScreenAIService() = default;
@@ -148,7 +149,13 @@ bool ScreenAIService::CallLibraryAnnotateFunction(
const SkBitmap& image, const SkBitmap& image,
char*& annotation_proto, char*& annotation_proto,
uint32_t& annotation_proto_length) { uint32_t& annotation_proto_length) {
#if BUILDFLAG(IS_WIN)
NOTIMPLEMENTED();
return false;
#else
DCHECK(annotate_function_);
return annotate_function_(image, annotation_proto, annotation_proto_length); return annotate_function_(image, annotation_proto, annotation_proto_length);
#endif
} }
void ScreenAIService::ExtractMainContent(const ui::AXTreeUpdate& snapshot, void ScreenAIService::ExtractMainContent(const ui::AXTreeUpdate& snapshot,
@@ -182,6 +189,7 @@ bool ScreenAIService::CallLibraryExtractMainContentFunction(
const uint32_t serialized_snapshot_length, const uint32_t serialized_snapshot_length,
int32_t*& node_ids, int32_t*& node_ids,
uint32_t& nodes_count) { uint32_t& nodes_count) {
DCHECK(extract_main_content_function_);
return extract_main_content_function_( return extract_main_content_function_(
serialized_snapshot, serialized_snapshot_length, node_ids, nodes_count); serialized_snapshot, serialized_snapshot_length, node_ids, nodes_count);
} }

@@ -8,6 +8,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/scoped_native_library.h" #include "base/scoped_native_library.h"
#include "build/build_config.h"
#include "components/services/screen_ai/public/mojom/screen_ai_service.mojom.h" #include "components/services/screen_ai/public/mojom/screen_ai_service.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/receiver.h"
@@ -44,6 +45,9 @@ class ScreenAIService : public mojom::ScreenAIService,
void ExtractMainContent(const ui::AXTreeUpdate& snapshot, void ExtractMainContent(const ui::AXTreeUpdate& snapshot,
ContentExtractionCallback callback) override; ContentExtractionCallback callback) override;
// mojom::ScreenAIService:
void LoadLibrary(const base::FilePath& library_path) override;
// mojom::ScreenAIService: // mojom::ScreenAIService:
void BindAnnotator( void BindAnnotator(
mojo::PendingReceiver<mojom::ScreenAIAnnotator> annotator) override; mojo::PendingReceiver<mojom::ScreenAIAnnotator> annotator) override;
@@ -59,7 +63,7 @@ class ScreenAIService : public mojom::ScreenAIService,
// Calls |init_function_| and returns the result. // Calls |init_function_| and returns the result.
// Library function calls are isloated to have specific compiler directives. // Library function calls are isloated to have specific compiler directives.
bool CallLibraryInitFunction(); bool CallLibraryInitFunction(const base::FilePath& models_path);
// Calls |annotate_function_| and returns the result. // Calls |annotate_function_| and returns the result.
// Library function calls are isloated to have specific compiler directives. // Library function calls are isloated to have specific compiler directives.
@@ -82,8 +86,9 @@ class ScreenAIService : public mojom::ScreenAIService,
bool /*init_main_content_extraction*/, bool /*init_main_content_extraction*/,
bool /*debug_mode*/, bool /*debug_mode*/,
const char* /*models_path*/); const char* /*models_path*/);
InitFunction init_function_; InitFunction init_function_ = nullptr;
#if !BUILDFLAG(IS_WIN)
// Sends the given bitmap to ScreenAI pipeline and returns visual annotations. // Sends the given bitmap to ScreenAI pipeline and returns visual annotations.
// The annotations will be returned as a serialized VisualAnnotation proto. // The annotations will be returned as a serialized VisualAnnotation proto.
// `serialized_visual_annotation` will be allocated for the output and the // `serialized_visual_annotation` will be allocated for the output and the
@@ -92,7 +97,8 @@ class ScreenAIService : public mojom::ScreenAIService,
const SkBitmap& /*bitmap*/, const SkBitmap& /*bitmap*/,
char*& /*serialized_visual_annotation*/, char*& /*serialized_visual_annotation*/,
uint32_t& /*serialized_visual_annotation_length*/); uint32_t& /*serialized_visual_annotation_length*/);
AnnotateFunction annotate_function_; AnnotateFunction annotate_function_ = nullptr;
#endif
// Passes the given accessibility tree proto to Screen2x pipeline and returns // Passes the given accessibility tree proto to Screen2x pipeline and returns
// the main content ids. // the main content ids.
@@ -104,7 +110,7 @@ class ScreenAIService : public mojom::ScreenAIService,
uint32_t /*serialized_view_hierarchy_length*/, uint32_t /*serialized_view_hierarchy_length*/,
int32_t*& /*&content_node_ids*/, int32_t*& /*&content_node_ids*/,
uint32_t& /*content_node_ids_length*/); uint32_t& /*content_node_ids_length*/);
ExtractMainContentFunction extract_main_content_function_; ExtractMainContentFunction extract_main_content_function_ = nullptr;
mojo::Receiver<mojom::ScreenAIService> receiver_; mojo::Receiver<mojom::ScreenAIService> receiver_;
@@ -114,7 +120,8 @@ class ScreenAIService : public mojom::ScreenAIService,
// The client that can receive annotator update messages. // The client that can receive annotator update messages.
mojo::Remote<mojom::ScreenAIAnnotatorClient> screen_ai_annotator_client_; mojo::Remote<mojom::ScreenAIAnnotatorClient> screen_ai_annotator_client_;
// The set of receivers used to receive messages from main content extractors. // The set of receivers used to receive messages from main content
// extractors.
mojo::ReceiverSet<mojom::Screen2xMainContentExtractor> mojo::ReceiverSet<mojom::Screen2xMainContentExtractor>
screen_2x_main_content_extractors_; screen_2x_main_content_extractors_;
}; };

@@ -2717,6 +2717,7 @@ source_set("browser") {
"__ATLHOST_H__", "__ATLHOST_H__",
] ]
deps += [ deps += [
"//components/services/screen_ai/public/cpp:utilities",
"//third_party/blink/public/common:font_unique_name_table_proto", "//third_party/blink/public/common:font_unique_name_table_proto",
"//third_party/iaccessible2", "//third_party/iaccessible2",
"//third_party/isimpledom", "//third_party/isimpledom",

@@ -18,6 +18,7 @@ include_rules = [
"+components/services/quarantine/test_support.h", "+components/services/quarantine/test_support.h",
"+components/services/quarantine/quarantine.h", "+components/services/quarantine/quarantine.h",
"+components/services/screen_ai/buildflags", "+components/services/screen_ai/buildflags",
"+components/services/screen_ai/public/cpp",
"+components/services/storage", "+components/services/storage",
"+components/services/storage/public", "+components/services/storage/public",
"+components/session_manager/core", "+components/session_manager/core",

@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "base/files/file_path.h"
#include "content/browser/utility_sandbox_delegate.h" #include "content/browser/utility_sandbox_delegate.h"
#include "base/check.h" #include "base/check.h"
#include "base/feature_list.h" #include "base/feature_list.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "components/services/screen_ai/public/cpp/utilities.h"
#include "content/public/browser/content_browser_client.h" #include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h" #include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
@@ -37,8 +39,8 @@ bool AudioPreSpawnTarget(sandbox::TargetConfig* config) {
// //
// For audio streams to create shared memory regions, lockdown level must be // For audio streams to create shared memory regions, lockdown level must be
// at least USER_LIMITED and delayed integrity level INTEGRITY_LEVEL_LOW, // at least USER_LIMITED and delayed integrity level INTEGRITY_LEVEL_LOW,
// otherwise CreateFileMapping() will fail with error code ERROR_ACCESS_DENIED // otherwise CreateFileMapping() will fail with error code
// (0x5). // ERROR_ACCESS_DENIED (0x5).
// //
// For audio input streams to use ISimpleAudioVolume interface, lockdown // For audio input streams to use ISimpleAudioVolume interface, lockdown
// level must be set to USER_NON_ADMIN, otherwise // level must be set to USER_NON_ADMIN, otherwise
@@ -192,6 +194,34 @@ bool XrCompositingPreSpawnTarget(sandbox::TargetConfig* config,
return true; return true;
} }
bool ScreenAIPreSpawnTarget(sandbox::TargetConfig* config,
sandbox::mojom::Sandbox sandbox_type) {
DCHECK(!config->IsConfigured());
auto result = config->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
sandbox::USER_LOCKDOWN);
if (result != sandbox::SBOX_ALL_OK)
return false;
result = sandbox::policy::SandboxWin::SetJobLevel(
sandbox_type, sandbox::JobLevel::kLimitedUser, 0, config);
if (result != sandbox::SBOX_ALL_OK)
return false;
// TODO(https://crbug.com/1278249): [LAUNCH BLOCKER] Remove this path and
// instead open files and send handles to the binary.
base::FilePath library_path = screen_ai::GetLatestComponentBinaryPath();
if (library_path.empty())
return false;
base::FilePath required_path =
library_path.DirName().Append(FILE_PATH_LITERAL("*.*"));
result = config->AddRule(sandbox::SubSystem::kFiles,
sandbox::Semantics::kFilesAllowReadonly,
required_path.value().c_str());
return result == sandbox::SBOX_ALL_OK;
}
} // namespace } // namespace
std::string UtilitySandboxedProcessLauncherDelegate::GetSandboxTag() { std::string UtilitySandboxedProcessLauncherDelegate::GetSandboxTag() {
@@ -264,6 +294,11 @@ bool UtilitySandboxedProcessLauncherDelegate::PreSpawnTarget(
return false; return false;
} }
if (sandbox_type_ == sandbox::mojom::Sandbox::kScreenAI) {
if (!ScreenAIPreSpawnTarget(config, sandbox_type_))
return false;
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kSpeechRecognition) { if (sandbox_type_ == sandbox::mojom::Sandbox::kSpeechRecognition) {
auto result = config->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); auto result = config->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
if (result != sandbox::SBOX_ALL_OK) if (result != sandbox::SBOX_ALL_OK)

@@ -60,7 +60,8 @@
#endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
#endif // BUILDFLAG(IS_CHROMEOS_ASH) #endif // BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) #if (BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) && \
(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)))
#include "components/services/screen_ai/sandbox/screen_ai_sandbox_hook_linux.h" // nogncheck #include "components/services/screen_ai/sandbox/screen_ai_sandbox_hook_linux.h" // nogncheck
#endif #endif

@@ -759,6 +759,7 @@ ResultCode GenerateConfigForSandboxedProcess(const base::CommandLine& cmd_line,
// Post-startup mitigations. // Post-startup mitigations.
mitigations = MITIGATION_DLL_SEARCH_ORDER; mitigations = MITIGATION_DLL_SEARCH_ORDER;
if (!cmd_line.HasSwitch(switches::kAllowThirdPartyModules) && if (!cmd_line.HasSwitch(switches::kAllowThirdPartyModules) &&
sandbox_type != Sandbox::kScreenAI &&
sandbox_type != Sandbox::kSpeechRecognition && sandbox_type != Sandbox::kSpeechRecognition &&
sandbox_type != Sandbox::kMediaFoundationCdm) { sandbox_type != Sandbox::kMediaFoundationCdm) {
mitigations |= MITIGATION_FORCE_MS_SIGNED_BINS; mitigations |= MITIGATION_FORCE_MS_SIGNED_BINS;
@@ -1271,6 +1272,8 @@ std::string SandboxWin::GetSandboxTypeInEnglish(Sandbox sandbox_type) {
#endif #endif
case Sandbox::kAudio: case Sandbox::kAudio:
return "Audio"; return "Audio";
case Sandbox::kScreenAI:
return "Screen AI";
case Sandbox::kSpeechRecognition: case Sandbox::kSpeechRecognition:
return "Speech Recognition"; return "Speech Recognition";
case Sandbox::kPdfConversion: case Sandbox::kPdfConversion: