Changeset 249707 in webkit
- Timestamp:
- Sep 9, 2019, 10:30:56 PM (7 years ago)
- Location:
- branches/safari-608-branch
- Files:
-
- 3 added
- 8 edited
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/editing/pasteboard/paste-cocoa-writer-markup-with-webkit-standard-font-family-expected.txt (added)
-
LayoutTests/editing/pasteboard/paste-cocoa-writer-markup-with-webkit-standard-font-family.html (added)
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/editing/EditingStyle.cpp (modified) (5 diffs)
-
Source/WebCore/editing/EditingStyle.h (modified) (3 diffs)
-
Source/WebCore/editing/markup.cpp (modified) (10 diffs)
-
Tools/ChangeLog (modified) (1 diff)
-
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (modified) (4 diffs)
-
Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteHTML.mm (modified) (1 diff)
-
Tools/TestWebKitAPI/Tests/WebKitCocoa/cocoa-writer-markup-with-lists.html (added)
Legend:
- Unmodified
- Added
- Removed
-
branches/safari-608-branch/LayoutTests/ChangeLog
r249698 r249707 1 2019-09-09 Ryosuke Niwa <rniwa@webkit.org> 2 3 REGRESSION (iOS 13): Bulleted list copied from Notes to Mail results in Times New Roman 4 https://bugs.webkit.org/show_bug.cgi?id=201490 5 6 Reviewed by Daniel Bates. 7 8 Added a test to make sure -webkit-standard font family name isn't stripped away when sanitization is not in effect. 9 10 * editing/pasteboard/paste-cocoa-writer-markup-with-webkit-standard-font-family-expected.txt: Added. 11 * editing/pasteboard/paste-cocoa-writer-markup-with-webkit-standard-font-family.html: Added. 12 1 13 2019-09-09 Kocsen Chung <kocsen_chung@apple.com> 2 14 -
branches/safari-608-branch/Source/WebCore/ChangeLog
r249698 r249707 1 2019-09-09 Ryosuke Niwa <rniwa@webkit.org> 2 3 REGRESSION (iOS 13): Bulleted list copied from Notes to Mail results in Times New Roman 4 https://bugs.webkit.org/show_bug.cgi?id=201490 5 6 Reviewed by Daniel Bates. 7 8 The bug was caused by an element in the pasted content not having any explicit font name resolving 9 to use the font-family value of `-webkit-standard`. When such an inline style is inserted into 10 Mail's WKWebView which sets a different font family, ReplaceSelectionCommand would fail to strip away, 11 making the pasted content using the default font family of Times New Roman. 12 13 Fixed the bug by stripping away font-family set to -webkit-standard in the sanitization document 14 since that's indicative of the pasted content not having any font family being specified. 15 16 In the future, we should consider making regular copy (as opposed to the copy for sanitization) 17 resolve generic font family names to concrete font names since different WKWebView might be using 18 different concrete font names. Unfortuantely, such a change is quite involved and risky since various 19 paste side code in EditingStyle that removes redundant inline styles (i.e. redundant `font-family`) 20 need to be aware of this special font family resolution. 21 22 Tests: editing/pasteboard/paste-cocoa-writer-markup-with-webkit-standard-font-family.html 23 PasteHTML.DoesNotAddStandardFontFamily 24 25 * editing/EditingStyle.cpp: 26 (WebCore::EditingStyle::mergeInlineAndImplicitStyleOfElement): 27 (WebCore::EditingStyle::wrappingStyleForSerialization): 28 (WebCore::familyNameFromCSSPrimitiveValue): Added. 29 (WebCore::loneFontFamilyName): Extracted from usesForbiddenSystemFontAsOnlyFontFamilyName. Fixed 30 a bug that it was not handling the case when `font-family` property's value is a CSSPrimitiveValue 31 instead of a CSSValueList. 32 (WebCore::usesForbiddenSystemFontAsOnlyFontFamilyName): Deleted. 33 (WebCore::EditingStyle::mergeStyleFromRulesForSerialization): Remove `font-family` property when 34 StandardFontFamilySerializationMode::Strip is specified and its value is `-webkit-standard`. 35 * editing/EditingStyle.h: 36 * editing/markup.cpp: 37 (WebCore::StyledMarkupAccumulator::StyledMarkupAccumulator): Added 38 StandardFontFamilySerializationMode as an argument. 39 (WebCore::StyledMarkupAccumulator::appendStartTag): 40 (WebCore::StyledMarkupAccumulator::serializeNodes): 41 (WebCore::serializePreservingVisualAppearanceInternal): Ditto. 42 (WebCore::serializePreservingVisualAppearance): Use StandardFontFamilySerializationMode::Keep 43 to preserve the pre-existing behavior. 44 (WebCore::sanitizedMarkupForFragmentInDocument): Use StandardFontFamilySerializationMode::Strip 45 as this is the code used by sanitization code. 46 1 47 2019-09-09 Kocsen Chung <kocsen_chung@apple.com> 2 48 -
branches/safari-608-branch/Source/WebCore/editing/EditingStyle.cpp
r247966 r249707 1158 1158 } 1159 1159 1160 void EditingStyle::mergeInlineAndImplicitStyleOfElement(StyledElement& element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude )1160 void EditingStyle::mergeInlineAndImplicitStyleOfElement(StyledElement& element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude, StandardFontFamilySerializationMode standardFontFamilySerializationMode) 1161 1161 { 1162 1162 auto styleFromRules = EditingStyle::create(); 1163 styleFromRules->mergeStyleFromRulesForSerialization(element );1163 styleFromRules->mergeStyleFromRulesForSerialization(element, standardFontFamilySerializationMode); 1164 1164 1165 1165 if (element.inlineStyle()) … … 1182 1182 } 1183 1183 1184 Ref<EditingStyle> EditingStyle::wrappingStyleForSerialization(Node& context, bool shouldAnnotate )1184 Ref<EditingStyle> EditingStyle::wrappingStyleForSerialization(Node& context, bool shouldAnnotate, StandardFontFamilySerializationMode standardFontFamilySerializationMode) 1185 1185 { 1186 1186 if (shouldAnnotate) { … … 1203 1203 for (Node* node = &context; node && !node->isDocumentNode(); node = node->parentNode()) { 1204 1204 if (is<StyledElement>(*node) && !isMailBlockquote(node)) 1205 wrappingStyle->mergeInlineAndImplicitStyleOfElement(downcast<StyledElement>(*node), EditingStyle::DoNotOverrideValues, EditingStyle::EditingPropertiesInEffect);1205 wrappingStyle->mergeInlineAndImplicitStyleOfElement(downcast<StyledElement>(*node), DoNotOverrideValues, EditingPropertiesInEffect, standardFontFamilySerializationMode); 1206 1206 } 1207 1207 … … 1283 1283 } 1284 1284 1285 static bool usesForbiddenSystemFontAsOnlyFontFamilyName(CSSValue& value) 1286 { 1285 static String familyNameFromCSSPrimitiveValue(const CSSPrimitiveValue& primitiveValue) 1286 { 1287 if (!primitiveValue.isFontFamily()) 1288 return { }; 1289 return primitiveValue.fontFamily().familyName; 1290 } 1291 1292 static String loneFontFamilyName(const CSSValue& value) 1293 { 1294 if (is<CSSPrimitiveValue>(value)) 1295 return familyNameFromCSSPrimitiveValue(downcast<CSSPrimitiveValue>(value)); 1296 1287 1297 if (!is<CSSValueList>(value) || downcast<CSSValueList>(value).length() != 1) 1288 return false;1298 return { }; 1289 1299 1290 1300 auto& item = *downcast<CSSValueList>(value).item(0); 1291 1301 if (!is<CSSPrimitiveValue>(item)) 1292 return false; 1293 1294 auto& primitiveValue = downcast<CSSPrimitiveValue>(item); 1295 if (!primitiveValue.isFontFamily()) 1296 return false; 1297 return FontCache::isSystemFontForbiddenForEditing(primitiveValue.fontFamily().familyName); 1298 } 1299 1300 void EditingStyle::mergeStyleFromRulesForSerialization(StyledElement& element) 1302 return { }; 1303 1304 return familyNameFromCSSPrimitiveValue(downcast<CSSPrimitiveValue>(item)); 1305 } 1306 1307 void EditingStyle::mergeStyleFromRulesForSerialization(StyledElement& element, StandardFontFamilySerializationMode standardFontFamilySerializationMode) 1301 1308 { 1302 1309 mergeStyleFromRules(element); … … 1314 1321 StyleProperties::PropertyReference property = m_mutableStyle->propertyAt(i); 1315 1322 CSSValue& value = *property.value(); 1316 if (property.id() == CSSPropertyFontFamily && usesForbiddenSystemFontAsOnlyFontFamilyName(value)) { 1317 shouldRemoveFontFamily = true; 1323 if (property.id() == CSSPropertyFontFamily) { 1324 auto familyName = loneFontFamilyName(value); 1325 if (FontCache::isSystemFontForbiddenForEditing(familyName) 1326 || (standardFontFamilySerializationMode == StandardFontFamilySerializationMode::Strip && familyName == standardFamily)) 1327 shouldRemoveFontFamily = true; 1318 1328 continue; 1319 1329 } -
branches/safari-608-branch/Source/WebCore/editing/EditingStyle.h
r242776 r249707 61 61 enum class TextDecorationChange { None, Add, Remove }; 62 62 63 // FIXME: "Keep" should be "Resolve" instead and resolve all generic font family names. 64 enum class StandardFontFamilySerializationMode : uint8_t { Keep, Strip }; 65 63 66 class EditingStyle : public RefCounted<EditingStyle> { 64 67 public: … … 142 145 enum CSSPropertyOverrideMode { OverrideValues, DoNotOverrideValues }; 143 146 void mergeInlineStyleOfElement(StyledElement&, CSSPropertyOverrideMode, PropertiesToInclude = AllProperties); 144 static Ref<EditingStyle> wrappingStyleForSerialization(Node& context, bool shouldAnnotate );147 static Ref<EditingStyle> wrappingStyleForSerialization(Node& context, bool shouldAnnotate, StandardFontFamilySerializationMode); 145 148 void mergeStyleFromRules(StyledElement&); 146 void mergeStyleFromRulesForSerialization(StyledElement& );149 void mergeStyleFromRulesForSerialization(StyledElement&, StandardFontFamilySerializationMode); 147 150 void removeStyleFromRulesAndContext(StyledElement&, Node* context); 148 151 void removePropertiesInElementDefaultStyle(Element&); … … 182 185 template<typename T> TriState triStateOfStyle(T& styleToCompare, ShouldIgnoreTextOnlyProperties) const; 183 186 bool conflictsWithInlineStyleOfElement(StyledElement&, RefPtr<MutableStyleProperties>* newInlineStyle, EditingStyle* extractedStyle) const; 184 void mergeInlineAndImplicitStyleOfElement(StyledElement&, CSSPropertyOverrideMode, PropertiesToInclude );187 void mergeInlineAndImplicitStyleOfElement(StyledElement&, CSSPropertyOverrideMode, PropertiesToInclude, StandardFontFamilySerializationMode); 185 188 void mergeStyle(const StyleProperties*, CSSPropertyOverrideMode); 186 189 -
branches/safari-608-branch/Source/WebCore/editing/markup.cpp
r247222 r249707 223 223 224 224 StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs, SerializeComposedTree, 225 AnnotateForInterchange, MSOListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized = nullptr);225 AnnotateForInterchange, StandardFontFamilySerializationMode, MSOListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized = nullptr); 226 226 227 227 Node* serializeNodes(const Position& start, const Position& end); … … 319 319 bool m_useComposedTree; 320 320 bool m_needsPositionStyleConversion; 321 StandardFontFamilySerializationMode m_standardFontFamilySerializationMode; 322 bool m_shouldPreserveMSOList; 321 323 bool m_needRelativeStyleWrapper { false }; 322 324 bool m_needClearingDiv { false }; 323 bool m_shouldPreserveMSOList;324 325 bool m_inMSOList { false }; 325 326 }; 326 327 327 328 inline StyledMarkupAccumulator::StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs urlsToResolve, SerializeComposedTree serializeComposedTree, 328 AnnotateForInterchange annotate, MSOListMode msoListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized)329 AnnotateForInterchange annotate, StandardFontFamilySerializationMode standardFontFamilySerializationMode, MSOListMode msoListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized) 329 330 : MarkupAccumulator(nodes, urlsToResolve) 330 331 , m_start(start) … … 334 335 , m_useComposedTree(serializeComposedTree == SerializeComposedTree::Yes) 335 336 , m_needsPositionStyleConversion(needsPositionStyleConversion) 337 , m_standardFontFamilySerializationMode(standardFontFamilySerializationMode) 336 338 , m_shouldPreserveMSOList(msoListMode == MSOListMode::Preserve) 337 339 { … … 532 534 if (shouldAnnotateOrForceInline) { 533 535 if (shouldAnnotate()) 534 newInlineStyle->mergeStyleFromRulesForSerialization(downcast<HTMLElement>(*const_cast<Element*>(&element)) );536 newInlineStyle->mergeStyleFromRulesForSerialization(downcast<HTMLElement>(*const_cast<Element*>(&element)), m_standardFontFamilySerializationMode); 535 537 536 538 if (addDisplayInline) … … 580 582 581 583 if (m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode()) 582 m_wrappingStyle = EditingStyle::wrappingStyleForSerialization(*m_highestNodeToBeSerialized->parentNode(), shouldAnnotate() );584 m_wrappingStyle = EditingStyle::wrappingStyleForSerialization(*m_highestNodeToBeSerialized->parentNode(), shouldAnnotate(), m_standardFontFamilySerializationMode); 583 585 584 586 return traverseNodesForSerialization(startNode.get(), pastEnd, NodeTraversalMode::EmitString); … … 823 825 824 826 static String serializePreservingVisualAppearanceInternal(const Position& start, const Position& end, Vector<Node*>* nodes, ResolveURLs urlsToResolve, SerializeComposedTree serializeComposedTree, 825 AnnotateForInterchange annotate, ConvertBlocksToInlines convertBlocksToInlines, MSOListMode msoListMode)827 AnnotateForInterchange annotate, ConvertBlocksToInlines convertBlocksToInlines, StandardFontFamilySerializationMode standardFontFamilySerializationMode, MSOListMode msoListMode) 826 828 { 827 829 static NeverDestroyed<const String> interchangeNewlineString(MAKE_STATIC_STRING_IMPL("<br class=\"" AppleInterchangeNewline "\">")); … … 849 851 Node* specialCommonAncestor = highestAncestorToWrapMarkup(start, end, *commonAncestor, annotate); 850 852 851 StyledMarkupAccumulator accumulator(start, end, nodes, urlsToResolve, serializeComposedTree, annotate, msoListMode, needsPositionStyleConversion, specialCommonAncestor);853 StyledMarkupAccumulator accumulator(start, end, nodes, urlsToResolve, serializeComposedTree, annotate, standardFontFamilySerializationMode, msoListMode, needsPositionStyleConversion, specialCommonAncestor); 852 854 853 855 Position startAdjustedForInterchangeNewline = start; … … 918 920 { 919 921 return serializePreservingVisualAppearanceInternal(range.startPosition(), range.endPosition(), nodes, urlsToReslve, SerializeComposedTree::No, 920 annotate, convertBlocksToInlines, MSOListMode::DoNotPreserve);922 annotate, convertBlocksToInlines, StandardFontFamilySerializationMode::Keep, MSOListMode::DoNotPreserve); 921 923 } 922 924 … … 924 926 { 925 927 return serializePreservingVisualAppearanceInternal(selection.start(), selection.end(), nodes, resolveURLs, serializeComposedTree, 926 AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, MSOListMode::DoNotPreserve);928 AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, StandardFontFamilySerializationMode::Keep, MSOListMode::DoNotPreserve); 927 929 } 928 930 … … 951 953 // SerializeComposedTree::No because there can't be a shadow tree in the pasted fragment. 952 954 auto result = serializePreservingVisualAppearanceInternal(firstPositionInNode(bodyElement.get()), lastPositionInNode(bodyElement.get()), nullptr, 953 ResolveURLs::YesExcludingLocalFileURLsForPrivacy, SerializeComposedTree::No, AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, msoListMode);955 ResolveURLs::YesExcludingLocalFileURLsForPrivacy, SerializeComposedTree::No, AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, StandardFontFamilySerializationMode::Strip, msoListMode); 954 956 955 957 if (msoListMode == MSOListMode::Preserve) { -
branches/safari-608-branch/Tools/ChangeLog
r249698 r249707 1 2019-09-09 Ryosuke Niwa <rniwa@webkit.org> 2 3 REGRESSION (iOS 13): Bulleted list copied from Notes to Mail results in Times New Roman 4 https://bugs.webkit.org/show_bug.cgi?id=201490 5 6 Reviewed by Daniel Bates. 7 8 Added a test. 9 10 * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj: 11 * TestWebKitAPI/Tests/WebKitCocoa/PasteHTML.mm: 12 * TestWebKitAPI/Tests/WebKitCocoa/cocoa-writer-markup-with-lists.html: Added. 13 1 14 2019-09-09 Kocsen Chung <kocsen_chung@apple.com> 2 15 -
branches/safari-608-branch/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
r249697 r249707 715 715 9B7A37C41F8AEBA5004AA228 /* CopyURL.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B7A37C21F8AEBA5004AA228 /* CopyURL.mm */; }; 716 716 9B7D740F1F8378770006C432 /* paste-rtfd.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B7D740E1F8377E60006C432 /* paste-rtfd.html */; }; 717 9B9332CE2320C745002D50E8 /* cocoa-writer-markup-with-lists.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B9332CD2320C73E002D50E8 /* cocoa-writer-markup-with-lists.html */; }; 717 718 9BAD7F3E22690F2000F8DA66 /* DeallocWebViewInEventListener.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BAD7F3D22690F1400F8DA66 /* DeallocWebViewInEventListener.mm */; }; 718 719 9BAE177B22E2BBFB00DF3098 /* cocoa-writer-markup-with-system-fonts.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9BAE177A22E2BB6B00DF3098 /* cocoa-writer-markup-with-system-fonts.html */; }; … … 1142 1143 9B270FEE1DDC2C0B002D53F3 /* closed-shadow-tree-test.html in Copy Resources */, 1143 1144 E5036F78211BC25400BFDBE2 /* color-drop.html in Copy Resources */, 1145 9B9332CE2320C745002D50E8 /* cocoa-writer-markup-with-lists.html in Copy Resources */, 1144 1146 F4B825D81EF4DBFB006E417F /* compressed-files.zip in Copy Resources */, 1145 1147 5C9E56871DF914AE00C9EE33 /* contentBlockerCheck.html in Copy Resources */, … … 2061 2063 9B7A37C21F8AEBA5004AA228 /* CopyURL.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CopyURL.mm; sourceTree = "<group>"; }; 2062 2064 9B7D740E1F8377E60006C432 /* paste-rtfd.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "paste-rtfd.html"; sourceTree = "<group>"; }; 2065 9B9332CD2320C73E002D50E8 /* cocoa-writer-markup-with-lists.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "cocoa-writer-markup-with-lists.html"; sourceTree = "<group>"; }; 2063 2066 9BAD7F3D22690F1400F8DA66 /* DeallocWebViewInEventListener.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DeallocWebViewInEventListener.mm; sourceTree = "<group>"; }; 2064 2067 9BAE177A22E2BB6B00DF3098 /* cocoa-writer-markup-with-system-fonts.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "cocoa-writer-markup-with-system-fonts.html"; sourceTree = "<group>"; }; … … 3137 3140 F4B825D61EF4DBD4006E417F /* compressed-files.zip */, 3138 3141 F469FB231F01803500401539 /* contenteditable-and-target.html */, 3142 9B9332CD2320C73E002D50E8 /* cocoa-writer-markup-with-lists.html */, 3139 3143 F41AB99C1EF4692C0083FA08 /* contenteditable-and-textarea.html */, 3140 3144 F4A32ECA1F0642F40047C544 /* contenteditable-in-iframe.html */, -
branches/safari-608-branch/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteHTML.mm
r247966 r249707 359 359 360 360 #endif // PLATFORM(COCOA) 361 TEST(PasteHTML, DoesNotAddStandardFontFamily) 362 { 363 writeHTMLToPasteboard([NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"cocoa-writer-markup-with-lists" ofType:@"html" inDirectory:@"TestWebKitAPI.resources"] encoding:NSUTF8StringEncoding error:NULL]); 364 365 auto webView = createWebViewWithCustomPasteboardDataSetting(true); 366 [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"]; 367 [webView stringByEvaluatingJavaScript:@"document.body.style.fontFamily = 'Arial'"]; 368 [webView paste:nil]; 369 370 EXPECT_WK_STREQ("[\"text/html\"]", [webView stringByEvaluatingJavaScript:@"JSON.stringify(clipboardData.types)"]); 371 [webView stringByEvaluatingJavaScript:@"window.htmlInDataTransfer = clipboardData.values[0]"]; 372 [webView stringByEvaluatingJavaScript:@"window.pastedHTML = editor.innerHTML"]; 373 374 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"pastedHTML.includes('Hello')"].boolValue); 375 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"pastedHTML.includes('font-weight: bold')"].boolValue); 376 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"!pastedHTML.includes('-webkit-standard')"].boolValue); 377 378 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"htmlInDataTransfer.includes('Hello')"].boolValue); 379 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"htmlInDataTransfer.includes('font-weight: bold')"].boolValue); 380 EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"!htmlInDataTransfer.includes('-webkit-standard')"].boolValue); 381 382 EXPECT_WK_STREQ([webView stringByEvaluatingJavaScript:@"getComputedStyle(document.querySelector('.s2')).fontFamily"], 383 [webView stringByEvaluatingJavaScript:@"getComputedStyle(document.body).fontFamily"]); 384 EXPECT_WK_STREQ([webView stringByEvaluatingJavaScript:@"getComputedStyle(document.querySelector('.s4')).fontFamily"], 385 [webView stringByEvaluatingJavaScript:@"getComputedStyle(document.body).fontFamily"]); 386 } 387
Note:
See TracChangeset
for help on using the changeset viewer.