Changeset 201887 in webkit
- Timestamp:
- Jun 9, 2016 3:03:53 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r201886 r201887 1 2016-06-09 Myles C. Maxfield <mmaxfield@apple.com> 2 3 Deleting a CSSOM style rule invalidates any previously-added FontFaces 4 https://bugs.webkit.org/show_bug.cgi?id=158450 5 6 Reviewed by Darin Adler. 7 8 * fast/text/font-face-set-cssom-expected.txt: Added. 9 * fast/text/font-face-set-cssom.html: Added. 10 1 11 2016-06-09 Ryan Haddad <ryanhaddad@apple.com> 2 12 -
trunk/Source/WebCore/ChangeLog
r201884 r201887 1 2016-06-09 Myles C. Maxfield <mmaxfield@apple.com> 2 3 Deleting a CSSOM style rule invalidates any previously-added FontFaces 4 https://bugs.webkit.org/show_bug.cgi?id=158450 5 6 Reviewed by Darin Adler. 7 8 This patch has two pieces: updating the CSSOM when the FontFace changes, and 9 updating the FontFace when the CSSOM changes. 10 11 1: Updating the CSSOM when the FontFace changes: CSSFontFaces already have a RefPtr 12 to their StyleRuleFontFace which represents their CSS-connection. When changing a 13 property of the CSSFontFace, we simply reach into the StyleRule and update it to 14 match. Our existing infrastructure of invalidation due to the attribute changes 15 makes sure that all the necessary updates occur. 16 17 2. Updating the FontFace when the CSSOM changes: If the CSSOM changes in a trivial 18 way (for example, a new @font-face is appended to the end of the last <style> 19 element), we can handle it directly. However, when something more invasive occurs, 20 we end up clearing the entire CSSFontSelector, and then adding all the style rules 21 from scratch. This involves three steps: 22 a) CSSFontSelector::buildStarted() is run, which means "we're about to start 23 building up all the @font-face rules from scratch." We take this opportunity 24 to purge as many fonts as possible. This is valuable because, for example, 25 this function gets run when the page gets put into the page cache, so we 26 want to destroy as much as possible. Not everything can be purged, however - 27 only CSS-connected fonts which have never been inspected by script are 28 purgeable. We don't allow fonts inspected by script to be purged because 29 purging might result in a font appearing from JavaScript to transition from 30 a success -> failure state, which we don't allow. 31 b) Upon style recalc (possibly asynchronously) CSSFontSelector::addFontFaceRule() 32 is called for each @font-face rule. We actually detect that we're in the 33 middle of a style rebuild, and defer this step. 34 c) When we're done adding all the font face rules, we call 35 CSSFontSelector::buildCompleted(). This is where we compare the newly built- 36 up list of font faces with what existed previously (as remembered in 37 CSSFontSelector::buildStarted()) in order to detect font faces which were 38 deleted from the document. Fonts which were newly added to the document 39 are handled naturally. 40 Fonts which have a property modified on them are created as if they were new. 41 However, instead of simply adding the CSSFontFace, we search for the existing 42 CSSFontFace (by CSS connection pointer) and tell the existing FontFace to 43 adopt this new CSSFontFace. This means that the JavaScript object will just 44 pick up any newly-written values in the CSSOM. It also means that the 45 "status" attribute of the JavaScript object is reset, but this is expected 46 and allowed by the spec. (For example, if you change the "src" attribute of 47 an @font-face block via the CSSOM, all bets are off when you inspect the 48 FontFace JS object representing that block.) 49 50 Test: fast/text/font-face-set-cssom.html 51 52 * css/CSSFontFace.cpp: 53 (WebCore::CSSFontFace::CSSFontFace): 54 (WebCore::CSSFontFace::setFamilies): 55 (WebCore::CSSFontFace::setStyle): 56 (WebCore::CSSFontFace::setWeight): 57 (WebCore::CSSFontFace::setUnicodeRange): 58 (WebCore::CSSFontFace::setVariantLigatures): 59 (WebCore::CSSFontFace::setVariantPosition): 60 (WebCore::CSSFontFace::setVariantCaps): 61 (WebCore::CSSFontFace::setVariantNumeric): 62 (WebCore::CSSFontFace::setVariantAlternates): 63 (WebCore::CSSFontFace::setVariantEastAsian): 64 (WebCore::CSSFontFace::setFeatureSettings): 65 (WebCore::CSSFontFace::initializeWrapper): 66 (WebCore::CSSFontFace::wrapper): 67 (WebCore::CSSFontFace::setWrapper): 68 (WebCore::CSSFontFace::purgeable): 69 (WebCore::CSSFontFace::updateStyleIfNeeded): 70 * css/CSSFontFace.h: 71 * css/CSSFontFaceSet.cpp: 72 (WebCore::CSSFontFaceSet::remove): 73 (WebCore::CSSFontFaceSet::containsCSSConnection): 74 (WebCore::CSSFontFaceSet::purge): 75 * css/CSSFontFaceSet.h: 76 * css/CSSFontSelector.cpp: 77 (WebCore::CSSFontSelector::buildStarted): 78 (WebCore::CSSFontSelector::buildCompleted): 79 (WebCore::CSSFontSelector::addFontFaceRule): 80 * css/CSSFontSelector.h: 81 * css/FontFace.cpp: 82 (WebCore::FontFace::family): 83 (WebCore::FontFace::style): 84 (WebCore::FontFace::weight): 85 (WebCore::FontFace::unicodeRange): 86 (WebCore::FontFace::variant): 87 (WebCore::FontFace::featureSettings): 88 (WebCore::FontFace::adopt): 89 * css/FontFace.h: 90 1 91 2016-06-09 Andy Estes <aestes@apple.com> 2 92 -
trunk/Source/WebCore/css/CSSFontFace.cpp
r201676 r201887 95 95 , m_wrapper(wrapper ? wrapper->createWeakPtr() : WeakPtr<FontFace>()) 96 96 , m_isLocalFallback(isLocalFallback) 97 , m_mayBePurged(!wrapper) 97 98 { 98 99 } … … 113 114 RefPtr<CSSValueList> oldFamilies = m_families; 114 115 m_families = &familyList; 116 117 if (m_cssConnection) 118 m_cssConnection->mutableProperties().setProperty(CSSPropertyFontFamily, &family); 115 119 116 120 iterateClients(m_clients, [&](Client& client) { … … 143 147 if (auto mask = calculateStyleMask(style)) { 144 148 m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontStyleMask)) | mask.value()); 149 150 if (m_cssConnection) 151 m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStyle, &style); 145 152 146 153 iterateClients(m_clients, [&](Client& client) { … … 193 200 m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontWeightMask)) | mask.value()); 194 201 202 if (m_cssConnection) 203 m_cssConnection->mutableProperties().setProperty(CSSPropertyFontWeight, &weight); 204 195 205 iterateClients(m_clients, [&](Client& client) { 196 206 client.fontPropertyChanged(*this); … … 215 225 } 216 226 227 if (m_cssConnection) 228 m_cssConnection->mutableProperties().setProperty(CSSPropertyUnicodeRange, &unicodeRange); 229 217 230 iterateClients(m_clients, [&](Client& client) { 218 231 client.fontPropertyChanged(*this); … … 231 244 m_variantSettings.contextualAlternates = ligatures.contextualAlternates; 232 245 246 if (m_cssConnection) 247 m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantLigatures, &variantLigatures); 248 233 249 iterateClients(m_clients, [&](Client& client) { 234 250 client.fontPropertyChanged(*this); … … 245 261 m_variantSettings.position = downcast<CSSPrimitiveValue>(variantPosition); 246 262 263 if (m_cssConnection) 264 m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantPosition, &variantPosition); 265 247 266 iterateClients(m_clients, [&](Client& client) { 248 267 client.fontPropertyChanged(*this); … … 258 277 259 278 m_variantSettings.caps = downcast<CSSPrimitiveValue>(variantCaps); 279 280 if (m_cssConnection) 281 m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantCaps, &variantCaps); 260 282 261 283 iterateClients(m_clients, [&](Client& client) { … … 276 298 m_variantSettings.numericSlashedZero = numeric.slashedZero; 277 299 300 if (m_cssConnection) 301 m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantNumeric, &variantNumeric); 302 278 303 iterateClients(m_clients, [&](Client& client) { 279 304 client.fontPropertyChanged(*this); … … 289 314 290 315 m_variantSettings.alternates = downcast<CSSPrimitiveValue>(variantAlternates); 316 317 if (m_cssConnection) 318 m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantAlternates, &variantAlternates); 291 319 292 320 iterateClients(m_clients, [&](Client& client) { … … 304 332 m_variantSettings.eastAsianWidth = eastAsian.width; 305 333 m_variantSettings.eastAsianRuby = eastAsian.ruby; 334 335 if (m_cssConnection) 336 m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantEastAsian, &variantEastAsian); 306 337 307 338 iterateClients(m_clients, [&](Client& client) { … … 332 363 m_featureSettings = WTFMove(settings); 333 364 365 if (m_cssConnection) 366 m_cssConnection->mutableProperties().setProperty(CSSPropertyFontFeatureSettings, &featureSettings); 367 334 368 iterateClients(m_clients, [&](Client& client) { 335 369 client.fontPropertyChanged(*this); … … 382 416 } 383 417 384 Ref<FontFace> CSSFontFace::wrapper() 385 { 386 if (m_wrapper) 387 return Ref<FontFace>(*m_wrapper.get()); 388 389 Ref<FontFace> wrapper = FontFace::create(*this); 418 void CSSFontFace::initializeWrapper() 419 { 390 420 switch (m_status) { 391 421 case Status::Pending: 392 422 break; 393 423 case Status::Loading: 394 wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);424 m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading); 395 425 break; 396 426 case Status::TimedOut: 397 wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);398 wrapper->fontStateChanged(*this, Status::Loading, Status::TimedOut);427 m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading); 428 m_wrapper->fontStateChanged(*this, Status::Loading, Status::TimedOut); 399 429 break; 400 430 case Status::Success: 401 wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);402 wrapper->fontStateChanged(*this, Status::Pending, Status::Success);431 m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading); 432 m_wrapper->fontStateChanged(*this, Status::Pending, Status::Success); 403 433 break; 404 434 case Status::Failure: 405 wrapper->fontStateChanged(*this, Status::Pending, Status::Loading); 406 wrapper->fontStateChanged(*this, Status::Pending, Status::Failure); 407 break; 408 } 435 m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading); 436 m_wrapper->fontStateChanged(*this, Status::Pending, Status::Failure); 437 break; 438 } 439 } 440 441 Ref<FontFace> CSSFontFace::wrapper() 442 { 443 if (m_wrapper) 444 return *m_wrapper.get(); 445 446 auto wrapper = FontFace::create(*this); 409 447 m_wrapper = wrapper->createWeakPtr(); 448 initializeWrapper(); 449 m_mayBePurged = false; 410 450 return wrapper; 451 } 452 453 void CSSFontFace::setWrapper(FontFace& newWrapper) 454 { 455 m_wrapper = newWrapper.createWeakPtr(); 456 initializeWrapper(); 411 457 } 412 458 … … 553 599 } 554 600 601 bool CSSFontFace::purgeable() const 602 { 603 return cssConnection() && m_mayBePurged; 604 } 605 606 void CSSFontFace::updateStyleIfNeeded() 607 { 608 if (m_fontSelector && m_fontSelector->document()) 609 m_fontSelector->document()->updateStyleIfNeeded(); 610 } 611 555 612 #if ENABLE(SVG_FONTS) 556 613 bool CSSFontFace::hasSVGFontFaceSource() const -
trunk/Source/WebCore/css/CSSFontFace.h
r201676 r201887 136 136 // We don't guarantee that the FontFace wrapper will be the same every time you ask for it. 137 137 Ref<FontFace> wrapper(); 138 void setWrapper(FontFace&); 139 FontFace* existingWrapper() { return m_wrapper.get(); } 138 140 139 141 bool webFontsShouldAlwaysFallBack() const; 142 143 bool purgeable() const; 144 145 void updateStyleIfNeeded(); 140 146 141 147 #if ENABLE(SVG_FONTS) … … 149 155 void setStatus(Status); 150 156 void notifyClientsOfFontPropertyChange(); 157 158 void initializeWrapper(); 151 159 152 160 void fontLoadEventOccurred(); … … 167 175 bool m_isLocalFallback { false }; 168 176 bool m_sourcesPopulated { false }; 177 bool m_mayBePurged { true }; 169 178 }; 170 179 -
trunk/Source/WebCore/css/CSSFontFaceSet.cpp
r201799 r201887 96 96 } 97 97 98 void CSSFontFaceSet::registerLocalFontFacesForFamily(const String& familyName) 99 { 100 ASSERT(!m_locallyInstalledFacesLookupTable.contains(familyName)); 98 void CSSFontFaceSet::ensureLocalFontFacesForFamilyRegistered(const String& familyName) 99 { 100 if (m_locallyInstalledFacesLookupTable.contains(familyName)) 101 return; 101 102 102 103 Vector<FontTraitsMask> traitsMasks = FontCache::singleton().getTraitsInFamily(familyName); … … 161 162 // m_locallyInstalledFontFaces grows without bound, eventually encorporating every font installed on the system. 162 163 // This is by design. 163 registerLocalFontFacesForFamily(familyName);164 ensureLocalFontFacesForFamilyRegistered(familyName); 164 165 familyFontFaces = { }; 165 166 } … … 188 189 if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut) 189 190 incrementActiveCount(); 191 192 if (face.cssConnection()) { 193 auto addResult = m_constituentCSSConnections.add(face.cssConnection(), &face); 194 ASSERT_UNUSED(addResult, addResult.isNewEntry); 195 } 190 196 } 191 197 … … 223 229 removeFromFacesLookupTable(face, *face.families()); 224 230 231 if (face.cssConnection()) { 232 bool removed = m_constituentCSSConnections.remove(face.cssConnection()); 233 ASSERT_UNUSED(removed, removed); 234 } 235 225 236 for (size_t i = 0; i < m_faces.size(); ++i) { 226 237 if (m_faces[i].ptr() == &face) { … … 237 248 } 238 249 250 CSSFontFace* CSSFontFaceSet::lookupByCSSConnection(StyleRuleFontFace& target) 251 { 252 return m_constituentCSSConnections.get(&target); 253 } 254 255 void CSSFontFaceSet::purge() 256 { 257 Vector<std::reference_wrapper<CSSFontFace>> toRemove; 258 for (auto& face : m_faces) { 259 if (face->purgeable()) 260 toRemove.append(face.get()); 261 } 262 263 for (auto& item : toRemove) 264 remove(item.get()); 265 } 266 239 267 void CSSFontFaceSet::clear() 240 268 { … … 245 273 m_locallyInstalledFacesLookupTable.clear(); 246 274 m_cache.clear(); 275 m_constituentCSSConnections.clear(); 247 276 m_facesPartitionIndex = 0; 248 277 m_status = Status::Loaded; -
trunk/Source/WebCore/css/CSSFontFaceSet.h
r197804 r201887 61 61 void add(CSSFontFace&); 62 62 void remove(const CSSFontFace&); 63 void purge(); 63 64 void clear(); 64 65 CSSFontFace& operator[](size_t i); 66 67 CSSFontFace* lookupByCSSConnection(StyleRuleFontFace&); 65 68 66 69 bool check(const String& font, const String& text, ExceptionCode&); … … 92 95 void fontPropertyChanged(CSSFontFace&, CSSValueList* oldFamilies = nullptr) override; 93 96 94 void registerLocalFontFacesForFamily(const String&);97 void ensureLocalFontFacesForFamilyRegistered(const String&); 95 98 96 99 static String familyNameFromPrimitive(const CSSPrimitiveValue&); … … 101 104 HashMap<String, Vector<Ref<CSSFontFace>>, ASCIICaseInsensitiveHash> m_locallyInstalledFacesLookupTable; 102 105 HashMap<String, HashMap<unsigned, RefPtr<CSSSegmentedFontFace>>, ASCIICaseInsensitiveHash> m_cache; 106 HashMap<StyleRuleFontFace*, CSSFontFace*> m_constituentCSSConnections; 103 107 size_t m_facesPartitionIndex { 0 }; // All entries in m_faces before this index are CSS-connected. 104 108 Status m_status { Status::Loaded }; -
trunk/Source/WebCore/css/CSSFontSelector.cpp
r201799 r201887 46 46 #include "Font.h" 47 47 #include "FontCache.h" 48 #include "FontFace.h" 48 49 #include "FontFaceSet.h" 49 50 #include "FontSelectorClient.h" … … 102 103 { 103 104 m_buildIsUnderway = true; 105 m_stagingArea.clear(); 106 m_cssFontFaceSet->purge(); 104 107 ++m_version; 108 109 m_cssConnectionsPossiblyToRemove.clear(); 110 m_cssConnectionsEncounteredDuringBuild.clear(); 111 for (size_t i = 0; i < m_cssFontFaceSet->faceCount(); ++i) { 112 CSSFontFace& face = m_cssFontFaceSet.get()[i]; 113 if (face.cssConnection()) 114 m_cssConnectionsPossiblyToRemove.add(&face); 115 } 105 116 } 106 117 … … 112 123 m_buildIsUnderway = false; 113 124 114 m_cssFontFaceSet->clear(); 125 // Some font faces weren't re-added during the build process. 126 for (auto& face : m_cssConnectionsPossiblyToRemove) { 127 auto* connection = face->cssConnection(); 128 ASSERT(connection); 129 if (!m_cssConnectionsEncounteredDuringBuild.contains(connection)) 130 m_cssFontFaceSet->remove(*face); 131 } 115 132 116 133 for (auto& item : m_stagingArea) … … 122 139 { 123 140 if (m_buildIsUnderway) { 141 m_cssConnectionsEncounteredDuringBuild.add(&fontFaceRule); 124 142 m_stagingArea.append({fontFaceRule, isInitiatingElementInUserAgentShadowTree}); 125 143 return; … … 187 205 if (fontFace->allSourcesFailed()) 188 206 return; 207 208 if (RefPtr<CSSFontFace> existingFace = m_cssFontFaceSet->lookupByCSSConnection(fontFaceRule)) { 209 m_cssFontFaceSet->remove(*existingFace); 210 if (auto* existingWrapper = existingFace->existingWrapper()) 211 existingWrapper->adopt(fontFace.get()); 212 } 189 213 190 214 m_cssFontFaceSet->add(fontFace.get()); -
trunk/Source/WebCore/css/CSSFontSelector.h
r201799 r201887 106 106 107 107 Vector<CachedResourceHandle<CachedFont>> m_fontsToBeginLoading; 108 HashSet<RefPtr<CSSFontFace>> m_cssConnectionsPossiblyToRemove; 109 HashSet<RefPtr<StyleRuleFontFace>> m_cssConnectionsEncounteredDuringBuild; 108 110 Timer m_beginLoadingTimer; 109 111 -
trunk/Source/WebCore/css/FontFace.cpp
r201676 r201887 271 271 String FontFace::family() const 272 272 { 273 const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded(); 273 274 return m_backing->families()->cssText(); 274 275 } … … 276 277 String FontFace::style() const 277 278 { 279 const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded(); 278 280 switch (m_backing->traitsMask() & FontStyleMask) { 279 281 case FontStyleNormalMask: … … 288 290 String FontFace::weight() const 289 291 { 292 const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded(); 290 293 switch (m_backing->traitsMask() & FontWeightMask) { 291 294 case FontWeight100Mask: … … 319 322 String FontFace::unicodeRange() const 320 323 { 324 const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded(); 321 325 if (!m_backing->ranges().size()) 322 326 return "U+0-10FFFF"; … … 329 333 String FontFace::variant() const 330 334 { 335 const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded(); 331 336 return computeFontVariant(m_backing->variantSettings())->cssText(); 332 337 } … … 334 339 String FontFace::featureSettings() const 335 340 { 341 const_cast<CSSFontFace&>(m_backing.get()).updateStyleIfNeeded(); 336 342 if (!m_backing->featureSettings().size()) 337 343 return "normal"; … … 358 364 ASSERT_NOT_REACHED(); 359 365 return LoadStatus::Error; 366 } 367 368 void FontFace::adopt(CSSFontFace& newFace) 369 { 370 m_promise = Nullopt; 371 m_backing->removeClient(*this); 372 m_backing = newFace; 373 m_backing->addClient(*this); 374 newFace.setWrapper(*this); 360 375 } 361 376 -
trunk/Source/WebCore/css/FontFace.h
r201394 r201887 70 70 void registerLoaded(Promise&&); 71 71 72 void adopt(CSSFontFace&); 73 72 74 void load(); 73 75
Note: See TracChangeset
for help on using the changeset viewer.