Skip to content

Commit

Permalink
Bug 1066965, make contentEditable and spellchecking to work in Shadow…
Browse files Browse the repository at this point in the history
…DOM, r=mrbkap
  • Loading branch information
Olli Pettay authored and Olli Pettay committed Mar 6, 2018
1 parent a87734a commit c4d5b20
Show file tree
Hide file tree
Showing 11 changed files with 63 additions and 31 deletions.
8 changes: 5 additions & 3 deletions dom/base/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1760,10 +1760,12 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
if (!hadParent) {
uint32_t editableDescendantChange = EditableInclusiveDescendantCount(this);
if (editableDescendantChange != 0) {
// If we are binding a subtree root to the document, we need to update
// the editable descendant count of all the ancestors.
// If we are binding a subtree root to the document, we need to update
// the editable descendant count of all the ancestors.
// But we don't cross Shadow DOM boundary.
// (The expected behavior with Shadow DOM is unclear)
nsIContent* parent = GetParent();
while (parent) {
while (parent && parent->IsElement()) {
parent->ChangeEditableDescendantCount(editableDescendantChange);
parent = parent->GetParent();
}
Expand Down
14 changes: 8 additions & 6 deletions dom/base/Selection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -907,10 +907,11 @@ CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset,
{
nsINode* start = aRange->GetStartContainer();
NS_ENSURE_STATE(aCompareNode && start);
// If the nodes that we're comparing are not in the same document,
// assume that aCompareNode will fall at the end of the ranges.
// If the nodes that we're comparing are not in the same document or in the
// same subtree, assume that aCompareNode will fall at the end of the ranges.
if (aCompareNode->GetComposedDoc() != start->GetComposedDoc() ||
!start->GetComposedDoc()) {
!start->GetComposedDoc() ||
aCompareNode->SubtreeRoot() != start->SubtreeRoot()) {
*aCmp = 1;
} else {
*aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
Expand All @@ -925,10 +926,11 @@ CompareToRangeEnd(nsINode* aCompareNode, int32_t aCompareOffset,
{
nsINode* end = aRange->GetEndContainer();
NS_ENSURE_STATE(aCompareNode && end);
// If the nodes that we're comparing are not in the same document,
// assume that aCompareNode will fall at the end of the ranges.
// If the nodes that we're comparing are not in the same document or in the
// same subtree, assume that aCompareNode will fall at the end of the ranges.
if (aCompareNode->GetComposedDoc() != end->GetComposedDoc() ||
!end->GetComposedDoc()) {
!end->GetComposedDoc() ||
aCompareNode->SubtreeRoot() != end->SubtreeRoot()) {
*aCmp = 1;
} else {
*aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
Expand Down
11 changes: 11 additions & 0 deletions dom/base/nsINode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,17 @@ nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions)
return SubtreeRoot();
}

nsINode*
nsINode::GetParentOrHostNode() const
{
if (mParent) {
return mParent;
}

const ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
return shadowRoot ? shadowRoot->GetHost() : nullptr;
}

nsINode*
nsINode::SubtreeRoot() const
{
Expand Down
6 changes: 6 additions & 0 deletions dom/base/nsINode.h
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,12 @@ class nsINode : public mozilla::dom::EventTarget
return mParent;
}

/**
* This is similar to above, but in case 'this' is ShadowRoot, we return its
* host element.
*/
nsINode* GetParentOrHostNode() const;

enum FlattenedParentType { eNotForStyle, eForStyle };

/**
Expand Down
23 changes: 12 additions & 11 deletions dom/html/nsGenericHTMLElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ nsGenericHTMLElement::IntrinsicState() const
uint32_t
nsGenericHTMLElement::EditableInclusiveDescendantCount()
{
bool isEditable = IsInUncomposedDoc() && HasFlag(NODE_IS_EDITABLE) &&
bool isEditable = IsInComposedDoc() && HasFlag(NODE_IS_EDITABLE) &&
GetContentEditableValue() == eTrue;
return EditableDescendantCount() + isEditable;
}
Expand All @@ -429,12 +429,14 @@ nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
aDocument->
AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
}
}

if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue) {
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(aDocument);
if (htmlDocument) {
htmlDocument->ChangeContentEditableCount(this, +1);
}
if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue &&
IsInComposedDoc()) {
nsCOMPtr<nsIHTMLDocument> htmlDocument =
do_QueryInterface(GetComposedDoc());
if (htmlDocument) {
htmlDocument->ChangeContentEditableCount(this, +1);
}
}

Expand All @@ -459,8 +461,7 @@ nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
RemoveFromNameTable();

if (GetContentEditableValue() == eTrue) {
//XXXsmaug Fix this for Shadow DOM, bug 1066965.
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetUncomposedDoc());
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetComposedDoc());
if (htmlDocument) {
htmlDocument->ChangeContentEditableCount(this, -1);
}
Expand Down Expand Up @@ -2737,8 +2738,7 @@ MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument)
void
nsGenericHTMLElement::ChangeEditableState(int32_t aChange)
{
//XXXsmaug Fix this for Shadow DOM, bug 1066965.
nsIDocument* document = GetUncomposedDoc();
nsIDocument* document = GetComposedDoc();
if (!document) {
return;
}
Expand All @@ -2751,7 +2751,8 @@ nsGenericHTMLElement::ChangeEditableState(int32_t aChange)
}

nsIContent* parent = GetParent();
while (parent) {
// Don't update across Shadow DOM boundary.
while (parent && parent->IsElement()) {
parent->ChangeEditableDescendantCount(aChange);
parent = parent->GetParent();
}
Expand Down
2 changes: 1 addition & 1 deletion editor/libeditor/EditorBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ EditorBase::GetDesiredSpellCheckState()
// Some of the page content might be editable and some not, if spellcheck=
// is explicitly set anywhere, so if there's anything editable on the page,
// return true and let the spellchecker figure it out.
nsCOMPtr<nsIHTMLDocument> doc = do_QueryInterface(content->GetUncomposedDoc());
nsCOMPtr<nsIHTMLDocument> doc = do_QueryInterface(content->GetComposedDoc());
return doc && doc->IsEditingOn();
}

Expand Down
6 changes: 4 additions & 2 deletions editor/libeditor/EditorEventListener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@ EditorEventListener::Focus(InternalFocusEvent* aFocusEvent)
return NS_OK;
}

nsIDOMEventTarget* target = aFocusEvent->GetDOMEventTarget();
nsIDOMEventTarget* target = aFocusEvent->GetOriginalDOMEventTarget();
nsCOMPtr<nsINode> node = do_QueryInterface(target);
NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);

Expand All @@ -1102,13 +1102,15 @@ EditorEventListener::Focus(InternalFocusEvent* aFocusEvent)
}

if (node->IsContent()) {
nsIContent* content =
node->AsContent()->FindFirstNonChromeOnlyAccessContent();
// XXX If the focus event target is a form control in contenteditable
// element, perhaps, the parent HTML editor should do nothing by this
// handler. However, FindSelectionRoot() returns the root element of the
// contenteditable editor. So, the editableRoot value is invalid for
// the plain text editor, and it will be set to the wrong limiter of
// the selection. However, fortunately, actual bugs are not found yet.
nsCOMPtr<nsIContent> editableRoot = editorBase->FindSelectionRoot(node);
nsCOMPtr<nsIContent> editableRoot = editorBase->FindSelectionRoot(content);

// make sure that the element is really focused in case an earlier
// listener in the chain changed the focus.
Expand Down
6 changes: 4 additions & 2 deletions editor/libeditor/HTMLEditRules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8743,8 +8743,9 @@ HTMLEditRules::ConfirmSelectionInBody()
nsINode* temp = selNode;

// check that selNode is inside body
//XXXsmaug this code is insane.
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
temp = temp->GetParentNode();
temp = temp->GetParentOrHostNode();
}

// if we aren't in the body, force the issue
Expand All @@ -8762,8 +8763,9 @@ HTMLEditRules::ConfirmSelectionInBody()
temp = selNode;

// check that selNode is inside body
//XXXsmaug this code is insane.
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
temp = temp->GetParentNode();
temp = temp->GetParentOrHostNode();
}

// if we aren't in the body, force the issue
Expand Down
11 changes: 8 additions & 3 deletions editor/libeditor/HTMLEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,13 +381,14 @@ HTMLEditor::FindSelectionRoot(nsINode* aNode)
aNode->IsContent(),
"aNode must be content or document node");

nsCOMPtr<nsIDocument> doc = aNode->GetUncomposedDoc();
nsCOMPtr<nsIDocument> doc = aNode->GetComposedDoc();
if (!doc) {
return nullptr;
}

nsCOMPtr<nsIContent> content;
if (doc->HasFlag(NODE_IS_EDITABLE) || !aNode->IsContent()) {
if (aNode->IsInUncomposedDoc() &&
(doc->HasFlag(NODE_IS_EDITABLE) || !aNode->IsContent())) {
content = doc->GetRootElement();
return content.forget();
}
Expand Down Expand Up @@ -4799,7 +4800,11 @@ HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent)
return true;
}

nsCOMPtr<nsIDOMEventTarget> target = aGUIEvent->GetDOMEventTarget();
nsCOMPtr<nsIDOMEventTarget> target = aGUIEvent->GetOriginalDOMEventTarget();
nsCOMPtr<nsIContent> content = do_QueryInterface(target);
if (content) {
target = content->FindFirstNonChromeOnlyAccessContent();
}
NS_ENSURE_TRUE(target, false);

nsCOMPtr<nsIDocument> document = GetDocument();
Expand Down
2 changes: 1 addition & 1 deletion editor/spellchecker/EditorSpellCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ EditorSpellCheck::UpdateCurrentDictionary(
RefPtr<DictionaryFetcher> fetcher =
new DictionaryFetcher(this, aCallback, mDictionaryFetcherGroup);
rootContent->GetLang(fetcher->mRootContentLang);
nsCOMPtr<nsIDocument> doc = rootContent->GetUncomposedDoc();
nsCOMPtr<nsIDocument> doc = rootContent->GetComposedDoc();
NS_ENSURE_STATE(doc);
doc->GetContentLanguage(fetcher->mRootDocContentLang);

Expand Down
5 changes: 3 additions & 2 deletions extensions/spellcheck/src/mozInlineSpellChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1398,8 +1398,9 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
// Now check that we're still looking at a range that's under
// aWordUtil.GetRootNode()
nsINode* rootNode = aWordUtil.GetRootNode();
if (!nsContentUtils::ContentIsDescendantOf(beginNode, rootNode) ||
!nsContentUtils::ContentIsDescendantOf(endNode, rootNode)) {
if (!beginNode->IsInComposedDoc() || !endNode->IsInComposedDoc() ||
!nsContentUtils::ContentIsShadowIncludingDescendantOf(beginNode, rootNode) ||
!nsContentUtils::ContentIsShadowIncludingDescendantOf(endNode, rootNode)) {
// Just bail out and don't try to spell-check this
return NS_OK;
}
Expand Down

0 comments on commit c4d5b20

Please sign in to comment.