mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 20:07:30 +09:00
Issue #2135 - Bug 1066965: Make contentEditable and spellchecking to work in Shadow DOM
This commit is contained in:
parent
b2c77e5eda
commit
010db07bf3
10 changed files with 60 additions and 30 deletions
|
|
@ -1726,9 +1726,10 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||||
uint32_t editableDescendantChange = EditableInclusiveDescendantCount(this);
|
uint32_t editableDescendantChange = EditableInclusiveDescendantCount(this);
|
||||||
if (editableDescendantChange != 0) {
|
if (editableDescendantChange != 0) {
|
||||||
// If we are binding a subtree root to the document, we need to update
|
// If we are binding a subtree root to the document, we need to update
|
||||||
// the editable descendant count of all the ancestors.
|
// the editable descendant count of all the ancestors. However, we don't
|
||||||
|
// cross the Shadow DOM boundary (expected behavior is unclear).
|
||||||
nsIContent* parent = GetParent();
|
nsIContent* parent = GetParent();
|
||||||
while (parent) {
|
while (parent && parent->IsElement()) {
|
||||||
parent->ChangeEditableDescendantCount(editableDescendantChange);
|
parent->ChangeEditableDescendantCount(editableDescendantChange);
|
||||||
parent = parent->GetParent();
|
parent = parent->GetParent();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -273,6 +273,18 @@ nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions)
|
||||||
return SubtreeRoot();
|
return SubtreeRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: was marked as constant.
|
||||||
|
nsINode*
|
||||||
|
nsINode::GetParentOrHostNode()
|
||||||
|
{
|
||||||
|
if (mParent) {
|
||||||
|
return mParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
|
||||||
|
return shadowRoot ? shadowRoot->GetHost() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
nsINode*
|
nsINode*
|
||||||
nsINode::SubtreeRoot() const
|
nsINode::SubtreeRoot() const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -942,6 +942,12 @@ public:
|
||||||
return mParent;
|
return mParent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is similar to above, but in case 'this' is ShadowRoot, we return its
|
||||||
|
* host element.
|
||||||
|
*/
|
||||||
|
nsINode* GetParentOrHostNode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the node that is the parent of this node in the flattened
|
* Returns the node that is the parent of this node in the flattened
|
||||||
* tree. This differs from the normal parent if the node is filtered
|
* tree. This differs from the normal parent if the node is filtered
|
||||||
|
|
|
||||||
|
|
@ -463,7 +463,7 @@ nsGenericHTMLElement::IntrinsicState() const
|
||||||
uint32_t
|
uint32_t
|
||||||
nsGenericHTMLElement::EditableInclusiveDescendantCount()
|
nsGenericHTMLElement::EditableInclusiveDescendantCount()
|
||||||
{
|
{
|
||||||
bool isEditable = IsInUncomposedDoc() && HasFlag(NODE_IS_EDITABLE) &&
|
bool isEditable = IsInComposedDoc() && HasFlag(NODE_IS_EDITABLE) &&
|
||||||
GetContentEditableValue() == eTrue;
|
GetContentEditableValue() == eTrue;
|
||||||
return EditableDescendantCount() + isEditable;
|
return EditableDescendantCount() + isEditable;
|
||||||
}
|
}
|
||||||
|
|
@ -484,14 +484,16 @@ nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||||
aDocument->
|
aDocument->
|
||||||
AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
|
AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue) {
|
if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue &&
|
||||||
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(aDocument);
|
IsInComposedDoc()) {
|
||||||
|
nsCOMPtr<nsIHTMLDocument> htmlDocument =
|
||||||
|
do_QueryInterface(GetComposedDoc());
|
||||||
if (htmlDocument) {
|
if (htmlDocument) {
|
||||||
htmlDocument->ChangeContentEditableCount(this, +1);
|
htmlDocument->ChangeContentEditableCount(this, +1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// We need to consider a labels element is moved to another subtree
|
// We need to consider a labels element is moved to another subtree
|
||||||
// with different root, it needs to update labels list and its root
|
// with different root, it needs to update labels list and its root
|
||||||
|
|
@ -514,8 +516,7 @@ nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||||
RemoveFromNameTable();
|
RemoveFromNameTable();
|
||||||
|
|
||||||
if (GetContentEditableValue() == eTrue) {
|
if (GetContentEditableValue() == eTrue) {
|
||||||
//XXXsmaug Fix this for Shadow DOM, bug 1066965.
|
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetComposedDoc());
|
||||||
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetUncomposedDoc());
|
|
||||||
if (htmlDocument) {
|
if (htmlDocument) {
|
||||||
htmlDocument->ChangeContentEditableCount(this, -1);
|
htmlDocument->ChangeContentEditableCount(this, -1);
|
||||||
}
|
}
|
||||||
|
|
@ -2774,8 +2775,7 @@ MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument)
|
||||||
void
|
void
|
||||||
nsGenericHTMLElement::ChangeEditableState(int32_t aChange)
|
nsGenericHTMLElement::ChangeEditableState(int32_t aChange)
|
||||||
{
|
{
|
||||||
//XXXsmaug Fix this for Shadow DOM, bug 1066965.
|
nsIDocument* document = GetComposedDoc();
|
||||||
nsIDocument* document = GetUncomposedDoc();
|
|
||||||
if (!document) {
|
if (!document) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -2788,7 +2788,8 @@ nsGenericHTMLElement::ChangeEditableState(int32_t aChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
nsIContent* parent = GetParent();
|
nsIContent* parent = GetParent();
|
||||||
while (parent) {
|
// Don't update across Shadow DOM boundary.
|
||||||
|
while (parent && parent->IsElement()) {
|
||||||
parent->ChangeEditableDescendantCount(aChange);
|
parent->ChangeEditableDescendantCount(aChange);
|
||||||
parent = parent->GetParent();
|
parent = parent->GetParent();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -682,7 +682,7 @@ nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallba
|
||||||
RefPtr<DictionaryFetcher> fetcher =
|
RefPtr<DictionaryFetcher> fetcher =
|
||||||
new DictionaryFetcher(this, aCallback, mDictionaryFetcherGroup);
|
new DictionaryFetcher(this, aCallback, mDictionaryFetcherGroup);
|
||||||
rootContent->GetLang(fetcher->mRootContentLang);
|
rootContent->GetLang(fetcher->mRootContentLang);
|
||||||
nsCOMPtr<nsIDocument> doc = rootContent->GetUncomposedDoc();
|
nsCOMPtr<nsIDocument> doc = rootContent->GetComposedDoc();
|
||||||
NS_ENSURE_STATE(doc);
|
NS_ENSURE_STATE(doc);
|
||||||
doc->GetContentLanguage(fetcher->mRootDocContentLang);
|
doc->GetContentLanguage(fetcher->mRootDocContentLang);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1114,7 +1114,7 @@ EditorEventListener::Focus(WidgetEvent* aFocusEvent)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMEventTarget> target = aFocusEvent->GetDOMEventTarget();
|
nsCOMPtr<nsIDOMEventTarget> target = aFocusEvent->GetOriginalDOMEventTarget();
|
||||||
nsCOMPtr<nsINode> node = do_QueryInterface(target);
|
nsCOMPtr<nsINode> node = do_QueryInterface(target);
|
||||||
NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
|
NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
|
||||||
|
|
||||||
|
|
@ -1126,13 +1126,15 @@ EditorEventListener::Focus(WidgetEvent* aFocusEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->IsNodeOfType(nsINode::eCONTENT)) {
|
if (node->IsNodeOfType(nsINode::eCONTENT)) {
|
||||||
|
nsIContent* content =
|
||||||
|
node->AsContent()->FindFirstNonChromeOnlyAccessContent();
|
||||||
// XXX If the focus event target is a form control in contenteditable
|
// XXX If the focus event target is a form control in contenteditable
|
||||||
// element, perhaps, the parent HTML editor should do nothing by this
|
// element, perhaps, the parent HTML editor should do nothing by this
|
||||||
// handler. However, FindSelectionRoot() returns the root element of the
|
// handler. However, FindSelectionRoot() returns the root element of the
|
||||||
// contenteditable editor. So, the editableRoot value is invalid for
|
// contenteditable editor. So, the editableRoot value is invalid for
|
||||||
// the plain text editor, and it will be set to the wrong limiter of
|
// the plain text editor, and it will be set to the wrong limiter of
|
||||||
// the selection. However, fortunately, actual bugs are not found yet.
|
// 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
|
// make sure that the element is really focused in case an earlier
|
||||||
// listener in the chain changed the focus.
|
// listener in the chain changed the focus.
|
||||||
|
|
|
||||||
|
|
@ -8013,7 +8013,7 @@ HTMLEditRules::ConfirmSelectionInBody()
|
||||||
|
|
||||||
// check that selNode is inside body
|
// check that selNode is inside body
|
||||||
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
|
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
|
||||||
temp = temp->GetParentNode();
|
temp = temp->GetParentOrHostNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we aren't in the body, force the issue
|
// if we aren't in the body, force the issue
|
||||||
|
|
@ -8033,7 +8033,7 @@ HTMLEditRules::ConfirmSelectionInBody()
|
||||||
|
|
||||||
// check that selNode is inside body
|
// check that selNode is inside body
|
||||||
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
|
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
|
||||||
temp = temp->GetParentNode();
|
temp = temp->GetParentOrHostNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we aren't in the body, force the issue
|
// if we aren't in the body, force the issue
|
||||||
|
|
|
||||||
|
|
@ -381,13 +381,14 @@ HTMLEditor::FindSelectionRoot(nsINode* aNode)
|
||||||
aNode->IsNodeOfType(nsINode::eCONTENT),
|
aNode->IsNodeOfType(nsINode::eCONTENT),
|
||||||
"aNode must be content or document node");
|
"aNode must be content or document node");
|
||||||
|
|
||||||
nsCOMPtr<nsIDocument> doc = aNode->GetUncomposedDoc();
|
nsCOMPtr<nsIDocument> doc = aNode->GetComposedDoc();
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIContent> content;
|
nsCOMPtr<nsIContent> content;
|
||||||
if (doc->HasFlag(NODE_IS_EDITABLE) || !aNode->IsContent()) {
|
if (aNode->IsInUncomposedDoc() &&
|
||||||
|
doc->HasFlag(NODE_IS_EDITABLE) || !aNode->IsContent()) {
|
||||||
content = doc->GetRootElement();
|
content = doc->GetRootElement();
|
||||||
return content.forget();
|
return content.forget();
|
||||||
}
|
}
|
||||||
|
|
@ -5135,7 +5136,11 @@ HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent)
|
||||||
return true;
|
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);
|
NS_ENSURE_TRUE(target, false);
|
||||||
|
|
||||||
nsCOMPtr<nsIDocument> document = GetDocument();
|
nsCOMPtr<nsIDocument> document = GetDocument();
|
||||||
|
|
|
||||||
|
|
@ -1483,8 +1483,9 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||||
// Now check that we're still looking at a range that's under
|
// Now check that we're still looking at a range that's under
|
||||||
// aWordUtil.GetRootNode()
|
// aWordUtil.GetRootNode()
|
||||||
nsINode* rootNode = aWordUtil.GetRootNode();
|
nsINode* rootNode = aWordUtil.GetRootNode();
|
||||||
if (!nsContentUtils::ContentIsDescendantOf(beginNode, rootNode) ||
|
if (!beginNode->IsInComposedDoc() || !endNode->IsInComposedDoc() ||
|
||||||
!nsContentUtils::ContentIsDescendantOf(endNode, rootNode)) {
|
!nsContentUtils::ContentIsShadowIncludingDescendantOf(beginNode, rootNode) ||
|
||||||
|
!nsContentUtils::ContentIsShadowIncludingDescendantOf(endNode, rootNode)) {
|
||||||
// Just bail out and don't try to spell-check this
|
// Just bail out and don't try to spell-check this
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3679,10 +3679,11 @@ CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset,
|
||||||
{
|
{
|
||||||
nsINode* start = aRange->GetStartParent();
|
nsINode* start = aRange->GetStartParent();
|
||||||
NS_ENSURE_STATE(aCompareNode && start);
|
NS_ENSURE_STATE(aCompareNode && start);
|
||||||
// If the nodes that we're comparing are not in the same document,
|
// If the nodes that we're comparing are not in the same document or in the
|
||||||
// assume that aCompareNode will fall at the end of the ranges.
|
// same subtree, assume that aCompareNode will fall at the end of the ranges.
|
||||||
if (aCompareNode->GetComposedDoc() != start->GetComposedDoc() ||
|
if (aCompareNode->GetComposedDoc() != start->GetComposedDoc() ||
|
||||||
!start->GetComposedDoc()) {
|
!start->GetComposedDoc() ||
|
||||||
|
aCompareNode->SubtreeRoot() != start->SubtreeRoot()) {
|
||||||
*aCmp = 1;
|
*aCmp = 1;
|
||||||
} else {
|
} else {
|
||||||
*aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
|
*aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
|
||||||
|
|
@ -3697,10 +3698,11 @@ CompareToRangeEnd(nsINode* aCompareNode, int32_t aCompareOffset,
|
||||||
{
|
{
|
||||||
nsINode* end = aRange->GetEndParent();
|
nsINode* end = aRange->GetEndParent();
|
||||||
NS_ENSURE_STATE(aCompareNode && end);
|
NS_ENSURE_STATE(aCompareNode && end);
|
||||||
// If the nodes that we're comparing are not in the same document,
|
// If the nodes that we're comparing are not in the same document or in the
|
||||||
// assume that aCompareNode will fall at the end of the ranges.
|
// same subtree, assume that aCompareNode will fall at the end of the ranges.
|
||||||
if (aCompareNode->GetComposedDoc() != end->GetComposedDoc() ||
|
if (aCompareNode->GetComposedDoc() != end->GetComposedDoc() ||
|
||||||
!end->GetComposedDoc()) {
|
!end->GetComposedDoc() ||
|
||||||
|
aCompareNode->SubtreeRoot() != end->SubtreeRoot()) {
|
||||||
*aCmp = 1;
|
*aCmp = 1;
|
||||||
} else {
|
} else {
|
||||||
*aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
|
*aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue