| Index: Source/core/css/FontFaceSet.cpp
|
| diff --git a/Source/core/css/FontFaceSet.cpp b/Source/core/css/FontFaceSet.cpp
|
| index 0af91b4b87649c1a9cbe255bac1b1e3fc1088a6e..c9838d2df51298ff3a7e460aa6f16527939f448c 100644
|
| --- a/Source/core/css/FontFaceSet.cpp
|
| +++ b/Source/core/css/FontFaceSet.cpp
|
| @@ -27,7 +27,11 @@
|
| #include "core/css/FontFaceSet.h"
|
|
|
| #include "RuntimeEnabledFeatures.h"
|
| +#include "V8FontFaceSet.h"
|
| #include "bindings/v8/Dictionary.h"
|
| +#include "bindings/v8/ScriptPromiseResolver.h"
|
| +#include "bindings/v8/ScriptScope.h"
|
| +#include "bindings/v8/ScriptState.h"
|
| #include "core/css/CSSFontFaceLoadEvent.h"
|
| #include "core/css/CSSFontFaceSource.h"
|
| #include "core/css/CSSFontSelector.h"
|
| @@ -44,76 +48,106 @@ namespace WebCore {
|
| static const int defaultFontSize = 10;
|
| static const char* const defaultFontFamily = "sans-serif";
|
|
|
| -class LoadFontCallback : public CSSSegmentedFontFace::LoadFontCallback {
|
| +class LoadFontPromiseResolver : public CSSSegmentedFontFace::LoadFontCallback {
|
| public:
|
| - static PassRefPtr<LoadFontCallback> create(int numLoading, PassRefPtr<VoidCallback> loadCallback, PassRefPtr<VoidCallback> errorCallback)
|
| + static PassRefPtr<LoadFontPromiseResolver> create(const FontFamily& family, ScriptExecutionContext* context)
|
| {
|
| - return adoptRef<LoadFontCallback>(new LoadFontCallback(numLoading, loadCallback, errorCallback));
|
| - }
|
| -
|
| - static PassRefPtr<LoadFontCallback> createFromParams(const Dictionary& params, const FontFamily& family)
|
| - {
|
| - RefPtr<VoidCallback> onsuccess;
|
| - RefPtr<VoidCallback> onerror;
|
| - params.get("onsuccess", onsuccess);
|
| - params.get("onerror", onerror);
|
| - if (!onsuccess && !onerror)
|
| - return 0;
|
| int numFamilies = 0;
|
| for (const FontFamily* f = &family; f; f = f->next())
|
| numFamilies++;
|
| - return LoadFontCallback::create(numFamilies, onsuccess, onerror);
|
| + return adoptRef<LoadFontPromiseResolver>(new LoadFontPromiseResolver(numFamilies, context));
|
| }
|
|
|
| virtual void notifyLoaded(CSSSegmentedFontFace*) OVERRIDE;
|
| virtual void notifyError(CSSSegmentedFontFace*) OVERRIDE;
|
| void loaded(Document*);
|
| void error(Document*);
|
| + void resolve();
|
| +
|
| + ScriptPromise promise()
|
| + {
|
| + ScriptPromise promise = m_resolver->promise();
|
| + m_resolver->detachPromise();
|
| + return promise;
|
| + }
|
| +
|
| private:
|
| - LoadFontCallback(int numLoading, PassRefPtr<VoidCallback> loadCallback, PassRefPtr<VoidCallback> errorCallback)
|
| + LoadFontPromiseResolver(int numLoading, ScriptExecutionContext* context)
|
| : m_numLoading(numLoading)
|
| , m_errorOccured(false)
|
| - , m_loadCallback(loadCallback)
|
| - , m_errorCallback(errorCallback)
|
| + , m_scriptState(ScriptState::current())
|
| + , m_resolver(ScriptPromiseResolver::create(context))
|
| { }
|
|
|
| int m_numLoading;
|
| bool m_errorOccured;
|
| - RefPtr<VoidCallback> m_loadCallback;
|
| - RefPtr<VoidCallback> m_errorCallback;
|
| + ScriptState* m_scriptState;
|
| + RefPtr<ScriptPromiseResolver> m_resolver;
|
| };
|
|
|
| -void LoadFontCallback::loaded(Document* document)
|
| +void LoadFontPromiseResolver::loaded(Document* document)
|
| {
|
| m_numLoading--;
|
| if (m_numLoading || !document)
|
| return;
|
|
|
| - if (m_errorOccured) {
|
| - if (m_errorCallback)
|
| - document->fonts()->scheduleCallback(m_errorCallback.release());
|
| - } else {
|
| - if (m_loadCallback)
|
| - document->fonts()->scheduleCallback(m_loadCallback.release());
|
| - }
|
| + document->fonts()->scheduleResolve(this);
|
| }
|
|
|
| -void LoadFontCallback::error(Document* document)
|
| +void LoadFontPromiseResolver::error(Document* document)
|
| {
|
| m_errorOccured = true;
|
| loaded(document);
|
| }
|
|
|
| -void LoadFontCallback::notifyLoaded(CSSSegmentedFontFace* face)
|
| +void LoadFontPromiseResolver::notifyLoaded(CSSSegmentedFontFace* face)
|
| {
|
| loaded(face->fontSelector()->document());
|
| }
|
|
|
| -void LoadFontCallback::notifyError(CSSSegmentedFontFace* face)
|
| +void LoadFontPromiseResolver::notifyError(CSSSegmentedFontFace* face)
|
| {
|
| error(face->fontSelector()->document());
|
| }
|
|
|
| +void LoadFontPromiseResolver::resolve()
|
| +{
|
| + ScriptScope scope(m_scriptState);
|
| + if (m_errorOccured)
|
| + m_resolver->reject(ScriptValue::createNull());
|
| + else
|
| + m_resolver->fulfill(ScriptValue::createNull());
|
| +}
|
| +
|
| +class FontsReadyPromiseResolver {
|
| +public:
|
| + static PassOwnPtr<FontsReadyPromiseResolver> create(ScriptExecutionContext* context)
|
| + {
|
| + return adoptPtr(new FontsReadyPromiseResolver(context));
|
| + }
|
| +
|
| + void call(PassRefPtr<FontFaceSet> fontFaceSet)
|
| + {
|
| + ScriptScope scope(m_scriptState);
|
| + m_resolver->fulfill(fontFaceSet);
|
| + }
|
| +
|
| + ScriptPromise promise()
|
| + {
|
| + ScriptPromise promise = m_resolver->promise();
|
| + m_resolver->detachPromise();
|
| + return promise;
|
| + }
|
| +
|
| +private:
|
| + FontsReadyPromiseResolver(ScriptExecutionContext* context)
|
| + : m_scriptState(ScriptState::current())
|
| + , m_resolver(ScriptPromiseResolver::create(context))
|
| + { }
|
| + ScriptState* m_scriptState;
|
| + RefPtr<ScriptPromiseResolver> m_resolver;
|
| +};
|
| +
|
| FontFaceSet::FontFaceSet(Document* document)
|
| : ActiveDOMObject(document)
|
| , m_loadingCount(0)
|
| @@ -152,6 +186,13 @@ ScriptExecutionContext* FontFaceSet::scriptExecutionContext() const
|
| return ActiveDOMObject::scriptExecutionContext();
|
| }
|
|
|
| +AtomicString FontFaceSet::status() const
|
| +{
|
| + DEFINE_STATIC_LOCAL(AtomicString, loading, ("loading", AtomicString::ConstructFromLiteral));
|
| + DEFINE_STATIC_LOCAL(AtomicString, loaded, ("loaded", AtomicString::ConstructFromLiteral));
|
| + return (m_loadingCount > 0 || m_shouldFireDoneEvent) ? loading : loaded;
|
| +}
|
| +
|
| void FontFaceSet::didLayout()
|
| {
|
| Document* d = document();
|
| @@ -159,7 +200,7 @@ void FontFaceSet::didLayout()
|
| m_histogram.record();
|
| if (!RuntimeEnabledFeatures::fontLoadEventsEnabled())
|
| return;
|
| - if (m_loadingCount || (!m_shouldFireDoneEvent && m_fontsReadyCallbacks.isEmpty()))
|
| + if (m_loadingCount || (!m_shouldFireDoneEvent && m_readyResolvers.isEmpty()))
|
| return;
|
| if (!m_timer.isActive())
|
| m_timer.startOneShot(0);
|
| @@ -168,7 +209,7 @@ void FontFaceSet::didLayout()
|
| void FontFaceSet::timerFired(Timer<FontFaceSet>*)
|
| {
|
| firePendingEvents();
|
| - firePendingCallbacks();
|
| + resolvePendingLoadPromises();
|
| fireDoneEventIfPossible();
|
| }
|
|
|
| @@ -190,22 +231,22 @@ void FontFaceSet::firePendingEvents()
|
| dispatchEvent(pendingEvents[index].release());
|
| }
|
|
|
| -void FontFaceSet::scheduleCallback(PassRefPtr<VoidCallback> callback)
|
| +void FontFaceSet::scheduleResolve(LoadFontPromiseResolver* resolver)
|
| {
|
| - m_pendingCallbacks.append(callback);
|
| + m_pendingLoadResolvers.append(resolver);
|
| if (!m_timer.isActive())
|
| m_timer.startOneShot(0);
|
| }
|
|
|
| -void FontFaceSet::firePendingCallbacks()
|
| +void FontFaceSet::resolvePendingLoadPromises()
|
| {
|
| - if (m_pendingCallbacks.isEmpty())
|
| + if (m_pendingLoadResolvers.isEmpty())
|
| return;
|
|
|
| - Vector<RefPtr<VoidCallback> > pendingCallbacks;
|
| - m_pendingCallbacks.swap(pendingCallbacks);
|
| - for (size_t index = 0; index < pendingCallbacks.size(); ++index)
|
| - pendingCallbacks[index]->handleEvent();
|
| + Vector<RefPtr<LoadFontPromiseResolver> > resolvers;
|
| + m_pendingLoadResolvers.swap(resolvers);
|
| + for (size_t index = 0; index < resolvers.size(); ++index)
|
| + resolvers[index]->resolve();
|
| }
|
|
|
| void FontFaceSet::beginFontLoading(FontFace* fontFace)
|
| @@ -248,18 +289,21 @@ void FontFaceSet::queueDoneEvent(FontFace* fontFace)
|
| }
|
| }
|
|
|
| -void FontFaceSet::notifyWhenFontsReady(PassRefPtr<VoidCallback> callback)
|
| +ScriptPromise FontFaceSet::ready()
|
| {
|
| - m_fontsReadyCallbacks.append(callback);
|
| + OwnPtr<FontsReadyPromiseResolver> resolver = FontsReadyPromiseResolver::create(scriptExecutionContext());
|
| + ScriptPromise promise = resolver->promise();
|
| + m_readyResolvers.append(resolver.release());
|
| if (!m_timer.isActive())
|
| m_timer.startOneShot(0);
|
| + return promise;
|
| }
|
|
|
| void FontFaceSet::fireDoneEventIfPossible()
|
| {
|
| - if (!m_pendingEvents.isEmpty() || !m_pendingCallbacks.isEmpty())
|
| + if (!m_pendingEvents.isEmpty() || !m_pendingLoadResolvers.isEmpty())
|
| return;
|
| - if (m_loadingCount || (!m_shouldFireDoneEvent && m_fontsReadyCallbacks.isEmpty()))
|
| + if (m_loadingCount || (!m_shouldFireDoneEvent && m_readyResolvers.isEmpty()))
|
| return;
|
|
|
| // If the layout was invalidated in between when we thought layout
|
| @@ -284,43 +328,64 @@ void FontFaceSet::fireDoneEventIfPossible()
|
| dispatchEvent(errorEvent);
|
| }
|
|
|
| - if (!m_fontsReadyCallbacks.isEmpty()) {
|
| - Vector<RefPtr<VoidCallback> > callbacks;
|
| - m_fontsReadyCallbacks.swap(callbacks);
|
| - for (size_t index = 0; index < callbacks.size(); ++index)
|
| - callbacks[index]->handleEvent();
|
| + if (!m_readyResolvers.isEmpty()) {
|
| + Vector<OwnPtr<FontsReadyPromiseResolver> > resolvers;
|
| + m_readyResolvers.swap(resolvers);
|
| + for (size_t index = 0; index < resolvers.size(); ++index)
|
| + resolvers[index]->call(this);
|
| }
|
| }
|
|
|
| -void FontFaceSet::loadFont(const Dictionary& params)
|
| +Vector<RefPtr<FontFace> > FontFaceSet::match(const String& fontString, const String&, ExceptionState& es)
|
| {
|
| - // FIXME: The text member of params is ignored.
|
| - String fontString;
|
| - if (!params.get("font", fontString))
|
| - return;
|
| + // FIXME: The second parameter (text) is ignored.
|
| + Vector<RefPtr<FontFace> > matchedFonts;
|
| +
|
| Font font;
|
| - if (!resolveFontStyle(fontString, font))
|
| - return;
|
| - RefPtr<LoadFontCallback> callback = LoadFontCallback::createFromParams(params, font.family());
|
| + if (!resolveFontStyle(fontString, font)) {
|
| + es.throwDOMException(SyntaxError);
|
| + return matchedFonts;
|
| + }
|
| +
|
| + for (const FontFamily* f = &font.family(); f; f = f->next()) {
|
| + CSSSegmentedFontFace* face = document()->styleResolver()->fontSelector()->getFontFace(font.fontDescription(), f->family());
|
| + if (face)
|
| + matchedFonts.append(face->fontFaces());
|
| + }
|
| + return matchedFonts;
|
| +}
|
| +
|
| +ScriptPromise FontFaceSet::load(const String& fontString, const String&, ExceptionState& es)
|
| +{
|
| + // FIXME: The second parameter (text) is ignored.
|
| + Font font;
|
| + if (!resolveFontStyle(fontString, font)) {
|
| + es.throwDOMException(SyntaxError);
|
| + return ScriptPromise();
|
| + }
|
|
|
| + Document* d = document();
|
| + RefPtr<LoadFontPromiseResolver> resolver = LoadFontPromiseResolver::create(font.family(), scriptExecutionContext());
|
| for (const FontFamily* f = &font.family(); f; f = f->next()) {
|
| - Document* d = document();
|
| CSSSegmentedFontFace* face = d->styleResolver()->fontSelector()->getFontFace(font.fontDescription(), f->family());
|
| if (!face) {
|
| - if (callback)
|
| - callback->error(d);
|
| + resolver->error(d);
|
| continue;
|
| }
|
| - face->loadFont(font.fontDescription(), callback);
|
| + face->loadFont(font.fontDescription(), resolver);
|
| }
|
| + return resolver->promise();
|
| }
|
|
|
| -bool FontFaceSet::checkFont(const String& fontString, const String&)
|
| +bool FontFaceSet::check(const String& fontString, const String&, ExceptionState& es)
|
| {
|
| // FIXME: The second parameter (text) is ignored.
|
| Font font;
|
| - if (!resolveFontStyle(fontString, font))
|
| + if (!resolveFontStyle(fontString, font)) {
|
| + es.throwDOMException(SyntaxError);
|
| return false;
|
| + }
|
| +
|
| for (const FontFamily* f = &font.family(); f; f = f->next()) {
|
| CSSSegmentedFontFace* face = document()->styleResolver()->fontSelector()->getFontFace(font.fontDescription(), f->family());
|
| if (!face || !face->checkFont())
|
| @@ -331,6 +396,9 @@ bool FontFaceSet::checkFont(const String& fontString, const String&)
|
|
|
| bool FontFaceSet::resolveFontStyle(const String& fontString, Font& font)
|
| {
|
| + if (fontString.isEmpty())
|
| + return false;
|
| +
|
| // Interpret fontString in the same way as the 'font' attribute of CanvasRenderingContext2D.
|
| RefPtr<MutableStylePropertySet> parsedStyle = MutableStylePropertySet::create();
|
| CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, fontString, true, CSSStrictMode, 0);
|
|
|