2001-09-26 02:53:13 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2016-07-07 12:35:13 +03:00
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
#include "mozilla/HTMLEditor.h"
|
|
|
|
|
2016-07-07 08:01:12 +03:00
|
|
|
#include "HTMLEditUtils.h"
|
2016-07-07 07:44:32 +03:00
|
|
|
#include "TextEditUtils.h"
|
2001-04-06 03:48:01 +04:00
|
|
|
#include "TypeInState.h"
|
2012-07-13 10:33:42 +04:00
|
|
|
#include "mozilla/Assertions.h"
|
2019-01-11 04:49:00 +03:00
|
|
|
#include "mozilla/ContentIterator.h"
|
2018-01-12 13:01:04 +03:00
|
|
|
#include "mozilla/EditAction.h"
|
2016-07-08 08:03:31 +03:00
|
|
|
#include "mozilla/EditorUtils.h"
|
2016-07-09 05:34:41 +03:00
|
|
|
#include "mozilla/SelectionState.h"
|
2017-12-21 08:52:32 +03:00
|
|
|
#include "mozilla/TextEditRules.h"
|
2014-04-10 20:09:40 +04:00
|
|
|
#include "mozilla/dom/Selection.h"
|
2012-07-13 10:33:42 +04:00
|
|
|
#include "mozilla/dom/Element.h"
|
|
|
|
#include "mozilla/mozalloc.h"
|
|
|
|
#include "nsAString.h"
|
|
|
|
#include "nsAttrName.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "nsCaseTreatment.h"
|
|
|
|
#include "nsComponentManagerUtils.h"
|
|
|
|
#include "nsDebug.h"
|
|
|
|
#include "nsError.h"
|
|
|
|
#include "nsGkAtoms.h"
|
2017-10-03 01:05:19 +03:00
|
|
|
#include "nsAtom.h"
|
2001-01-28 23:13:07 +03:00
|
|
|
#include "nsIContent.h"
|
2014-02-28 03:04:46 +04:00
|
|
|
#include "nsNameSpaceManager.h"
|
2012-07-13 10:33:42 +04:00
|
|
|
#include "nsINode.h"
|
|
|
|
#include "nsISupportsImpl.h"
|
|
|
|
#include "nsLiteralString.h"
|
2014-11-02 15:04:13 +03:00
|
|
|
#include "nsRange.h"
|
2012-07-13 10:33:42 +04:00
|
|
|
#include "nsReadableUtils.h"
|
|
|
|
#include "nsString.h"
|
|
|
|
#include "nsStringFwd.h"
|
|
|
|
#include "nsTArray.h"
|
|
|
|
#include "nsUnicharUtils.h"
|
|
|
|
#include "nscore.h"
|
2001-01-28 23:13:07 +03:00
|
|
|
|
2012-07-13 10:33:42 +04:00
|
|
|
class nsISupports;
|
2012-02-01 14:54:22 +04:00
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
namespace mozilla {
|
|
|
|
|
|
|
|
using namespace dom;
|
2003-06-03 02:21:06 +04:00
|
|
|
|
2018-11-30 04:21:59 +03:00
|
|
|
static already_AddRefed<nsAtom> AtomizeAttribute(const nsAString& aAttribute) {
|
|
|
|
if (aAttribute.IsEmpty()) {
|
|
|
|
return nullptr; // Don't use nsGkAtoms::_empty for attribute.
|
|
|
|
}
|
|
|
|
return NS_Atomize(aAttribute);
|
|
|
|
}
|
|
|
|
|
Bug 1463327 - part 1: Change scope of some methods of EditorBase which won't be called by non-helper classes of editing to protected r=m_kato
EditorBase (and other editor classes) have 2 type of public methods. One is
true-public methods. I.e., they should be able to be called by anybody.
E.g., command handlers, event listeners, or JS via nsIEditor interface.
The other is semi-public methods. They are not called by the above examples
but called by other classes which are helper classes to handle edit actions.
E.g., TextEditRules, HTMLEditRules, HTMLEditUtils, CSSEditUtils and Transaction
classes.
When we will implement InputEvent.inputType, we need to create new stack
class and create its instance at every true-public methods to manage current
inputType (like TextEditRules::AutoSafeEditorData). Therefore, it should not
happen that new code starts to call semi-public methods without the new
stack class instance.
For preventing this issue, we should make EditorBase have only the true-public
methods as public. The other public methods should be protected and their
users should be friend classes. Then, we can protect such method from external
classes.
Note that this patch just moves the methods without any changes in EditorBase.h
(except removes GetName() since there is no body of this method and removes
IterDirection since it's unused).
MozReview-Commit-ID: HBseKLL6pxx
--HG--
extra : rebase_source : 2251ff659d831d01a6778d38f4e2714fcf2d6ef4
2018-05-22 10:08:43 +03:00
|
|
|
bool HTMLEditor::IsEmptyTextNode(nsINode& aNode) {
|
2012-10-05 04:37:45 +04:00
|
|
|
bool isEmptyTextNode = false;
|
Bug 1463327 - part 1: Change scope of some methods of EditorBase which won't be called by non-helper classes of editing to protected r=m_kato
EditorBase (and other editor classes) have 2 type of public methods. One is
true-public methods. I.e., they should be able to be called by anybody.
E.g., command handlers, event listeners, or JS via nsIEditor interface.
The other is semi-public methods. They are not called by the above examples
but called by other classes which are helper classes to handle edit actions.
E.g., TextEditRules, HTMLEditRules, HTMLEditUtils, CSSEditUtils and Transaction
classes.
When we will implement InputEvent.inputType, we need to create new stack
class and create its instance at every true-public methods to manage current
inputType (like TextEditRules::AutoSafeEditorData). Therefore, it should not
happen that new code starts to call semi-public methods without the new
stack class instance.
For preventing this issue, we should make EditorBase have only the true-public
methods as public. The other public methods should be protected and their
users should be friend classes. Then, we can protect such method from external
classes.
Note that this patch just moves the methods without any changes in EditorBase.h
(except removes GetName() since there is no body of this method and removes
IterDirection since it's unused).
MozReview-Commit-ID: HBseKLL6pxx
--HG--
extra : rebase_source : 2251ff659d831d01a6778d38f4e2714fcf2d6ef4
2018-05-22 10:08:43 +03:00
|
|
|
return EditorBase::IsTextNode(&aNode) &&
|
|
|
|
NS_SUCCEEDED(IsEmptyNode(&aNode, &isEmptyTextNode)) && isEmptyTextNode;
|
2012-10-05 04:37:45 +04:00
|
|
|
}
|
|
|
|
|
2018-10-24 07:17:42 +03:00
|
|
|
nsresult HTMLEditor::SetInlinePropertyAsAction(nsAtom& aProperty,
|
|
|
|
nsAtom* aAttribute,
|
|
|
|
const nsAString& aValue) {
|
2018-10-30 13:00:17 +03:00
|
|
|
AutoEditActionDataSetter editActionData(
|
|
|
|
*this,
|
|
|
|
HTMLEditUtils::GetEditActionForFormatText(aProperty, aAttribute, true));
|
|
|
|
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
2019-02-19 09:30:50 +03:00
|
|
|
switch (editActionData.GetEditAction()) {
|
|
|
|
case EditAction::eSetFontFamilyProperty:
|
|
|
|
MOZ_ASSERT(!aValue.IsVoid());
|
|
|
|
// XXX Should we trim unnecessary whitespaces?
|
|
|
|
editActionData.SetData(aValue);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2018-10-30 13:00:17 +03:00
|
|
|
|
2018-10-24 07:17:42 +03:00
|
|
|
AutoTransactionBatch treatAsOneTransaction(*this);
|
|
|
|
|
|
|
|
if (&aProperty == nsGkAtoms::sup) {
|
|
|
|
// Superscript and Subscript styles are mutually exclusive.
|
|
|
|
nsresult rv = RemoveInlinePropertyInternal(nsGkAtoms::sub, nullptr);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
} else if (&aProperty == nsGkAtoms::sub) {
|
|
|
|
// Superscript and Subscript styles are mutually exclusive.
|
|
|
|
nsresult rv = RemoveInlinePropertyInternal(nsGkAtoms::sup, nullptr);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nsresult rv = SetInlinePropertyInternal(aProperty, aAttribute, aValue);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2012-04-20 10:33:21 +04:00
|
|
|
NS_IMETHODIMP
|
2017-08-25 08:40:45 +03:00
|
|
|
HTMLEditor::SetInlineProperty(const nsAString& aProperty,
|
|
|
|
const nsAString& aAttribute,
|
|
|
|
const nsAString& aValue) {
|
2017-10-03 01:05:19 +03:00
|
|
|
RefPtr<nsAtom> property = NS_Atomize(aProperty);
|
2018-08-13 08:16:10 +03:00
|
|
|
if (NS_WARN_IF(!property)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
2018-11-30 04:21:59 +03:00
|
|
|
RefPtr<nsAtom> attribute = AtomizeAttribute(aAttribute);
|
2018-10-30 13:00:17 +03:00
|
|
|
AutoEditActionDataSetter editActionData(
|
|
|
|
*this,
|
|
|
|
HTMLEditUtils::GetEditActionForFormatText(*property, attribute, true));
|
|
|
|
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
2019-02-19 09:30:50 +03:00
|
|
|
switch (editActionData.GetEditAction()) {
|
|
|
|
case EditAction::eSetFontFamilyProperty:
|
|
|
|
MOZ_ASSERT(!aValue.IsVoid());
|
|
|
|
// XXX Should we trim unnecessary whitespaces?
|
|
|
|
editActionData.SetData(aValue);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2018-08-13 08:16:10 +03:00
|
|
|
return SetInlinePropertyInternal(*property, attribute, aValue);
|
2017-08-25 08:40:45 +03:00
|
|
|
}
|
|
|
|
|
2018-08-13 08:16:10 +03:00
|
|
|
nsresult HTMLEditor::SetInlinePropertyInternal(nsAtom& aProperty,
|
|
|
|
nsAtom* aAttribute,
|
|
|
|
const nsAString& aValue) {
|
2018-10-30 13:02:58 +03:00
|
|
|
MOZ_ASSERT(IsEditActionDataAvailable());
|
|
|
|
|
2018-08-13 08:16:10 +03:00
|
|
|
if (NS_WARN_IF(!mRules)) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
2017-12-21 08:52:32 +03:00
|
|
|
RefPtr<TextEditRules> rules(mRules);
|
2017-08-08 05:25:36 +03:00
|
|
|
CommitComposition();
|
2001-01-28 23:13:07 +03:00
|
|
|
|
2018-10-30 13:02:58 +03:00
|
|
|
if (SelectionRefPtr()->IsCollapsed()) {
|
2015-04-24 14:27:35 +03:00
|
|
|
// Manipulating text attributes on a collapsed selection only sets state
|
2012-04-20 10:33:21 +04:00
|
|
|
// for the next text insertion
|
2018-08-13 08:16:10 +03:00
|
|
|
mTypeInState->SetProp(&aProperty, aAttribute, aValue);
|
2012-05-05 13:00:06 +04:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
|
2018-10-24 12:42:06 +03:00
|
|
|
AutoPlaceholderBatch treatAsOneTransaction(*this);
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 14:12:34 +03:00
|
|
|
AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
|
2018-05-28 17:13:41 +03:00
|
|
|
*this, EditSubAction::eInsertElement, nsIEditor::eNext);
|
2018-10-30 13:01:38 +03:00
|
|
|
AutoSelectionRestorer restoreSelectionLater(*this);
|
2018-08-01 15:30:14 +03:00
|
|
|
AutoTransactionsConserveSelection dontChangeMySelection(*this);
|
2012-04-20 10:33:21 +04:00
|
|
|
|
2011-09-29 10:19:26 +04:00
|
|
|
bool cancel, handled;
|
2018-05-28 16:05:10 +03:00
|
|
|
EditSubActionInfo subActionInfo(EditSubAction::eSetTextProperty);
|
2013-03-23 07:47:57 +04:00
|
|
|
// Protect the edit rules object from dying
|
2018-10-30 13:02:58 +03:00
|
|
|
nsresult rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 14:12:34 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
if (!cancel && !handled) {
|
2015-04-24 14:27:35 +03:00
|
|
|
// Loop through the ranges in the selection
|
2018-10-30 13:02:58 +03:00
|
|
|
AutoRangeArray arrayOfRanges(SelectionRefPtr());
|
2017-06-23 10:46:54 +03:00
|
|
|
for (auto& range : arrayOfRanges.mRanges) {
|
2015-04-24 14:27:35 +03:00
|
|
|
// Adjust range to include any ancestors whose children are entirely
|
2012-04-20 10:33:21 +04:00
|
|
|
// selected
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = PromoteInlineRange(*range);
|
2018-08-13 08:16:10 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
|
2015-04-24 14:27:35 +03:00
|
|
|
// Check for easy case: both range endpoints in same text node
|
2017-07-11 14:53:04 +03:00
|
|
|
nsCOMPtr<nsINode> startNode = range->GetStartContainer();
|
2017-07-11 15:11:37 +03:00
|
|
|
nsCOMPtr<nsINode> endNode = range->GetEndContainer();
|
2015-04-19 15:28:50 +03:00
|
|
|
if (startNode && startNode == endNode && startNode->GetAsText()) {
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = SetInlinePropertyOnTextNode(
|
|
|
|
*startNode->GetAsText(), range->StartOffset(), range->EndOffset(),
|
2018-08-13 08:16:10 +03:00
|
|
|
aProperty, aAttribute, aValue);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
continue;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2012-04-20 10:33:21 +04:00
|
|
|
// Not the easy case. Range not contained in single text node. There
|
|
|
|
// are up to three phases here. There are all the nodes reported by the
|
|
|
|
// subtree iterator to be processed. And there are potentially a
|
|
|
|
// starting textnode and an ending textnode which are only partially
|
|
|
|
// contained by the range.
|
2001-01-28 23:13:07 +03:00
|
|
|
|
2012-04-20 10:33:21 +04:00
|
|
|
// Let's handle the nodes reported by the iterator. These nodes are
|
|
|
|
// entirely contained in the selection range. We build up a list of them
|
|
|
|
// (since doing operations on the document during iteration would perturb
|
|
|
|
// the iterator).
|
2004-01-24 03:46:17 +03:00
|
|
|
|
2015-04-24 14:27:35 +03:00
|
|
|
nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
|
2012-04-20 10:33:21 +04:00
|
|
|
|
2015-04-24 14:27:35 +03:00
|
|
|
// Iterate range and build up array
|
2019-01-11 04:52:26 +03:00
|
|
|
ContentSubtreeIterator subtreeIter;
|
|
|
|
rv = subtreeIter.Init(range);
|
2012-04-20 10:33:21 +04:00
|
|
|
// Init returns an error if there are no nodes in range. This can easily
|
|
|
|
// happen with the subtree iterator if the selection doesn't contain any
|
|
|
|
// *whole* nodes.
|
2016-10-19 12:09:33 +03:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
2019-01-11 04:52:26 +03:00
|
|
|
for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
|
|
|
|
OwningNonNull<nsINode> node = *subtreeIter.GetCurrentNode();
|
2004-01-24 03:46:17 +03:00
|
|
|
|
2015-04-24 14:27:35 +03:00
|
|
|
if (node->IsContent() && IsEditable(node)) {
|
|
|
|
arrayOfNodes.AppendElement(*node->AsContent());
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-24 14:27:35 +03:00
|
|
|
// First check the start parent of the range to see if it needs to be
|
|
|
|
// separately handled (it does if it's a text node, due to how the
|
2012-04-20 10:33:21 +04:00
|
|
|
// subtree iterator works - it will not have reported it).
|
2015-04-19 15:28:50 +03:00
|
|
|
if (startNode && startNode->GetAsText() && IsEditable(startNode)) {
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = SetInlinePropertyOnTextNode(
|
|
|
|
*startNode->GetAsText(), range->StartOffset(), startNode->Length(),
|
2017-12-26 06:25:45 +03:00
|
|
|
aProperty, aAttribute, aValue);
|
2018-08-13 08:16:10 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
}
|
|
|
|
|
2015-04-24 14:27:35 +03:00
|
|
|
// Then loop through the list, set the property on each node
|
|
|
|
for (auto& node : arrayOfNodes) {
|
2018-08-13 08:16:10 +03:00
|
|
|
rv = SetInlinePropertyOnNode(*node, aProperty, aAttribute, aValue);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
}
|
|
|
|
|
2015-04-24 14:27:35 +03:00
|
|
|
// Last check the end parent of the range to see if it needs to be
|
|
|
|
// separately handled (it does if it's a text node, due to how the
|
2012-04-20 10:33:21 +04:00
|
|
|
// subtree iterator works - it will not have reported it).
|
2015-04-19 15:28:50 +03:00
|
|
|
if (endNode && endNode->GetAsText() && IsEditable(endNode)) {
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = SetInlinePropertyOnTextNode(*endNode->GetAsText(), 0,
|
2018-08-13 08:16:10 +03:00
|
|
|
range->EndOffset(), aProperty,
|
2017-12-26 06:25:45 +03:00
|
|
|
aAttribute, aValue);
|
2018-08-13 08:16:10 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
}
|
2018-08-13 08:16:10 +03:00
|
|
|
if (cancel) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-10-30 13:02:58 +03:00
|
|
|
rv = rules->DidDoAction(subActionInfo, rv);
|
2018-08-13 08:16:10 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2012-05-29 11:12:51 +04:00
|
|
|
// Helper function for SetInlinePropertyOn*: is aNode a simple old <b>, <font>,
|
|
|
|
// <span style="">, etc. that we can reuse instead of creating a new one?
|
2016-07-09 05:42:33 +03:00
|
|
|
bool HTMLEditor::IsSimpleModifiableNode(nsIContent* aContent, nsAtom* aProperty,
|
2017-12-26 06:25:45 +03:00
|
|
|
nsAtom* aAttribute,
|
2016-07-09 05:42:33 +03:00
|
|
|
const nsAString* aValue) {
|
2012-05-29 11:12:51 +04:00
|
|
|
// aContent can be null, in which case we'll return false in a few lines
|
|
|
|
MOZ_ASSERT(aProperty);
|
|
|
|
MOZ_ASSERT_IF(aAttribute, aValue);
|
|
|
|
|
|
|
|
nsCOMPtr<dom::Element> element = do_QueryInterface(aContent);
|
|
|
|
if (!element) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// First check for <b>, <i>, etc.
|
2015-03-03 14:08:59 +03:00
|
|
|
if (element->IsHTMLElement(aProperty) && !element->GetAttrCount() &&
|
2017-12-26 06:25:45 +03:00
|
|
|
!aAttribute) {
|
2012-05-29 11:12:51 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Special cases for various equivalencies: <strong>, <em>, <s>
|
|
|
|
if (!element->GetAttrCount() &&
|
2015-03-03 14:08:59 +03:00
|
|
|
((aProperty == nsGkAtoms::b &&
|
|
|
|
element->IsHTMLElement(nsGkAtoms::strong)) ||
|
|
|
|
(aProperty == nsGkAtoms::i && element->IsHTMLElement(nsGkAtoms::em)) ||
|
|
|
|
(aProperty == nsGkAtoms::strike &&
|
|
|
|
element->IsHTMLElement(nsGkAtoms::s)))) {
|
2012-05-29 11:12:51 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now look for things like <font>
|
2017-12-26 06:25:45 +03:00
|
|
|
if (aAttribute) {
|
2012-12-14 02:08:44 +04:00
|
|
|
nsString attrValue;
|
2015-03-03 14:08:59 +03:00
|
|
|
if (element->IsHTMLElement(aProperty) &&
|
2017-12-26 06:25:45 +03:00
|
|
|
IsOnlyAttribute(element, aAttribute) &&
|
|
|
|
element->GetAttr(kNameSpaceID_None, aAttribute, attrValue) &&
|
2012-12-14 02:08:44 +04:00
|
|
|
attrValue.Equals(*aValue, nsCaseInsensitiveStringComparator())) {
|
2012-05-29 11:12:51 +04:00
|
|
|
// This is not quite correct, because it excludes cases like
|
|
|
|
// <font face=000> being the same as <font face=#000000>.
|
|
|
|
// Property-specific handling is needed (bug 760211).
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No luck so far. Now we check for a <span> with a single style=""
|
|
|
|
// attribute that sets only the style we're looking for, if this type of
|
|
|
|
// style supports it
|
2018-02-02 12:42:25 +03:00
|
|
|
if (!CSSEditUtils::IsCSSEditableProperty(element, aProperty, aAttribute) ||
|
2015-03-03 14:08:59 +03:00
|
|
|
!element->IsHTMLElement(nsGkAtoms::span) ||
|
|
|
|
element->GetAttrCount() != 1 ||
|
2012-05-29 11:12:51 +04:00
|
|
|
!element->HasAttr(kNameSpaceID_None, nsGkAtoms::style)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some CSS styles are not so simple. For instance, underline is
|
|
|
|
// "text-decoration: underline", which decomposes into four different text-*
|
|
|
|
// properties. So for now, we just create a span, add the desired style, and
|
|
|
|
// see if it matches.
|
2014-08-13 15:53:33 +04:00
|
|
|
nsCOMPtr<Element> newSpan = CreateHTMLContent(nsGkAtoms::span);
|
|
|
|
NS_ASSERTION(newSpan, "CreateHTMLContent failed");
|
|
|
|
NS_ENSURE_TRUE(newSpan, false);
|
2016-07-07 12:35:13 +03:00
|
|
|
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(newSpan, aProperty, aAttribute,
|
2012-05-29 11:12:51 +04:00
|
|
|
aValue,
|
|
|
|
/*suppress transaction*/ true);
|
|
|
|
|
2018-02-02 12:42:25 +03:00
|
|
|
return CSSEditUtils::ElementsSameStyle(newSpan, element);
|
2012-05-29 11:12:51 +04:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::SetInlinePropertyOnTextNode(
|
|
|
|
Text& aText, int32_t aStartOffset, int32_t aEndOffset, nsAtom& aProperty,
|
|
|
|
nsAtom* aAttribute, const nsAString& aValue) {
|
2015-04-19 15:28:50 +03:00
|
|
|
if (!aText.GetParentNode() ||
|
|
|
|
!CanContainTag(*aText.GetParentNode(), aProperty)) {
|
2012-05-01 10:34:52 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
|
|
|
|
// Don't need to do anything if no characters actually selected
|
|
|
|
if (aStartOffset == aEndOffset) {
|
|
|
|
return NS_OK;
|
2003-09-03 17:58:00 +04:00
|
|
|
}
|
|
|
|
|
2015-04-19 15:28:50 +03:00
|
|
|
// Don't need to do anything if property already set on node
|
2018-02-02 12:42:25 +03:00
|
|
|
if (CSSEditUtils::IsCSSEditableProperty(&aText, &aProperty, aAttribute)) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// The HTML styles defined by aProperty/aAttribute have a CSS equivalence
|
|
|
|
// for node; let's check if it carries those CSS styles
|
2018-02-02 12:42:25 +03:00
|
|
|
if (CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
|
2016-07-07 12:35:13 +03:00
|
|
|
&aText, &aProperty, aAttribute, aValue, CSSEditUtils::eComputed)) {
|
2015-04-19 15:28:50 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
} else if (IsTextPropertySetByContent(&aText, &aProperty, aAttribute,
|
|
|
|
&aValue)) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2012-05-05 13:00:06 +04:00
|
|
|
|
2017-11-12 05:30:30 +03:00
|
|
|
// Make the range an independent node.
|
|
|
|
nsCOMPtr<nsIContent> textNodeForTheRange = &aText;
|
|
|
|
|
|
|
|
// Split at the end of the range.
|
|
|
|
EditorRawDOMPoint atEnd(textNodeForTheRange, aEndOffset);
|
|
|
|
if (!atEnd.IsEndOfContainer()) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// We need to split off back of text node
|
2017-11-12 05:30:30 +03:00
|
|
|
ErrorResult error;
|
2018-04-09 20:16:49 +03:00
|
|
|
textNodeForTheRange = SplitNodeWithTransaction(atEnd, error);
|
2017-11-12 05:30:30 +03:00
|
|
|
if (NS_WARN_IF(error.Failed())) {
|
|
|
|
return error.StealNSResult();
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-05-05 13:00:06 +04:00
|
|
|
|
2017-11-12 05:30:30 +03:00
|
|
|
// Split at the start of the range.
|
|
|
|
EditorRawDOMPoint atStart(textNodeForTheRange, aStartOffset);
|
|
|
|
if (!atStart.IsStartOfContainer()) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// We need to split off front of text node
|
2017-11-12 05:30:30 +03:00
|
|
|
ErrorResult error;
|
2018-04-09 20:16:49 +03:00
|
|
|
nsCOMPtr<nsIContent> newLeftNode = SplitNodeWithTransaction(atStart, error);
|
2017-11-12 05:30:30 +03:00
|
|
|
if (NS_WARN_IF(error.Failed())) {
|
|
|
|
return error.StealNSResult();
|
|
|
|
}
|
|
|
|
Unused << newLeftNode;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-05-05 13:00:06 +04:00
|
|
|
|
|
|
|
if (aAttribute) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// Look for siblings that are correct type of node
|
2017-11-12 05:30:30 +03:00
|
|
|
nsIContent* sibling = GetPriorHTMLSibling(textNodeForTheRange);
|
2015-04-19 15:28:50 +03:00
|
|
|
if (IsSimpleModifiableNode(sibling, &aProperty, aAttribute, &aValue)) {
|
|
|
|
// Previous sib is already right kind of inline node; slide this over
|
2018-04-12 17:58:52 +03:00
|
|
|
return MoveNodeToEndWithTransaction(*textNodeForTheRange, *sibling);
|
2012-05-05 13:00:06 +04:00
|
|
|
}
|
2017-11-12 05:30:30 +03:00
|
|
|
sibling = GetNextHTMLSibling(textNodeForTheRange);
|
2015-04-19 15:28:50 +03:00
|
|
|
if (IsSimpleModifiableNode(sibling, &aProperty, aAttribute, &aValue)) {
|
|
|
|
// Following sib is already right kind of inline node; slide this over
|
2018-04-12 17:58:52 +03:00
|
|
|
return MoveNodeWithTransaction(*textNodeForTheRange,
|
|
|
|
EditorRawDOMPoint(sibling, 0));
|
2012-05-05 13:00:06 +04:00
|
|
|
}
|
2001-10-22 10:22:52 +04:00
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
|
|
|
|
// Reparent the node inside inline node with appropriate {attribute,value}
|
2017-11-12 05:30:30 +03:00
|
|
|
return SetInlinePropertyOnNode(*textNodeForTheRange, aProperty, aAttribute,
|
|
|
|
aValue);
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent& aNode,
|
2017-10-03 01:05:19 +03:00
|
|
|
nsAtom& aProperty,
|
2017-12-26 06:25:45 +03:00
|
|
|
nsAtom* aAttribute,
|
2016-07-09 05:42:33 +03:00
|
|
|
const nsAString& aValue) {
|
2012-04-17 21:50:19 +04:00
|
|
|
// If this is an element that can't be contained in a span, we have to
|
|
|
|
// recurse to its children.
|
2015-04-24 14:27:35 +03:00
|
|
|
if (!TagCanContain(*nsGkAtoms::span, aNode)) {
|
|
|
|
if (aNode.HasChildren()) {
|
|
|
|
nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
|
2012-05-05 13:00:06 +04:00
|
|
|
|
|
|
|
// Populate the list.
|
2015-04-24 14:27:35 +03:00
|
|
|
for (nsCOMPtr<nsIContent> child = aNode.GetFirstChild(); child;
|
2012-05-05 13:00:06 +04:00
|
|
|
child = child->GetNextSibling()) {
|
Bug 1463327 - part 1: Change scope of some methods of EditorBase which won't be called by non-helper classes of editing to protected r=m_kato
EditorBase (and other editor classes) have 2 type of public methods. One is
true-public methods. I.e., they should be able to be called by anybody.
E.g., command handlers, event listeners, or JS via nsIEditor interface.
The other is semi-public methods. They are not called by the above examples
but called by other classes which are helper classes to handle edit actions.
E.g., TextEditRules, HTMLEditRules, HTMLEditUtils, CSSEditUtils and Transaction
classes.
When we will implement InputEvent.inputType, we need to create new stack
class and create its instance at every true-public methods to manage current
inputType (like TextEditRules::AutoSafeEditorData). Therefore, it should not
happen that new code starts to call semi-public methods without the new
stack class instance.
For preventing this issue, we should make EditorBase have only the true-public
methods as public. The other public methods should be protected and their
users should be friend classes. Then, we can protect such method from external
classes.
Note that this patch just moves the methods without any changes in EditorBase.h
(except removes GetName() since there is no body of this method and removes
IterDirection since it's unused).
MozReview-Commit-ID: HBseKLL6pxx
--HG--
extra : rebase_source : 2251ff659d831d01a6778d38f4e2714fcf2d6ef4
2018-05-22 10:08:43 +03:00
|
|
|
if (IsEditable(child) && !IsEmptyTextNode(*child)) {
|
2015-04-24 14:27:35 +03:00
|
|
|
arrayOfNodes.AppendElement(*child);
|
2012-04-17 21:50:19 +04:00
|
|
|
}
|
2012-05-05 13:00:06 +04:00
|
|
|
}
|
2012-04-17 21:50:19 +04:00
|
|
|
|
2012-05-05 13:00:06 +04:00
|
|
|
// Then loop through the list, set the property on each node.
|
2015-04-24 14:27:35 +03:00
|
|
|
for (auto& node : arrayOfNodes) {
|
2015-04-24 14:27:35 +03:00
|
|
|
nsresult rv =
|
|
|
|
SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
|
2012-05-05 13:00:06 +04:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2012-04-17 21:50:19 +04:00
|
|
|
}
|
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
return NS_OK;
|
2012-04-17 21:50:19 +04:00
|
|
|
}
|
|
|
|
|
2012-05-29 11:12:51 +04:00
|
|
|
// First check if there's an adjacent sibling we can put our node into.
|
2015-04-24 14:27:35 +03:00
|
|
|
nsCOMPtr<nsIContent> previousSibling = GetPriorHTMLSibling(&aNode);
|
|
|
|
nsCOMPtr<nsIContent> nextSibling = GetNextHTMLSibling(&aNode);
|
|
|
|
if (IsSimpleModifiableNode(previousSibling, &aProperty, aAttribute,
|
|
|
|
&aValue)) {
|
2018-04-12 17:58:52 +03:00
|
|
|
nsresult rv = MoveNodeToEndWithTransaction(aNode, *previousSibling);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-04-24 14:27:35 +03:00
|
|
|
if (IsSimpleModifiableNode(nextSibling, &aProperty, aAttribute, &aValue)) {
|
2018-04-09 21:46:44 +03:00
|
|
|
rv = JoinNodesWithTransaction(*previousSibling, *nextSibling);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-05-29 11:12:51 +04:00
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-04-24 14:27:35 +03:00
|
|
|
if (IsSimpleModifiableNode(nextSibling, &aProperty, aAttribute, &aValue)) {
|
2018-04-12 17:58:52 +03:00
|
|
|
nsresult rv =
|
|
|
|
MoveNodeWithTransaction(aNode, EditorRawDOMPoint(nextSibling, 0));
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-05-29 11:12:51 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-04-24 14:27:35 +03:00
|
|
|
// Don't need to do anything if property already set on node
|
2018-02-02 12:42:25 +03:00
|
|
|
if (CSSEditUtils::IsCSSEditableProperty(&aNode, &aProperty, aAttribute)) {
|
|
|
|
if (CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
|
2016-07-07 12:35:13 +03:00
|
|
|
&aNode, &aProperty, aAttribute, aValue, CSSEditUtils::eComputed)) {
|
2012-05-23 11:49:29 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-04-24 14:27:35 +03:00
|
|
|
} else if (IsTextPropertySetByContent(&aNode, &aProperty, aAttribute,
|
|
|
|
&aValue)) {
|
2012-05-23 11:49:29 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-02-02 12:42:25 +03:00
|
|
|
bool useCSS = (IsCSSEnabled() && CSSEditUtils::IsCSSEditableProperty(
|
|
|
|
&aNode, &aProperty, aAttribute)) ||
|
2012-04-24 14:57:49 +04:00
|
|
|
// bgcolor is always done using CSS
|
2017-12-26 06:25:45 +03:00
|
|
|
aAttribute == nsGkAtoms::bgcolor;
|
2012-04-17 21:50:19 +04:00
|
|
|
|
2012-04-17 14:18:08 +04:00
|
|
|
if (useCSS) {
|
2018-04-13 12:17:04 +03:00
|
|
|
RefPtr<dom::Element> tmp;
|
2012-04-20 10:33:21 +04:00
|
|
|
// We only add style="" to <span>s with no attributes (bug 746515). If we
|
|
|
|
// don't have one, we need to make one.
|
2015-04-24 14:27:35 +03:00
|
|
|
if (aNode.IsHTMLElement(nsGkAtoms::span) &&
|
|
|
|
!aNode.AsElement()->GetAttrCount()) {
|
|
|
|
tmp = aNode.AsElement();
|
2012-06-06 11:36:17 +04:00
|
|
|
} else {
|
2018-04-13 12:17:04 +03:00
|
|
|
tmp = InsertContainerWithTransaction(aNode, *nsGkAtoms::span);
|
|
|
|
if (NS_WARN_IF(!tmp)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2012-04-17 14:18:08 +04:00
|
|
|
}
|
2012-06-06 11:36:17 +04:00
|
|
|
|
2012-04-20 10:33:21 +04:00
|
|
|
// Add the CSS styles corresponding to the HTML style request
|
2016-12-12 13:03:36 +03:00
|
|
|
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(tmp, &aProperty, aAttribute,
|
|
|
|
&aValue, false);
|
2012-04-20 10:33:21 +04:00
|
|
|
return NS_OK;
|
2002-01-09 16:51:37 +03:00
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
|
2001-01-28 23:13:07 +03:00
|
|
|
// is it already the right kind of node, but with wrong attribute?
|
2015-04-24 14:27:35 +03:00
|
|
|
if (aNode.IsHTMLElement(&aProperty)) {
|
2018-04-12 10:58:33 +03:00
|
|
|
if (NS_WARN_IF(!aAttribute)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
// Just set the attribute on it.
|
2018-04-12 10:58:33 +03:00
|
|
|
return SetAttributeWithTransaction(*aNode.AsElement(), *aAttribute, aValue);
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-04-20 10:33:21 +04:00
|
|
|
|
2012-04-20 10:33:21 +04:00
|
|
|
// ok, chuck it in its very own container
|
2018-04-13 12:17:04 +03:00
|
|
|
RefPtr<Element> tmp = InsertContainerWithTransaction(
|
|
|
|
aNode, aProperty, aAttribute ? *aAttribute : *nsGkAtoms::_empty, aValue);
|
|
|
|
if (NS_WARN_IF(!tmp)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2014-08-20 16:25:16 +04:00
|
|
|
return NS_OK;
|
2012-04-20 10:33:21 +04:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::SetInlinePropertyOnNode(nsIContent& aNode,
|
2017-10-03 01:05:19 +03:00
|
|
|
nsAtom& aProperty,
|
2017-12-26 06:25:45 +03:00
|
|
|
nsAtom* aAttribute,
|
2016-07-09 05:42:33 +03:00
|
|
|
const nsAString& aValue) {
|
2015-04-24 14:27:35 +03:00
|
|
|
nsCOMPtr<nsIContent> previousSibling = aNode.GetPreviousSibling(),
|
|
|
|
nextSibling = aNode.GetNextSibling();
|
|
|
|
NS_ENSURE_STATE(aNode.GetParentNode());
|
|
|
|
OwningNonNull<nsINode> parent = *aNode.GetParentNode();
|
2012-04-20 10:33:21 +04:00
|
|
|
|
2016-10-19 12:09:33 +03:00
|
|
|
nsresult rv = RemoveStyleInside(aNode, &aProperty, aAttribute);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2012-04-20 10:33:21 +04:00
|
|
|
|
2015-04-24 14:27:35 +03:00
|
|
|
if (aNode.GetParentNode()) {
|
2012-04-20 10:33:21 +04:00
|
|
|
// The node is still where it was
|
2015-04-24 14:27:35 +03:00
|
|
|
return SetInlinePropertyOnNodeImpl(aNode, aProperty, aAttribute, aValue);
|
2012-04-20 10:33:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// It's vanished. Use the old siblings for reference to construct a
|
|
|
|
// list. But first, verify that the previous/next siblings are still
|
|
|
|
// where we expect them; otherwise we have to give up.
|
2012-10-09 16:31:24 +04:00
|
|
|
if ((previousSibling && previousSibling->GetParentNode() != parent) ||
|
|
|
|
(nextSibling && nextSibling->GetParentNode() != parent)) {
|
2012-04-20 10:33:21 +04:00
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
2015-04-24 14:27:35 +03:00
|
|
|
nsTArray<OwningNonNull<nsIContent>> nodesToSet;
|
2012-04-20 10:33:21 +04:00
|
|
|
nsCOMPtr<nsIContent> cur = previousSibling ? previousSibling->GetNextSibling()
|
|
|
|
: parent->GetFirstChild();
|
2015-04-24 14:27:35 +03:00
|
|
|
for (; cur && cur != nextSibling; cur = cur->GetNextSibling()) {
|
2012-04-20 10:33:21 +04:00
|
|
|
if (IsEditable(cur)) {
|
2015-04-24 14:27:35 +03:00
|
|
|
nodesToSet.AppendElement(*cur);
|
2012-04-20 10:33:21 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-24 14:27:35 +03:00
|
|
|
for (auto& node : nodesToSet) {
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = SetInlinePropertyOnNodeImpl(node, aProperty, aAttribute, aValue);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2012-04-20 10:33:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2018-11-26 09:31:56 +03:00
|
|
|
nsresult HTMLEditor::SplitStyleAboveRange(nsRange* aRange, nsAtom* aProperty,
|
2017-12-26 06:25:45 +03:00
|
|
|
nsAtom* aAttribute) {
|
2018-11-26 09:31:56 +03:00
|
|
|
MOZ_ASSERT(IsEditActionDataAvailable());
|
|
|
|
|
|
|
|
if (NS_WARN_IF(!aRange)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
2011-12-22 22:16:56 +04:00
|
|
|
|
2018-11-26 09:31:56 +03:00
|
|
|
nsCOMPtr<nsINode> startNode = aRange->GetStartContainer();
|
|
|
|
int32_t startOffset = aRange->StartOffset();
|
|
|
|
nsCOMPtr<nsINode> endNode = aRange->GetEndContainer();
|
|
|
|
int32_t endOffset = aRange->EndOffset();
|
2011-12-22 22:16:56 +04:00
|
|
|
|
2016-05-01 16:11:28 +03:00
|
|
|
nsCOMPtr<nsINode> origStartNode = startNode;
|
2011-12-22 22:16:56 +04:00
|
|
|
|
2001-01-28 23:13:07 +03:00
|
|
|
// split any matching style nodes above the start of range
|
|
|
|
{
|
2018-11-26 09:31:56 +03:00
|
|
|
AutoTrackDOMPoint tracker(RangeUpdaterRef(), address_of(endNode),
|
|
|
|
&endOffset);
|
2016-10-19 12:09:33 +03:00
|
|
|
nsresult rv = SplitStyleAbovePoint(address_of(startNode), &startOffset,
|
|
|
|
aProperty, aAttribute);
|
2018-11-26 09:31:56 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2002-06-12 18:04:47 +04:00
|
|
|
|
2001-01-28 23:13:07 +03:00
|
|
|
// second verse, same as the first...
|
2016-10-19 12:09:33 +03:00
|
|
|
nsresult rv = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty,
|
|
|
|
aAttribute);
|
2018-11-26 09:31:56 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2011-12-22 22:16:56 +04:00
|
|
|
|
2001-01-28 23:13:07 +03:00
|
|
|
// reset the range
|
2018-11-26 09:31:56 +03:00
|
|
|
rv = aRange->SetStartAndEnd(startNode, startOffset, endNode, endOffset);
|
2017-05-30 07:18:25 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::SplitStyleAbovePoint(
|
|
|
|
nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
|
|
|
|
// null here means we split all properties
|
|
|
|
nsAtom* aProperty, nsAtom* aAttribute, nsIContent** aOutLeftNode,
|
|
|
|
nsIContent** aOutRightNode) {
|
2016-04-23 14:02:28 +03:00
|
|
|
NS_ENSURE_TRUE(aNode && *aNode && aOffset, NS_ERROR_NULL_POINTER);
|
2016-05-01 16:11:28 +03:00
|
|
|
NS_ENSURE_TRUE((*aNode)->IsContent(), NS_OK);
|
|
|
|
|
2017-11-16 06:09:57 +03:00
|
|
|
if (aOutLeftNode) {
|
|
|
|
*aOutLeftNode = nullptr;
|
|
|
|
}
|
|
|
|
if (aOutRightNode) {
|
|
|
|
*aOutRightNode = nullptr;
|
|
|
|
}
|
|
|
|
|
2016-05-01 16:11:28 +03:00
|
|
|
// Split any matching style nodes above the node/offset
|
2018-08-09 11:22:50 +03:00
|
|
|
nsCOMPtr<nsIContent> node = (*aNode)->AsContent();
|
2002-01-09 16:51:37 +03:00
|
|
|
|
2012-02-01 14:54:22 +04:00
|
|
|
bool useCSS = IsCSSEnabled();
|
2002-01-09 16:51:37 +03:00
|
|
|
|
2011-09-29 10:19:26 +04:00
|
|
|
bool isSet;
|
2016-05-01 16:11:28 +03:00
|
|
|
while (!IsBlockNode(node) && node->GetParent() &&
|
|
|
|
IsEditable(node->GetParent())) {
|
2011-10-17 18:59:28 +04:00
|
|
|
isSet = false;
|
2018-02-02 12:42:25 +03:00
|
|
|
if (useCSS &&
|
|
|
|
CSSEditUtils::IsCSSEditableProperty(node, aProperty, aAttribute)) {
|
2016-05-01 16:11:28 +03:00
|
|
|
// The HTML style defined by aProperty/aAttribute has a CSS equivalence
|
|
|
|
// in this implementation for the node; let's check if it carries those
|
|
|
|
// CSS styles
|
2002-01-09 16:51:37 +03:00
|
|
|
nsAutoString firstValue;
|
2018-02-02 12:42:25 +03:00
|
|
|
isSet = CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
|
2016-12-12 13:03:36 +03:00
|
|
|
node, aProperty, aAttribute, firstValue, CSSEditUtils::eSpecified);
|
2002-01-09 16:51:37 +03:00
|
|
|
}
|
2014-10-22 16:45:30 +04:00
|
|
|
if ( // node is the correct inline prop
|
2015-03-03 14:08:59 +03:00
|
|
|
(aProperty && node->IsHTMLElement(aProperty)) ||
|
2014-10-22 16:45:30 +04:00
|
|
|
// node is href - test if really <a href=...
|
2016-07-07 08:01:12 +03:00
|
|
|
(aProperty == nsGkAtoms::href && HTMLEditUtils::IsLink(node)) ||
|
2014-10-22 16:45:30 +04:00
|
|
|
// or node is any prop, and we asked to split them all
|
2018-08-09 11:22:50 +03:00
|
|
|
(!aProperty && NodeIsProperty(*node)) ||
|
2014-10-22 16:45:30 +04:00
|
|
|
// or the style is specified in the style attribute
|
|
|
|
isSet) {
|
2016-05-01 16:11:28 +03:00
|
|
|
// Found a style node we need to split
|
2017-11-16 06:09:57 +03:00
|
|
|
SplitNodeResult splitNodeResult = SplitNodeDeepWithTransaction(
|
2018-04-09 20:16:49 +03:00
|
|
|
*node, EditorRawDOMPoint(*aNode, *aOffset),
|
|
|
|
SplitAtEdges::eAllowToCreateEmptyContainer);
|
2017-11-16 06:09:57 +03:00
|
|
|
NS_WARNING_ASSERTION(splitNodeResult.Succeeded(),
|
|
|
|
"Failed to split the node");
|
|
|
|
|
|
|
|
EditorRawDOMPoint atRightNode(splitNodeResult.SplitPoint());
|
2017-12-07 12:45:52 +03:00
|
|
|
*aNode = atRightNode.GetContainer();
|
2017-11-16 06:09:57 +03:00
|
|
|
*aOffset = atRightNode.Offset();
|
|
|
|
if (aOutLeftNode) {
|
|
|
|
NS_IF_ADDREF(*aOutLeftNode = splitNodeResult.GetPreviousNode());
|
|
|
|
}
|
|
|
|
if (aOutRightNode) {
|
|
|
|
NS_IF_ADDREF(*aOutRightNode = splitNodeResult.GetNextNode());
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2014-10-22 16:45:30 +04:00
|
|
|
node = node->GetParent();
|
2018-08-09 11:22:50 +03:00
|
|
|
if (NS_WARN_IF(!node)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2016-05-01 16:11:28 +03:00
|
|
|
|
2001-01-28 23:13:07 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::ClearStyle(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
|
2017-12-26 06:25:45 +03:00
|
|
|
nsAtom* aProperty, nsAtom* aAttribute) {
|
2018-11-26 09:31:56 +03:00
|
|
|
MOZ_ASSERT(IsEditActionDataAvailable());
|
|
|
|
|
2016-05-01 16:11:28 +03:00
|
|
|
nsCOMPtr<nsIContent> leftNode, rightNode;
|
2016-10-19 12:09:33 +03:00
|
|
|
nsresult rv =
|
|
|
|
SplitStyleAbovePoint(aNode, aOffset, aProperty, aAttribute,
|
|
|
|
getter_AddRefs(leftNode), getter_AddRefs(rightNode));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2016-05-01 16:11:28 +03:00
|
|
|
|
2012-05-10 16:00:14 +04:00
|
|
|
if (leftNode) {
|
|
|
|
bool bIsEmptyNode;
|
|
|
|
IsEmptyNode(leftNode, &bIsEmptyNode, false, true);
|
|
|
|
if (bIsEmptyNode) {
|
|
|
|
// delete leftNode if it became empty
|
2018-04-10 10:23:54 +03:00
|
|
|
rv = DeleteNodeWithTransaction(*leftNode);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-05-10 16:00:14 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rightNode) {
|
2016-05-01 16:11:28 +03:00
|
|
|
nsCOMPtr<nsINode> secondSplitParent = GetLeftmostChild(rightNode);
|
2016-07-08 08:15:21 +03:00
|
|
|
// don't try to split non-containers (br's, images, hr's, etc.)
|
2012-05-10 16:00:14 +04:00
|
|
|
if (!secondSplitParent) {
|
|
|
|
secondSplitParent = rightNode;
|
|
|
|
}
|
2014-08-20 16:25:16 +04:00
|
|
|
nsCOMPtr<Element> savedBR;
|
2012-05-10 16:00:14 +04:00
|
|
|
if (!IsContainer(secondSplitParent)) {
|
2016-07-07 07:44:32 +03:00
|
|
|
if (TextEditUtils::IsBreak(secondSplitParent)) {
|
2014-08-20 16:25:16 +04:00
|
|
|
savedBR = do_QueryInterface(secondSplitParent);
|
|
|
|
NS_ENSURE_STATE(savedBR);
|
2012-05-10 16:00:14 +04:00
|
|
|
}
|
|
|
|
|
2016-05-01 16:11:28 +03:00
|
|
|
secondSplitParent = secondSplitParent->GetParentNode();
|
2012-05-10 16:00:14 +04:00
|
|
|
}
|
|
|
|
*aOffset = 0;
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = SplitStyleAbovePoint(address_of(secondSplitParent), aOffset, aProperty,
|
|
|
|
aAttribute, getter_AddRefs(leftNode),
|
|
|
|
getter_AddRefs(rightNode));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2017-05-25 11:15:54 +03:00
|
|
|
|
|
|
|
if (rightNode) {
|
|
|
|
bool bIsEmptyNode;
|
|
|
|
IsEmptyNode(rightNode, &bIsEmptyNode, false, true);
|
|
|
|
if (bIsEmptyNode) {
|
|
|
|
// delete rightNode if it became empty
|
2018-04-10 10:23:54 +03:00
|
|
|
rv = DeleteNodeWithTransaction(*rightNode);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2017-05-25 11:15:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!leftNode) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2012-05-10 16:00:14 +04:00
|
|
|
// should be impossible to not get a new leftnode here
|
2016-05-01 16:11:28 +03:00
|
|
|
nsCOMPtr<nsINode> newSelParent = GetLeftmostChild(leftNode);
|
2012-05-10 16:00:14 +04:00
|
|
|
if (!newSelParent) {
|
2016-05-01 16:11:28 +03:00
|
|
|
newSelParent = leftNode;
|
2012-05-10 16:00:14 +04:00
|
|
|
}
|
|
|
|
// If rightNode starts with a br, suck it out of right node and into
|
|
|
|
// leftNode. This is so we you don't revert back to the previous style
|
|
|
|
// if you happen to click at the end of a line.
|
|
|
|
if (savedBR) {
|
2018-04-12 17:58:52 +03:00
|
|
|
rv =
|
|
|
|
MoveNodeWithTransaction(*savedBR, EditorRawDOMPoint(newSelParent, 0));
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-05-10 16:00:14 +04:00
|
|
|
}
|
|
|
|
// remove the style on this new hierarchy
|
2012-08-22 19:56:38 +04:00
|
|
|
int32_t newSelOffset = 0;
|
2012-05-10 16:00:14 +04:00
|
|
|
{
|
|
|
|
// Track the point at the new hierarchy. This is so we can know where
|
|
|
|
// to put the selection after we call RemoveStyleInside().
|
|
|
|
// RemoveStyleInside() could remove any and all of those nodes, so I
|
|
|
|
// have to use the range tracking system to find the right spot to put
|
|
|
|
// selection.
|
2018-11-26 09:31:56 +03:00
|
|
|
AutoTrackDOMPoint tracker(RangeUpdaterRef(), address_of(newSelParent),
|
2016-06-24 09:08:16 +03:00
|
|
|
&newSelOffset);
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = RemoveStyleInside(*leftNode, aProperty, aAttribute);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2012-05-10 16:00:14 +04:00
|
|
|
}
|
|
|
|
// reset our node offset values to the resulting new sel point
|
2016-05-01 16:11:40 +03:00
|
|
|
*aNode = newSelParent;
|
2012-05-10 16:00:14 +04:00
|
|
|
*aOffset = newSelOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
bool HTMLEditor::NodeIsProperty(nsINode& aNode) {
|
2016-05-01 16:12:15 +03:00
|
|
|
return IsContainer(&aNode) && IsEditable(&aNode) && !IsBlockNode(&aNode) &&
|
|
|
|
!aNode.IsHTMLElement(nsGkAtoms::a);
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::RemoveStyleInside(nsIContent& aNode, nsAtom* aProperty,
|
2017-12-26 06:25:45 +03:00
|
|
|
nsAtom* aAttribute,
|
2016-07-09 05:42:33 +03:00
|
|
|
const bool aChildrenOnly /* = false */) {
|
2018-04-12 16:23:04 +03:00
|
|
|
if (!aNode.IsElement()) {
|
2015-05-13 18:58:26 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2001-01-28 23:13:07 +03:00
|
|
|
// first process the children
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<nsIContent> child = aNode.GetFirstChild();
|
2012-04-17 21:50:19 +04:00
|
|
|
while (child) {
|
2001-01-28 23:13:07 +03:00
|
|
|
// cache next sibling since we might remove child
|
2015-05-13 18:58:26 +03:00
|
|
|
nsCOMPtr<nsIContent> next = child->GetNextSibling();
|
2016-10-19 12:09:33 +03:00
|
|
|
nsresult rv = RemoveStyleInside(*child, aProperty, aAttribute);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2015-05-13 18:58:26 +03:00
|
|
|
child = next.forget();
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// then process the node itself
|
2012-08-21 22:31:19 +04:00
|
|
|
if (!aChildrenOnly &&
|
2016-10-24 05:27:45 +03:00
|
|
|
// node is prop we asked for
|
|
|
|
((aProperty && aNode.NodeInfo()->NameAtom() == aProperty) ||
|
|
|
|
// but check for link (<a href=...)
|
|
|
|
(aProperty == nsGkAtoms::href && HTMLEditUtils::IsLink(&aNode)) ||
|
|
|
|
// and for named anchors
|
|
|
|
(aProperty == nsGkAtoms::name && HTMLEditUtils::IsNamedAnchor(&aNode)) ||
|
|
|
|
// or node is any prop and we asked for that
|
|
|
|
(!aProperty && NodeIsProperty(aNode)))) {
|
2015-05-28 18:58:42 +03:00
|
|
|
// if we weren't passed an attribute, then we want to
|
2001-01-28 23:13:07 +03:00
|
|
|
// remove any matching inlinestyles entirely
|
2017-12-26 06:25:45 +03:00
|
|
|
if (!aAttribute) {
|
2017-12-07 21:13:50 +03:00
|
|
|
bool hasStyleAttr =
|
|
|
|
aNode.AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::style);
|
|
|
|
bool hasClassAttr =
|
|
|
|
aNode.AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::_class);
|
2012-04-17 21:50:19 +04:00
|
|
|
if (aProperty && (hasStyleAttr || hasClassAttr)) {
|
2003-06-17 12:45:54 +04:00
|
|
|
// aNode carries inline styles or a class attribute so we can't
|
|
|
|
// just remove the element... We need to create above the element
|
|
|
|
// a span that will carry those styles or class, then we can delete
|
|
|
|
// the node.
|
2016-12-20 14:53:00 +03:00
|
|
|
RefPtr<Element> spanNode =
|
2018-04-13 12:17:04 +03:00
|
|
|
InsertContainerWithTransaction(aNode, *nsGkAtoms::span);
|
2018-04-12 10:58:33 +03:00
|
|
|
if (NS_WARN_IF(!spanNode)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
nsresult rv = CloneAttributeWithTransaction(
|
|
|
|
*nsGkAtoms::style, *spanNode, *aNode.AsElement());
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
rv = CloneAttributeWithTransaction(*nsGkAtoms::_class, *spanNode,
|
|
|
|
*aNode.AsElement());
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2003-06-17 12:45:54 +04:00
|
|
|
}
|
2018-04-12 16:23:04 +03:00
|
|
|
nsresult rv = RemoveContainerWithTransaction(*aNode.AsElement());
|
2016-10-19 12:09:33 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2017-12-05 20:05:51 +03:00
|
|
|
} else if (aNode.IsElement()) {
|
2012-04-17 21:50:19 +04:00
|
|
|
// otherwise we just want to eliminate the attribute
|
2017-12-26 06:25:45 +03:00
|
|
|
if (aNode.AsElement()->HasAttr(kNameSpaceID_None, aAttribute)) {
|
2001-01-28 23:13:07 +03:00
|
|
|
// if this matching attribute is the ONLY one on the node,
|
|
|
|
// then remove the whole node. Otherwise just nix the attribute.
|
2017-12-26 06:25:45 +03:00
|
|
|
if (IsOnlyAttribute(aNode.AsElement(), aAttribute)) {
|
2018-04-12 16:23:04 +03:00
|
|
|
nsresult rv = RemoveContainerWithTransaction(*aNode.AsElement());
|
2016-10-19 12:09:33 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-04-17 21:50:19 +04:00
|
|
|
} else {
|
2018-04-12 10:58:33 +03:00
|
|
|
nsresult rv =
|
|
|
|
RemoveAttributeWithTransaction(*aNode.AsElement(), *aAttribute);
|
2016-10-19 12:09:33 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-04-17 21:50:19 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!aChildrenOnly &&
|
2018-02-02 12:42:25 +03:00
|
|
|
CSSEditUtils::IsCSSEditableProperty(&aNode, aProperty, aAttribute)) {
|
2012-04-17 21:50:19 +04:00
|
|
|
// the HTML style defined by aProperty/aAttribute has a CSS equivalence in
|
|
|
|
// this implementation for the node aNode; let's check if it carries those
|
|
|
|
// css styles
|
2017-02-23 11:10:27 +03:00
|
|
|
if (aNode.IsElement()) {
|
2018-02-02 12:42:25 +03:00
|
|
|
bool hasAttribute = CSSEditUtils::HaveCSSEquivalentStyles(
|
|
|
|
aNode, aProperty, aAttribute, CSSEditUtils::eSpecified);
|
2017-02-23 11:10:27 +03:00
|
|
|
if (hasAttribute) {
|
|
|
|
// yes, tmp has the corresponding css declarations in its style
|
|
|
|
// attribute
|
|
|
|
// let's remove them
|
|
|
|
mCSSEditUtils->RemoveCSSEquivalentToHTMLStyle(
|
|
|
|
aNode.AsElement(), aProperty, aAttribute, nullptr, false);
|
|
|
|
// remove the node if it is a span or font, if its style attribute is
|
|
|
|
// empty or absent, and if it does not have a class nor an id
|
|
|
|
RemoveElementIfNoStyleOrIdOrClass(*aNode.AsElement());
|
|
|
|
}
|
2002-01-09 16:51:37 +03:00
|
|
|
}
|
2001-10-22 10:22:52 +04:00
|
|
|
}
|
2012-04-17 21:50:19 +04:00
|
|
|
|
2016-10-24 05:27:45 +03:00
|
|
|
// Or node is big or small and we are setting font size
|
|
|
|
if (aChildrenOnly) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
if (aProperty == nsGkAtoms::font &&
|
|
|
|
(aNode.IsHTMLElement(nsGkAtoms::big) ||
|
|
|
|
aNode.IsHTMLElement(nsGkAtoms::small)) &&
|
2017-12-26 06:25:45 +03:00
|
|
|
aAttribute == nsGkAtoms::size) {
|
2014-08-20 16:25:16 +04:00
|
|
|
// if we are setting font size, remove any nested bigs and smalls
|
2018-04-12 16:23:04 +03:00
|
|
|
return RemoveContainerWithTransaction(*aNode.AsElement());
|
2012-04-17 21:50:19 +04:00
|
|
|
}
|
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2017-12-05 20:05:51 +03:00
|
|
|
bool HTMLEditor::IsOnlyAttribute(const Element* aElement, nsAtom* aAttribute) {
|
|
|
|
MOZ_ASSERT(aElement);
|
2012-05-05 13:00:06 +04:00
|
|
|
|
2017-12-05 20:05:51 +03:00
|
|
|
uint32_t attrCount = aElement->GetAttrCount();
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t i = 0; i < attrCount; ++i) {
|
2017-12-05 20:05:51 +03:00
|
|
|
const nsAttrName* name = aElement->GetAttrNameAt(i);
|
2005-12-29 00:52:39 +03:00
|
|
|
if (!name->NamespaceEquals(kNameSpaceID_None)) {
|
2011-10-17 18:59:28 +04:00
|
|
|
return false;
|
2005-12-29 00:52:39 +03:00
|
|
|
}
|
2012-05-05 13:00:06 +04:00
|
|
|
|
2005-12-29 00:52:39 +03:00
|
|
|
// if it's the attribute we know about, or a special _moz attribute,
|
|
|
|
// keep looking
|
2017-12-26 06:25:45 +03:00
|
|
|
if (name->LocalName() != aAttribute) {
|
|
|
|
nsAutoString attrString;
|
|
|
|
name->LocalName()->ToString(attrString);
|
|
|
|
if (!StringBeginsWith(attrString, NS_LITERAL_STRING("_moz"))) {
|
|
|
|
return false;
|
|
|
|
}
|
2005-12-29 00:52:39 +03:00
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
// if we made it through all of them without finding a real attribute
|
2011-10-17 18:59:28 +04:00
|
|
|
// other than aAttribute, then return true
|
|
|
|
return true;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange& aRange) {
|
2016-05-01 16:12:51 +03:00
|
|
|
// We assume that <a> is not nested.
|
2017-07-11 14:53:04 +03:00
|
|
|
nsCOMPtr<nsINode> startNode = aRange.GetStartContainer();
|
2016-05-01 16:12:51 +03:00
|
|
|
int32_t startOffset = aRange.StartOffset();
|
2017-07-11 15:11:37 +03:00
|
|
|
nsCOMPtr<nsINode> endNode = aRange.GetEndContainer();
|
2016-05-01 16:12:51 +03:00
|
|
|
int32_t endOffset = aRange.EndOffset();
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2016-05-01 16:12:51 +03:00
|
|
|
nsCOMPtr<nsINode> parent = startNode;
|
2002-04-11 17:33:30 +04:00
|
|
|
|
2016-05-01 16:12:51 +03:00
|
|
|
while (parent && !parent->IsHTMLElement(nsGkAtoms::body) &&
|
2016-07-07 08:01:12 +03:00
|
|
|
!HTMLEditUtils::IsNamedAnchor(parent)) {
|
2016-05-01 16:12:51 +03:00
|
|
|
parent = parent->GetParentNode();
|
2002-04-11 17:33:30 +04:00
|
|
|
}
|
2016-05-01 16:12:51 +03:00
|
|
|
NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
|
|
|
|
|
2016-07-07 08:01:12 +03:00
|
|
|
if (HTMLEditUtils::IsNamedAnchor(parent)) {
|
2016-05-01 16:12:51 +03:00
|
|
|
startNode = parent->GetParentNode();
|
2018-01-23 16:30:18 +03:00
|
|
|
startOffset = startNode ? startNode->ComputeIndexOf(parent) : -1;
|
2002-04-11 17:33:30 +04:00
|
|
|
}
|
|
|
|
|
2016-05-01 16:12:51 +03:00
|
|
|
parent = endNode;
|
|
|
|
while (parent && !parent->IsHTMLElement(nsGkAtoms::body) &&
|
2016-07-07 08:01:12 +03:00
|
|
|
!HTMLEditUtils::IsNamedAnchor(parent)) {
|
2016-05-01 16:12:51 +03:00
|
|
|
parent = parent->GetParentNode();
|
2002-04-11 17:33:30 +04:00
|
|
|
}
|
2016-05-01 16:12:51 +03:00
|
|
|
NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
|
|
|
|
|
2016-07-07 08:01:12 +03:00
|
|
|
if (HTMLEditUtils::IsNamedAnchor(parent)) {
|
2016-05-01 16:12:51 +03:00
|
|
|
endNode = parent->GetParentNode();
|
2018-01-23 16:30:18 +03:00
|
|
|
endOffset = endNode ? endNode->ComputeIndexOf(parent) + 1 : 0;
|
2002-04-11 17:33:30 +04:00
|
|
|
}
|
|
|
|
|
2017-05-30 07:18:25 +03:00
|
|
|
nsresult rv =
|
|
|
|
aRange.SetStartAndEnd(startNode, startOffset, endNode, endOffset);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2016-05-01 16:12:51 +03:00
|
|
|
|
|
|
|
return NS_OK;
|
2002-04-11 17:33:30 +04:00
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::PromoteInlineRange(nsRange& aRange) {
|
2017-07-11 14:53:04 +03:00
|
|
|
nsCOMPtr<nsINode> startNode = aRange.GetStartContainer();
|
2016-05-01 16:12:39 +03:00
|
|
|
int32_t startOffset = aRange.StartOffset();
|
2017-07-11 15:11:37 +03:00
|
|
|
nsCOMPtr<nsINode> endNode = aRange.GetEndContainer();
|
2016-05-01 16:12:39 +03:00
|
|
|
int32_t endOffset = aRange.EndOffset();
|
|
|
|
|
|
|
|
while (startNode && !startNode->IsHTMLElement(nsGkAtoms::body) &&
|
|
|
|
IsEditable(startNode) && IsAtFrontOfNode(*startNode, startOffset)) {
|
|
|
|
nsCOMPtr<nsINode> parent = startNode->GetParentNode();
|
|
|
|
NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
|
2018-01-23 16:30:18 +03:00
|
|
|
startOffset = parent->ComputeIndexOf(startNode);
|
2001-01-28 23:13:07 +03:00
|
|
|
startNode = parent;
|
|
|
|
}
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2016-05-01 16:12:39 +03:00
|
|
|
while (endNode && !endNode->IsHTMLElement(nsGkAtoms::body) &&
|
|
|
|
IsEditable(endNode) && IsAtEndOfNode(*endNode, endOffset)) {
|
|
|
|
nsCOMPtr<nsINode> parent = endNode->GetParentNode();
|
|
|
|
NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
|
|
|
|
// We are AFTER this node
|
2018-01-23 16:30:18 +03:00
|
|
|
endOffset = 1 + parent->ComputeIndexOf(endNode);
|
2001-01-28 23:13:07 +03:00
|
|
|
endNode = parent;
|
|
|
|
}
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2017-05-30 07:18:25 +03:00
|
|
|
nsresult rv =
|
|
|
|
aRange.SetStartAndEnd(startNode, startOffset, endNode, endOffset);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2016-05-01 16:12:39 +03:00
|
|
|
|
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
bool HTMLEditor::IsAtFrontOfNode(nsINode& aNode, int32_t aOffset) {
|
2011-07-15 01:06:37 +04:00
|
|
|
if (!aOffset) {
|
2011-10-17 18:59:28 +04:00
|
|
|
return true;
|
2011-07-15 01:06:37 +04:00
|
|
|
}
|
|
|
|
|
2016-05-01 16:12:39 +03:00
|
|
|
if (IsTextNode(&aNode)) {
|
2011-10-17 18:59:28 +04:00
|
|
|
return false;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2016-05-01 16:12:39 +03:00
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> firstNode = GetFirstEditableChild(aNode);
|
|
|
|
NS_ENSURE_TRUE(firstNode, true);
|
2018-01-23 16:30:18 +03:00
|
|
|
if (aNode.ComputeIndexOf(firstNode) < aOffset) {
|
2016-05-01 16:12:39 +03:00
|
|
|
return false;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2016-05-01 16:12:39 +03:00
|
|
|
return true;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
bool HTMLEditor::IsAtEndOfNode(nsINode& aNode, int32_t aOffset) {
|
2016-05-01 16:12:39 +03:00
|
|
|
if (aOffset == (int32_t)aNode.Length()) {
|
|
|
|
return true;
|
|
|
|
}
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2016-05-01 16:12:39 +03:00
|
|
|
if (IsTextNode(&aNode)) {
|
2011-10-17 18:59:28 +04:00
|
|
|
return false;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2016-05-01 16:12:39 +03:00
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> lastNode = GetLastEditableChild(aNode);
|
|
|
|
NS_ENSURE_TRUE(lastNode, true);
|
2018-01-23 16:30:18 +03:00
|
|
|
if (aNode.ComputeIndexOf(lastNode) < aOffset) {
|
2016-05-01 16:12:39 +03:00
|
|
|
return true;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2016-05-01 16:12:39 +03:00
|
|
|
return false;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
nsresult HTMLEditor::GetInlinePropertyBase(nsAtom& aProperty,
|
2017-12-26 06:25:45 +03:00
|
|
|
nsAtom* aAttribute,
|
2016-07-09 05:42:33 +03:00
|
|
|
const nsAString* aValue,
|
|
|
|
bool* aFirst, bool* aAny, bool* aAll,
|
2017-08-25 09:59:17 +03:00
|
|
|
nsAString* outValue) {
|
2018-10-30 13:04:08 +03:00
|
|
|
MOZ_ASSERT(IsEditActionDataAvailable());
|
|
|
|
|
2012-04-18 18:26:02 +04:00
|
|
|
*aAny = false;
|
|
|
|
*aAll = true;
|
|
|
|
*aFirst = false;
|
|
|
|
bool first = true;
|
2002-01-09 16:51:37 +03:00
|
|
|
|
2018-10-30 13:04:08 +03:00
|
|
|
bool isCollapsed = SelectionRefPtr()->IsCollapsed();
|
|
|
|
RefPtr<nsRange> range = SelectionRefPtr()->GetRangeAt(0);
|
2015-04-19 15:28:50 +03:00
|
|
|
// XXX: Should be a while loop, to get each separate range
|
2001-01-28 23:13:07 +03:00
|
|
|
// XXX: ERROR_HANDLING can currentItem be null?
|
2013-02-07 18:17:47 +04:00
|
|
|
if (range) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// For each range, set a flag
|
|
|
|
bool firstNodeInRange = true;
|
2001-01-28 23:13:07 +03:00
|
|
|
|
2012-04-18 18:26:02 +04:00
|
|
|
if (isCollapsed) {
|
2017-07-11 14:53:04 +03:00
|
|
|
nsCOMPtr<nsINode> collapsedNode = range->GetStartContainer();
|
2018-10-30 13:04:08 +03:00
|
|
|
if (NS_WARN_IF(!collapsedNode)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2011-09-29 10:19:26 +04:00
|
|
|
bool isSet, theSetting;
|
2012-05-03 11:09:44 +04:00
|
|
|
nsString tOutString;
|
2012-04-18 18:26:02 +04:00
|
|
|
if (aAttribute) {
|
2017-12-26 06:25:45 +03:00
|
|
|
mTypeInState->GetTypingState(isSet, theSetting, &aProperty, aAttribute,
|
2012-04-18 18:26:02 +04:00
|
|
|
&tOutString);
|
|
|
|
if (outValue) {
|
2001-04-07 04:45:26 +04:00
|
|
|
outValue->Assign(tOutString);
|
2012-04-18 18:26:02 +04:00
|
|
|
}
|
|
|
|
} else {
|
2015-04-19 15:28:50 +03:00
|
|
|
mTypeInState->GetTypingState(isSet, theSetting, &aProperty);
|
2012-04-18 18:26:02 +04:00
|
|
|
}
|
|
|
|
if (isSet) {
|
2001-04-07 04:45:26 +04:00
|
|
|
*aFirst = *aAny = *aAll = theSetting;
|
2001-01-28 23:13:07 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2012-05-03 11:09:44 +04:00
|
|
|
|
2018-02-02 12:42:25 +03:00
|
|
|
if (CSSEditUtils::IsCSSEditableProperty(collapsedNode, &aProperty,
|
|
|
|
aAttribute)) {
|
2015-04-24 12:57:00 +03:00
|
|
|
if (aValue) {
|
|
|
|
tOutString.Assign(*aValue);
|
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
*aFirst = *aAny = *aAll =
|
2018-02-02 12:42:25 +03:00
|
|
|
CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
|
|
|
|
collapsedNode, &aProperty, aAttribute, tOutString,
|
|
|
|
CSSEditUtils::eComputed);
|
2012-05-03 11:09:44 +04:00
|
|
|
if (outValue) {
|
|
|
|
outValue->Assign(tOutString);
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2011-12-22 22:16:56 +04:00
|
|
|
|
2015-04-19 15:28:50 +03:00
|
|
|
isSet = IsTextPropertySetByContent(collapsedNode, &aProperty, aAttribute,
|
|
|
|
aValue, outValue);
|
2012-05-03 11:09:44 +04:00
|
|
|
*aFirst = *aAny = *aAll = isSet;
|
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2015-04-19 15:28:50 +03:00
|
|
|
// Non-collapsed selection
|
2001-01-28 23:13:07 +03:00
|
|
|
|
|
|
|
nsAutoString firstValue, theValue;
|
2004-01-24 03:46:17 +03:00
|
|
|
|
2017-07-11 15:11:37 +03:00
|
|
|
nsCOMPtr<nsINode> endNode = range->GetEndContainer();
|
2015-04-19 15:28:50 +03:00
|
|
|
int32_t endOffset = range->EndOffset();
|
2004-01-24 03:46:17 +03:00
|
|
|
|
2019-01-11 04:52:26 +03:00
|
|
|
PostContentIterator postOrderIter;
|
|
|
|
DebugOnly<nsresult> rvIgnored = postOrderIter.Init(range);
|
|
|
|
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
|
|
|
|
"Failed to initialize post-order content iterator");
|
|
|
|
for (; !postOrderIter.IsDone(); postOrderIter.Next()) {
|
|
|
|
if (!postOrderIter.GetCurrentNode()->IsContent()) {
|
2012-06-10 16:39:21 +04:00
|
|
|
continue;
|
|
|
|
}
|
2019-01-11 04:50:19 +03:00
|
|
|
nsCOMPtr<nsIContent> content =
|
2019-01-11 04:52:26 +03:00
|
|
|
postOrderIter.GetCurrentNode()->AsContent();
|
2001-08-03 00:31:09 +04:00
|
|
|
|
2015-04-19 15:28:50 +03:00
|
|
|
if (content->IsHTMLElement(nsGkAtoms::body)) {
|
2001-08-03 00:31:09 +04:00
|
|
|
break;
|
2012-04-18 18:26:02 +04:00
|
|
|
}
|
2001-08-03 00:31:09 +04:00
|
|
|
|
2001-02-07 12:24:21 +03:00
|
|
|
// just ignore any non-editable nodes
|
2015-04-19 15:28:50 +03:00
|
|
|
if (content->GetAsText() &&
|
Bug 1463327 - part 1: Change scope of some methods of EditorBase which won't be called by non-helper classes of editing to protected r=m_kato
EditorBase (and other editor classes) have 2 type of public methods. One is
true-public methods. I.e., they should be able to be called by anybody.
E.g., command handlers, event listeners, or JS via nsIEditor interface.
The other is semi-public methods. They are not called by the above examples
but called by other classes which are helper classes to handle edit actions.
E.g., TextEditRules, HTMLEditRules, HTMLEditUtils, CSSEditUtils and Transaction
classes.
When we will implement InputEvent.inputType, we need to create new stack
class and create its instance at every true-public methods to manage current
inputType (like TextEditRules::AutoSafeEditorData). Therefore, it should not
happen that new code starts to call semi-public methods without the new
stack class instance.
For preventing this issue, we should make EditorBase have only the true-public
methods as public. The other public methods should be protected and their
users should be friend classes. Then, we can protect such method from external
classes.
Note that this patch just moves the methods without any changes in EditorBase.h
(except removes GetName() since there is no body of this method and removes
IterDirection since it's unused).
MozReview-Commit-ID: HBseKLL6pxx
--HG--
extra : rebase_source : 2251ff659d831d01a6778d38f4e2714fcf2d6ef4
2018-05-22 10:08:43 +03:00
|
|
|
(!IsEditable(content) || IsEmptyTextNode(*content))) {
|
2012-04-18 18:26:02 +04:00
|
|
|
continue;
|
2001-02-07 12:24:21 +03:00
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
if (content->GetAsText()) {
|
2012-04-18 18:26:02 +04:00
|
|
|
if (!isCollapsed && first && firstNodeInRange) {
|
2011-10-17 18:59:28 +04:00
|
|
|
firstNodeInRange = false;
|
2017-07-19 16:49:52 +03:00
|
|
|
if (range->StartOffset() == content->Length()) {
|
2012-04-18 18:26:02 +04:00
|
|
|
continue;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
} else if (content == endNode && !endOffset) {
|
2012-04-18 18:26:02 +04:00
|
|
|
continue;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-04-18 18:26:02 +04:00
|
|
|
} else if (content->IsElement()) {
|
|
|
|
// handle non-text leaf nodes here
|
|
|
|
continue;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-06-10 16:39:21 +04:00
|
|
|
|
|
|
|
bool isSet = false;
|
|
|
|
if (first) {
|
2018-02-02 12:42:25 +03:00
|
|
|
if (CSSEditUtils::IsCSSEditableProperty(content, &aProperty,
|
|
|
|
aAttribute)) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// The HTML styles defined by aProperty/aAttribute have a CSS
|
2012-06-10 16:39:21 +04:00
|
|
|
// equivalence in this implementation for node; let's check if it
|
2015-04-19 15:28:50 +03:00
|
|
|
// carries those CSS styles
|
2012-06-10 16:39:21 +04:00
|
|
|
if (aValue) {
|
|
|
|
firstValue.Assign(*aValue);
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2018-02-02 12:42:25 +03:00
|
|
|
isSet = CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
|
|
|
|
content, &aProperty, aAttribute, firstValue,
|
|
|
|
CSSEditUtils::eComputed);
|
2012-04-18 18:26:02 +04:00
|
|
|
} else {
|
2015-04-19 15:28:50 +03:00
|
|
|
isSet = IsTextPropertySetByContent(content, &aProperty, aAttribute,
|
|
|
|
aValue, &firstValue);
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-06-10 16:39:21 +04:00
|
|
|
*aFirst = isSet;
|
|
|
|
first = false;
|
|
|
|
if (outValue) {
|
|
|
|
*outValue = firstValue;
|
|
|
|
}
|
|
|
|
} else {
|
2018-02-02 12:42:25 +03:00
|
|
|
if (CSSEditUtils::IsCSSEditableProperty(content, &aProperty,
|
|
|
|
aAttribute)) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// The HTML styles defined by aProperty/aAttribute have a CSS
|
|
|
|
// equivalence in this implementation for node; let's check if it
|
|
|
|
// carries those CSS styles
|
2012-06-10 16:39:21 +04:00
|
|
|
if (aValue) {
|
|
|
|
theValue.Assign(*aValue);
|
|
|
|
}
|
2018-02-02 12:42:25 +03:00
|
|
|
isSet = CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
|
|
|
|
content, &aProperty, aAttribute, theValue,
|
|
|
|
CSSEditUtils::eComputed);
|
2012-04-18 18:26:02 +04:00
|
|
|
} else {
|
2015-04-19 15:28:50 +03:00
|
|
|
isSet = IsTextPropertySetByContent(content, &aProperty, aAttribute,
|
|
|
|
aValue, &theValue);
|
2012-06-10 16:39:21 +04:00
|
|
|
}
|
|
|
|
if (firstValue != theValue) {
|
2012-04-18 18:26:02 +04:00
|
|
|
*aAll = false;
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-06-10 16:39:21 +04:00
|
|
|
|
|
|
|
if (isSet) {
|
|
|
|
*aAny = true;
|
|
|
|
} else {
|
|
|
|
*aAll = false;
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
}
|
2012-04-18 18:26:02 +04:00
|
|
|
if (!*aAny) {
|
|
|
|
// make sure that if none of the selection is set, we don't report all is
|
|
|
|
// set
|
2011-10-17 18:59:28 +04:00
|
|
|
*aAll = false;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
NS_IMETHODIMP
|
2017-08-25 08:40:45 +03:00
|
|
|
HTMLEditor::GetInlineProperty(const nsAString& aProperty,
|
|
|
|
const nsAString& aAttribute,
|
|
|
|
const nsAString& aValue, bool* aFirst, bool* aAny,
|
|
|
|
bool* aAll) {
|
2017-10-03 01:05:19 +03:00
|
|
|
RefPtr<nsAtom> property = NS_Atomize(aProperty);
|
2018-11-30 04:21:59 +03:00
|
|
|
RefPtr<nsAtom> attribute = AtomizeAttribute(aAttribute);
|
2017-12-26 06:25:45 +03:00
|
|
|
return GetInlineProperty(property, attribute, aValue, aFirst, aAny, aAll);
|
2017-08-25 08:40:45 +03:00
|
|
|
}
|
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
nsresult HTMLEditor::GetInlineProperty(nsAtom* aProperty, nsAtom* aAttribute,
|
2016-07-09 05:42:33 +03:00
|
|
|
const nsAString& aValue, bool* aFirst,
|
|
|
|
bool* aAny, bool* aAll) {
|
2018-10-30 13:00:17 +03:00
|
|
|
if (NS_WARN_IF(!aProperty) || NS_WARN_IF(!aFirst) || NS_WARN_IF(!aAny) ||
|
|
|
|
NS_WARN_IF(!aAll)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
|
|
|
|
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
const nsAString* val = nullptr;
|
2003-05-24 01:34:47 +04:00
|
|
|
if (!aValue.IsEmpty()) val = &aValue;
|
2017-12-26 06:25:45 +03:00
|
|
|
return GetInlinePropertyBase(*aProperty, aAttribute, val, aFirst, aAny, aAll,
|
|
|
|
nullptr);
|
2001-04-07 04:45:26 +04:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
NS_IMETHODIMP
|
2017-08-25 08:40:45 +03:00
|
|
|
HTMLEditor::GetInlinePropertyWithAttrValue(const nsAString& aProperty,
|
|
|
|
const nsAString& aAttribute,
|
|
|
|
const nsAString& aValue,
|
|
|
|
bool* aFirst, bool* aAny, bool* aAll,
|
|
|
|
nsAString& outValue) {
|
2017-10-03 01:05:19 +03:00
|
|
|
RefPtr<nsAtom> property = NS_Atomize(aProperty);
|
2018-11-30 04:21:59 +03:00
|
|
|
RefPtr<nsAtom> attribute = AtomizeAttribute(aAttribute);
|
2017-12-26 06:25:45 +03:00
|
|
|
return GetInlinePropertyWithAttrValue(property, attribute, aValue, aFirst,
|
2017-09-07 05:20:51 +03:00
|
|
|
aAny, aAll, outValue);
|
2017-08-25 08:40:45 +03:00
|
|
|
}
|
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
nsresult HTMLEditor::GetInlinePropertyWithAttrValue(
|
2016-07-09 05:42:33 +03:00
|
|
|
nsAtom* aProperty, nsAtom* aAttribute, const nsAString& aValue,
|
|
|
|
bool* aFirst, bool* aAny, bool* aAll, nsAString& outValue) {
|
2018-10-30 13:00:17 +03:00
|
|
|
if (NS_WARN_IF(!aProperty) || NS_WARN_IF(!aFirst) || NS_WARN_IF(!aAny) ||
|
|
|
|
NS_WARN_IF(!aAll)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
|
|
|
|
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
const nsAString* val = nullptr;
|
2003-05-24 01:34:47 +04:00
|
|
|
if (!aValue.IsEmpty()) val = &aValue;
|
2017-12-26 06:25:45 +03:00
|
|
|
return GetInlinePropertyBase(*aProperty, aAttribute, val, aFirst, aAny, aAll,
|
|
|
|
&outValue);
|
2001-04-07 04:45:26 +04:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
HTMLEditor::RemoveAllInlineProperties() {
|
2018-10-30 13:00:17 +03:00
|
|
|
AutoEditActionDataSetter editActionData(
|
|
|
|
*this, EditAction::eRemoveAllInlineStyleProperties);
|
|
|
|
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
2018-10-24 12:42:06 +03:00
|
|
|
AutoPlaceholderBatch treatAsOneTransaction(*this);
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 14:12:34 +03:00
|
|
|
AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
|
2018-05-28 17:40:45 +03:00
|
|
|
*this, EditSubAction::eRemoveAllTextProperties, nsIEditor::eNext);
|
2003-06-03 02:21:06 +04:00
|
|
|
|
2018-08-13 09:16:24 +03:00
|
|
|
nsresult rv = RemoveInlinePropertyInternal(nullptr, nullptr);
|
2016-10-19 12:09:33 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2017-08-25 09:59:17 +03:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2018-10-30 13:00:17 +03:00
|
|
|
nsresult HTMLEditor::RemoveInlinePropertyAsAction(nsAtom& aProperty,
|
|
|
|
nsAtom* aAttribute) {
|
|
|
|
AutoEditActionDataSetter editActionData(
|
|
|
|
*this,
|
|
|
|
HTMLEditUtils::GetEditActionForFormatText(aProperty, aAttribute, false));
|
|
|
|
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
2019-02-19 09:30:50 +03:00
|
|
|
switch (editActionData.GetEditAction()) {
|
|
|
|
case EditAction::eRemoveFontFamilyProperty:
|
|
|
|
MOZ_ASSERT(!EmptyString().IsVoid());
|
|
|
|
editActionData.SetData(EmptyString());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2018-10-30 13:00:17 +03:00
|
|
|
nsresult rv = RemoveInlinePropertyInternal(&aProperty, aAttribute);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
NS_IMETHODIMP
|
2017-08-25 08:40:45 +03:00
|
|
|
HTMLEditor::RemoveInlineProperty(const nsAString& aProperty,
|
|
|
|
const nsAString& aAttribute) {
|
2017-10-03 01:05:19 +03:00
|
|
|
RefPtr<nsAtom> property = NS_Atomize(aProperty);
|
2018-11-30 04:21:59 +03:00
|
|
|
RefPtr<nsAtom> attribute = AtomizeAttribute(aAttribute);
|
2018-10-30 13:00:17 +03:00
|
|
|
|
|
|
|
AutoEditActionDataSetter editActionData(
|
|
|
|
*this,
|
|
|
|
HTMLEditUtils::GetEditActionForFormatText(*property, attribute, false));
|
|
|
|
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
2019-02-19 09:30:50 +03:00
|
|
|
switch (editActionData.GetEditAction()) {
|
|
|
|
case EditAction::eRemoveFontFamilyProperty:
|
|
|
|
MOZ_ASSERT(!EmptyString().IsVoid());
|
|
|
|
editActionData.SetData(EmptyString());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2018-08-13 09:16:24 +03:00
|
|
|
return RemoveInlinePropertyInternal(property, attribute);
|
2017-08-25 08:40:45 +03:00
|
|
|
}
|
|
|
|
|
2018-08-13 09:16:24 +03:00
|
|
|
nsresult HTMLEditor::RemoveInlinePropertyInternal(nsAtom* aProperty,
|
|
|
|
nsAtom* aAttribute) {
|
2018-10-30 13:02:58 +03:00
|
|
|
MOZ_ASSERT(IsEditActionDataAvailable());
|
2018-11-30 04:21:59 +03:00
|
|
|
MOZ_ASSERT(aAttribute != nsGkAtoms::_empty);
|
2018-10-30 13:02:58 +03:00
|
|
|
|
2018-08-13 09:16:24 +03:00
|
|
|
if (NS_WARN_IF(!mRules)) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
2017-08-08 05:25:36 +03:00
|
|
|
CommitComposition();
|
2001-01-28 23:13:07 +03:00
|
|
|
|
2018-10-30 13:02:58 +03:00
|
|
|
if (SelectionRefPtr()->IsCollapsed()) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// Manipulating text attributes on a collapsed selection only sets state
|
|
|
|
// for the next text insertion
|
2001-01-28 23:13:07 +03:00
|
|
|
|
|
|
|
// For links, aProperty uses "href", use "a" instead
|
2014-08-29 15:43:24 +04:00
|
|
|
if (aProperty == nsGkAtoms::href || aProperty == nsGkAtoms::name) {
|
|
|
|
aProperty = nsGkAtoms::a;
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
|
2012-05-05 13:00:06 +04:00
|
|
|
if (aProperty) {
|
2017-12-26 06:25:45 +03:00
|
|
|
mTypeInState->ClearProp(aProperty, aAttribute);
|
2012-05-05 13:00:06 +04:00
|
|
|
} else {
|
|
|
|
mTypeInState->ClearAllProps();
|
|
|
|
}
|
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-05-05 13:00:06 +04:00
|
|
|
|
2018-10-24 12:42:06 +03:00
|
|
|
AutoPlaceholderBatch treatAsOneTransaction(*this);
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 14:12:34 +03:00
|
|
|
AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
|
2018-05-28 16:05:10 +03:00
|
|
|
*this, EditSubAction::eRemoveTextProperty, nsIEditor::eNext);
|
2018-10-30 13:01:38 +03:00
|
|
|
AutoSelectionRestorer restoreSelectionLater(*this);
|
2018-08-01 15:30:14 +03:00
|
|
|
AutoTransactionsConserveSelection dontChangeMySelection(*this);
|
2015-04-19 15:28:50 +03:00
|
|
|
|
2011-09-29 10:19:26 +04:00
|
|
|
bool cancel, handled;
|
2018-05-28 16:05:10 +03:00
|
|
|
EditSubActionInfo subActionInfo(EditSubAction::eRemoveTextProperty);
|
2013-03-23 07:47:57 +04:00
|
|
|
// Protect the edit rules object from dying
|
2017-12-21 08:52:32 +03:00
|
|
|
RefPtr<TextEditRules> rules(mRules);
|
2018-10-30 13:02:58 +03:00
|
|
|
nsresult rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 14:12:34 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
if (!cancel && !handled) {
|
|
|
|
// Loop through the ranges in the selection
|
2016-12-15 09:01:53 +03:00
|
|
|
// Since ranges might be modified by SplitStyleAboveRange, we need hold
|
|
|
|
// current ranges
|
2018-10-30 13:02:58 +03:00
|
|
|
AutoRangeArray arrayOfRanges(SelectionRefPtr());
|
2017-06-23 10:46:54 +03:00
|
|
|
for (auto& range : arrayOfRanges.mRanges) {
|
2014-08-29 15:43:24 +04:00
|
|
|
if (aProperty == nsGkAtoms::name) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// Promote range if it starts or end in a named anchor and we want to
|
|
|
|
// remove named anchors
|
2017-06-23 10:46:54 +03:00
|
|
|
rv = PromoteRangeIfStartsOrEndsInNamedAnchor(*range);
|
2016-10-19 12:09:33 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
} else {
|
|
|
|
// Adjust range to include any ancestors whose children are entirely
|
|
|
|
// selected
|
2017-06-23 10:46:54 +03:00
|
|
|
rv = PromoteInlineRange(*range);
|
2016-10-19 12:09:33 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2002-04-11 17:33:30 +04:00
|
|
|
}
|
|
|
|
|
2015-04-19 15:28:50 +03:00
|
|
|
// Remove this style from ancestors of our range endpoints, splitting
|
|
|
|
// them as appropriate
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = SplitStyleAboveRange(range, aProperty, aAttribute);
|
2018-08-13 09:16:24 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2002-06-12 18:04:47 +04:00
|
|
|
|
2015-04-19 15:28:50 +03:00
|
|
|
// Check for easy case: both range endpoints in same text node
|
2017-07-11 14:53:04 +03:00
|
|
|
nsCOMPtr<nsINode> startNode = range->GetStartContainer();
|
2017-07-11 15:11:37 +03:00
|
|
|
nsCOMPtr<nsINode> endNode = range->GetEndContainer();
|
2015-04-19 15:28:50 +03:00
|
|
|
if (startNode && startNode == endNode && startNode->GetAsText()) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// We're done with this range!
|
2018-02-02 12:42:25 +03:00
|
|
|
if (IsCSSEnabled() && CSSEditUtils::IsCSSEditableProperty(
|
|
|
|
startNode, aProperty, aAttribute)) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// The HTML style defined by aProperty/aAttribute has a CSS
|
|
|
|
// equivalence in this implementation for startNode
|
2018-02-02 12:42:25 +03:00
|
|
|
if (CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
|
|
|
|
startNode, aProperty, aAttribute, EmptyString(),
|
|
|
|
CSSEditUtils::eComputed)) {
|
2015-04-19 15:28:50 +03:00
|
|
|
// startNode's computed style indicates the CSS equivalence to the
|
|
|
|
// HTML style to remove is applied; but we found no element in the
|
|
|
|
// ancestors of startNode carrying specified styles; assume it
|
|
|
|
// comes from a rule and try to insert a span "inverting" the style
|
2018-02-02 12:42:25 +03:00
|
|
|
if (CSSEditUtils::IsCSSInvertible(*aProperty, aAttribute)) {
|
2015-04-19 15:28:50 +03:00
|
|
|
NS_NAMED_LITERAL_STRING(value, "-moz-editor-invert-value");
|
|
|
|
SetInlinePropertyOnTextNode(
|
|
|
|
*startNode->GetAsText(), range->StartOffset(),
|
|
|
|
range->EndOffset(), *aProperty, aAttribute, value);
|
2002-01-09 16:51:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
} else {
|
|
|
|
// Not the easy case. Range not contained in single text node.
|
2001-01-28 23:13:07 +03:00
|
|
|
|
2016-05-01 16:11:52 +03:00
|
|
|
nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
|
2015-04-19 15:28:50 +03:00
|
|
|
|
|
|
|
// Iterate range and build up array
|
2019-01-11 04:52:26 +03:00
|
|
|
ContentSubtreeIterator subtreeIter;
|
|
|
|
DebugOnly<nsresult> rvIgnored = subtreeIter.Init(range);
|
|
|
|
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
|
|
|
|
"Failed to initialize subtree iterator");
|
|
|
|
for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
|
|
|
|
nsCOMPtr<nsINode> node = subtreeIter.GetCurrentNode();
|
2018-08-13 09:16:24 +03:00
|
|
|
if (NS_WARN_IF(!node)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2016-05-01 16:11:52 +03:00
|
|
|
if (IsEditable(node) && node->IsContent()) {
|
|
|
|
arrayOfNodes.AppendElement(*node->AsContent());
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
|
|
|
|
// Loop through the list, remove the property on each node
|
|
|
|
for (auto& node : arrayOfNodes) {
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = RemoveStyleInside(node, aProperty, aAttribute);
|
2018-08-13 09:16:24 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
if (IsCSSEnabled() &&
|
2018-02-02 12:42:25 +03:00
|
|
|
CSSEditUtils::IsCSSEditableProperty(node, aProperty,
|
|
|
|
aAttribute) &&
|
|
|
|
CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
|
|
|
|
node, aProperty, aAttribute, EmptyString(),
|
|
|
|
CSSEditUtils::eComputed) &&
|
2015-04-19 15:28:50 +03:00
|
|
|
// startNode's computed style indicates the CSS equivalence to
|
|
|
|
// the HTML style to remove is applied; but we found no element
|
|
|
|
// in the ancestors of startNode carrying specified styles;
|
|
|
|
// assume it comes from a rule and let's try to insert a span
|
|
|
|
// "inverting" the style
|
2018-02-02 12:42:25 +03:00
|
|
|
CSSEditUtils::IsCSSInvertible(*aProperty, aAttribute)) {
|
2015-04-19 15:28:50 +03:00
|
|
|
NS_NAMED_LITERAL_STRING(value, "-moz-editor-invert-value");
|
2016-05-01 16:11:52 +03:00
|
|
|
SetInlinePropertyOnNode(node, *aProperty, aAttribute, value);
|
2002-01-09 16:51:37 +03:00
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-13 09:16:24 +03:00
|
|
|
|
|
|
|
if (cancel) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-10-30 13:02:58 +03:00
|
|
|
rv = rules->DidDoAction(subActionInfo, rv);
|
2018-08-13 09:16:24 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2015-04-19 15:28:50 +03:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
HTMLEditor::IncreaseFontSize() {
|
2018-10-30 13:00:17 +03:00
|
|
|
AutoEditActionDataSetter editActionData(*this,
|
|
|
|
EditAction::eIncrementFontSize);
|
|
|
|
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
2015-04-24 14:27:36 +03:00
|
|
|
return RelativeFontChange(FontSize::incr);
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
HTMLEditor::DecreaseFontSize() {
|
2018-10-30 13:00:17 +03:00
|
|
|
AutoEditActionDataSetter editActionData(*this,
|
|
|
|
EditAction::eDecrementFontSize);
|
|
|
|
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
2015-04-24 14:27:36 +03:00
|
|
|
return RelativeFontChange(FontSize::decr);
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::RelativeFontChange(FontSize aDir) {
|
2018-10-30 13:04:08 +03:00
|
|
|
MOZ_ASSERT(IsEditActionDataAvailable());
|
|
|
|
|
2017-08-08 05:25:36 +03:00
|
|
|
CommitComposition();
|
2001-01-28 23:13:07 +03:00
|
|
|
|
2015-04-24 14:27:36 +03:00
|
|
|
// If selection is collapsed, set typing state
|
2018-10-30 13:04:08 +03:00
|
|
|
if (SelectionRefPtr()->IsCollapsed()) {
|
2017-10-03 01:05:19 +03:00
|
|
|
nsAtom& atom = aDir == FontSize::incr ? *nsGkAtoms::big : *nsGkAtoms::small;
|
2002-01-09 16:51:37 +03:00
|
|
|
|
|
|
|
// Let's see in what kind of element the selection is
|
2018-10-30 13:04:08 +03:00
|
|
|
if (NS_WARN_IF(!SelectionRefPtr()->RangeCount())) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
RefPtr<nsRange> firstRange = SelectionRefPtr()->GetRangeAt(0);
|
|
|
|
if (NS_WARN_IF(!firstRange) ||
|
|
|
|
NS_WARN_IF(!firstRange->GetStartContainer())) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
OwningNonNull<nsINode> selectedNode = *firstRange->GetStartContainer();
|
2015-04-24 14:27:36 +03:00
|
|
|
if (IsTextNode(selectedNode)) {
|
2018-10-30 13:04:08 +03:00
|
|
|
if (NS_WARN_IF(!selectedNode->GetParentNode())) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-04-24 14:27:36 +03:00
|
|
|
selectedNode = *selectedNode->GetParentNode();
|
2002-01-09 16:51:37 +03:00
|
|
|
}
|
2015-04-24 14:27:36 +03:00
|
|
|
if (!CanContainTag(selectedNode, atom)) {
|
2012-05-01 10:34:52 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2002-01-09 16:51:37 +03:00
|
|
|
|
2015-04-24 14:27:36 +03:00
|
|
|
// Manipulating text attributes on a collapsed selection only sets state
|
|
|
|
// for the next text insertion
|
2017-12-26 06:25:45 +03:00
|
|
|
mTypeInState->SetProp(&atom, nullptr, EmptyString());
|
2012-05-05 13:00:06 +04:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2015-04-24 14:27:36 +03:00
|
|
|
|
|
|
|
// Wrap with txn batching, rules sniffing, and selection preservation code
|
2018-10-24 12:42:06 +03:00
|
|
|
AutoPlaceholderBatch treatAsOneTransaction(*this);
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 14:12:34 +03:00
|
|
|
AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
|
2018-05-28 16:05:10 +03:00
|
|
|
*this, EditSubAction::eSetTextProperty, nsIEditor::eNext);
|
2018-10-30 13:01:38 +03:00
|
|
|
AutoSelectionRestorer restoreSelectionLater(*this);
|
2018-08-01 15:30:14 +03:00
|
|
|
AutoTransactionsConserveSelection dontChangeMySelection(*this);
|
2001-01-28 23:13:07 +03:00
|
|
|
|
2015-04-24 14:27:36 +03:00
|
|
|
// Loop through the ranges in the selection
|
2018-10-30 13:04:08 +03:00
|
|
|
AutoRangeArray arrayOfRanges(SelectionRefPtr());
|
2017-06-23 10:46:54 +03:00
|
|
|
for (auto& range : arrayOfRanges.mRanges) {
|
2015-04-24 14:27:36 +03:00
|
|
|
// Adjust range to include any ancestors with entirely selected children
|
2016-10-19 12:09:33 +03:00
|
|
|
nsresult rv = PromoteInlineRange(*range);
|
2018-10-30 13:04:08 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-04-24 14:27:36 +03:00
|
|
|
|
|
|
|
// Check for easy case: both range endpoints in same text node
|
2017-07-11 14:53:04 +03:00
|
|
|
nsCOMPtr<nsINode> startNode = range->GetStartContainer();
|
2017-07-11 15:11:37 +03:00
|
|
|
nsCOMPtr<nsINode> endNode = range->GetEndContainer();
|
2015-04-24 14:27:36 +03:00
|
|
|
if (startNode == endNode && IsTextNode(startNode)) {
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = RelativeFontChangeOnTextNode(aDir, *startNode->GetAsText(),
|
|
|
|
range->StartOffset(),
|
|
|
|
range->EndOffset());
|
2018-10-30 13:04:08 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-04-24 14:27:36 +03:00
|
|
|
} else {
|
|
|
|
// Not the easy case. Range not contained in single text node. There
|
|
|
|
// are up to three phases here. There are all the nodes reported by the
|
|
|
|
// subtree iterator to be processed. And there are potentially a
|
|
|
|
// starting textnode and an ending textnode which are only partially
|
|
|
|
// contained by the range.
|
|
|
|
|
|
|
|
// Let's handle the nodes reported by the iterator. These nodes are
|
|
|
|
// entirely contained in the selection range. We build up a list of them
|
|
|
|
// (since doing operations on the document during iteration would perturb
|
|
|
|
// the iterator).
|
|
|
|
|
|
|
|
// Iterate range and build up array
|
2019-01-11 04:52:26 +03:00
|
|
|
ContentSubtreeIterator subtreeIter;
|
|
|
|
rv = subtreeIter.Init(range);
|
2016-10-19 12:09:33 +03:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
2015-04-24 14:27:36 +03:00
|
|
|
nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
|
2019-01-11 04:52:26 +03:00
|
|
|
for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
|
|
|
|
if (NS_WARN_IF(!subtreeIter.GetCurrentNode()->IsContent())) {
|
2018-10-30 13:04:08 +03:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2019-01-11 04:49:00 +03:00
|
|
|
OwningNonNull<nsIContent> node =
|
2019-01-11 04:52:26 +03:00
|
|
|
*subtreeIter.GetCurrentNode()->AsContent();
|
2004-01-24 03:46:17 +03:00
|
|
|
|
2012-05-18 12:29:39 +04:00
|
|
|
if (IsEditable(node)) {
|
2015-04-24 14:27:36 +03:00
|
|
|
arrayOfNodes.AppendElement(node);
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
}
|
2015-04-24 14:27:36 +03:00
|
|
|
|
|
|
|
// Now that we have the list, do the font size change on each node
|
|
|
|
for (auto& node : arrayOfNodes) {
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = RelativeFontChangeOnNode(aDir == FontSize::incr ? +1 : -1, node);
|
2018-10-30 13:04:08 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
}
|
2015-04-24 14:27:36 +03:00
|
|
|
// Now check the start and end parents of the range to see if they need
|
|
|
|
// to be separately handled (they do if they are text nodes, due to how
|
|
|
|
// the subtree iterator works - it will not have reported them).
|
|
|
|
if (IsTextNode(startNode) && IsEditable(startNode)) {
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = RelativeFontChangeOnTextNode(aDir, *startNode->GetAsText(),
|
|
|
|
range->StartOffset(),
|
|
|
|
startNode->Length());
|
2018-10-30 13:04:08 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2015-04-24 14:27:36 +03:00
|
|
|
if (IsTextNode(endNode) && IsEditable(endNode)) {
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = RelativeFontChangeOnTextNode(aDir, *endNode->GetAsText(), 0,
|
|
|
|
range->EndOffset());
|
2018-10-30 13:04:08 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-24 14:27:36 +03:00
|
|
|
|
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::RelativeFontChangeOnTextNode(FontSize aDir,
|
|
|
|
Text& aTextNode,
|
|
|
|
int32_t aStartOffset,
|
|
|
|
int32_t aEndOffset) {
|
2016-05-01 16:12:27 +03:00
|
|
|
// Don't need to do anything if no characters actually selected
|
|
|
|
if (aStartOffset == aEndOffset) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2016-05-01 16:12:27 +03:00
|
|
|
if (!aTextNode.GetParentNode() ||
|
|
|
|
!CanContainTag(*aTextNode.GetParentNode(), *nsGkAtoms::big)) {
|
2012-05-01 10:34:52 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2002-01-09 16:51:37 +03:00
|
|
|
|
2001-01-28 23:13:07 +03:00
|
|
|
// -1 is a magic value meaning to the end of node
|
2016-05-01 16:12:27 +03:00
|
|
|
if (aEndOffset == -1) {
|
|
|
|
aEndOffset = aTextNode.Length();
|
|
|
|
}
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2017-11-12 05:30:30 +03:00
|
|
|
// Make the range an independent node.
|
|
|
|
nsCOMPtr<nsIContent> textNodeForTheRange = &aTextNode;
|
|
|
|
|
|
|
|
// Split at the end of the range.
|
|
|
|
EditorRawDOMPoint atEnd(textNodeForTheRange, aEndOffset);
|
|
|
|
if (!atEnd.IsEndOfContainer()) {
|
2016-05-01 16:12:27 +03:00
|
|
|
// We need to split off back of text node
|
2017-11-12 05:30:30 +03:00
|
|
|
ErrorResult error;
|
2018-04-09 20:16:49 +03:00
|
|
|
textNodeForTheRange = SplitNodeWithTransaction(atEnd, error);
|
2017-11-12 05:30:30 +03:00
|
|
|
if (NS_WARN_IF(error.Failed())) {
|
|
|
|
return error.StealNSResult();
|
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2017-11-12 05:30:30 +03:00
|
|
|
|
|
|
|
// Split at the start of the range.
|
|
|
|
EditorRawDOMPoint atStart(textNodeForTheRange, aStartOffset);
|
|
|
|
if (!atStart.IsStartOfContainer()) {
|
2016-05-01 16:12:27 +03:00
|
|
|
// We need to split off front of text node
|
2017-11-12 05:30:30 +03:00
|
|
|
ErrorResult error;
|
2018-04-09 20:16:49 +03:00
|
|
|
nsCOMPtr<nsIContent> newLeftNode = SplitNodeWithTransaction(atStart, error);
|
2017-11-12 05:30:30 +03:00
|
|
|
if (NS_WARN_IF(error.Failed())) {
|
|
|
|
return error.StealNSResult();
|
|
|
|
}
|
|
|
|
Unused << newLeftNode;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2001-12-07 18:28:47 +03:00
|
|
|
|
2016-05-01 16:12:27 +03:00
|
|
|
// Look for siblings that are correct type of node
|
2017-10-03 01:05:19 +03:00
|
|
|
nsAtom* nodeType = aDir == FontSize::incr ? nsGkAtoms::big : nsGkAtoms::small;
|
2017-11-12 05:30:30 +03:00
|
|
|
nsCOMPtr<nsIContent> sibling = GetPriorHTMLSibling(textNodeForTheRange);
|
2015-03-03 14:08:59 +03:00
|
|
|
if (sibling && sibling->IsHTMLElement(nodeType)) {
|
2016-05-01 16:12:27 +03:00
|
|
|
// Previous sib is already right kind of inline node; slide this over
|
2018-04-12 17:58:52 +03:00
|
|
|
nsresult rv = MoveNodeToEndWithTransaction(*textNodeForTheRange, *sibling);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2016-05-01 16:12:27 +03:00
|
|
|
return NS_OK;
|
2001-10-22 10:22:52 +04:00
|
|
|
}
|
2017-11-12 05:30:30 +03:00
|
|
|
sibling = GetNextHTMLSibling(textNodeForTheRange);
|
2015-03-03 14:08:59 +03:00
|
|
|
if (sibling && sibling->IsHTMLElement(nodeType)) {
|
2016-05-01 16:12:27 +03:00
|
|
|
// Following sib is already right kind of inline node; slide this over
|
2018-04-12 17:58:52 +03:00
|
|
|
nsresult rv = MoveNodeWithTransaction(*textNodeForTheRange,
|
|
|
|
EditorRawDOMPoint(sibling, 0));
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2016-05-01 16:12:27 +03:00
|
|
|
return NS_OK;
|
2001-10-22 10:22:52 +04:00
|
|
|
}
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2016-05-01 16:12:27 +03:00
|
|
|
// Else reparent the node inside font node with appropriate relative size
|
2018-04-13 12:17:04 +03:00
|
|
|
RefPtr<Element> newElement =
|
|
|
|
InsertContainerWithTransaction(*textNodeForTheRange, *nodeType);
|
|
|
|
if (NS_WARN_IF(!newElement)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2014-08-20 16:25:16 +04:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::RelativeFontChangeHelper(int32_t aSizeChange,
|
|
|
|
nsINode* aNode) {
|
2012-05-18 12:29:39 +04:00
|
|
|
MOZ_ASSERT(aNode);
|
|
|
|
|
2001-10-22 10:22:52 +04:00
|
|
|
/* This routine looks for all the font nodes in the tree rooted by aNode,
|
|
|
|
including aNode itself, looking for font nodes that have the size attr
|
|
|
|
set. Any such nodes need to have big or small put inside them, since
|
|
|
|
they override any big/small that are above them.
|
|
|
|
*/
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2001-10-22 10:22:52 +04:00
|
|
|
// Can only change font size by + or - 1
|
2012-05-18 12:29:39 +04:00
|
|
|
if (aSizeChange != 1 && aSizeChange != -1) {
|
2001-10-22 10:22:52 +04:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
2012-05-18 12:29:39 +04:00
|
|
|
}
|
2001-10-22 10:22:52 +04:00
|
|
|
|
2012-05-18 12:29:39 +04:00
|
|
|
// If this is a font node with size, put big/small inside it.
|
2015-03-03 14:08:59 +03:00
|
|
|
if (aNode->IsHTMLElement(nsGkAtoms::font) &&
|
2012-05-18 12:29:39 +04:00
|
|
|
aNode->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::size)) {
|
|
|
|
// Cycle through children and adjust relative font size.
|
2017-08-04 20:12:46 +03:00
|
|
|
AutoTArray<nsCOMPtr<nsIContent>, 10> childList;
|
|
|
|
for (nsIContent* child = aNode->GetFirstChild(); child;
|
|
|
|
child = child->GetNextSibling()) {
|
|
|
|
childList.AppendElement(child);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& child : childList) {
|
|
|
|
nsresult rv = RelativeFontChangeOnNode(aSizeChange, child);
|
2012-05-18 12:29:39 +04:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2001-10-22 10:22:52 +04:00
|
|
|
}
|
2013-01-01 01:49:49 +04:00
|
|
|
|
|
|
|
// RelativeFontChangeOnNode already calls us recursively,
|
|
|
|
// so we don't need to check our children again.
|
|
|
|
return NS_OK;
|
2001-10-22 10:22:52 +04:00
|
|
|
}
|
|
|
|
|
2013-01-01 01:49:49 +04:00
|
|
|
// Otherwise cycle through the children.
|
2017-08-04 20:12:46 +03:00
|
|
|
AutoTArray<nsCOMPtr<nsIContent>, 10> childList;
|
|
|
|
for (nsIContent* child = aNode->GetFirstChild(); child;
|
|
|
|
child = child->GetNextSibling()) {
|
|
|
|
childList.AppendElement(child);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& child : childList) {
|
|
|
|
nsresult rv = RelativeFontChangeHelper(aSizeChange, child);
|
2012-05-18 12:29:39 +04:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2001-10-22 10:22:52 +04:00
|
|
|
}
|
2002-01-09 16:51:37 +03:00
|
|
|
|
2012-05-18 12:29:39 +04:00
|
|
|
return NS_OK;
|
2001-10-22 10:22:52 +04:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::RelativeFontChangeOnNode(int32_t aSizeChange,
|
|
|
|
nsIContent* aNode) {
|
2012-05-18 12:29:39 +04:00
|
|
|
MOZ_ASSERT(aNode);
|
2001-01-28 23:13:07 +03:00
|
|
|
// Can only change font size by + or - 1
|
2012-05-18 12:29:39 +04:00
|
|
|
if (aSizeChange != 1 && aSizeChange != -1) {
|
2001-01-28 23:13:07 +03:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
2012-05-18 12:29:39 +04:00
|
|
|
}
|
2001-01-28 23:13:07 +03:00
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
nsAtom* atom;
|
2012-05-01 10:34:52 +04:00
|
|
|
if (aSizeChange == 1) {
|
|
|
|
atom = nsGkAtoms::big;
|
|
|
|
} else {
|
|
|
|
atom = nsGkAtoms::small;
|
|
|
|
}
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2012-05-18 12:29:39 +04:00
|
|
|
// Is it the opposite of what we want?
|
2015-03-03 14:08:59 +03:00
|
|
|
if ((aSizeChange == 1 && aNode->IsHTMLElement(nsGkAtoms::small)) ||
|
|
|
|
(aSizeChange == -1 && aNode->IsHTMLElement(nsGkAtoms::big))) {
|
2001-10-22 10:22:52 +04:00
|
|
|
// first populate any nested font tags that have the size attr set
|
2012-05-18 12:29:39 +04:00
|
|
|
nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2001-01-28 23:13:07 +03:00
|
|
|
// in that case, just remove this node and pull up the children
|
2018-04-12 16:23:04 +03:00
|
|
|
return RemoveContainerWithTransaction(*aNode->AsElement());
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-05-18 12:29:39 +04:00
|
|
|
|
2001-01-28 23:13:07 +03:00
|
|
|
// can it be put inside a "big" or "small"?
|
2014-11-02 15:04:13 +03:00
|
|
|
if (TagCanContain(*atom, *aNode)) {
|
2001-10-22 10:22:52 +04:00
|
|
|
// first populate any nested font tags that have the size attr set
|
2012-05-18 12:29:39 +04:00
|
|
|
nsresult rv = RelativeFontChangeHelper(aSizeChange, aNode);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2001-01-28 23:13:07 +03:00
|
|
|
// ok, chuck it in.
|
2001-10-22 10:22:52 +04:00
|
|
|
// first look at siblings of aNode for matching bigs or smalls.
|
|
|
|
// if we find one, move aNode into it.
|
2012-05-18 12:29:39 +04:00
|
|
|
nsIContent* sibling = GetPriorHTMLSibling(aNode);
|
2015-03-03 14:08:59 +03:00
|
|
|
if (sibling && sibling->IsHTMLElement(atom)) {
|
2001-10-22 10:22:52 +04:00
|
|
|
// previous sib is already right kind of inline node; slide this over into
|
|
|
|
// it
|
2018-04-12 17:58:52 +03:00
|
|
|
return MoveNodeToEndWithTransaction(*aNode, *sibling);
|
2001-10-22 10:22:52 +04:00
|
|
|
}
|
2012-05-18 12:29:39 +04:00
|
|
|
|
|
|
|
sibling = GetNextHTMLSibling(aNode);
|
2015-03-03 14:08:59 +03:00
|
|
|
if (sibling && sibling->IsHTMLElement(atom)) {
|
2001-10-22 10:22:52 +04:00
|
|
|
// following sib is already right kind of inline node; slide this over
|
|
|
|
// into it
|
2018-04-12 17:58:52 +03:00
|
|
|
return MoveNodeWithTransaction(*aNode, EditorRawDOMPoint(sibling, 0));
|
2001-10-22 10:22:52 +04:00
|
|
|
}
|
2012-05-18 12:29:39 +04:00
|
|
|
|
2001-10-22 10:22:52 +04:00
|
|
|
// else insert it above aNode
|
2018-04-13 12:17:04 +03:00
|
|
|
RefPtr<Element> newElement = InsertContainerWithTransaction(*aNode, *atom);
|
|
|
|
if (NS_WARN_IF(!newElement)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2014-08-20 16:25:16 +04:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-05-18 12:29:39 +04:00
|
|
|
|
2001-01-28 23:13:07 +03:00
|
|
|
// none of the above? then cycle through the children.
|
|
|
|
// MOOSE: we should group the children together if possible
|
|
|
|
// into a single "big" or "small". For the moment they are
|
2015-05-28 18:58:42 +03:00
|
|
|
// each getting their own.
|
2017-08-04 20:12:46 +03:00
|
|
|
AutoTArray<nsCOMPtr<nsIContent>, 10> childList;
|
|
|
|
for (nsIContent* child = aNode->GetFirstChild(); child;
|
|
|
|
child = child->GetNextSibling()) {
|
|
|
|
childList.AppendElement(child);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& child : childList) {
|
|
|
|
nsresult rv = RelativeFontChangeOnNode(aSizeChange, child);
|
2012-05-18 12:29:39 +04:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2012-05-18 12:29:39 +04:00
|
|
|
|
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2015-05-28 18:58:42 +03:00
|
|
|
NS_IMETHODIMP
|
2016-07-09 05:42:33 +03:00
|
|
|
HTMLEditor::GetFontFaceState(bool* aMixed, nsAString& outFace) {
|
2018-10-30 13:00:17 +03:00
|
|
|
if (NS_WARN_IF(!aMixed)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
2011-10-17 18:59:28 +04:00
|
|
|
*aMixed = true;
|
2003-07-18 06:27:19 +04:00
|
|
|
outFace.Truncate();
|
2001-04-07 04:45:26 +04:00
|
|
|
|
2018-10-30 13:00:17 +03:00
|
|
|
AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
|
|
|
|
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
2011-09-29 10:19:26 +04:00
|
|
|
bool first, any, all;
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2017-12-26 06:25:45 +03:00
|
|
|
nsresult rv = GetInlinePropertyBase(*nsGkAtoms::font, nsGkAtoms::face,
|
2017-03-14 05:26:36 +03:00
|
|
|
nullptr, &first, &any, &all, &outFace);
|
2016-10-19 12:09:33 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (any && !all) {
|
|
|
|
return NS_OK; // mixed
|
|
|
|
}
|
|
|
|
if (all) {
|
2011-10-17 18:59:28 +04:00
|
|
|
*aMixed = false;
|
2016-10-19 12:09:33 +03:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2003-06-12 18:51:37 +04:00
|
|
|
// if there is no font face, check for tt
|
2016-10-19 12:09:33 +03:00
|
|
|
rv = GetInlinePropertyBase(*nsGkAtoms::tt, nullptr, nullptr, &first, &any,
|
|
|
|
&all, nullptr);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (any && !all) {
|
|
|
|
return rv; // mixed
|
|
|
|
}
|
|
|
|
if (all) {
|
2011-10-17 18:59:28 +04:00
|
|
|
*aMixed = false;
|
2014-08-29 15:43:24 +04:00
|
|
|
outFace.AssignLiteral("tt");
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2016-10-19 12:09:33 +03:00
|
|
|
if (!any) {
|
2001-01-28 23:13:07 +03:00
|
|
|
// there was no font face attrs of any kind. We are in normal font.
|
2003-07-18 06:27:19 +04:00
|
|
|
outFace.Truncate();
|
2011-10-17 18:59:28 +04:00
|
|
|
*aMixed = false;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2016-10-19 12:09:33 +03:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::GetFontColorState(bool* aMixed, nsAString& aOutColor) {
|
2018-08-27 09:52:35 +03:00
|
|
|
if (NS_WARN_IF(!aMixed)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
2011-10-17 18:59:28 +04:00
|
|
|
*aMixed = true;
|
2003-07-18 06:27:19 +04:00
|
|
|
aOutColor.Truncate();
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2018-10-30 13:00:17 +03:00
|
|
|
AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
|
|
|
|
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
2011-09-29 10:19:26 +04:00
|
|
|
bool first, any, all;
|
2017-12-26 06:25:45 +03:00
|
|
|
nsresult rv = GetInlinePropertyBase(*nsGkAtoms::font, nsGkAtoms::color,
|
2017-03-14 05:26:36 +03:00
|
|
|
nullptr, &first, &any, &all, &aOutColor);
|
2018-08-27 09:52:35 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2016-10-19 12:09:33 +03:00
|
|
|
if (any && !all) {
|
|
|
|
return NS_OK; // mixed
|
|
|
|
}
|
|
|
|
if (all) {
|
2011-10-17 18:59:28 +04:00
|
|
|
*aMixed = false;
|
2016-10-19 12:09:33 +03:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2015-05-28 18:58:42 +03:00
|
|
|
|
2016-10-19 12:09:33 +03:00
|
|
|
if (!any) {
|
2001-01-28 23:13:07 +03:00
|
|
|
// there was no font color attrs of any kind..
|
2003-07-18 06:27:19 +04:00
|
|
|
aOutColor.Truncate();
|
2011-10-17 18:59:28 +04:00
|
|
|
*aMixed = false;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
2016-10-19 12:09:33 +03:00
|
|
|
return NS_OK;
|
2001-01-28 23:13:07 +03:00
|
|
|
}
|
|
|
|
|
2002-01-09 16:51:37 +03:00
|
|
|
// the return value is true only if the instance of the HTML editor we created
|
|
|
|
// can handle CSS styles (for instance, Composer can, Messenger can't) and if
|
|
|
|
// the CSS preference is checked
|
2018-08-28 12:46:53 +03:00
|
|
|
NS_IMETHODIMP
|
2016-07-09 05:42:33 +03:00
|
|
|
HTMLEditor::GetIsCSSEnabled(bool* aIsCSSEnabled) {
|
2012-02-01 14:54:22 +04:00
|
|
|
*aIsCSSEnabled = IsCSSEnabled();
|
2002-01-09 16:51:37 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
static bool HasNonEmptyAttribute(Element* aElement, nsAtom* aName) {
|
2012-02-01 14:54:22 +04:00
|
|
|
MOZ_ASSERT(aElement);
|
|
|
|
|
|
|
|
nsAutoString value;
|
|
|
|
return aElement->GetAttr(kNameSpaceID_None, aName, value) && !value.IsEmpty();
|
|
|
|
}
|
2002-02-18 13:49:15 +03:00
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
bool HTMLEditor::HasStyleOrIdOrClass(Element* aElement) {
|
2012-02-01 14:54:22 +04:00
|
|
|
MOZ_ASSERT(aElement);
|
2002-02-18 13:49:15 +03:00
|
|
|
|
|
|
|
// remove the node if its style attribute is empty or absent,
|
|
|
|
// and if it does not have a class nor an id
|
2012-02-01 14:54:22 +04:00
|
|
|
return HasNonEmptyAttribute(aElement, nsGkAtoms::style) ||
|
|
|
|
HasNonEmptyAttribute(aElement, nsGkAtoms::_class) ||
|
|
|
|
HasNonEmptyAttribute(aElement, nsGkAtoms::id);
|
2002-02-18 13:49:15 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 05:42:33 +03:00
|
|
|
nsresult HTMLEditor::RemoveElementIfNoStyleOrIdOrClass(Element& aElement) {
|
2002-02-18 13:49:15 +03:00
|
|
|
// early way out if node is not the right kind of element
|
2015-05-13 18:58:25 +03:00
|
|
|
if ((!aElement.IsHTMLElement(nsGkAtoms::span) &&
|
|
|
|
!aElement.IsHTMLElement(nsGkAtoms::font)) ||
|
|
|
|
HasStyleOrIdOrClass(&aElement)) {
|
2002-02-18 13:49:15 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2012-02-01 14:54:22 +04:00
|
|
|
|
2018-04-12 16:23:04 +03:00
|
|
|
return RemoveContainerWithTransaction(aElement);
|
2002-02-18 13:49:15 +03:00
|
|
|
}
|
2016-07-09 05:42:33 +03:00
|
|
|
|
|
|
|
} // namespace mozilla
|