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) {
int result = EvalJs(shell(),
"(async () => {"
" 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++;"
" }"
" return count;"
......@@ -119,7 +121,9 @@ IN_PROC_BROWSER_TEST_F(FontAccessManagerImplBrowserTest, LocaleTest) {
EvalJs(shell(),
"(async () => {"
" 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') {"
" fullName = item.fullName;"
" break;"
......
......@@ -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_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_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.h",
"$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 = [
"$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.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.h",
"$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(
"//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/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_metadata.idl",
"//third_party/blink/renderer/modules/font_access/font_table_map.idl",
......
......@@ -6,8 +6,6 @@ import("//third_party/blink/renderer/modules/modules.gni")
blink_modules_sources("font_access") {
sources = [
"font_iterator.cc",
"font_iterator.h",
"font_manager.cc",
"font_manager.h",
"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 @@
#include "base/feature_list.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/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/script_value.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/frame/local_dom_window.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/query_options.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
namespace blink {
namespace {
void ReturnDataFunction(const v8::FunctionCallbackInfo<v8::Value>& info) {
V8SetReturnValue(info, info.Data());
}
} // namespace
using mojom::blink::FontEnumerationStatus;
FontManager::FontManager(ExecutionContext* context)
: ExecutionContextLifecycleObserver(context) {
......@@ -43,37 +36,19 @@ FontManager::FontManager(ExecutionContext* context)
}
}
ScriptValue 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,
ScriptPromise FontManager::query(ScriptState* script_state,
const QueryOptions* options) {
DCHECK(options->hasSelect());
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
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(
options->select(),
WTF::Bind(&FontManager::DidShowFontChooser, WrapWeakPersistent(this),
......@@ -89,38 +64,98 @@ void FontManager::Trace(blink::Visitor* visitor) const {
void FontManager::DidShowFontChooser(
ScriptPromiseResolver* resolver,
mojom::blink::FontEnumerationStatus status,
FontEnumerationStatus status,
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) {
case mojom::blink::FontEnumerationStatus::kOk:
case FontEnumerationStatus::kOk:
break;
case mojom::blink::FontEnumerationStatus::kUnimplemented:
case FontEnumerationStatus::kUnimplemented:
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotSupportedError,
"Not yet supported on this platform."));
return;
case mojom::blink::FontEnumerationStatus::kCanceled:
return true;
case FontEnumerationStatus::kCanceled:
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kAbortError, "The user canceled the operation."));
return;
case mojom::blink::FontEnumerationStatus::kNeedsUserActivation:
return true;
case FontEnumerationStatus::kNeedsUserActivation:
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kSecurityError, "User activation is required."));
return;
case mojom::blink::FontEnumerationStatus::kUnexpectedError:
return true;
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:
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kUnknownError, "An unexpected error occured."));
return;
return true;
}
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));
return false;
}
void FontManager::ContextDestroyed() {
......
......@@ -5,6 +5,7 @@
#ifndef 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 "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"
......@@ -16,7 +17,6 @@
namespace blink {
class ScriptState;
class ScriptValue;
class ScriptPromise;
class ScriptPromiseResolver;
class QueryOptions;
......@@ -33,8 +33,7 @@ class FontManager final : public ScriptWrappable,
FontManager operator=(const FontManager&) = delete;
// FontManager IDL interface implementation.
ScriptValue query(ScriptState*, const QueryOptions* options, ExceptionState&);
ScriptPromise showFontChooser(ScriptState*, const QueryOptions* options);
ScriptPromise query(ScriptState*, const QueryOptions* options);
void Trace(blink::Visitor*) const override;
......@@ -42,6 +41,14 @@ class FontManager final : public ScriptWrappable,
void DidShowFontChooser(ScriptPromiseResolver* resolver,
mojom::blink::FontEnumerationStatus status,
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 OnDisconnect();
......
......@@ -8,6 +8,5 @@
SecureContext,
RuntimeEnabled=FontAccess
] interface FontManager {
[CallWith=ScriptState, RaisesException, Measure] object query(optional QueryOptions options = {});
[CallWith=ScriptState, RuntimeEnabled=FontAccessChooser] Promise<sequence<FontMetadata>> showFontChooser(optional QueryOptions options = {});
[CallWith=ScriptState, Measure] Promise<sequence<FontMetadata>> query(optional QueryOptions options = {});
};
......@@ -3,15 +3,11 @@
# found in the LICENSE file.
modules_idl_files = [
"font_iterator.idl",
"font_metadata.idl",
"font_table_map.idl",
"font_manager.idl",
]
modules_dictionary_idl_files = [
"font_iterator_entry.idl",
"query_options.idl",
]
modules_dictionary_idl_files = [ "query_options.idl" ]
modules_dependency_idl_files = [ "navigator_fonts.idl" ]
......@@ -4,5 +4,6 @@
// https://wicg.github.io/local-font-access/
dictionary QueryOptions {
boolean persistentAccess = false;
sequence<DOMString> select = [];
};
......@@ -17,8 +17,8 @@
await new Promise(resolve => step_timeout(resolve, 100));
await window.test_driver.bless('show a font chooser.<br>Please select at least one font.');
const promise = navigator.fonts.showFontChooser()
promise_rejects_dom(t, 'SecurityError', navigator.fonts.showFontChooser());
const promise = navigator.fonts.query()
promise_rejects_dom(t, 'SecurityError', navigator.fonts.query());
const fonts = await promise;
}, 'showFontChooser multiple choosers');
}, 'query() multiple choosers');
</script>
<!doctype html>
<title>Local Font Access: Chooser</title>
<title>Local Font Access: Chooser, Selection Options</title>
<meta charset=utf-8>
<script src="/resources/testharness.js"></script>
......@@ -21,24 +21,24 @@
promise_test(async t => {
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.
const fonts = await navigator.fonts.showFontChooser({select: ['ArialMT']});
const fonts = await navigator.fonts.query({select: ['ArialMT']});
assert_true(Array.isArray(fonts));
assert_equals(fonts.length, 1);
const postscriptName = fonts[0].postscriptName;
assert_equals(typeof postscriptName, "string");
assert_greater_than(postscriptName.length, 0);
}, 'showFontChooser with selection works');
}, 'query() with selection works');
promise_test(async t => {
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_greater_than_equal(fonts.length, 1);
const postscriptName = fonts[0].postscriptName;
assert_equals(typeof postscriptName, "string");
assert_greater_than(postscriptName.length, 0);
}, 'showFontChooser with empty selection bag works');
}, 'query() with empty selection bag works');
})();
</script>
<!doctype html>
<title>Local Font Access: Chooser</title>
<meta charset=utf-8>
<script src="/resources/testharness.js"></script>
......@@ -16,13 +17,13 @@
await new Promise(resolve => step_timeout(resolve, 100));
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_greater_than_equal(fonts.length, 1);
const postscriptName = fonts[0].postscriptName;
assert_equals(typeof postscriptName, "string");
assert_greater_than(postscriptName.length, 0);
}, 'showFontChooser works');
}, 'query() with chooser works');
</script>
......@@ -306,14 +306,14 @@ function getMoreExpectedTables(expectations) {
return output;
}
async function filterEnumeration(iterator, expectedFonts) {
async function filterEnumeration(fonts, expectedFonts) {
const nameSet = new Set();
for (const e of expectedFonts) {
nameSet.add(e.postscriptName);
}
const output = [];
for await (const f of iterator) {
for (const f of fonts) {
if (nameSet.has(f.postscriptName)) {
output.push(f);
}
......
'use strict';
font_access_test(async t => {
const iterator = navigator.fonts.query();
if (!isPlatformSupported()) {
await promise_rejects_dom(t, 'NotSupportedError', (async () => {
for await (const f of iterator) {
}
})());
await promise_rejects_dom(t, 'NotSupportedError', navigator.fonts.query());
return;
}
const expectedFonts = await filterEnumeration(iterator,
getEnumerationTestSet({
labelFilter: [TEST_SIZE_CATEGORY.small]}));
const fonts = await navigator.fonts.query({persistentAccess: true});
const expectedFonts = await filterEnumeration(
fonts, getEnumerationTestSet({labelFilter: [TEST_SIZE_CATEGORY.small]}));
const additionalExpectedTables = getMoreExpectedTables(expectedFonts);
for (const f of expectedFonts) {
......
'use strict';
const standard_fonts_tests = [
null,
undefined,
{},
{select: []},
];
......@@ -13,47 +11,31 @@ for (const test of standard_fonts_tests) {
font_access_test(async t => {
if (!isPlatformSupported()) {
await promise_rejects_dom(
t, 'NotSupportedError', (async () => {
for await (const f of navigator.fonts.query()) {
}
})());
t, 'NotSupportedError', navigator.fonts.query());
return;
}
const iterator = navigator.fonts.query();
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);
}
const fonts =
await navigator.fonts.query({persistentAccess: true, ...test});
assert_fonts_exist(availableFonts, getEnumerationTestSet());
assert_fonts_exist(fonts, getEnumerationTestSet());
}, `query(): standard fonts returned for input: ${inputAsString}`);
}
font_access_test(async t => {
const iterator = navigator.fonts.query();
if (!isPlatformSupported()) {
await promise_rejects_dom(t, 'NotSupportedError', (async () => {
for await (const f of iterator) {
}
})());
await promise_rejects_dom(t, 'NotSupportedError', navigator.fonts.query());
return;
}
const fonts = await navigator.fonts.query({persistentAccess: true});
// The following tests that fonts are sorted. Postscript names are expected to
// be encoded in a subset of the ASCII character set.
// See: https://docs.microsoft.com/en-us/typography/opentype/spec/name
// Should the Postscript name contain characters that are multi-byte, this
// test may erroneously fail.
let previousFont = null;
for await (const font of iterator) {
for (const font of fonts) {
if (previousFont) {
assert_true(
previousFont.postscriptName < font.postscriptName,
......@@ -67,20 +49,16 @@ font_access_test(async t => {
font_access_test(async t => {
if (!isPlatformSupported()) {
await promise_rejects_dom(t, 'NotSupportedError', (async () => {
for await (const f of navigator.fonts.query()) {
}
})());
await promise_rejects_dom(t, 'NotSupportedError', navigator.fonts.query());
return;
}
const test = {select: [getEnumerationTestSet()[0].postscriptName]};
const iterator = navigator.fonts.query(test);
const 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_equals(
fonts.length, test.select.length,
......@@ -109,18 +87,12 @@ for (const test of non_ascii_input) {
font_access_test(async t => {
if (!isPlatformSupported()) {
await promise_rejects_dom(
t, 'NotSupportedError', (async () => {
for await (const f of navigator.fonts.query()) {
}
})());
t, 'NotSupportedError', navigator.fonts.query());
return;
}
const fonts = [];
const iterator = navigator.fonts.query(test);
for await (const f of iterator) {
fonts.push(f);
}
const fonts =
await navigator.fonts.query({persistentAccess: true, ...test});
assert_equals(
fonts.length, 0,
`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