Commit 46e53fb6 authored by Olivier Yiptong's avatar Olivier Yiptong Committed by Chromium LUCI CQ

FontAccess: Unify picker and enumeration functionality

This change unifies the font chooser and enumeration capabilities under
one API. A developer will be able to choose between using enumeration
and a chooser by passing an option parameter to the API.

Bug: 1140267
Change-Id: I4cdbb7c89d4795255c2cf709ec76f8c518d1e699
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2638548
Commit-Queue: Olivier Yiptong <oyiptong@chromium.org>
Reviewed-by: default avatarJoshua Bell <jsbell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#846315}
parent 321b9e0f
...@@ -99,7 +99,9 @@ IN_PROC_BROWSER_TEST_F(FontAccessManagerImplBrowserTest, EnumerationTest) { ...@@ -99,7 +99,9 @@ IN_PROC_BROWSER_TEST_F(FontAccessManagerImplBrowserTest, EnumerationTest) {
int result = EvalJs(shell(), int result = EvalJs(shell(),
"(async () => {" "(async () => {"
" let count = 0;" " let count = 0;"
" for await (const item of navigator.fonts.query()) {" " const fonts = await "
"navigator.fonts.query({persistentAccess: true});"
" for (const item of fonts) {"
" count++;" " count++;"
" }" " }"
" return count;" " return count;"
...@@ -119,7 +121,9 @@ IN_PROC_BROWSER_TEST_F(FontAccessManagerImplBrowserTest, LocaleTest) { ...@@ -119,7 +121,9 @@ IN_PROC_BROWSER_TEST_F(FontAccessManagerImplBrowserTest, LocaleTest) {
EvalJs(shell(), EvalJs(shell(),
"(async () => {" "(async () => {"
" let fullName = '';" " let fullName = '';"
" for await (const item of navigator.fonts.query()) {" " const fonts = await navigator.fonts.query({persistentAccess: "
"true});"
" for (const item of fonts) {"
" if (item.postscriptName == 'MicrosoftYaHei') {" " if (item.postscriptName == 'MicrosoftYaHei') {"
" fullName = item.fullName;" " fullName = item.fullName;"
" break;" " break;"
......
...@@ -301,8 +301,6 @@ generated_dictionary_sources_in_modules = [ ...@@ -301,8 +301,6 @@ generated_dictionary_sources_in_modules = [
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_system_handle_permission_descriptor.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_system_handle_permission_descriptor.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_system_remove_options.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_system_remove_options.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_system_remove_options.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_system_remove_options.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_font_iterator_entry.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_font_iterator_entry.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gain_options.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gain_options.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gain_options.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gain_options.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gamepad_axis_event_init.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_gamepad_axis_event_init.cc",
...@@ -1504,8 +1502,6 @@ generated_interface_sources_in_modules = [ ...@@ -1504,8 +1502,6 @@ generated_interface_sources_in_modules = [
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_writer.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_writer.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_writer_sync.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_writer_sync.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_writer_sync.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_writer_sync.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_font_iterator.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_font_iterator.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_font_manager.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_font_manager.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_font_manager.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_font_manager.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_font_metadata.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_font_metadata.cc",
......
...@@ -254,8 +254,6 @@ static_idl_files_in_modules = get_path_info( ...@@ -254,8 +254,6 @@ static_idl_files_in_modules = get_path_info(
"//third_party/blink/renderer/modules/filesystem/metadata_callback.idl", "//third_party/blink/renderer/modules/filesystem/metadata_callback.idl",
"//third_party/blink/renderer/modules/filesystem/shared_worker_global_scope_file_system.idl", "//third_party/blink/renderer/modules/filesystem/shared_worker_global_scope_file_system.idl",
"//third_party/blink/renderer/modules/filesystem/window_file_system.idl", "//third_party/blink/renderer/modules/filesystem/window_file_system.idl",
"//third_party/blink/renderer/modules/font_access/font_iterator.idl",
"//third_party/blink/renderer/modules/font_access/font_iterator_entry.idl",
"//third_party/blink/renderer/modules/font_access/font_manager.idl", "//third_party/blink/renderer/modules/font_access/font_manager.idl",
"//third_party/blink/renderer/modules/font_access/font_metadata.idl", "//third_party/blink/renderer/modules/font_access/font_metadata.idl",
"//third_party/blink/renderer/modules/font_access/font_table_map.idl", "//third_party/blink/renderer/modules/font_access/font_table_map.idl",
......
...@@ -6,8 +6,6 @@ import("//third_party/blink/renderer/modules/modules.gni") ...@@ -6,8 +6,6 @@ import("//third_party/blink/renderer/modules/modules.gni")
blink_modules_sources("font_access") { blink_modules_sources("font_access") {
sources = [ sources = [
"font_iterator.cc",
"font_iterator.h",
"font_manager.cc", "font_manager.cc",
"font_manager.h", "font_manager.h",
"font_metadata.cc", "font_metadata.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/font_access/font_iterator.h"
#include "build/build_config.h"
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/public/common/font_access/font_enumeration_table.pb.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_font_iterator_entry.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/modules/font_access/font_metadata.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
namespace blink {
FontIterator::FontIterator(ExecutionContext* context,
const Vector<String>& selection)
: ExecutionContextLifecycleObserver(context) {
context->GetBrowserInterfaceBroker().GetInterface(
remote_manager_.BindNewPipeAndPassReceiver());
remote_manager_.set_disconnect_handler(
WTF::Bind(&FontIterator::OnDisconnect, WrapWeakPersistent(this)));
for (const String& postscriptName : selection) {
// While postscript names are encoded in a subset of ASCII, we convert the
// input into UTF8. This will still allow exact matches to occur.
selection_.insert(postscriptName.Utf8());
}
}
ScriptPromise FontIterator::next(ScriptState* script_state) {
if (permission_status_ == PermissionStatus::ASK) {
if (!pending_resolver_) {
remote_manager_->EnumerateLocalFonts(WTF::Bind(
&FontIterator::DidGetEnumerationResponse, WrapWeakPersistent(this)));
pending_resolver_ =
MakeGarbageCollected<ScriptPromiseResolver>(script_state);
}
return pending_resolver_->Promise();
}
if (permission_status_ == PermissionStatus::DENIED) {
return ScriptPromise::RejectWithDOMException(
script_state,
MakeGarbageCollected<DOMException>(DOMExceptionCode::kNotAllowedError,
"Permission Error"));
}
return ScriptPromise::Cast(script_state, ToV8(GetNextEntry(), script_state));
}
void FontIterator::Trace(Visitor* visitor) const {
ScriptWrappable::Trace(visitor);
ContextLifecycleObserver::Trace(visitor);
visitor->Trace(entries_);
visitor->Trace(pending_resolver_);
}
FontIteratorEntry* FontIterator::GetNextEntry() {
auto* result = FontIteratorEntry::Create();
if (entries_.IsEmpty()) {
result->setDone(true);
return result;
}
FontMetadata* entry = entries_.TakeFirst();
result->setValue(entry);
return result;
}
void FontIterator::DidGetEnumerationResponse(
FontEnumerationStatus status,
base::ReadOnlySharedMemoryRegion region) {
switch (status) {
case FontEnumerationStatus::kOk:
break;
case FontEnumerationStatus::kUnimplemented:
pending_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotSupportedError,
"Not yet supported on this platform."));
pending_resolver_.Clear();
return;
case FontEnumerationStatus::kNeedsUserActivation:
pending_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kSecurityError, "User activation is required."));
pending_resolver_.Clear();
return;
case FontEnumerationStatus::kNotVisible:
pending_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kSecurityError, "Page needs to be visible."));
pending_resolver_.Clear();
return;
case FontEnumerationStatus::kPermissionDenied:
permission_status_ = PermissionStatus::DENIED;
pending_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotAllowedError, "Permission not granted."));
pending_resolver_.Clear();
return;
case FontEnumerationStatus::kUnexpectedError:
default:
pending_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kUnknownError, "An unexpected error occured."));
pending_resolver_.Clear();
return;
}
permission_status_ = PermissionStatus::GRANTED;
base::ReadOnlySharedMemoryMapping mapping = region.Map();
FontEnumerationTable table;
if (mapping.size() > INT_MAX) {
// Cannot deserialize without overflow.
pending_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kDataError, "Font data exceeds memory limit."));
pending_resolver_.Clear();
return;
}
table.ParseFromArray(mapping.memory(), static_cast<int>(mapping.size()));
for (const auto& element : table.fonts()) {
// If the selection list contains items, only allow items that match.
if (!selection_.empty() &&
selection_.find(element.postscript_name().c_str()) == selection_.end())
continue;
auto entry = FontEnumerationEntry{
String::FromUTF8(element.postscript_name().c_str()),
String::FromUTF8(element.full_name().c_str()),
String::FromUTF8(element.family().c_str())};
entries_.push_back(FontMetadata::Create(std::move(entry)));
}
pending_resolver_->Resolve(GetNextEntry());
pending_resolver_.Clear();
}
void FontIterator::ContextDestroyed() {
remote_manager_.reset();
}
void FontIterator::OnDisconnect() {
remote_manager_.reset();
}
} // namespace blink
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FONT_ACCESS_FONT_ITERATOR_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_FONT_ACCESS_FONT_ITERATOR_H_
#include "base/memory/read_only_shared_memory_region.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/mojom/font_access/font_access.mojom-blink.h"
#include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
namespace blink {
using mojom::blink::FontEnumerationStatus;
class ScriptPromise;
class ScriptPromiseResolver;
class ScriptState;
class FontMetadata;
class FontIteratorEntry;
class FontIterator final : public ScriptWrappable,
public ExecutionContextLifecycleObserver {
DEFINE_WRAPPERTYPEINFO();
public:
using PermissionStatus = mojom::blink::PermissionStatus;
FontIterator(ExecutionContext* context, const Vector<String>& filter);
ScriptPromise next(ScriptState*);
void Trace(Visitor*) const override;
private:
FontIteratorEntry* GetNextEntry();
void DidGetEnumerationResponse(FontEnumerationStatus,
base::ReadOnlySharedMemoryRegion);
void ContextDestroyed() override;
void OnDisconnect();
HeapDeque<Member<FontMetadata>> entries_;
Member<ScriptPromiseResolver> pending_resolver_;
mojo::Remote<mojom::blink::FontAccessManager> remote_manager_;
PermissionStatus permission_status_ = PermissionStatus::ASK;
// Used to select from results before they go back to the script.
// We use a std::string here because this is used at the boundary
// between Blink and Chromium and avoids unnecessary conversions.
std::set<std::string> selection_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_FONT_ACCESS_FONT_ITERATOR_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO(https://crbug.com/1062425): Determine if async iterator performance cost is worthwhile.
// Async iterator returned by FontManager.query().
// https://wicg.github.io/local-font-access/
// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-asynciterator-interface
[
SecureContext,
LegacyNoInterfaceObject,
RuntimeEnabled=FontAccess
] interface FontIterator {
[CallWith=ScriptState] Promise<any> next();
};
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Used by FontIterator to represents results of next() calls.
// https://wicg.github.io/local-font-access/
// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-iteratorresult-interface
dictionary FontIteratorEntry {
FontMetadata value;
boolean done = false;
};
...@@ -9,27 +9,20 @@ ...@@ -9,27 +9,20 @@
#include "base/feature_list.h" #include "base/feature_list.h"
#include "third_party/blink/public/common/browser_interface_broker_proxy.h" #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/font_access/font_enumeration_table.pb.h"
#include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h" #include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/modules/font_access/font_iterator.h"
#include "third_party/blink/renderer/modules/font_access/font_metadata.h" #include "third_party/blink/renderer/modules/font_access/font_metadata.h"
#include "third_party/blink/renderer/modules/font_access/query_options.h" #include "third_party/blink/renderer/modules/font_access/query_options.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h"
namespace blink { namespace blink {
namespace { using mojom::blink::FontEnumerationStatus;
void ReturnDataFunction(const v8::FunctionCallbackInfo<v8::Value>& info) {
V8SetReturnValue(info, info.Data());
}
} // namespace
FontManager::FontManager(ExecutionContext* context) FontManager::FontManager(ExecutionContext* context)
: ExecutionContextLifecycleObserver(context) { : ExecutionContextLifecycleObserver(context) {
...@@ -43,37 +36,19 @@ FontManager::FontManager(ExecutionContext* context) ...@@ -43,37 +36,19 @@ FontManager::FontManager(ExecutionContext* context)
} }
} }
ScriptValue FontManager::query(ScriptState* script_state, ScriptPromise FontManager::query(ScriptState* script_state,
const QueryOptions* options,
ExceptionState& exception_state) {
DCHECK(options->hasSelect());
if (exception_state.HadException())
return ScriptValue();
auto* iterator = MakeGarbageCollected<FontIterator>(
ExecutionContext::From(script_state), options->select());
auto* isolate = script_state->GetIsolate();
auto context = script_state->GetContext();
v8::Local<v8::Object> result = v8::Object::New(isolate);
if (!result
->Set(context, v8::Symbol::GetAsyncIterator(isolate),
v8::Function::New(context, &ReturnDataFunction,
ToV8(iterator, script_state))
.ToLocalChecked())
.ToChecked()) {
return ScriptValue();
}
return ScriptValue(script_state->GetIsolate(), result);
}
ScriptPromise FontManager::showFontChooser(ScriptState* script_state,
const QueryOptions* options) { const QueryOptions* options) {
DCHECK(options->hasSelect()); DCHECK(options->hasSelect());
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise(); ScriptPromise promise = resolver->Promise();
if (options->persistentAccess()) {
remote_manager_->EnumerateLocalFonts(WTF::Bind(
&FontManager::DidGetEnumerationResponse, WrapWeakPersistent(this),
WrapPersistent(resolver), options->select()));
return promise;
}
remote_manager_->ChooseLocalFonts( remote_manager_->ChooseLocalFonts(
options->select(), options->select(),
WTF::Bind(&FontManager::DidShowFontChooser, WrapWeakPersistent(this), WTF::Bind(&FontManager::DidShowFontChooser, WrapWeakPersistent(this),
...@@ -89,38 +64,98 @@ void FontManager::Trace(blink::Visitor* visitor) const { ...@@ -89,38 +64,98 @@ void FontManager::Trace(blink::Visitor* visitor) const {
void FontManager::DidShowFontChooser( void FontManager::DidShowFontChooser(
ScriptPromiseResolver* resolver, ScriptPromiseResolver* resolver,
mojom::blink::FontEnumerationStatus status, FontEnumerationStatus status,
Vector<mojom::blink::FontMetadataPtr> fonts) { Vector<mojom::blink::FontMetadataPtr> fonts) {
if (RejectPromiseIfNecessary(status, resolver))
return;
auto entries = HeapVector<Member<FontMetadata>>();
for (const auto& font : fonts) {
auto entry = FontEnumerationEntry{font->postscript_name, font->full_name,
font->family};
entries.push_back(FontMetadata::Create(std::move(entry)));
}
resolver->Resolve(std::move(entries));
}
void FontManager::DidGetEnumerationResponse(
ScriptPromiseResolver* resolver,
const Vector<String>& selection,
FontEnumerationStatus status,
base::ReadOnlySharedMemoryRegion region) {
if (RejectPromiseIfNecessary(status, resolver))
return;
base::ReadOnlySharedMemoryMapping mapping = region.Map();
FontEnumerationTable table;
if (mapping.size() > INT_MAX) {
// Cannot deserialize without overflow.
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kDataError, "Font data exceeds memory limit."));
return;
}
// Used to compare with data coming from the browser to avoid conversions.
std::set<std::string> selection_utf8;
for (const String& postscriptName : selection) {
// While postscript names are encoded in a subset of ASCII, we convert the
// input into UTF8. This will still allow exact matches to occur.
selection_utf8.insert(postscriptName.Utf8());
}
HeapVector<Member<FontMetadata>> entries;
table.ParseFromArray(mapping.memory(), static_cast<int>(mapping.size()));
for (const auto& element : table.fonts()) {
// If the selection list contains items, only allow items that match.
if (!selection_utf8.empty() &&
selection_utf8.find(element.postscript_name().c_str()) ==
selection_utf8.end())
continue;
auto entry = FontEnumerationEntry{
String::FromUTF8(element.postscript_name().c_str()),
String::FromUTF8(element.full_name().c_str()),
String::FromUTF8(element.family().c_str())};
entries.push_back(FontMetadata::Create(std::move(entry)));
}
resolver->Resolve(std::move(entries));
}
bool FontManager::RejectPromiseIfNecessary(const FontEnumerationStatus& status,
ScriptPromiseResolver* resolver) {
switch (status) { switch (status) {
case mojom::blink::FontEnumerationStatus::kOk: case FontEnumerationStatus::kOk:
break; break;
case mojom::blink::FontEnumerationStatus::kUnimplemented: case FontEnumerationStatus::kUnimplemented:
resolver->Reject(MakeGarbageCollected<DOMException>( resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotSupportedError, DOMExceptionCode::kNotSupportedError,
"Not yet supported on this platform.")); "Not yet supported on this platform."));
return; return true;
case mojom::blink::FontEnumerationStatus::kCanceled: case FontEnumerationStatus::kCanceled:
resolver->Reject(MakeGarbageCollected<DOMException>( resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kAbortError, "The user canceled the operation.")); DOMExceptionCode::kAbortError, "The user canceled the operation."));
return; return true;
case mojom::blink::FontEnumerationStatus::kNeedsUserActivation: case FontEnumerationStatus::kNeedsUserActivation:
resolver->Reject(MakeGarbageCollected<DOMException>( resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kSecurityError, "User activation is required.")); DOMExceptionCode::kSecurityError, "User activation is required."));
return; return true;
case mojom::blink::FontEnumerationStatus::kUnexpectedError: case FontEnumerationStatus::kNotVisible:
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kSecurityError, "Page needs to be visible."));
return true;
case FontEnumerationStatus::kPermissionDenied:
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotAllowedError, "Permission not granted."));
return true;
case FontEnumerationStatus::kUnexpectedError:
default: default:
resolver->Reject(MakeGarbageCollected<DOMException>( resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kUnknownError, "An unexpected error occured.")); DOMExceptionCode::kUnknownError, "An unexpected error occured."));
return; return true;
} }
return false;
auto entries = HeapVector<Member<FontMetadata>>();
for (const auto& font : fonts) {
auto entry = FontEnumerationEntry{font->postscript_name, font->full_name,
font->family};
entries.push_back(FontMetadata::Create(std::move(entry)));
}
resolver->Resolve(std::move(entries));
} }
void FontManager::ContextDestroyed() { void FontManager::ContextDestroyed() {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FONT_ACCESS_FONT_MANAGER_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FONT_ACCESS_FONT_MANAGER_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_FONT_ACCESS_FONT_MANAGER_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_FONT_ACCESS_FONT_MANAGER_H_
#include "base/memory/read_only_shared_memory_region.h"
#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/mojom/font_access/font_access.mojom-blink.h" #include "third_party/blink/public/mojom/font_access/font_access.mojom-blink.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
...@@ -16,7 +17,6 @@ ...@@ -16,7 +17,6 @@
namespace blink { namespace blink {
class ScriptState; class ScriptState;
class ScriptValue;
class ScriptPromise; class ScriptPromise;
class ScriptPromiseResolver; class ScriptPromiseResolver;
class QueryOptions; class QueryOptions;
...@@ -33,8 +33,7 @@ class FontManager final : public ScriptWrappable, ...@@ -33,8 +33,7 @@ class FontManager final : public ScriptWrappable,
FontManager operator=(const FontManager&) = delete; FontManager operator=(const FontManager&) = delete;
// FontManager IDL interface implementation. // FontManager IDL interface implementation.
ScriptValue query(ScriptState*, const QueryOptions* options, ExceptionState&); ScriptPromise query(ScriptState*, const QueryOptions* options);
ScriptPromise showFontChooser(ScriptState*, const QueryOptions* options);
void Trace(blink::Visitor*) const override; void Trace(blink::Visitor*) const override;
...@@ -42,6 +41,14 @@ class FontManager final : public ScriptWrappable, ...@@ -42,6 +41,14 @@ class FontManager final : public ScriptWrappable,
void DidShowFontChooser(ScriptPromiseResolver* resolver, void DidShowFontChooser(ScriptPromiseResolver* resolver,
mojom::blink::FontEnumerationStatus status, mojom::blink::FontEnumerationStatus status,
Vector<mojom::blink::FontMetadataPtr> fonts); Vector<mojom::blink::FontMetadataPtr> fonts);
void DidGetEnumerationResponse(ScriptPromiseResolver* resolver,
const Vector<String>& selection,
mojom::blink::FontEnumerationStatus,
base::ReadOnlySharedMemoryRegion);
// Returns whether the resolver has rejected.
bool RejectPromiseIfNecessary(
const mojom::blink::FontEnumerationStatus& status,
ScriptPromiseResolver* resolver);
void ContextDestroyed() override; void ContextDestroyed() override;
void OnDisconnect(); void OnDisconnect();
......
...@@ -8,6 +8,5 @@ ...@@ -8,6 +8,5 @@
SecureContext, SecureContext,
RuntimeEnabled=FontAccess RuntimeEnabled=FontAccess
] interface FontManager { ] interface FontManager {
[CallWith=ScriptState, RaisesException, Measure] object query(optional QueryOptions options = {}); [CallWith=ScriptState, Measure] Promise<sequence<FontMetadata>> query(optional QueryOptions options = {});
[CallWith=ScriptState, RuntimeEnabled=FontAccessChooser] Promise<sequence<FontMetadata>> showFontChooser(optional QueryOptions options = {});
}; };
...@@ -3,15 +3,11 @@ ...@@ -3,15 +3,11 @@
# found in the LICENSE file. # found in the LICENSE file.
modules_idl_files = [ modules_idl_files = [
"font_iterator.idl",
"font_metadata.idl", "font_metadata.idl",
"font_table_map.idl", "font_table_map.idl",
"font_manager.idl", "font_manager.idl",
] ]
modules_dictionary_idl_files = [ modules_dictionary_idl_files = [ "query_options.idl" ]
"font_iterator_entry.idl",
"query_options.idl",
]
modules_dependency_idl_files = [ "navigator_fonts.idl" ] modules_dependency_idl_files = [ "navigator_fonts.idl" ]
...@@ -4,5 +4,6 @@ ...@@ -4,5 +4,6 @@
// https://wicg.github.io/local-font-access/ // https://wicg.github.io/local-font-access/
dictionary QueryOptions { dictionary QueryOptions {
boolean persistentAccess = false;
sequence<DOMString> select = []; sequence<DOMString> select = [];
}; };
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
await new Promise(resolve => step_timeout(resolve, 100)); await new Promise(resolve => step_timeout(resolve, 100));
await window.test_driver.bless('show a font chooser.<br>Please select at least one font.'); await window.test_driver.bless('show a font chooser.<br>Please select at least one font.');
const promise = navigator.fonts.showFontChooser() const promise = navigator.fonts.query()
promise_rejects_dom(t, 'SecurityError', navigator.fonts.showFontChooser()); promise_rejects_dom(t, 'SecurityError', navigator.fonts.query());
const fonts = await promise; const fonts = await promise;
}, 'showFontChooser multiple choosers'); }, 'query() multiple choosers');
</script> </script>
<!doctype html> <!doctype html>
<title>Local Font Access: Chooser</title> <title>Local Font Access: Chooser, Selection Options</title>
<meta charset=utf-8> <meta charset=utf-8>
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
...@@ -21,24 +21,24 @@ ...@@ -21,24 +21,24 @@
promise_test(async t => { promise_test(async t => {
await window.test_driver.bless('show a font chooser.<br />Please select all the fonts that shows up.'); await window.test_driver.bless('show a font chooser.<br />Please select all the fonts that shows up.');
// Arial is considered to be web-safe. // Arial is considered to be web-safe.
const fonts = await navigator.fonts.showFontChooser({select: ['ArialMT']}); const fonts = await navigator.fonts.query({select: ['ArialMT']});
assert_true(Array.isArray(fonts)); assert_true(Array.isArray(fonts));
assert_equals(fonts.length, 1); assert_equals(fonts.length, 1);
const postscriptName = fonts[0].postscriptName; const postscriptName = fonts[0].postscriptName;
assert_equals(typeof postscriptName, "string"); assert_equals(typeof postscriptName, "string");
assert_greater_than(postscriptName.length, 0); assert_greater_than(postscriptName.length, 0);
}, 'showFontChooser with selection works'); }, 'query() with selection works');
promise_test(async t => { promise_test(async t => {
await window.test_driver.bless('show a font chooser.<br />Please select all the fonts that shows up.'); await window.test_driver.bless('show a font chooser.<br />Please select all the fonts that shows up.');
const fonts = await navigator.fonts.showFontChooser({select: []}); const fonts = await navigator.fonts.query({select: []});
assert_true(Array.isArray(fonts)); assert_true(Array.isArray(fonts));
assert_greater_than_equal(fonts.length, 1); assert_greater_than_equal(fonts.length, 1);
const postscriptName = fonts[0].postscriptName; const postscriptName = fonts[0].postscriptName;
assert_equals(typeof postscriptName, "string"); assert_equals(typeof postscriptName, "string");
assert_greater_than(postscriptName.length, 0); assert_greater_than(postscriptName.length, 0);
}, 'showFontChooser with empty selection bag works'); }, 'query() with empty selection bag works');
})(); })();
</script> </script>
<!doctype html> <!doctype html>
<title>Local Font Access: Chooser</title>
<meta charset=utf-8> <meta charset=utf-8>
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
...@@ -16,13 +17,13 @@ ...@@ -16,13 +17,13 @@
await new Promise(resolve => step_timeout(resolve, 100)); await new Promise(resolve => step_timeout(resolve, 100));
await window.test_driver.bless('show a font chooser.<br />Please select at least one font.'); await window.test_driver.bless('show a font chooser.<br />Please select at least one font.');
const fonts = await navigator.fonts.showFontChooser(); const fonts = await navigator.fonts.query();
assert_true(Array.isArray(fonts)); assert_true(Array.isArray(fonts));
assert_greater_than_equal(fonts.length, 1); assert_greater_than_equal(fonts.length, 1);
const postscriptName = fonts[0].postscriptName; const postscriptName = fonts[0].postscriptName;
assert_equals(typeof postscriptName, "string"); assert_equals(typeof postscriptName, "string");
assert_greater_than(postscriptName.length, 0); assert_greater_than(postscriptName.length, 0);
}, 'showFontChooser works'); }, 'query() with chooser works');
</script> </script>
...@@ -306,14 +306,14 @@ function getMoreExpectedTables(expectations) { ...@@ -306,14 +306,14 @@ function getMoreExpectedTables(expectations) {
return output; return output;
} }
async function filterEnumeration(iterator, expectedFonts) { async function filterEnumeration(fonts, expectedFonts) {
const nameSet = new Set(); const nameSet = new Set();
for (const e of expectedFonts) { for (const e of expectedFonts) {
nameSet.add(e.postscriptName); nameSet.add(e.postscriptName);
} }
const output = []; const output = [];
for await (const f of iterator) { for (const f of fonts) {
if (nameSet.has(f.postscriptName)) { if (nameSet.has(f.postscriptName)) {
output.push(f); output.push(f);
} }
......
'use strict'; 'use strict';
font_access_test(async t => { font_access_test(async t => {
const iterator = navigator.fonts.query();
if (!isPlatformSupported()) { if (!isPlatformSupported()) {
await promise_rejects_dom(t, 'NotSupportedError', (async () => { await promise_rejects_dom(t, 'NotSupportedError', navigator.fonts.query());
for await (const f of iterator) {
}
})());
return; return;
} }
const expectedFonts = await filterEnumeration(iterator, const fonts = await navigator.fonts.query({persistentAccess: true});
getEnumerationTestSet({ const expectedFonts = await filterEnumeration(
labelFilter: [TEST_SIZE_CATEGORY.small]})); fonts, getEnumerationTestSet({labelFilter: [TEST_SIZE_CATEGORY.small]}));
const additionalExpectedTables = getMoreExpectedTables(expectedFonts); const additionalExpectedTables = getMoreExpectedTables(expectedFonts);
for (const f of expectedFonts) { for (const f of expectedFonts) {
......
'use strict'; 'use strict';
const standard_fonts_tests = [ const standard_fonts_tests = [
null,
undefined,
{}, {},
{select: []}, {select: []},
]; ];
...@@ -13,47 +11,31 @@ for (const test of standard_fonts_tests) { ...@@ -13,47 +11,31 @@ for (const test of standard_fonts_tests) {
font_access_test(async t => { font_access_test(async t => {
if (!isPlatformSupported()) { if (!isPlatformSupported()) {
await promise_rejects_dom( await promise_rejects_dom(
t, 'NotSupportedError', (async () => { t, 'NotSupportedError', navigator.fonts.query());
for await (const f of navigator.fonts.query()) {
}
})());
return; return;
} }
const iterator = navigator.fonts.query(); const fonts =
await navigator.fonts.query({persistentAccess: true, ...test});
assert_equals(typeof iterator, 'object', 'query() should return an Object');
assert_true(
!!iterator[Symbol.asyncIterator],
'query() has an asyncIterator method');
const availableFonts = [];
for await (const f of iterator) {
availableFonts.push(f);
}
assert_fonts_exist(availableFonts, getEnumerationTestSet()); assert_fonts_exist(fonts, getEnumerationTestSet());
}, `query(): standard fonts returned for input: ${inputAsString}`); }, `query(): standard fonts returned for input: ${inputAsString}`);
} }
font_access_test(async t => { font_access_test(async t => {
const iterator = navigator.fonts.query();
if (!isPlatformSupported()) { if (!isPlatformSupported()) {
await promise_rejects_dom(t, 'NotSupportedError', (async () => { await promise_rejects_dom(t, 'NotSupportedError', navigator.fonts.query());
for await (const f of iterator) {
}
})());
return; return;
} }
const fonts = await navigator.fonts.query({persistentAccess: true});
// The following tests that fonts are sorted. Postscript names are expected to // The following tests that fonts are sorted. Postscript names are expected to
// be encoded in a subset of the ASCII character set. // be encoded in a subset of the ASCII character set.
// See: https://docs.microsoft.com/en-us/typography/opentype/spec/name // See: https://docs.microsoft.com/en-us/typography/opentype/spec/name
// Should the Postscript name contain characters that are multi-byte, this // Should the Postscript name contain characters that are multi-byte, this
// test may erroneously fail. // test may erroneously fail.
let previousFont = null; let previousFont = null;
for await (const font of iterator) { for (const font of fonts) {
if (previousFont) { if (previousFont) {
assert_true( assert_true(
previousFont.postscriptName < font.postscriptName, previousFont.postscriptName < font.postscriptName,
...@@ -67,20 +49,16 @@ font_access_test(async t => { ...@@ -67,20 +49,16 @@ font_access_test(async t => {
font_access_test(async t => { font_access_test(async t => {
if (!isPlatformSupported()) { if (!isPlatformSupported()) {
await promise_rejects_dom(t, 'NotSupportedError', (async () => { await promise_rejects_dom(t, 'NotSupportedError', navigator.fonts.query());
for await (const f of navigator.fonts.query()) {
}
})());
return; return;
} }
const test = {select: [getEnumerationTestSet()[0].postscriptName]}; const test = {
const iterator = navigator.fonts.query(test); persistentAccess: true,
select: [getEnumerationTestSet()[0].postscriptName]
};
const fonts = await navigator.fonts.query(test);
const fonts = [];
for await (const f of iterator) {
fonts.push(f);
}
assert_postscript_name_exists(fonts, test.select); assert_postscript_name_exists(fonts, test.select);
assert_equals( assert_equals(
fonts.length, test.select.length, fonts.length, test.select.length,
...@@ -109,18 +87,12 @@ for (const test of non_ascii_input) { ...@@ -109,18 +87,12 @@ for (const test of non_ascii_input) {
font_access_test(async t => { font_access_test(async t => {
if (!isPlatformSupported()) { if (!isPlatformSupported()) {
await promise_rejects_dom( await promise_rejects_dom(
t, 'NotSupportedError', (async () => { t, 'NotSupportedError', navigator.fonts.query());
for await (const f of navigator.fonts.query()) {
}
})());
return; return;
} }
const fonts = []; const fonts =
const iterator = navigator.fonts.query(test); await navigator.fonts.query({persistentAccess: true, ...test});
for await (const f of iterator) {
fonts.push(f);
}
assert_equals( assert_equals(
fonts.length, 0, fonts.length, 0,
`There should be no results. Instead got: ${JSON.stringify(fonts)}`); `There should be no results. Instead got: ${JSON.stringify(fonts)}`);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment