Bug 253870 - Make disabled form controls selectable. r=masayuki,MarcoZ

This rejiggers a bit the way selection focus is handled so that focusing a
disabled form control with the mouse handles selection properly, and hides the
document selection and so on.

This matches the behavior of other browsers as far as I can tell.

Given now readonly and disabled editors behave the same, we can simplify a bit
the surrounding editor code.

Differential Revision: https://phabricator.services.mozilla.com/D66464

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Emilio Cobos Álvarez 2020-03-19 13:18:16 +00:00
Родитель e4db0e4496
Коммит 04828e3a4a
32 изменённых файлов: 347 добавлений и 311 удалений

Просмотреть файл

@ -126,6 +126,12 @@
];
this.invoke = function changeDOMSelection_invoke() {
// HyperTextAccessible::GetSelectionDOMRanges ignores hidden selections.
// Here we may be focusing an editable element (and thus hiding the
// main document selection), so blur it so that we test what we want to
// test.
document.activeElement.blur();
var sel = window.getSelection();
var range = document.createRange();
range.setStart(getNode(aNodeID1), aNodeOffset1);

Просмотреть файл

@ -5933,6 +5933,29 @@ Nullable<WindowProxyHolder> Document::GetDefaultView() const {
return WindowProxyHolder(win->GetBrowsingContext());
}
nsIContent* Document::GetUnretargetedFocusedContent() const {
nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
if (!window) {
return nullptr;
}
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
window, nsFocusManager::eOnlyCurrentWindow,
getter_AddRefs(focusedWindow));
if (!focusedContent) {
return nullptr;
}
// be safe and make sure the element is from this document
if (focusedContent->OwnerDoc() != this) {
return nullptr;
}
if (focusedContent->ChromeOnlyAccess()) {
return focusedContent->FindFirstNonChromeOnlyAccessContent();
}
return focusedContent;
}
Element* Document::GetActiveElement() {
// Get the focused element.
Element* focusedElement = GetRetargetedFocusedElement();

Просмотреть файл

@ -3293,6 +3293,7 @@ class Document : public nsINode,
mozilla::ErrorResult& rv);
Nullable<WindowProxyHolder> GetDefaultView() const;
Element* GetActiveElement();
nsIContent* GetUnretargetedFocusedContent() const;
bool HasFocus(ErrorResult& rv) const;
void GetDesignMode(nsAString& aDesignMode);
void SetDesignMode(const nsAString& aDesignMode,

Просмотреть файл

@ -256,25 +256,13 @@ nsIContent* DocumentOrShadowRoot::Retarget(nsIContent* aContent) const {
}
Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
if (nsCOMPtr<nsPIDOMWindowOuter> window = AsNode().OwnerDoc()->GetWindow()) {
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
window, nsFocusManager::eOnlyCurrentWindow,
getter_AddRefs(focusedWindow));
// be safe and make sure the element is from this document
if (focusedContent && focusedContent->OwnerDoc() == AsNode().OwnerDoc()) {
if (focusedContent->ChromeOnlyAccess()) {
focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
}
if (focusedContent) {
if (nsIContent* retarget = Retarget(focusedContent)) {
return retarget->AsElement();
}
}
}
auto* content = AsNode().OwnerDoc()->GetUnretargetedFocusedContent();
if (!content) {
return nullptr;
}
if (nsIContent* retarget = Retarget(content)) {
return retarget->AsElement();
}
return nullptr;
}

Просмотреть файл

