Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / FontFaceSet.cpp
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
23  * DAMAGE.
24  */
25
26 #include "config.h"
27 #include "core/css/FontFaceSet.h"
28
29 #include "bindings/core/v8/Dictionary.h"
30 #include "bindings/core/v8/ScriptPromiseResolver.h"
31 #include "bindings/core/v8/ScriptState.h"
32 #include "core/css/CSSFontSelector.h"
33 #include "core/css/CSSSegmentedFontFace.h"
34 #include "core/css/FontFaceCache.h"
35 #include "core/css/FontFaceSetLoadEvent.h"
36 #include "core/css/StylePropertySet.h"
37 #include "core/css/parser/CSSParser.h"
38 #include "core/css/resolver/StyleResolver.h"
39 #include "core/dom/Document.h"
40 #include "core/dom/StyleEngine.h"
41 #include "core/frame/FrameView.h"
42 #include "core/frame/LocalFrame.h"
43 #include "core/rendering/style/StyleInheritedData.h"
44 #include "public/platform/Platform.h"
45
46 namespace blink {
47
48 static const int defaultFontSize = 10;
49 static const char defaultFontFamily[] = "sans-serif";
50
51 class LoadFontPromiseResolver FINAL : public FontFace::LoadFontCallback {
52 public:
53     static PassRefPtrWillBeRawPtr<LoadFontPromiseResolver> create(FontFaceArray faces, ScriptState* scriptState)
54     {
55         return adoptRefWillBeNoop(new LoadFontPromiseResolver(faces, scriptState));
56     }
57
58     void loadFonts(ExecutionContext*);
59     ScriptPromise promise() { return m_resolver->promise(); }
60
61     virtual void notifyLoaded(FontFace*) OVERRIDE;
62     virtual void notifyError(FontFace*) OVERRIDE;
63
64     virtual void trace(Visitor*) OVERRIDE;
65
66 private:
67     LoadFontPromiseResolver(FontFaceArray faces, ScriptState* scriptState)
68         : m_numLoading(faces.size())
69         , m_errorOccured(false)
70         , m_resolver(ScriptPromiseResolver::create(scriptState))
71     {
72         m_fontFaces.swap(faces);
73     }
74
75     WillBeHeapVector<RefPtrWillBeMember<FontFace> > m_fontFaces;
76     int m_numLoading;
77     bool m_errorOccured;
78     RefPtr<ScriptPromiseResolver> m_resolver;
79 };
80
81 void LoadFontPromiseResolver::loadFonts(ExecutionContext* context)
82 {
83     if (!m_numLoading) {
84         m_resolver->resolve(m_fontFaces);
85         return;
86     }
87
88     for (size_t i = 0; i < m_fontFaces.size(); i++)
89         m_fontFaces[i]->loadWithCallback(this, context);
90 }
91
92 void LoadFontPromiseResolver::notifyLoaded(FontFace* fontFace)
93 {
94     m_numLoading--;
95     if (m_numLoading || m_errorOccured)
96         return;
97
98     m_resolver->resolve(m_fontFaces);
99 }
100
101 void LoadFontPromiseResolver::notifyError(FontFace* fontFace)
102 {
103     m_numLoading--;
104     if (!m_errorOccured) {
105         m_errorOccured = true;
106         m_resolver->reject(fontFace->error());
107     }
108 }
109
110 void LoadFontPromiseResolver::trace(Visitor* visitor)
111 {
112     visitor->trace(m_fontFaces);
113     LoadFontCallback::trace(visitor);
114 }
115
116 class FontsReadyPromiseResolver {
117 public:
118     static PassOwnPtr<FontsReadyPromiseResolver> create(ScriptState* scriptState)
119     {
120         return adoptPtr(new FontsReadyPromiseResolver(scriptState));
121     }
122
123     void resolve(PassRefPtrWillBeRawPtr<FontFaceSet> fontFaceSet)
124     {
125         m_resolver->resolve(fontFaceSet);
126     }
127
128     ScriptPromise promise() { return m_resolver->promise(); }
129
130 private:
131     explicit FontsReadyPromiseResolver(ScriptState* scriptState)
132         : m_resolver(ScriptPromiseResolver::create(scriptState))
133     {
134     }
135
136     RefPtr<ScriptPromiseResolver> m_resolver;
137 };
138
139 FontFaceSet::FontFaceSet(Document& document)
140     : ActiveDOMObject(&document)
141     , m_shouldFireLoadingEvent(false)
142     , m_asyncRunner(this, &FontFaceSet::handlePendingEventsAndPromises)
143 {
144     suspendIfNeeded();
145 }
146
147 FontFaceSet::~FontFaceSet()
148 {
149 }
150
151 Document* FontFaceSet::document() const
152 {
153     return toDocument(executionContext());
154 }
155
156 bool FontFaceSet::inActiveDocumentContext() const
157 {
158     ExecutionContext* context = executionContext();
159     return context && toDocument(context)->isActive();
160 }
161
162 void FontFaceSet::addFontFacesToFontFaceCache(FontFaceCache* fontFaceCache, CSSFontSelector* fontSelector)
163 {
164     for (WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::iterator it = m_nonCSSConnectedFaces.begin(); it != m_nonCSSConnectedFaces.end(); ++it)
165         fontFaceCache->addFontFace(fontSelector, *it, false);
166 }
167
168 const AtomicString& FontFaceSet::interfaceName() const
169 {
170     return EventTargetNames::FontFaceSet;
171 }
172
173 ExecutionContext* FontFaceSet::executionContext() const
174 {
175     return ActiveDOMObject::executionContext();
176 }
177
178 AtomicString FontFaceSet::status() const
179 {
180     DEFINE_STATIC_LOCAL(AtomicString, loading, ("loading", AtomicString::ConstructFromLiteral));
181     DEFINE_STATIC_LOCAL(AtomicString, loaded, ("loaded", AtomicString::ConstructFromLiteral));
182     return (!m_loadingFonts.isEmpty() || hasLoadedFonts()) ? loading : loaded;
183 }
184
185 void FontFaceSet::handlePendingEventsAndPromisesSoon()
186 {
187     // m_asyncRunner will be automatically stopped on destruction.
188     m_asyncRunner.runAsync();
189 }
190
191 void FontFaceSet::didLayout()
192 {
193     if (document()->frame()->isMainFrame() && m_loadingFonts.isEmpty())
194         m_histogram.record();
195     if (!m_loadingFonts.isEmpty() || (!hasLoadedFonts() && m_readyResolvers.isEmpty()))
196         return;
197     handlePendingEventsAndPromisesSoon();
198 }
199
200 void FontFaceSet::handlePendingEventsAndPromises()
201 {
202     fireLoadingEvent();
203     fireDoneEventIfPossible();
204 }
205
206 void FontFaceSet::fireLoadingEvent()
207 {
208     if (m_shouldFireLoadingEvent) {
209         m_shouldFireLoadingEvent = false;
210         dispatchEvent(FontFaceSetLoadEvent::createForFontFaces(EventTypeNames::loading));
211     }
212 }
213
214 void FontFaceSet::suspend()
215 {
216     m_asyncRunner.suspend();
217 }
218
219 void FontFaceSet::resume()
220 {
221     m_asyncRunner.resume();
222 }
223
224 void FontFaceSet::stop()
225 {
226     m_asyncRunner.stop();
227 }
228
229 void FontFaceSet::beginFontLoading(FontFace* fontFace)
230 {
231     m_histogram.incrementCount();
232     addToLoadingFonts(fontFace);
233 }
234
235 void FontFaceSet::fontLoaded(FontFace* fontFace)
236 {
237     m_histogram.updateStatus(fontFace);
238     m_loadedFonts.append(fontFace);
239     removeFromLoadingFonts(fontFace);
240 }
241
242 void FontFaceSet::loadError(FontFace* fontFace)
243 {
244     m_histogram.updateStatus(fontFace);
245     m_failedFonts.append(fontFace);
246     removeFromLoadingFonts(fontFace);
247 }
248
249 void FontFaceSet::addToLoadingFonts(PassRefPtrWillBeRawPtr<FontFace> fontFace)
250 {
251     if (m_loadingFonts.isEmpty() && !hasLoadedFonts()) {
252         m_shouldFireLoadingEvent = true;
253         handlePendingEventsAndPromisesSoon();
254     }
255     m_loadingFonts.add(fontFace);
256 }
257
258 void FontFaceSet::removeFromLoadingFonts(PassRefPtrWillBeRawPtr<FontFace> fontFace)
259 {
260     m_loadingFonts.remove(fontFace);
261     if (m_loadingFonts.isEmpty())
262         handlePendingEventsAndPromisesSoon();
263 }
264
265 ScriptPromise FontFaceSet::ready(ScriptState* scriptState)
266 {
267     if (!inActiveDocumentContext())
268         return ScriptPromise();
269     OwnPtr<FontsReadyPromiseResolver> resolver = FontsReadyPromiseResolver::create(scriptState);
270     ScriptPromise promise = resolver->promise();
271     m_readyResolvers.append(resolver.release());
272     handlePendingEventsAndPromisesSoon();
273     return promise;
274 }
275
276 void FontFaceSet::add(FontFace* fontFace, ExceptionState& exceptionState)
277 {
278     if (!inActiveDocumentContext())
279         return;
280     if (!fontFace) {
281         exceptionState.throwTypeError("The argument is not a FontFace.");
282         return;
283     }
284     if (m_nonCSSConnectedFaces.contains(fontFace))
285         return;
286     if (isCSSConnectedFontFace(fontFace)) {
287         exceptionState.throwDOMException(InvalidModificationError, "Cannot add a CSS-connected FontFace.");
288         return;
289     }
290     CSSFontSelector* fontSelector = document()->styleEngine()->fontSelector();
291     m_nonCSSConnectedFaces.add(fontFace);
292     fontSelector->fontFaceCache()->addFontFace(fontSelector, fontFace, false);
293     if (fontFace->loadStatus() == FontFace::Loading)
294         addToLoadingFonts(fontFace);
295     fontSelector->fontFaceInvalidated();
296 }
297
298 void FontFaceSet::clear()
299 {
300     if (!inActiveDocumentContext() || m_nonCSSConnectedFaces.isEmpty())
301         return;
302     CSSFontSelector* fontSelector = document()->styleEngine()->fontSelector();
303     FontFaceCache* fontFaceCache = fontSelector->fontFaceCache();
304     for (WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::iterator it = m_nonCSSConnectedFaces.begin(); it != m_nonCSSConnectedFaces.end(); ++it) {
305         fontFaceCache->removeFontFace(it->get(), false);
306         if ((*it)->loadStatus() == FontFace::Loading)
307             removeFromLoadingFonts(*it);
308     }
309     m_nonCSSConnectedFaces.clear();
310     fontSelector->fontFaceInvalidated();
311 }
312
313 bool FontFaceSet::remove(FontFace* fontFace, ExceptionState& exceptionState)
314 {
315     if (!inActiveDocumentContext())
316         return false;
317     if (!fontFace) {
318         exceptionState.throwTypeError("The argument is not a FontFace.");
319         return false;
320     }
321     WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::iterator it = m_nonCSSConnectedFaces.find(fontFace);
322     if (it != m_nonCSSConnectedFaces.end()) {
323         m_nonCSSConnectedFaces.remove(it);
324         CSSFontSelector* fontSelector = document()->styleEngine()->fontSelector();
325         fontSelector->fontFaceCache()->removeFontFace(fontFace, false);
326         if (fontFace->loadStatus() == FontFace::Loading)
327             removeFromLoadingFonts(fontFace);
328         fontSelector->fontFaceInvalidated();
329         return true;
330     }
331     if (isCSSConnectedFontFace(fontFace))
332         exceptionState.throwDOMException(InvalidModificationError, "Cannot delete a CSS-connected FontFace.");
333     return false;
334 }
335
336 bool FontFaceSet::has(FontFace* fontFace, ExceptionState& exceptionState) const
337 {
338     if (!inActiveDocumentContext())
339         return false;
340     if (!fontFace) {
341         exceptionState.throwTypeError("The argument is not a FontFace.");
342         return false;
343     }
344     return m_nonCSSConnectedFaces.contains(fontFace) || isCSSConnectedFontFace(fontFace);
345 }
346
347 const WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >& FontFaceSet::cssConnectedFontFaceList() const
348 {
349     Document* d = document();
350     d->ensureStyleResolver(); // Flush pending style changes.
351     return d->styleEngine()->fontSelector()->fontFaceCache()->cssConnectedFontFaces();
352 }
353
354 bool FontFaceSet::isCSSConnectedFontFace(FontFace* fontFace) const
355 {
356     return cssConnectedFontFaceList().contains(fontFace);
357 }
358
359 void FontFaceSet::forEach(FontFaceSetForEachCallback* callback, const ScriptValue& thisArg) const
360 {
361     forEachInternal(callback, &thisArg);
362 }
363
364 void FontFaceSet::forEach(FontFaceSetForEachCallback* callback) const
365 {
366     forEachInternal(callback, 0);
367 }
368
369 void FontFaceSet::forEachInternal(FontFaceSetForEachCallback* callback, const ScriptValue* thisArg) const
370 {
371     if (!inActiveDocumentContext())
372         return;
373     const WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >& cssConnectedFaces = cssConnectedFontFaceList();
374     WillBeHeapVector<RefPtrWillBeMember<FontFace> > fontFaces;
375     fontFaces.reserveInitialCapacity(cssConnectedFaces.size() + m_nonCSSConnectedFaces.size());
376     for (WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::const_iterator it = cssConnectedFaces.begin(); it != cssConnectedFaces.end(); ++it)
377         fontFaces.append(*it);
378     for (WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::const_iterator it = m_nonCSSConnectedFaces.begin(); it != m_nonCSSConnectedFaces.end(); ++it)
379         fontFaces.append(*it);
380
381     for (size_t i = 0; i < fontFaces.size(); ++i) {
382         FontFace* face = fontFaces[i].get();
383         if (thisArg)
384             callback->handleItem(*thisArg, face, face, const_cast<FontFaceSet*>(this));
385         else
386             callback->handleItem(face, face, const_cast<FontFaceSet*>(this));
387     }
388 }
389
390 unsigned long FontFaceSet::size() const
391 {
392     if (!inActiveDocumentContext())
393         return m_nonCSSConnectedFaces.size();
394     return cssConnectedFontFaceList().size() + m_nonCSSConnectedFaces.size();
395 }
396
397 void FontFaceSet::fireDoneEventIfPossible()
398 {
399     if (m_shouldFireLoadingEvent)
400         return;
401     if (!m_loadingFonts.isEmpty() || (!hasLoadedFonts() && m_readyResolvers.isEmpty()))
402         return;
403
404     // If the layout was invalidated in between when we thought layout
405     // was updated and when we're ready to fire the event, just wait
406     // until after the next layout before firing events.
407     Document* d = document();
408     if (!d->view() || d->view()->needsLayout())
409         return;
410
411     if (hasLoadedFonts()) {
412         RefPtrWillBeRawPtr<FontFaceSetLoadEvent> doneEvent = nullptr;
413         RefPtrWillBeRawPtr<FontFaceSetLoadEvent> errorEvent = nullptr;
414         doneEvent = FontFaceSetLoadEvent::createForFontFaces(EventTypeNames::loadingdone, m_loadedFonts);
415         m_loadedFonts.clear();
416         if (!m_failedFonts.isEmpty()) {
417             errorEvent = FontFaceSetLoadEvent::createForFontFaces(EventTypeNames::loadingerror, m_failedFonts);
418             m_failedFonts.clear();
419         }
420         dispatchEvent(doneEvent);
421         if (errorEvent)
422             dispatchEvent(errorEvent);
423     }
424
425     if (!m_readyResolvers.isEmpty()) {
426         Vector<OwnPtr<FontsReadyPromiseResolver> > resolvers;
427         m_readyResolvers.swap(resolvers);
428         for (size_t index = 0; index < resolvers.size(); ++index)
429             resolvers[index]->resolve(this);
430     }
431 }
432
433 ScriptPromise FontFaceSet::load(ScriptState* scriptState, const String& fontString, const String& text)
434 {
435     if (!inActiveDocumentContext())
436         return ScriptPromise();
437
438     Font font;
439     if (!resolveFontStyle(fontString, font)) {
440         RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
441         ScriptPromise promise = resolver->promise();
442         resolver->reject(DOMException::create(SyntaxError, "Could not resolve '" + fontString + "' as a font."));
443         return promise;
444     }
445
446     FontFaceCache* fontFaceCache = document()->styleEngine()->fontSelector()->fontFaceCache();
447     FontFaceArray faces;
448     for (const FontFamily* f = &font.fontDescription().family(); f; f = f->next()) {
449         CSSSegmentedFontFace* segmentedFontFace = fontFaceCache->get(font.fontDescription(), f->family());
450         if (segmentedFontFace)
451             segmentedFontFace->match(text, faces);
452     }
453
454     RefPtrWillBeRawPtr<LoadFontPromiseResolver> resolver = LoadFontPromiseResolver::create(faces, scriptState);
455     ScriptPromise promise = resolver->promise();
456     resolver->loadFonts(executionContext()); // After this, resolver->promise() may return null.
457     return promise;
458 }
459
460 bool FontFaceSet::check(const String& fontString, const String& text, ExceptionState& exceptionState)
461 {
462     if (!inActiveDocumentContext())
463         return false;
464
465     Font font;
466     if (!resolveFontStyle(fontString, font)) {
467         exceptionState.throwDOMException(SyntaxError, "Could not resolve '" + fontString + "' as a font.");
468         return false;
469     }
470
471     CSSFontSelector* fontSelector = document()->styleEngine()->fontSelector();
472     FontFaceCache* fontFaceCache = fontSelector->fontFaceCache();
473
474     bool hasLoadedFaces = false;
475     for (const FontFamily* f = &font.fontDescription().family(); f; f = f->next()) {
476         CSSSegmentedFontFace* face = fontFaceCache->get(font.fontDescription(), f->family());
477         if (face) {
478             if (!face->checkFont(text))
479                 return false;
480             hasLoadedFaces = true;
481         }
482     }
483     if (hasLoadedFaces)
484         return true;
485     for (const FontFamily* f = &font.fontDescription().family(); f; f = f->next()) {
486         if (fontSelector->isPlatformFontAvailable(font.fontDescription(), f->family()))
487             return true;
488     }
489     return false;
490 }
491
492 bool FontFaceSet::resolveFontStyle(const String& fontString, Font& font)
493 {
494     if (fontString.isEmpty())
495         return false;
496
497     // Interpret fontString in the same way as the 'font' attribute of CanvasRenderingContext2D.
498     RefPtrWillBeRawPtr<MutableStylePropertySet> parsedStyle = MutableStylePropertySet::create();
499     CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, fontString, true, HTMLStandardMode, 0);
500     if (parsedStyle->isEmpty())
501         return false;
502
503     String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont);
504     if (fontValue == "inherit" || fontValue == "initial")
505         return false;
506
507     RefPtr<RenderStyle> style = RenderStyle::create();
508
509     FontFamily fontFamily;
510     fontFamily.setFamily(defaultFontFamily);
511
512     FontDescription defaultFontDescription;
513     defaultFontDescription.setFamily(fontFamily);
514     defaultFontDescription.setSpecifiedSize(defaultFontSize);
515     defaultFontDescription.setComputedSize(defaultFontSize);
516
517     style->setFontDescription(defaultFontDescription);
518
519     style->font().update(style->font().fontSelector());
520
521     // Now map the font property longhands into the style.
522     CSSPropertyValue properties[] = {
523         CSSPropertyValue(CSSPropertyFontFamily, *parsedStyle),
524         CSSPropertyValue(CSSPropertyFontStretch, *parsedStyle),
525         CSSPropertyValue(CSSPropertyFontStyle, *parsedStyle),
526         CSSPropertyValue(CSSPropertyFontVariant, *parsedStyle),
527         CSSPropertyValue(CSSPropertyFontWeight, *parsedStyle),
528         CSSPropertyValue(CSSPropertyFontSize, *parsedStyle),
529         CSSPropertyValue(CSSPropertyLineHeight, *parsedStyle),
530     };
531     StyleResolver& styleResolver = document()->ensureStyleResolver();
532     styleResolver.applyPropertiesToStyle(properties, WTF_ARRAY_LENGTH(properties), style.get());
533
534     font = style->font();
535     font.update(document()->styleEngine()->fontSelector());
536     return true;
537 }
538
539 void FontFaceSet::FontLoadHistogram::updateStatus(FontFace* fontFace)
540 {
541     if (m_status == Reported)
542         return;
543     if (fontFace->hadBlankText())
544         m_status = HadBlankText;
545     else if (m_status == NoWebFonts)
546         m_status = DidNotHaveBlankText;
547 }
548
549 void FontFaceSet::FontLoadHistogram::record()
550 {
551     if (!m_recorded) {
552         m_recorded = true;
553         blink::Platform::current()->histogramCustomCounts("WebFont.WebFontsInPage", m_count, 1, 100, 50);
554     }
555     if (m_status == HadBlankText || m_status == DidNotHaveBlankText) {
556         blink::Platform::current()->histogramEnumeration("WebFont.HadBlankText", m_status == HadBlankText ? 1 : 0, 2);
557         m_status = Reported;
558     }
559 }
560
561 static const char* supplementName()
562 {
563     return "FontFaceSet";
564 }
565
566 PassRefPtrWillBeRawPtr<FontFaceSet> FontFaceSet::from(Document& document)
567 {
568     RefPtrWillBeRawPtr<FontFaceSet> fonts = static_cast<FontFaceSet*>(SupplementType::from(document, supplementName()));
569     if (!fonts) {
570         fonts = FontFaceSet::create(document);
571         SupplementType::provideTo(document, supplementName(), fonts);
572     }
573
574     return fonts.release();
575 }
576
577 void FontFaceSet::didLayout(Document& document)
578 {
579     if (FontFaceSet* fonts = static_cast<FontFaceSet*>(SupplementType::from(document, supplementName())))
580         fonts->didLayout();
581 }
582
583 #if ENABLE(OILPAN)
584 void FontFaceSet::trace(Visitor* visitor)
585 {
586     visitor->trace(m_loadingFonts);
587     visitor->trace(m_loadedFonts);
588     visitor->trace(m_failedFonts);
589     visitor->trace(m_nonCSSConnectedFaces);
590     DocumentSupplement::trace(visitor);
591     EventTargetWithInlineData::trace(visitor);
592 }
593 #endif
594
595 } // namespace blink