зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
e4db0e4496
Коммит
04828e3a4a
|
@ -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;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче