Bug 1272623 part.3 HTMLEditRules::ReapplyCachedStyles() shouldn't set style to TypeInState if it's currently applied r=smaug

The target node of HTMLEditRules::ReapplyCachedStyles() may be styled with its parent.  When HTMLEditRules::ReapplyCachedStyles() is called, it shouldn't restore another style cache if it's already specified in current DOM tree.

MozReview-Commit-ID: DKCpQ8YyW7

--HG--
extra : rebase_source : 854b4cc6382f5357919bce1e106e71fe66b15444
This commit is contained in:
Masayuki Nakano 2017-01-06 22:35:48 +09:00
parent b51e97e916
commit b2778c3a90
4 changed files with 108 additions and 54 deletions

View File

@ -185,26 +185,32 @@ HTMLEditRules::InitFields()
mJoinOffset = 0;
mNewBlock = nullptr;
mRangeItem = new RangeItem();
// populate mCachedStyles
mCachedStyles[0] = StyleCache(nsGkAtoms::b, EmptyString(), EmptyString());
mCachedStyles[1] = StyleCache(nsGkAtoms::i, EmptyString(), EmptyString());
mCachedStyles[2] = StyleCache(nsGkAtoms::u, EmptyString(), EmptyString());
mCachedStyles[3] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("face"), EmptyString());
mCachedStyles[4] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("size"), EmptyString());
mCachedStyles[5] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("color"), EmptyString());
mCachedStyles[6] = StyleCache(nsGkAtoms::tt, EmptyString(), EmptyString());
mCachedStyles[7] = StyleCache(nsGkAtoms::em, EmptyString(), EmptyString());
mCachedStyles[8] = StyleCache(nsGkAtoms::strong, EmptyString(), EmptyString());
mCachedStyles[9] = StyleCache(nsGkAtoms::dfn, EmptyString(), EmptyString());
mCachedStyles[10] = StyleCache(nsGkAtoms::code, EmptyString(), EmptyString());
mCachedStyles[11] = StyleCache(nsGkAtoms::samp, EmptyString(), EmptyString());
mCachedStyles[12] = StyleCache(nsGkAtoms::var, EmptyString(), EmptyString());
mCachedStyles[13] = StyleCache(nsGkAtoms::cite, EmptyString(), EmptyString());
mCachedStyles[14] = StyleCache(nsGkAtoms::abbr, EmptyString(), EmptyString());
mCachedStyles[15] = StyleCache(nsGkAtoms::acronym, EmptyString(), EmptyString());
mCachedStyles[16] = StyleCache(nsGkAtoms::backgroundColor, EmptyString(), EmptyString());
mCachedStyles[17] = StyleCache(nsGkAtoms::sub, EmptyString(), EmptyString());
mCachedStyles[18] = StyleCache(nsGkAtoms::sup, EmptyString(), EmptyString());
InitStyleCacheArray(mCachedStyles);
}
void
HTMLEditRules::InitStyleCacheArray(StyleCache aStyleCache[SIZE_STYLE_TABLE])
{
aStyleCache[0] = StyleCache(nsGkAtoms::b, EmptyString());
aStyleCache[1] = StyleCache(nsGkAtoms::i, EmptyString());
aStyleCache[2] = StyleCache(nsGkAtoms::u, EmptyString());
aStyleCache[3] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("face"));
aStyleCache[4] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("size"));
aStyleCache[5] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("color"));
aStyleCache[6] = StyleCache(nsGkAtoms::tt, EmptyString());
aStyleCache[7] = StyleCache(nsGkAtoms::em, EmptyString());
aStyleCache[8] = StyleCache(nsGkAtoms::strong, EmptyString());
aStyleCache[9] = StyleCache(nsGkAtoms::dfn, EmptyString());
aStyleCache[10] = StyleCache(nsGkAtoms::code, EmptyString());
aStyleCache[11] = StyleCache(nsGkAtoms::samp, EmptyString());
aStyleCache[12] = StyleCache(nsGkAtoms::var, EmptyString());
aStyleCache[13] = StyleCache(nsGkAtoms::cite, EmptyString());
aStyleCache[14] = StyleCache(nsGkAtoms::abbr, EmptyString());
aStyleCache[15] = StyleCache(nsGkAtoms::acronym, EmptyString());
aStyleCache[16] = StyleCache(nsGkAtoms::backgroundColor, EmptyString());
aStyleCache[17] = StyleCache(nsGkAtoms::sub, EmptyString());
aStyleCache[18] = StyleCache(nsGkAtoms::sup, EmptyString());
}
HTMLEditRules::~HTMLEditRules()
@ -7064,16 +7070,31 @@ HTMLEditRules::CacheInlineStyles(nsIDOMNode* aNode)
NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
NS_ENSURE_STATE(mHTMLEditor);
nsresult rv = GetInlineStyles(aNode, mCachedStyles);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
HTMLEditRules::GetInlineStyles(nsIDOMNode* aNode,
StyleCache aStyleCache[SIZE_STYLE_TABLE])
{
MOZ_ASSERT(aNode);
MOZ_ASSERT(mHTMLEditor);
bool useCSS = mHTMLEditor->IsCSSEnabled();
for (int32_t j = 0; j < SIZE_STYLE_TABLE; ++j) {
for (size_t j = 0; j < SIZE_STYLE_TABLE; ++j) {
// If type-in state is set, don't intervene
bool typeInSet, unused;
if (NS_WARN_IF(!mHTMLEditor)) {
return NS_ERROR_UNEXPECTED;
}
mHTMLEditor->mTypeInState->GetTypingState(typeInSet, unused,
mCachedStyles[j].tag, mCachedStyles[j].attr, nullptr);
aStyleCache[j].tag, aStyleCache[j].attr, nullptr);
if (typeInSet) {
continue;
}
@ -7081,21 +7102,21 @@ HTMLEditRules::CacheInlineStyles(nsIDOMNode* aNode)
bool isSet = false;
nsAutoString outValue;
// Don't use CSS for <font size>, we don't support it usefully (bug 780035)
if (!useCSS || (mCachedStyles[j].tag == nsGkAtoms::font &&
mCachedStyles[j].attr.EqualsLiteral("size"))) {
if (!useCSS || (aStyleCache[j].tag == nsGkAtoms::font &&
aStyleCache[j].attr.EqualsLiteral("size"))) {
NS_ENSURE_STATE(mHTMLEditor);
mHTMLEditor->IsTextPropertySetByContent(aNode, mCachedStyles[j].tag,
&(mCachedStyles[j].attr), nullptr,
mHTMLEditor->IsTextPropertySetByContent(aNode, aStyleCache[j].tag,
&(aStyleCache[j].attr), nullptr,
isSet, &outValue);
} else {
NS_ENSURE_STATE(mHTMLEditor);
isSet = mHTMLEditor->mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(
aNode, mCachedStyles[j].tag, &(mCachedStyles[j].attr), outValue,
aNode, aStyleCache[j].tag, &(aStyleCache[j].attr), outValue,
CSSEditUtils::eComputed);
}
if (isSet) {
mCachedStyles[j].mPresent = true;
mCachedStyles[j].value.Assign(outValue);
aStyleCache[j].mPresent = true;
aStyleCache[j].value.Assign(outValue);
}
}
return NS_OK;
@ -7132,7 +7153,16 @@ HTMLEditRules::ReapplyCachedStyles()
return NS_OK;
}
for (int32_t i = 0; i < SIZE_STYLE_TABLE; ++i) {
StyleCache styleAtInsertionPoint[SIZE_STYLE_TABLE];
InitStyleCacheArray(styleAtInsertionPoint);
nsCOMPtr<nsIDOMNode> selDOMNode = do_QueryInterface(selNode);
MOZ_ASSERT(selDOMNode);
nsresult rv = GetInlineStyles(selDOMNode, styleAtInsertionPoint);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
for (size_t i = 0; i < SIZE_STYLE_TABLE; ++i) {
if (mCachedStyles[i].mPresent) {
bool bFirst, bAny, bAll;
bFirst = bAny = bAll = false;
@ -7156,8 +7186,11 @@ HTMLEditRules::ReapplyCachedStyles()
&curValue, false);
NS_ENSURE_SUCCESS(rv, rv);
}
// this style has disappeared through deletion. Add to our typeinstate:
if (!bAny || IsStyleCachePreservingAction(mTheAction)) {
// This style has disappeared through deletion. Let's add the styles to
// mTypeInState when same style isn't applied to the node already.
if ((!bAny || IsStyleCachePreservingAction(mTheAction)) &&
(!styleAtInsertionPoint[i].mPresent ||
styleAtInsertionPoint[i].value != mCachedStyles[i].value)) {
NS_ENSURE_STATE(mHTMLEditor);
mHTMLEditor->mTypeInState->SetProp(mCachedStyles[i].tag,
mCachedStyles[i].attr,
@ -7173,7 +7206,7 @@ void
HTMLEditRules::ClearCachedStyles()
{
// clear the mPresent bits in mCachedStyles array
for (uint32_t j = 0; j < SIZE_STYLE_TABLE; j++) {
for (size_t j = 0; j < SIZE_STYLE_TABLE; j++) {
mCachedStyles[j].mPresent = false;
mCachedStyles[j].value.Truncate();
}

View File

@ -58,6 +58,14 @@ struct StyleCache final : public PropItem
MOZ_COUNT_CTOR(StyleCache);
}
StyleCache(nsIAtom* aTag,
const nsAString& aAttr)
: PropItem(aTag, aAttr, EmptyString())
, mPresent(false)
{
MOZ_COUNT_CTOR(StyleCache);
}
~StyleCache()
{
MOZ_COUNT_DTOR(StyleCache);
@ -405,6 +413,19 @@ protected:
nsresult ChangeIndentation(Element& aElement, Change aChange);
void DocumentModifiedWorker();
/**
* InitStyleCacheArray() initializes aStyleCache for usable with
* GetInlineStyles().
*/
void InitStyleCacheArray(StyleCache aStyleCache[SIZE_STYLE_TABLE]);
/**
* GetInlineStyles() retrieves the style of aNode and modifies each item of
* aStyleCache.
*/
nsresult GetInlineStyles(nsIDOMNode* aNode,
StyleCache aStyleCache[SIZE_STYLE_TABLE]);
protected:
HTMLEditor* mHTMLEditor;
RefPtr<nsRange> mDocChangeRange;
@ -418,6 +439,11 @@ protected:
uint32_t mJoinOffset;
nsCOMPtr<Element> mNewBlock;
RefPtr<RangeItem> mRangeItem;
// XXX In strict speaking, mCachedStyles isn't enough to cache inline styles
// because inline style can be specified with "style" attribute and/or
// CSS in <style> elements or CSS files. So, we need to look for better
// implementation about this.
StyleCache mCachedStyles[SIZE_STYLE_TABLE];
};

View File

@ -168,8 +168,6 @@ SimpleTest.waitForFocus(function() {
is(editor.innerHTML, "<p>before</p><p><strike><i><b>test</b></i></strike>after</p>",
"#02-03-2 Typing Enter after setting style to selected text should keep the styles");
// TODO: Following tests should pass with next patch.
// #03-01 Replacing in <b style="font-weight: normal;"> shouldn't cause new <b>.
editor.innerHTML = "<b style=\"font-weight: normal;\">beforeselectionafter</b>";
selection.collapse(editor.firstChild.firstChild, "before".length);
@ -179,8 +177,8 @@ SimpleTest.waitForFocus(function() {
synthesizeKey("s", { code: "KeyS" });
synthesizeKey("t", { code: "KeyT" });
todo_is(editor.innerHTML, "<b style=\"font-weight: normal;\">beforetestafter</b>",
"#03-01 Replacing text in styled inline elements should respect the styles");
is(editor.innerHTML, "<b style=\"font-weight: normal;\">beforetestafter</b>",
"#03-01 Replacing text in styled inline elements should respect the styles");
// #03-02 Typing something after removing selected text in <b style="font-weight: normal;"> shouldn't cause new <b>.
editor.innerHTML = "<b style=\"font-weight: normal;\">beforeselectionafter</b>";
@ -192,8 +190,8 @@ SimpleTest.waitForFocus(function() {
synthesizeKey("s", { code: "KeyS" });
synthesizeKey("t", { code: "KeyT" });
todo_is(editor.innerHTML, "<b style=\"font-weight: normal;\">beforetestafter</b>",
"#03-02 Inserting text after removing text in styled inline elements should respect the styles");
is(editor.innerHTML, "<b style=\"font-weight: normal;\">beforetestafter</b>",
"#03-02 Inserting text after removing text in styled inline elements should respect the styles");
// #03-03 Typing something after typing Enter at selected text in <b style="font-weight: normal;"> shouldn't cause new <b>.
editor.innerHTML = "<b style=\"font-weight: normal;\">beforeselectionafter</b>";
@ -205,8 +203,8 @@ SimpleTest.waitForFocus(function() {
synthesizeKey("s", { code: "KeyS" });
synthesizeKey("t", { code: "KeyT" });
todo_is(editor.innerHTML, "<b style=\"font-weight: normal;\">before<br>testafter</b>",
"#03-03-1 Inserting text after typing Enter at selected text in styled inline elements should respect the styles");
is(editor.innerHTML, "<b style=\"font-weight: normal;\">before<br>testafter</b>",
"#03-03-1 Inserting text after typing Enter at selected text in styled inline elements should respect the styles");
editor.innerHTML = "<p><b style=\"font-weight: normal;\">beforeselectionafter</b></p>";
selection.collapse(editor.firstChild.firstChild.firstChild, "before".length);
@ -217,8 +215,8 @@ SimpleTest.waitForFocus(function() {
synthesizeKey("s", { code: "KeyS" });
synthesizeKey("t", { code: "KeyT" });
todo_is(editor.innerHTML, "<p><b style=\"font-weight: normal;\">before</b></p><p><b style=\"font-weight: normal;\">testafter</b></p>",
"#03-03-2 Inserting text after typing Enter at selected text in styled inline elements should respect the styles");
is(editor.innerHTML, "<p><b style=\"font-weight: normal;\">before</b></p><p><b style=\"font-weight: normal;\">testafter</b></p>",
"#03-03-2 Inserting text after typing Enter at selected text in styled inline elements should respect the styles");
// #04-01 Replacing in some styled inline elements shouldn't cause new same elements.
editor.innerHTML = "<strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">beforeselectionafter</b></i></strike>";
@ -229,8 +227,8 @@ SimpleTest.waitForFocus(function() {
synthesizeKey("s", { code: "KeyS" });
synthesizeKey("t", { code: "KeyT" });
todo_is(editor.innerHTML, "<strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">beforetestafter</b></i></strike>",
"#04-01 Replacing text in styled inline elements should respect the styles");
is(editor.innerHTML, "<strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">beforetestafter</b></i></strike>",
"#04-01 Replacing text in styled inline elements should respect the styles");
// #04-02 Typing something after removing selected text in some styled inline elements shouldn't cause new same elements.
editor.innerHTML = "<strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">beforeselectionafter</b>";
@ -242,8 +240,8 @@ SimpleTest.waitForFocus(function() {
synthesizeKey("s", { code: "KeyS" });
synthesizeKey("t", { code: "KeyT" });
todo_is(editor.innerHTML, "<strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">beforetestafter</b></i></strike>",
"#04-02 Inserting text after removing text in styled inline elements should respect the styles");
is(editor.innerHTML, "<strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">beforetestafter</b></i></strike>",
"#04-02 Inserting text after removing text in styled inline elements should respect the styles");
// #04-03 Typing something after typing Enter at selected text in some styled inline elements shouldn't cause new same elements.
editor.innerHTML = "<strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">beforeselectionafter</b>";
@ -255,8 +253,8 @@ SimpleTest.waitForFocus(function() {
synthesizeKey("s", { code: "KeyS" });
synthesizeKey("t", { code: "KeyT" });
todo_is(editor.innerHTML, "<strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">before<br>testafter</b></i></strike>",
"#04-03-1 Inserting text after typing Enter at selected text in styled inline elements should respect the styles");
is(editor.innerHTML, "<strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">before<br>testafter</b></i></strike>",
"#04-03-1 Inserting text after typing Enter at selected text in styled inline elements should respect the styles");
editor.innerHTML = "<p><strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">beforeselectionafter</b></p>";
selection.collapse(editor.firstChild.firstChild.firstChild.firstChild.firstChild, "before".length);
@ -267,8 +265,8 @@ SimpleTest.waitForFocus(function() {
synthesizeKey("s", { code: "KeyS" });
synthesizeKey("t", { code: "KeyT" });
todo_is(editor.innerHTML, "<p><strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">before</b></i></strike></p><p><strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">testafter</b></i></strike></p>",
"#04-03-2 Inserting text after typing Enter at selected text in styled inline elements should respect the styles");
is(editor.innerHTML, "<p><strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">before</b></i></strike></p><p><strike style=\"text-decoration: none;\"><i style=\"font-style: normal;\"><b style=\"font-weight: normal;\">testafter</b></i></strike></p>",
"#04-03-2 Inserting text after typing Enter at selected text in styled inline elements should respect the styles");
SimpleTest.finish();
});

View File

@ -282,9 +282,6 @@
[[["stylewithcss","false"\],["inserttext","a"\]\] "<p>foo<span style=color:#aBcDeF>[bar</span><span style=color:#fEdCbA>baz\]</span>quz" compare innerHTML]
expected: FAIL
[[["inserttext","a"\]\] "foo<font color=brown><a href=http://www.google.com>[bar\]</a></font>baz" compare innerHTML]
expected: FAIL
[[["stylewithcss","true"\],["inserttext","a"\]\] "[foo<b>bar\]</b>baz" compare innerHTML]
expected: FAIL