@ -17,6 +17,7 @@
#include "imgIContainer.h"
#include "imgIRequest.h"
#include "nsFocusManager.h"
#include "nsFrameSelection.h"
#include "mozilla/dom/DataTransfer.h"
#include "nsIDocShell.h"
@ -673,37 +674,27 @@ static nsresult AppendImagePromise(nsITransferable* aTransferable,
}
#endif // XP_WIN
nsIContent* nsCopySupport::GetSelectionForCopy(Document* aDocument,
Selection** aSelection) {
*aSelection = nullptr;
already_AddRefed<Selection> nsCopySupport::GetSelectionForCopy(
Document* aDocument) {
PresShell* presShell = aDocument->GetPresShell();
if (!presShell) {
return nullptr;
}
nsCOMPtr<nsIContent> focusedContent;
nsCOMPtr<nsISelectionController> selectionController =
presShell->GetSelectionControllerForFocusedContent(
getter_AddRefs(focusedContent));
if (!selectionController) {
RefPtr<nsFrameSelection> frameSel = presShell->GetLastFocusedFrameSelection();
if (!frameSel) {
return nullptr;
}
RefPtr<Selection> sel = selectionController->GetSelection(
nsISelectionController::SELECTION_NORMAL);
sel.forget(aSelection);
return focusedContent;
RefPtr<Selection> sel = frameSel->GetSelection(SelectionType::eNormal);
return sel.forget();
}
bool nsCopySupport::CanCopy(Document* aDocument) {
if (!aDocument) return false;
RefPtr<Selection> sel;
GetSelectionForCopy(aDocument, getter_AddRefs(sel));
NS_ENSURE_TRUE(sel, false);
return !sel->IsCollapsed();
RefPtr<Selection> sel = GetSelectionForCopy(aDocument);
return sel && !sel->IsCollapsed();
}
static bool IsInsideRuby(nsINode* aNode) {
@ -717,7 +708,6 @@ static bool IsInsideRuby(nsINode* aNode) {
static bool IsSelectionInsideRuby(Selection* aSelection) {
uint32_t rangeCount = aSelection->RangeCount();
;
for (auto i : IntegerRange(rangeCount)) {
nsRange* range = aSelection->GetRangeAt(i);
if (!IsInsideRuby(range->GetClosestCommonInclusiveAncestor())) {
@ -776,7 +766,7 @@ bool nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
// If a selection was not supplied, try to find it.
RefPtr<Selection> sel = aSelection;
if (!sel) {
GetSelectionForCopy(doc, getter_AddRefs(sel));
sel = GetSelectionForCopy(doc);
}
// Retrieve the event target node from the start of the selection.

Просмотреть файл

@ -69,8 +69,8 @@ class nsCopySupport {
* and this focused content node returned. Otherwise, aSelection will be
* set to the document's selection and null will be returned.
*/
static nsIContent* GetSelectionForCopy(mozilla::dom::Document* aDocument,
mozilla::dom::Selection** aSelection);
static already_AddRefed<mozilla::dom::Selection> GetSelectionForCopy(
mozilla::dom::Document* aDocument);
/**
* Returns true if a copy operation is currently permitted based on the

Просмотреть файл

@ -83,6 +83,23 @@ interface nsISelectionController : nsISelectionDisplay
[noscript,nostdcall,notxpcom,binaryname(GetSelection)]
Selection getDOMSelection(in short aType);
/**
* Called when the selection controller should take the focus.
*
* This will take care to hide the previously-focused selection, show this
* selection, and repaint both.
*/
[noscript,nostdcall,notxpcom]
void selectionWillTakeFocus();
/**
* Called when the selection controller has lost the focus.
*
* This will take care to hide and repaint the selection.
*/
[noscript,nostdcall,notxpcom]
void selectionWillLoseFocus();
const short SCROLL_SYNCHRONOUS = 1<<1;
const short SCROLL_FIRST_ANCESTOR_ONLY = 1<<2;
const short SCROLL_CENTER_VERTICALLY = 1<<4;

Просмотреть файл

@ -635,6 +635,7 @@ skip-if = toolkit == 'android' || headless #bug 904183
skip-if = toolkit == 'android' || headless #bug 904183
[test_copypaste.xhtml]
skip-if = headless #bug 904183
[test_copypaste_disabled.html]
[test_createHTMLDocument.html]
[test_data_uri.html]
skip-if = verify

Просмотреть файл

@ -0,0 +1,90 @@
<!doctype html>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="copypaste.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<style>
@font-face {
font-family: Ahem;
src: url("Ahem.ttf");
}
body { font-family: Ahem; font-size: 20px; }
input, textarea {
font: inherit;
-moz-appearance: none;
padding: 0;
border: 0;
scrollbar-width: none;
}
</style>
<input id="disabled-input" disabled value="abcd"> efgh <br> <textarea rows=1 id="disabled-textarea" disabled>ijkl</textarea> mnop
<script>
function dragSelect(e, x1, x2, x3) {
dir = x2 > x1 ? 1 : -1;
synthesizeMouse(e, x1, 5, { type: "mousedown" });
synthesizeMouse(e, x1 + dir, 5, { type: "mousemove" });
if (x3)
synthesizeMouse(e, x3, 5, { type: "mousemove" });
synthesizeMouse(e, x2 - dir, 5, { type: "mousemove" });
synthesizeMouse(e, x2, 5, { type: "mouseup" });
}
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(async function() {
const docShell = SpecialPowers.wrap(window).docShell;
const documentViewer = docShell.contentViewer.QueryInterface(
SpecialPowers.Ci.nsIContentViewerEdit
);
const clipboard = SpecialPowers.Services.clipboard;
function copySelectionToClipboard() {
return SimpleTest.promiseClipboardChange(
() => true,
() => {
documentViewer.copySelection();
}
);
}
function getLoadContext() {
return docShell.QueryInterface(SpecialPowers.Ci.nsILoadContext);
}
function getClipboardData(mime) {
var transferable = SpecialPowers.Cc[
"@mozilla.org/widget/transferable;1"
].createInstance(SpecialPowers.Ci.nsITransferable);
transferable.init(getLoadContext());
transferable.addDataFlavor(mime);
clipboard.getData(transferable, 1);
var data = SpecialPowers.createBlankObject();
transferable.getTransferData(mime, data);
return data;
}
function testClipboardValue(mime, expected) {
var data = SpecialPowers.wrap(getClipboardData(mime));
is(
data.value == null
? data.value
: data.value.QueryInterface(SpecialPowers.Ci.nsISupportsString).data,
expected,
mime + " value in the clipboard"
);
return data.value;
}
for (let id of ["disabled-input", "disabled-textarea"]) {
let element = document.getElementById(id);
dragSelect(element, 0, 60);
await copySelectionToClipboard();
testClipboardValue("text/unicode", element.value.substr(0, 3));
}
SimpleTest.finish();
});
</script>

Просмотреть файл

@ -317,16 +317,15 @@ nsresult ContentEventHandler::InitCommon(SelectionType aSelectionType,
nsresult rv = InitBasic(aRequireFlush);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelectionController> selectionController;
RefPtr<nsFrameSelection> frameSel;
if (PresShell* presShell = mDocument->GetPresShell()) {
selectionController = presShell->GetSelectionControllerForFocusedContent();
frameSel = presShell->GetLastFocusedFrameSelection();
}
if (NS_WARN_IF(!selectionController)) {
if (NS_WARN_IF(!frameSel)) {
return NS_ERROR_NOT_AVAILABLE;
}
mSelection =
selectionController->GetSelection(ToRawSelectionType(aSelectionType));
mSelection = frameSel->GetSelection(aSelectionType);
if (NS_WARN_IF(!mSelection)) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -335,8 +334,7 @@ nsresult ContentEventHandler::InitCommon(SelectionType aSelectionType,
if (mSelection->Type() == SelectionType::eNormal) {
normalSelection = mSelection;
} else {
normalSelection = selectionController->GetSelection(
nsISelectionController::SELECTION_NORMAL);
normalSelection = frameSel->GetSelection(SelectionType::eNormal);
if (NS_WARN_IF(!normalSelection)) {
return NS_ERROR_NOT_AVAILABLE;
}

Просмотреть файл

@ -5165,7 +5165,7 @@ nsresult EventStateManager::HandleMiddleClickPaste(
if (NS_WARN_IF(!document)) {
return NS_ERROR_FAILURE;
}
nsCopySupport::GetSelectionForCopy(document, getter_AddRefs(selection));
selection = nsCopySupport::GetSelectionForCopy(document);
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
@ -5214,8 +5214,7 @@ nsresult EventStateManager::HandleMiddleClickPaste(
}
// Check if the editor is still the good target to paste.
if (aTextEditor->Destroyed() || aTextEditor->IsReadonly() ||
aTextEditor->IsDisabled()) {
if (aTextEditor->Destroyed() || aTextEditor->IsReadonly()) {
// XXX Should we consume the event when the editor is readonly and/or
// disabled?
return NS_OK;

Просмотреть файл

@ -6639,18 +6639,13 @@ void HTMLInputElement::OnValueChanged(ValueChangeKind aKind) {
}
bool HTMLInputElement::HasCachedSelection() {
bool isCached = false;
TextControlState* state = GetEditorState();
if (state) {
isCached = state->IsSelectionCached() &&
state->HasNeverInitializedBefore() &&
state->GetSelectionProperties().GetStart() !=
state->GetSelectionProperties().GetEnd();
if (isCached) {
state->WillInitEagerly();
}
if (!state) {
return false;
}
return isCached;
return state->IsSelectionCached() && state->HasNeverInitializedBefore() &&
state->GetSelectionProperties().GetStart() !=
state->GetSelectionProperties().GetEnd();
}
void HTMLInputElement::FieldSetDisabledChanged(bool aNotify) {

Просмотреть файл

@ -174,10 +174,6 @@ class RestoreSelectionState : public Runnable {
mFrame->SetSelectionRange(properties.GetStart(), properties.GetEnd(),
properties.GetDirection());
}
if (!mTextControlState->mSelectionRestoreEagerInit) {
mTextControlState->HideSelectionIfBlurred();
}
mTextControlState->mSelectionRestoreEagerInit = false;
}
if (mTextControlState) {
@ -217,7 +213,6 @@ class MOZ_RAII AutoRestoreEditorState final {
// appearing the method in profile. So, this class should check if it's
// necessary to call.
uint32_t flags = mSavedFlags;
flags &= ~(nsIEditor::eEditorDisabledMask);
flags &= ~(nsIEditor::eEditorReadonlyMask);
flags |= nsIEditor::eEditorDontEchoPassword;
if (mSavedFlags != flags) {
@ -361,6 +356,8 @@ class TextInputSelectionController final : public nsSupportsWeakReference,
int16_t aStartOffset,
int16_t aEndOffset,
bool* aRetval) override;
void SelectionWillTakeFocus() override;
void SelectionWillLoseFocus() override;
private:
RefPtr<nsFrameSelection> mFrameSelection;
@ -771,6 +768,22 @@ TextInputSelectionController::SelectAll() {
return frameSelection->SelectAll();
}
void TextInputSelectionController::SelectionWillTakeFocus() {
if (mFrameSelection) {
if (PresShell* shell = mFrameSelection->GetPresShell()) {
shell->FrameSelectionWillTakeFocus(*mFrameSelection);
}
}
}
void TextInputSelectionController::SelectionWillLoseFocus() {
if (mFrameSelection) {
if (PresShell* shell = mFrameSelection->GetPresShell()) {
shell->FrameSelectionWillLoseFocus(*mFrameSelection);
}
}
}
NS_IMETHODIMP
TextInputSelectionController::CheckVisibility(nsINode* node,
int16_t startOffset,
@ -1396,7 +1409,6 @@ TextControlState::TextControlState(TextControlElement* aOwningElement)
mEditorInitialized(false),
mValueTransferInProgress(false),
mSelectionCached(true),
mSelectionRestoreEagerInit(false),
mPlaceholderVisibility(false),
mPreviewVisibility(false)
// When adding more member variable initializations here, add the same
@ -1419,7 +1431,6 @@ TextControlState* TextControlState::Construct(
state->mEditorInitialized = false;
state->mValueTransferInProgress = false;
state->mSelectionCached = true;
state->mSelectionRestoreEagerInit = false;
state->mPlaceholderVisibility = false;
state->mPreviewVisibility = false;
// When adding more member variable initializations here, add the same
@ -1640,7 +1651,9 @@ nsresult TextControlState::BindToFrame(nsTextControlFrame* aFrame) {
mTextListener = new TextInputListener(mTextCtrlElement);
mTextListener->SetFrame(mBoundFrame);
mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
// Editor will override this as needed from InitializeSelection.
mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
// Get the caret and make it a selection listener.
// FYI: It's safe to use raw pointer for calling
@ -1884,21 +1897,13 @@ nsresult TextControlState::PrepareEditor(const nsAString* aValue) {
editorFlags = newTextEditor->Flags();
// Check if the readonly attribute is set.
if (mTextCtrlElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly)) {
//
// TODO: Should probably call IsDisabled(), as it is cheaper.
if (mTextCtrlElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) ||
mTextCtrlElement->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
editorFlags |= nsIEditor::eEditorReadonlyMask;
}
// Check if the disabled attribute is set.
// TODO: call IsDisabled() here!
if (mTextCtrlElement->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
editorFlags |= nsIEditor::eEditorDisabledMask;
}
// Disable the selection if necessary.
if (newTextEditor->IsDisabled()) {
mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
}
SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
if (shouldInitializeEditor) {
@ -2385,6 +2390,10 @@ void TextControlState::UnbindFromFrame(nsTextControlFrame* aFrame) {
AutoTextControlHandlingState handlingUnbindFromFrame(
*this, TextControlAction::UnbindFromFrame);
if (mSelCon) {
mSelCon->SelectionWillLoseFocus();
}
// We need to start storing the value outside of the editor if we're not
// going to use it anymore, so retrieve it for now.
nsAutoString value;
@ -3087,13 +3096,6 @@ void TextControlState::UpdateOverlayTextVisibility(bool aNotify) {
}
}
void TextControlState::HideSelectionIfBlurred() {
MOZ_ASSERT(mSelCon, "Should have a selection controller if we have a frame!");
if (!nsContentUtils::IsFocusedContent(mTextCtrlElement)) {
mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
}
}
bool TextControlState::EditorHasComposition() {
return mTextEditor && mTextEditor->IsIMEComposing();
}

Просмотреть файл

@ -274,7 +274,6 @@ class TextControlState final : public SupportsWeakPtr<TextControlState> {
void SetPreviewText(const nsAString& aValue, bool aNotify);
void GetPreviewText(nsAString& aValue);
bool GetPreviewVisibility() { return mPreviewVisibility; }
void HideSelectionIfBlurred();
struct SelectionProperties {
public:
@ -315,7 +314,6 @@ class TextControlState final : public SupportsWeakPtr<TextControlState> {
bool IsSelectionCached() const { return mSelectionCached; }
SelectionProperties& GetSelectionProperties() { return mSelectionProperties; }
MOZ_CAN_RUN_SCRIPT void SetSelectionProperties(SelectionProperties& aProps);
void WillInitEagerly() { mSelectionRestoreEagerInit = true; }
bool HasNeverInitializedBefore() const { return !mEverInited; }
// Sync up our selection properties with our editor prior to being destroyed.
// This will invoke UnbindFromFrame() to ensure that we grab whatever
@ -456,8 +454,6 @@ class TextControlState final : public SupportsWeakPtr<TextControlState> {
bool mValueTransferInProgress; // Whether a value is being transferred to the
// frame
bool mSelectionCached; // Whether mSelectionProperties is valid
mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing
// because of selection restore
bool mPlaceholderVisibility;
bool mPreviewVisibility;

Просмотреть файл

@ -285,12 +285,6 @@ nsresult EditorBase::Init(Document& aDocument, Element* aRoot,
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsISelectionController::SetCaretReadOnly(false) failed, but ignored");
rvIgnored = selectionController->SetDisplaySelection(
nsISelectionController::SELECTION_ON);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsISelectionController::SetDisplaySelection(nsISelectionController::"
"SELECTION_ON) failed, but ignored");
// Show all the selection reflected to user.
rvIgnored =
selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
@ -309,7 +303,7 @@ nsresult EditorBase::Init(Document& aDocument, Element* aRoot,
// Make sure that the editor will be destroyed properly
mDidPreDestroy = false;
// Make sure that the ediotr will be created properly
// Make sure that the editor will be created properly
mDidPostCreate = false;
return NS_OK;
@ -2505,7 +2499,7 @@ nsresult EditorBase::GetPreferredIMEState(IMEState* aState) {
aState->mEnabled = IMEState::ENABLED;
aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
if (IsReadonly() || IsDisabled()) {
if (IsReadonly()) {
aState->mEnabled = IMEState::DISABLED;
return NS_OK;
}
@ -5039,7 +5033,7 @@ nsresult EditorBase::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
"HandleKeyPressEvent gets non-keypress event");
// if we are readonly or disabled, then do nothing.
if (IsReadonly() || IsDisabled()) {
if (IsReadonly()) {
// consume backspace for disabled and readonly textfields, to prevent
// back in history, which could be confusing to users
if (aKeyboardEvent->mKeyCode == NS_VK_BACK) {
@ -5134,21 +5128,13 @@ nsresult EditorBase::InitializeSelection(EventTarget* aFocusEventTarget) {
caret->SetIgnoreUserModify(targetNode->OwnerDoc()->HasFlag(NODE_IS_EDITABLE));
// Init selection
rvIgnored = selectionController->SetDisplaySelection(
nsISelectionController::SELECTION_ON);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsISelectionController::SetDisplaySelection() failed, but ignored");
rvIgnored =
selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsISelectionController::SetSelectionFlags() failed, but ignored");
rvIgnored = selectionController->RepaintSelection(
nsISelectionController::SELECTION_NORMAL);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsISelectionController::RepaintSelection() failed, but ignored");
selectionController->SelectionWillTakeFocus();
// If the computed selection root isn't root content, we should set it
// as selection ancestor limit. However, if that is root element, it means
@ -5192,26 +5178,6 @@ nsresult EditorBase::InitializeSelection(EventTarget* aFocusEventTarget) {
return NS_OK;
}
class RepaintSelectionRunner final : public Runnable {
public:
explicit RepaintSelectionRunner(nsISelectionController* aSelectionController)
: Runnable("RepaintSelectionRunner"),
mSelectionController(aSelectionController) {}
NS_IMETHOD Run() override {
DebugOnly<nsresult> rvIgnored = mSelectionController->RepaintSelection(
nsISelectionController::SELECTION_NORMAL);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsISelectionController::RepaintSelection(nsISelectionController::"
"SELECTION_NORMAL) failed, but ignored");
return NS_OK;
}
private:
nsCOMPtr<nsISelectionController> mSelectionController;
};
nsresult EditorBase::FinalizeSelection() {
nsCOMPtr<nsISelectionController> selectionController =
GetSelectionController();
@ -5243,58 +5209,11 @@ nsresult EditorBase::FinalizeSelection() {
return NS_ERROR_NOT_INITIALIZED;
}
focusManager->UpdateCaretForCaretBrowsingMode();
if (!HasIndependentSelection()) {
// If this editor doesn't have an independent selection, i.e., it must
// mean that it is an HTML editor, the selection controller is shared with
// presShell. So, even this editor loses focus, other part of the document
// may still have focus.
RefPtr<Document> doc = GetDocument();
ErrorResult ret;
if (!doc || !doc->HasFocus(ret)) {
// If the document already lost focus, mark the selection as disabled.
DebugOnly<nsresult> rvIgnored = selectionController->SetDisplaySelection(
nsISelectionController::SELECTION_DISABLED);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsISelectionController::SetDisplaySelection(nsISelectionController::"
"SELECTION_DISABLED) failed, but ignored");
} else {
// Otherwise, mark selection as normal because outside of a
// contenteditable element should be selected with normal selection
// color after here.
DebugOnly<nsresult> rvIgnored = selectionController->SetDisplaySelection(
nsISelectionController::SELECTION_ON);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsISelectionController::SetDisplaySelection(nsISelectionController::"
"SELECTION_ON) failed, but ignored");
if (nsCOMPtr<nsINode> node = do_QueryInterface(GetDOMEventTarget())) {
if (node->OwnerDoc()->GetUnretargetedFocusedContent() != node) {
selectionController->SelectionWillLoseFocus();
}
} else if (IsFormWidget() || IsPasswordEditor() || IsReadonly() ||
IsDisabled() || IsInputFiltered()) {
// In <input> or <textarea>, the independent selection should be hidden
// while this editor doesn't have focus.
DebugOnly<nsresult> rvIgnored = selectionController->SetDisplaySelection(
nsISelectionController::SELECTION_HIDDEN);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsISelectionController::SetDisplaySelection(nsISelectionController::"
"SELECTION_HIDDEN) failed, but ignored");
} else {
// Otherwise, although we're not sure how this case happens, the
// independent selection should be marked as disabled.
DebugOnly<nsresult> rvIgnored = selectionController->SetDisplaySelection(
nsISelectionController::SELECTION_DISABLED);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored),
"nsISelectionController::SetDisplaySelection(nsISelectionController::"
"SELECTION_DISABLED) failed, but ignored");
}
// FinalizeSelection might be called from ContentRemoved even if selection
// isn't updated. So we need to call RepaintSelection after updated it.
nsContentUtils::AddScriptRunner(
new RepaintSelectionRunner(selectionController));
return NS_OK;
}

Просмотреть файл

@ -491,10 +491,6 @@ class EditorBase : public nsIEditor,
return (mFlags & nsIEditor::eEditorReadonlyMask) != 0;
}
bool IsDisabled() const {
return (mFlags & nsIEditor::eEditorDisabledMask) != 0;
}
bool IsInputFiltered() const {
return (mFlags & nsIEditor::eEditorFilterInputMask) != 0;
}
@ -2565,8 +2561,7 @@ class EditorBase : public nsIEditor,
// Check for password/readonly/disabled, which are not spellchecked
// regardless of DOM. Also, check to see if spell check should be skipped
// or not.
return !IsPasswordEditor() && !IsReadonly() && !IsDisabled() &&
!ShouldSkipSpellCheck();
return !IsPasswordEditor() && !IsReadonly() && !ShouldSkipSpellCheck();
}
/**

Просмотреть файл

@ -670,7 +670,7 @@ nsresult EditorEventListener::MouseClick(WidgetMouseEvent* aMouseClickEvent) {
}
// nothing to do if editor isn't editable or clicked on out of the editor.
RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
if (textEditor->IsReadonly() || textEditor->IsDisabled() ||
if (textEditor->IsReadonly() ||
!textEditor->IsAcceptableInputEvent(aMouseClickEvent)) {
return NS_OK;
}
@ -840,8 +840,8 @@ nsresult EditorEventListener::DragOverOrDrop(DragEvent* aDragEvent) {
return NS_OK;
}
bool notEditable = !dropParentContent->IsEditable() ||
mEditorBase->IsReadonly() || mEditorBase->IsDisabled();
bool notEditable =
!dropParentContent->IsEditable() || mEditorBase->IsReadonly();
// First of all, hide caret if we won't insert the drop data into the editor
// obviously.
@ -977,7 +977,7 @@ bool EditorEventListener::DragEventHasSupportingData(
bool EditorEventListener::CanInsertAtDropPosition(DragEvent* aDragEvent) {
MOZ_ASSERT(
!DetachedFromEditorOrDefaultPrevented(aDragEvent->WidgetEventPtr()));
MOZ_ASSERT(!mEditorBase->IsReadonly() && !mEditorBase->IsDisabled());
MOZ_ASSERT(!mEditorBase->IsReadonly());
MOZ_ASSERT(DragEventHasSupportingData(aDragEvent));
// If there is no source node, this is probably an external drag and the
@ -1086,8 +1086,8 @@ nsresult EditorEventListener::HandleChangeComposition(
return NS_OK;
}
// if we are readonly or disabled, then do nothing.
if (textEditor->IsReadonly() || textEditor->IsDisabled()) {
// if we are readonly, then do nothing.
if (textEditor->IsReadonly()) {
return NS_OK;
}
@ -1117,11 +1117,7 @@ nsresult EditorEventListener::Focus(InternalFocusEvent* aFocusEvent) {
return NS_OK;
}
// Don't turn on selection and caret when the editor is disabled.
RefPtr<EditorBase> editorBase(mEditorBase);
if (editorBase->IsDisabled()) {
return NS_OK;
}
// Spell check a textarea the first time that it is focused.
SpellCheckIfNeeded();

Просмотреть файл

@ -15,6 +15,9 @@ class nsAtom;
namespace mozilla {
class HTMLEditUtils final {
using Element = dom::Element;
using Selection = dom::Selection;
public:
static bool IsInlineStyle(nsINode* aNode);
/**

Просмотреть файл

@ -662,7 +662,7 @@ nsresult HTMLEditor::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
// NOTE: When you change this method, you should also change:
// * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
if (IsReadonly() || IsDisabled()) {
if (IsReadonly()) {
// When we're not editable, the events are handled on EditorBase, so, we can
// bypass TextEditor.
nsresult rv = EditorBase::HandleKeyPressEvent(aKeyboardEvent);
@ -1675,7 +1675,7 @@ nsresult HTMLEditor::InsertElementAtSelectionAsAction(
"EditorBase::CommitComposition() failed, but ignored");
// XXX Oh, this should be done before dispatching `beforeinput` event.
if (IsReadonly() || IsDisabled()) {
if (IsReadonly()) {
return NS_OK;
}
@ -5349,7 +5349,7 @@ bool HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) {
nsresult HTMLEditor::GetPreferredIMEState(IMEState* aState) {
// HTML editor don't prefer the CSS ime-mode because IE didn't do so too.
aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
if (IsReadonly() || IsDisabled()) {
if (IsReadonly()) {
aState->mEnabled = IMEState::DISABLED;
} else {
aState->mEnabled = IMEState::ENABLED;

Просмотреть файл

@ -340,8 +340,8 @@ nsresult HTMLEditor::DoInsertHTMLWithContext(
"Selection::Collapse() failed, but ignored");
}
// XXX Why don't we test these first?
if (IsReadonly() || IsDisabled()) {
// XXX Why don't we test this first?
if (IsReadonly()) {
return NS_OK;
}
@ -1979,7 +1979,7 @@ nsresult HTMLEditor::PasteAsQuotationAsAction(int32_t aClipboardType,
MOZ_ASSERT(aClipboardType == nsIClipboard::kGlobalClipboard ||
aClipboardType == nsIClipboard::kSelectionClipboard);
if (IsReadonly() || IsDisabled()) {
if (IsReadonly()) {
return NS_OK;
}
@ -2162,7 +2162,7 @@ nsresult HTMLEditor::InsertWithQuotationsAsSubAction(
const nsAString& aQuotedText) {
MOZ_ASSERT(IsEditActionDataAvailable());
if (IsReadonly() || IsDisabled()) {
if (IsReadonly()) {
return NS_OK;
}
@ -2405,7 +2405,7 @@ nsresult HTMLEditor::InsertAsPlaintextQuotation(const nsAString& aQuotedText,
*aNodeInserted = nullptr;
}
if (IsReadonly() || IsDisabled()) {
if (IsReadonly()) {
return NS_OK;
}
@ -2656,7 +2656,7 @@ nsresult HTMLEditor::InsertAsCitedQuotationInternal(
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(!IsPlaintextEditor());
if (IsReadonly() || IsDisabled()) {
if (IsReadonly()) {
return NS_OK;
}

Просмотреть файл

@ -40,9 +40,9 @@ namespace mozilla {
using namespace dom;
#define CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED \
if (IsReadonly() || IsDisabled()) { \
return EditActionCanceled(NS_OK); \
#define CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY \
if (IsReadonly()) { \
return EditActionCanceled(NS_OK); \
}
nsresult TextEditor::InitEditorContentAndSelection() {
@ -189,7 +189,7 @@ EditActionResult TextEditor::InsertLineFeedCharacterAtSelection() {
UndefineCaretBidiLevel();
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY
if (mMaxTextLength >= 0) {
nsAutoString insertionString(NS_LITERAL_STRING("\n"));
@ -487,7 +487,7 @@ EditActionResult TextEditor::HandleInsertText(
}
// XXX Why don't we cancel here? Shouldn't we do this first?
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY
MaybeDoAutoPasswordMasking();
@ -630,7 +630,7 @@ EditActionResult TextEditor::SetTextWithoutTransaction(
UndefineCaretBidiLevel();
// XXX If we're setting value, shouldn't we keep setting the new value here?
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY
MaybeDoAutoPasswordMasking();
@ -748,7 +748,7 @@ EditActionResult TextEditor::HandleDeleteSelection(
UndefineCaretBidiLevel();
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY
// if there is only padding <br> element for empty editor, cancel the
// operation.

Просмотреть файл

@ -316,7 +316,7 @@ nsresult TextEditor::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
// And also when you add new key handling, you need to change the subclass's
// HandleKeyPressEvent()'s switch statement.
if (IsReadonly() || IsDisabled()) {
if (IsReadonly()) {
// When we're not editable, the events handled on EditorBase.
return EditorBase::HandleKeyPressEvent(aKeyboardEvent);
}
@ -1494,7 +1494,7 @@ NS_IMETHODIMP TextEditor::GetTextLength(int32_t* aCount) {
}
nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
if (aCount == 0 || IsReadonly() || IsDisabled()) {
if (aCount == 0 || IsReadonly()) {
return NS_OK;
}
@ -1575,7 +1575,7 @@ nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
}
nsresult TextEditor::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
if (aCount == 0 || IsReadonly() || IsDisabled()) {
if (aCount == 0 || IsReadonly()) {
return NS_OK;
}
@ -2016,7 +2016,7 @@ nsresult TextEditor::InsertWithQuotationsAsSubAction(
const nsAString& aQuotedText) {
MOZ_ASSERT(IsEditActionDataAvailable());
if (IsReadonly() || IsDisabled()) {
if (IsReadonly()) {
return NS_OK;
}

Просмотреть файл

@ -58,33 +58,31 @@ interface nsIEditor : nsISupports
const long eEditorPasswordMask = 0x0004;
// editing events are disabled. Editor may still accept focus.
const long eEditorReadonlyMask = 0x0008;
// all events are disabled (like scrolling). Editor will not accept focus.
const long eEditorDisabledMask = 0x0010;
// text input is limited to certain character types, use mFilter
const long eEditorFilterInputMask = 0x0020;
const long eEditorFilterInputMask = 0x0010;
// use mail-compose editing rules
const long eEditorMailMask = 0x0040;
const long eEditorMailMask = 0x0020;
// allow the editor to set font: monospace on the root node
const long eEditorEnableWrapHackMask = 0x0080;
const long eEditorEnableWrapHackMask = 0x0040;
// bit for widgets (form elements)
const long eEditorWidgetMask = 0x0100;
const long eEditorWidgetMask = 0x0080;
// this HTML editor should not create css styles
const long eEditorNoCSSMask = 0x0200;
const long eEditorNoCSSMask = 0x0100;
// whether HTML document specific actions are executed or not.
// e.g., if this flag is set, the editor doesn't handle Tab key.
// besides, anchors of HTML are not clickable.
const long eEditorAllowInteraction = 0x0400;
const long eEditorAllowInteraction = 0x0200;
// when this is set, the characters in password editor are always masked.
// see bug 530367 for the detail.
const long eEditorDontEchoPassword = 0x0800;
const long eEditorDontEchoPassword = 0x0400;
// when this flag is set, the internal direction of the editor is RTL.
// if neither of the direction flags are set, the direction is determined
// from the text control's content node.
const long eEditorRightToLeft = 0x1000;
const long eEditorRightToLeft = 0x0800;
// when this flag is set, the internal direction of the editor is LTR.
const long eEditorLeftToRight = 0x2000;
const long eEditorLeftToRight = 0x1000;
// when this flag is set, the editor's text content is not spell checked.
const long eEditorSkipSpellCheck = 0x4000;
const long eEditorSkipSpellCheck = 0x2000;
/*
* The valid values for newlines handling.

Просмотреть файл

@ -95,9 +95,9 @@ skip-if(Android) needs-focus == spellcheck-non-latin-korean.html spellcheck-non-
== caret_on_presshell_reinit.html caret_on_presshell_reinit-ref.html
fuzzy-if(browserIsRemote,0-255,0-3) asserts-if(browserIsRemote,0-3) == caret_on_presshell_reinit-2.html caret_on_presshell_reinit-ref.html # bug 959132 for assertions
fuzzy-if(asyncPan&&!layersGPUAccelerated,0-102,0-2824) == 642800.html 642800-ref.html
== selection_visibility_after_reframe.html selection_visibility_after_reframe-ref.html
!= selection_visibility_after_reframe-2.html selection_visibility_after_reframe-ref.html
!= selection_visibility_after_reframe-3.html selection_visibility_after_reframe-ref.html
needs-focus == selection_visibility_after_reframe.html selection_visibility_after_reframe-ref.html
needs-focus != selection_visibility_after_reframe-2.html selection_visibility_after_reframe-ref.html
needs-focus != selection_visibility_after_reframe-3.html selection_visibility_after_reframe-ref.html
== 672709.html 672709-ref.html
== 338427-1.html 338427-1-ref.html
needs-focus == 674212-spellcheck.html 674212-spellcheck-ref.html

Просмотреть файл

@ -4,6 +4,7 @@
<input value="foo">
<script>
var i = document.querySelector("input");
i.focus();
i.selectionStart = 1;
i.selectionEnd = 2;
</script>

Просмотреть файл

@ -4,6 +4,7 @@
<input value="foo">
<script>
var i = document.querySelector("input");
i.focus();
i.selectionStart = 1;
i.selectionEnd = 2;
i.style.display = "none";

Просмотреть файл

@ -1281,6 +1281,8 @@ void PresShell::Destroy() {
mCaret = nullptr;
}
mFocusedFrameSelection = nullptr;
if (mSelection) {
RefPtr<nsFrameSelection> frameSelection = mSelection;
frameSelection->DisconnectFromPresShell();
@ -1542,6 +1544,67 @@ void PresShell::AddAuthorSheet(StyleSheet* aSheet) {
mDocument->ApplicableStylesChanged();
}
void PresShell::SelectionWillTakeFocus() {
if (mSelection) {
FrameSelectionWillTakeFocus(*mSelection);
}
}
void PresShell::SelectionWillLoseFocus() {
// Do nothing, the main selection is the default focused selection.
}
void PresShell::FrameSelectionWillLoseFocus(nsFrameSelection& aFrameSelection) {
if (mFocusedFrameSelection != &aFrameSelection) {
return;
}
// Do nothing, the main selection is the default focused selection.
if (&aFrameSelection == mSelection) {
return;
}
RefPtr<nsFrameSelection> old = std::move(mFocusedFrameSelection);
MOZ_ASSERT(!mFocusedFrameSelection);
if (old->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN) {
old->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
old->RepaintSelection(SelectionType::eNormal);
}
if (mSelection) {
FrameSelectionWillTakeFocus(*mSelection);
}
}
void PresShell::FrameSelectionWillTakeFocus(nsFrameSelection& aFrameSelection) {
if (mFocusedFrameSelection == &aFrameSelection) {
#ifdef XP_MACOSX
// FIXME: Mac needs to update the global selection cache, even if the
// document's focused selection doesn't change, and this is currently done
// from RepaintSelection. Maybe we should move part of the global selection
// handling here, or something of that sort, unclear.
aFrameSelection.RepaintSelection(SelectionType::eNormal);
#endif
return;
}
RefPtr<nsFrameSelection> old = std::move(mFocusedFrameSelection);
mFocusedFrameSelection = &aFrameSelection;
if (old &&
old->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN) {
old->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
old->RepaintSelection(SelectionType::eNormal);
}
if (aFrameSelection.GetDisplaySelection() !=
nsISelectionController::SELECTION_ON) {
aFrameSelection.SetDisplaySelection(nsISelectionController::SELECTION_ON);
aFrameSelection.RepaintSelection(SelectionType::eNormal);
}
}
NS_IMETHODIMP
PresShell::SetDisplaySelection(int16_t aToggle) {
RefPtr<nsFrameSelection> frameSelection = mSelection;
@ -1583,41 +1646,16 @@ Selection* PresShell::GetSelection(RawSelectionType aRawSelectionType) {
}
Selection* PresShell::GetCurrentSelection(SelectionType aSelectionType) {
if (!mSelection) return nullptr;
if (!mSelection) {
return nullptr;
}
RefPtr<nsFrameSelection> frameSelection = mSelection;
return frameSelection->GetSelection(aSelectionType);
}
already_AddRefed<nsISelectionController>
PresShell::GetSelectionControllerForFocusedContent(
nsIContent** aFocusedContent) {
if (aFocusedContent) {
*aFocusedContent = nullptr;
}
if (mDocument) {
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
nsCOMPtr<nsIContent> focusedContent = nsFocusManager::GetFocusedDescendant(
mDocument->GetWindow(), nsFocusManager::eOnlyCurrentWindow,
getter_AddRefs(focusedWindow));
if (focusedContent) {
nsIFrame* frame = focusedContent->GetPrimaryFrame();
if (frame) {
nsCOMPtr<nsISelectionController> selectionController;
frame->GetSelectionController(mPresContext,
getter_AddRefs(selectionController));
if (selectionController) {
if (aFocusedContent) {
focusedContent.forget(aFocusedContent);
}
return selectionController.forget();
}
}
}
}
nsCOMPtr<nsISelectionController> self(this);
return self.forget();
nsFrameSelection* PresShell::GetLastFocusedFrameSelection() {
return mFocusedFrameSelection ? mFocusedFrameSelection : mSelection;
}
NS_IMETHODIMP

Просмотреть файл

@ -558,6 +558,12 @@ class PresShell final : public nsStubDocumentObserver,
void ClearFrameRefs(nsIFrame* aFrame);
// Clears the selection of the older focused frame selection if any.
void FrameSelectionWillTakeFocus(nsFrameSelection&);
// Clears and repaint mFocusedFrameSelection if it matches the argument.
void FrameSelectionWillLoseFocus(nsFrameSelection&);
/**
* Get a reference rendering context. This is a context that should not
* be rendered to, but is suitable for measuring text and performing
@ -623,22 +629,10 @@ class PresShell final : public nsStubDocumentObserver,
dom::Selection* GetCurrentSelection(SelectionType aSelectionType);
/**
* Gets a selection controller for the focused content in the DOM window
* for mDocument.
*
* @param aFocusedContent If there is focused content in the DOM window,
* the focused content will be returned. This may
* be nullptr if it's not necessary.
* @return A selection controller for focused content.
* E.g., if an <input> element has focus, returns
* the independent selection controller of it.
* If the DOM window does not have focused content
* (similar to Document.activeElement), returns
* nullptr.
* Gets the last selection that took focus in this document. This is basically
* the frame selection that's visible to the user.
*/
already_AddRefed<nsISelectionController>
GetSelectionControllerForFocusedContent(
nsIContent** aFocusedContent = nullptr);
nsFrameSelection* GetLastFocusedFrameSelection();
/**
* Interface to dispatch events via the presshell
@ -1272,6 +1266,8 @@ class PresShell final : public nsStubDocumentObserver,
SelectionRegion aRegion,
int16_t aFlags) override;
NS_IMETHOD RepaintSelection(RawSelectionType aRawSelectionType) override;
void SelectionWillTakeFocus() override;
void SelectionWillLoseFocus() override;
/**
* Set a "resolution" for the document, which if not 1.0 will
@ -2832,6 +2828,10 @@ class PresShell final : public nsStubDocumentObserver,
UniquePtr<nsCSSFrameConstructor> mFrameConstructor;
nsViewManager* mViewManager; // [WEAK] docViewer owns it so I don't have to
RefPtr<nsFrameSelection> mSelection;
// The frame selection that last took focus on this shell, which we need to
// hide if we focus another selection. May or may not be the same as
// `mSelection`.
RefPtr<nsFrameSelection> mFocusedFrameSelection;
RefPtr<nsCaret> mCaret;
RefPtr<nsCaret> mOriginalCaret;
RefPtr<AccessibleCaretEventHub> mAccessibleCaretEventHub;

Просмотреть файл

@ -2629,7 +2629,7 @@ NS_IMETHODIMP nsDocumentViewer::GetContents(const char* mimeType,
// Now we have the selection. Make sure it's nonzero:
RefPtr<Selection> sel;
if (selectionOnly) {
nsCopySupport::GetSelectionForCopy(mDocument, getter_AddRefs(sel));
sel = nsCopySupport::GetSelectionForCopy(mDocument);
NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
if (sel->IsCollapsed()) {

Просмотреть файл

@ -1053,42 +1053,21 @@ nsresult nsTextControlFrame::AttributeChanged(int32_t aNameSpaceID,
return NS_OK;
}
if (nsGkAtoms::readonly == aAttribute) {
if (AttributeExists(nsGkAtoms::readonly)) { // set readonly
if (nsGkAtoms::readonly == aAttribute || nsGkAtoms::disabled == aAttribute) {
if (AttributeExists(aAttribute)) {
if (nsContentUtils::IsFocusedContent(mContent)) {
selCon->SetCaretEnabled(false);
}
textEditor->AddFlags(nsIEditor::eEditorReadonlyMask);
} else { // unset readonly
if (!textEditor->IsDisabled() &&
nsContentUtils::IsFocusedContent(mContent)) {
selCon->SetCaretEnabled(true);
}
textEditor->RemoveFlags(nsIEditor::eEditorReadonlyMask);
}
return NS_OK;
}
if (nsGkAtoms::disabled == aAttribute) {
int16_t displaySelection = nsISelectionController::SELECTION_OFF;
const bool focused = nsContentUtils::IsFocusedContent(mContent);
const bool hasAttr = AttributeExists(nsGkAtoms::disabled);
bool disable;
if (hasAttr) { // set disabled
disable = true;
} else { // unset disabled
disable = false;
displaySelection = focused ? nsISelectionController::SELECTION_ON
: nsISelectionController::SELECTION_HIDDEN;
}
selCon->SetDisplaySelection(displaySelection);
if (focused) {
selCon->SetCaretEnabled(!hasAttr);
}
if (disable) {
textEditor->AddFlags(nsIEditor::eEditorDisabledMask);
} else {
textEditor->RemoveFlags(nsIEditor::eEditorDisabledMask);
if (!AttributeExists(aAttribute == nsGkAtoms::readonly
? nsGkAtoms::disabled
: nsGkAtoms::readonly)) {
if (nsContentUtils::IsFocusedContent(mContent)) {
selCon->SetCaretEnabled(true);
}
textEditor->RemoveFlags(nsIEditor::eEditorReadonlyMask);
}
}
return NS_OK;
}

Просмотреть файл

@ -1296,6 +1296,8 @@ nsresult nsFrameSelection::TakeFocus(nsIContent* aNewFocus,
return NS_ERROR_FAILURE;
}
mPresShell->FrameSelectionWillTakeFocus(*this);
// Clear all table selection data
mTableSelection.mMode = TableSelectionMode::None;
mTableSelection.mDragSelectingCells = false;

Просмотреть файл

@ -1207,9 +1207,7 @@ function runEditorFlagChangeTests() {
description + "The editor doesn't get focus");
is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
description + "IME isn't enabled on HTML editor");
const kIMEStateChangeFlags =
Ci.nsIEditor.eEditorReadonlyMask |
Ci.nsIEditor.eEditorDisabledMask;
const kIMEStateChangeFlags = Ci.nsIEditor.eEditorReadonlyMask;
var editor = window.docShell.editor;
var flags = editor.flags;