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
This commit is contained in:
@@ -126,6 +126,12 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
this.invoke = function changeDOMSelection_invoke() {
|
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 sel = window.getSelection();
|
||||||
var range = document.createRange();
|
var range = document.createRange();
|
||||||
range.setStart(getNode(aNodeID1), aNodeOffset1);
|
range.setStart(getNode(aNodeID1), aNodeOffset1);
|
||||||
|
|||||||
@@ -5933,6 +5933,29 @@ Nullable<WindowProxyHolder> Document::GetDefaultView() const {
|
|||||||
return WindowProxyHolder(win->GetBrowsingContext());
|
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() {
|
Element* Document::GetActiveElement() {
|
||||||
// Get the focused element.
|
// Get the focused element.
|
||||||
Element* focusedElement = GetRetargetedFocusedElement();
|
Element* focusedElement = GetRetargetedFocusedElement();
|
||||||
|
|||||||
@@ -3293,6 +3293,7 @@ class Document : public nsINode,
|
|||||||
mozilla::ErrorResult& rv);
|
mozilla::ErrorResult& rv);
|
||||||
Nullable<WindowProxyHolder> GetDefaultView() const;
|
Nullable<WindowProxyHolder> GetDefaultView() const;
|
||||||
Element* GetActiveElement();
|
Element* GetActiveElement();
|
||||||
|
nsIContent* GetUnretargetedFocusedContent() const;
|
||||||
bool HasFocus(ErrorResult& rv) const;
|
bool HasFocus(ErrorResult& rv) const;
|
||||||
void GetDesignMode(nsAString& aDesignMode);
|
void GetDesignMode(nsAString& aDesignMode);
|
||||||
void SetDesignMode(const nsAString& aDesignMode,
|
void SetDesignMode(const nsAString& aDesignMode,
|
||||||
|
|||||||
@@ -256,25 +256,13 @@ nsIContent* DocumentOrShadowRoot::Retarget(nsIContent* aContent) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
|
Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
|
||||||
if (nsCOMPtr<nsPIDOMWindowOuter> window = AsNode().OwnerDoc()->GetWindow()) {
|
auto* content = AsNode().OwnerDoc()->GetUnretargetedFocusedContent();
|
||||||
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
|
if (!content) {
|
||||||
nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
|
return nullptr;
|
||||||
window, nsFocusManager::eOnlyCurrentWindow,
|
}
|
||||||
getter_AddRefs(focusedWindow));
|
if (nsIContent* retarget = Retarget(content)) {
|
||||||
// be safe and make sure the element is from this document
|
return retarget->AsElement();
|
||||||
if (focusedContent && focusedContent->OwnerDoc() == AsNode().OwnerDoc()) {
|
|
||||||
if (focusedContent->ChromeOnlyAccess()) {
|
|
||||||
focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (focusedContent) {
|
|
||||||
if (nsIContent* retarget = Retarget(focusedContent)) {
|
|
||||||
return retarget->AsElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "imgIContainer.h"
|
#include "imgIContainer.h"
|
||||||
#include "imgIRequest.h"
|
#include "imgIRequest.h"
|
||||||
#include "nsFocusManager.h"
|
#include "nsFocusManager.h"
|
||||||
|
#include "nsFrameSelection.h"
|
||||||
#include "mozilla/dom/DataTransfer.h"
|
#include "mozilla/dom/DataTransfer.h"
|
||||||
|
|
||||||
#include "nsIDocShell.h"
|
#include "nsIDocShell.h"
|
||||||
@@ -673,37 +674,27 @@ static nsresult AppendImagePromise(nsITransferable* aTransferable,
|
|||||||
}
|
}
|
||||||
#endif // XP_WIN
|
#endif // XP_WIN
|
||||||
|
|
||||||
nsIContent* nsCopySupport::GetSelectionForCopy(Document* aDocument,
|
already_AddRefed<Selection> nsCopySupport::GetSelectionForCopy(
|
||||||
Selection** aSelection) {
|
Document* aDocument) {
|
||||||
*aSelection = nullptr;
|
|
||||||
|
|
||||||
PresShell* presShell = aDocument->GetPresShell();
|
PresShell* presShell = aDocument->GetPresShell();
|
||||||
if (!presShell) {
|
if (!presShell) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIContent> focusedContent;
|
RefPtr<nsFrameSelection> frameSel = presShell->GetLastFocusedFrameSelection();
|
||||||
nsCOMPtr<nsISelectionController> selectionController =
|
if (!frameSel) {
|
||||||
presShell->GetSelectionControllerForFocusedContent(
|
|
||||||
getter_AddRefs(focusedContent));
|
|
||||||
if (!selectionController) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<Selection> sel = selectionController->GetSelection(
|
RefPtr<Selection> sel = frameSel->GetSelection(SelectionType::eNormal);
|
||||||
nsISelectionController::SELECTION_NORMAL);
|
return sel.forget();
|
||||||
sel.forget(aSelection);
|
|
||||||
return focusedContent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nsCopySupport::CanCopy(Document* aDocument) {
|
bool nsCopySupport::CanCopy(Document* aDocument) {
|
||||||
if (!aDocument) return false;
|
if (!aDocument) return false;
|
||||||
|
|
||||||
RefPtr<Selection> sel;
|
RefPtr<Selection> sel = GetSelectionForCopy(aDocument);
|
||||||
GetSelectionForCopy(aDocument, getter_AddRefs(sel));
|
return sel && !sel->IsCollapsed();
|
||||||
NS_ENSURE_TRUE(sel, false);
|
|
||||||
|
|
||||||
return !sel->IsCollapsed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsInsideRuby(nsINode* aNode) {
|
static bool IsInsideRuby(nsINode* aNode) {
|
||||||
@@ -717,7 +708,6 @@ static bool IsInsideRuby(nsINode* aNode) {
|
|||||||
|
|
||||||
static bool IsSelectionInsideRuby(Selection* aSelection) {
|
static bool IsSelectionInsideRuby(Selection* aSelection) {
|
||||||
uint32_t rangeCount = aSelection->RangeCount();
|
uint32_t rangeCount = aSelection->RangeCount();
|
||||||
;
|
|
||||||
for (auto i : IntegerRange(rangeCount)) {
|
for (auto i : IntegerRange(rangeCount)) {
|
||||||
nsRange* range = aSelection->GetRangeAt(i);
|
nsRange* range = aSelection->GetRangeAt(i);
|
||||||
if (!IsInsideRuby(range->GetClosestCommonInclusiveAncestor())) {
|
if (!IsInsideRuby(range->GetClosestCommonInclusiveAncestor())) {
|
||||||
@@ -776,7 +766,7 @@ bool nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
|
|||||||
// If a selection was not supplied, try to find it.
|
// If a selection was not supplied, try to find it.
|
||||||
RefPtr<Selection> sel = aSelection;
|
RefPtr<Selection> sel = aSelection;
|
||||||
if (!sel) {
|
if (!sel) {
|
||||||
GetSelectionForCopy(doc, getter_AddRefs(sel));
|
sel = GetSelectionForCopy(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the event target node from the start of the selection.
|
// 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
|
* and this focused content node returned. Otherwise, aSelection will be
|
||||||
* set to the document's selection and null will be returned.
|
* set to the document's selection and null will be returned.
|
||||||
*/
|
*/
|
||||||
static nsIContent* GetSelectionForCopy(mozilla::dom::Document* aDocument,
|
static already_AddRefed<mozilla::dom::Selection> GetSelectionForCopy(
|
||||||
mozilla::dom::Selection** aSelection);
|
mozilla::dom::Document* aDocument);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if a copy operation is currently permitted based on the
|
* Returns true if a copy operation is currently permitted based on the
|
||||||
|
|||||||
@@ -83,6 +83,23 @@ interface nsISelectionController : nsISelectionDisplay
|
|||||||
[noscript,nostdcall,notxpcom,binaryname(GetSelection)]
|
[noscript,nostdcall,notxpcom,binaryname(GetSelection)]
|
||||||
Selection getDOMSelection(in short aType);
|
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_SYNCHRONOUS = 1<<1;
|
||||||
const short SCROLL_FIRST_ANCESTOR_ONLY = 1<<2;
|
const short SCROLL_FIRST_ANCESTOR_ONLY = 1<<2;
|
||||||
const short SCROLL_CENTER_VERTICALLY = 1<<4;
|
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
|
skip-if = toolkit == 'android' || headless #bug 904183
|
||||||
[test_copypaste.xhtml]
|
[test_copypaste.xhtml]
|
||||||
skip-if = headless #bug 904183
|
skip-if = headless #bug 904183
|
||||||
|
[test_copypaste_disabled.html]
|
||||||
[test_createHTMLDocument.html]
|
[test_createHTMLDocument.html]
|
||||||
[test_data_uri.html]
|
[test_data_uri.html]
|
||||||
skip-if = verify
|
skip-if = verify
|
||||||
|
|||||||
90
dom/base/test/test_copypaste_disabled.html
Normal file
90
dom/base/test/test_copypaste_disabled.html
Normal file
@@ -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);
|
nsresult rv = InitBasic(aRequireFlush);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsCOMPtr<nsISelectionController> selectionController;
|
RefPtr<nsFrameSelection> frameSel;
|
||||||
if (PresShell* presShell = mDocument->GetPresShell()) {
|
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;
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
mSelection =
|
mSelection = frameSel->GetSelection(aSelectionType);
|
||||||
selectionController->GetSelection(ToRawSelectionType(aSelectionType));
|
|
||||||
if (NS_WARN_IF(!mSelection)) {
|
if (NS_WARN_IF(!mSelection)) {
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
@@ -335,8 +334,7 @@ nsresult ContentEventHandler::InitCommon(SelectionType aSelectionType,
|
|||||||
if (mSelection->Type() == SelectionType::eNormal) {
|
if (mSelection->Type() == SelectionType::eNormal) {
|
||||||
normalSelection = mSelection;
|
normalSelection = mSelection;
|
||||||
} else {
|
} else {
|
||||||
normalSelection = selectionController->GetSelection(
|
normalSelection = frameSel->GetSelection(SelectionType::eNormal);
|
||||||
nsISelectionController::SELECTION_NORMAL);
|
|
||||||
if (NS_WARN_IF(!normalSelection)) {
|
if (NS_WARN_IF(!normalSelection)) {
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5165,7 +5165,7 @@ nsresult EventStateManager::HandleMiddleClickPaste(
|
|||||||
if (NS_WARN_IF(!document)) {
|
if (NS_WARN_IF(!document)) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
nsCopySupport::GetSelectionForCopy(document, getter_AddRefs(selection));
|
selection = nsCopySupport::GetSelectionForCopy(document);
|
||||||
if (NS_WARN_IF(!selection)) {
|
if (NS_WARN_IF(!selection)) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -5214,8 +5214,7 @@ nsresult EventStateManager::HandleMiddleClickPaste(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the editor is still the good target to paste.
|
// Check if the editor is still the good target to paste.
|
||||||
if (aTextEditor->Destroyed() || aTextEditor->IsReadonly() ||
|
if (aTextEditor->Destroyed() || aTextEditor->IsReadonly()) {
|
||||||
aTextEditor->IsDisabled()) {
|
|
||||||
// XXX Should we consume the event when the editor is readonly and/or
|
// XXX Should we consume the event when the editor is readonly and/or
|
||||||
// disabled?
|
// disabled?
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|||||||
@@ -6639,18 +6639,13 @@ void HTMLInputElement::OnValueChanged(ValueChangeKind aKind) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool HTMLInputElement::HasCachedSelection() {
|
bool HTMLInputElement::HasCachedSelection() {
|
||||||
bool isCached = false;
|
|
||||||
TextControlState* state = GetEditorState();
|
TextControlState* state = GetEditorState();
|
||||||
if (state) {
|
if (!state) {
|
||||||
isCached = state->IsSelectionCached() &&
|
return false;
|
||||||
state->HasNeverInitializedBefore() &&
|
|
||||||
state->GetSelectionProperties().GetStart() !=
|
|
||||||
state->GetSelectionProperties().GetEnd();
|
|
||||||
if (isCached) {
|
|
||||||
state->WillInitEagerly();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return isCached;
|
return state->IsSelectionCached() && state->HasNeverInitializedBefore() &&
|
||||||
|
state->GetSelectionProperties().GetStart() !=
|
||||||
|
state->GetSelectionProperties().GetEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLInputElement::FieldSetDisabledChanged(bool aNotify) {
|
void HTMLInputElement::FieldSetDisabledChanged(bool aNotify) {
|
||||||
|
|||||||
@@ -174,10 +174,6 @@ class RestoreSelectionState : public Runnable {
|
|||||||
mFrame->SetSelectionRange(properties.GetStart(), properties.GetEnd(),
|
mFrame->SetSelectionRange(properties.GetStart(), properties.GetEnd(),
|
||||||
properties.GetDirection());
|
properties.GetDirection());
|
||||||
}
|
}
|
||||||
if (!mTextControlState->mSelectionRestoreEagerInit) {
|
|
||||||
mTextControlState->HideSelectionIfBlurred();
|
|
||||||
}
|
|
||||||
mTextControlState->mSelectionRestoreEagerInit = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mTextControlState) {
|
if (mTextControlState) {
|
||||||
@@ -217,7 +213,6 @@ class MOZ_RAII AutoRestoreEditorState final {
|
|||||||
// appearing the method in profile. So, this class should check if it's
|
// appearing the method in profile. So, this class should check if it's
|
||||||
// necessary to call.
|
// necessary to call.
|
||||||
uint32_t flags = mSavedFlags;
|
uint32_t flags = mSavedFlags;
|
||||||
flags &= ~(nsIEditor::eEditorDisabledMask);
|
|
||||||
flags &= ~(nsIEditor::eEditorReadonlyMask);
|
flags &= ~(nsIEditor::eEditorReadonlyMask);
|
||||||
flags |= nsIEditor::eEditorDontEchoPassword;
|
flags |= nsIEditor::eEditorDontEchoPassword;
|
||||||
if (mSavedFlags != flags) {
|
if (mSavedFlags != flags) {
|
||||||
@@ -361,6 +356,8 @@ class TextInputSelectionController final : public nsSupportsWeakReference,
|
|||||||
int16_t aStartOffset,
|
int16_t aStartOffset,
|
||||||
int16_t aEndOffset,
|
int16_t aEndOffset,
|
||||||
bool* aRetval) override;
|
bool* aRetval) override;
|
||||||
|
void SelectionWillTakeFocus() override;
|
||||||
|
void SelectionWillLoseFocus() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RefPtr<nsFrameSelection> mFrameSelection;
|
RefPtr<nsFrameSelection> mFrameSelection;
|
||||||
@@ -771,6 +768,22 @@ TextInputSelectionController::SelectAll() {
|
|||||||
return frameSelection->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
|
NS_IMETHODIMP
|
||||||
TextInputSelectionController::CheckVisibility(nsINode* node,
|
TextInputSelectionController::CheckVisibility(nsINode* node,
|
||||||
int16_t startOffset,
|
int16_t startOffset,
|
||||||
@@ -1396,7 +1409,6 @@ TextControlState::TextControlState(TextControlElement* aOwningElement)
|
|||||||
mEditorInitialized(false),
|
mEditorInitialized(false),
|
||||||
mValueTransferInProgress(false),
|
mValueTransferInProgress(false),
|
||||||
mSelectionCached(true),
|
mSelectionCached(true),
|
||||||
mSelectionRestoreEagerInit(false),
|
|
||||||
mPlaceholderVisibility(false),
|
mPlaceholderVisibility(false),
|
||||||
mPreviewVisibility(false)
|
mPreviewVisibility(false)
|
||||||
// When adding more member variable initializations here, add the same
|
// When adding more member variable initializations here, add the same
|
||||||
@@ -1419,7 +1431,6 @@ TextControlState* TextControlState::Construct(
|
|||||||
state->mEditorInitialized = false;
|
state->mEditorInitialized = false;
|
||||||
state->mValueTransferInProgress = false;
|
state->mValueTransferInProgress = false;
|
||||||
state->mSelectionCached = true;
|
state->mSelectionCached = true;
|
||||||
state->mSelectionRestoreEagerInit = false;
|
|
||||||
state->mPlaceholderVisibility = false;
|
state->mPlaceholderVisibility = false;
|
||||||
state->mPreviewVisibility = false;
|
state->mPreviewVisibility = false;
|
||||||
// When adding more member variable initializations here, add the same
|
// When adding more member variable initializations here, add the same
|
||||||
@@ -1640,7 +1651,9 @@ nsresult TextControlState::BindToFrame(nsTextControlFrame* aFrame) {
|
|||||||
mTextListener = new TextInputListener(mTextCtrlElement);
|
mTextListener = new TextInputListener(mTextCtrlElement);
|
||||||
|
|
||||||
mTextListener->SetFrame(mBoundFrame);
|
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.
|
// Get the caret and make it a selection listener.
|
||||||
// FYI: It's safe to use raw pointer for calling
|
// FYI: It's safe to use raw pointer for calling
|
||||||
@@ -1884,21 +1897,13 @@ nsresult TextControlState::PrepareEditor(const nsAString* aValue) {
|
|||||||
editorFlags = newTextEditor->Flags();
|
editorFlags = newTextEditor->Flags();
|
||||||
|
|
||||||
// Check if the readonly attribute is set.
|
// 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;
|
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);
|
SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
|
||||||
|
|
||||||
if (shouldInitializeEditor) {
|
if (shouldInitializeEditor) {
|
||||||
@@ -2385,6 +2390,10 @@ void TextControlState::UnbindFromFrame(nsTextControlFrame* aFrame) {
|
|||||||
AutoTextControlHandlingState handlingUnbindFromFrame(
|
AutoTextControlHandlingState handlingUnbindFromFrame(
|
||||||
*this, TextControlAction::UnbindFromFrame);
|
*this, TextControlAction::UnbindFromFrame);
|
||||||
|
|
||||||
|
if (mSelCon) {
|
||||||
|
mSelCon->SelectionWillLoseFocus();
|
||||||
|
}
|
||||||
|
|
||||||
// We need to start storing the value outside of the editor if we're not
|
// 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.
|
// going to use it anymore, so retrieve it for now.
|
||||||
nsAutoString value;
|
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() {
|
bool TextControlState::EditorHasComposition() {
|
||||||
return mTextEditor && mTextEditor->IsIMEComposing();
|
return mTextEditor && mTextEditor->IsIMEComposing();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -274,7 +274,6 @@ class TextControlState final : public SupportsWeakPtr<TextControlState> {
|
|||||||
void SetPreviewText(const nsAString& aValue, bool aNotify);
|
void SetPreviewText(const nsAString& aValue, bool aNotify);
|
||||||
void GetPreviewText(nsAString& aValue);
|
void GetPreviewText(nsAString& aValue);
|
||||||
bool GetPreviewVisibility() { return mPreviewVisibility; }
|
bool GetPreviewVisibility() { return mPreviewVisibility; }
|
||||||
void HideSelectionIfBlurred();
|
|
||||||
|
|
||||||
struct SelectionProperties {
|
struct SelectionProperties {
|
||||||
public:
|
public:
|
||||||
@@ -315,7 +314,6 @@ class TextControlState final : public SupportsWeakPtr<TextControlState> {
|
|||||||
bool IsSelectionCached() const { return mSelectionCached; }
|
bool IsSelectionCached() const { return mSelectionCached; }
|
||||||
SelectionProperties& GetSelectionProperties() { return mSelectionProperties; }
|
SelectionProperties& GetSelectionProperties() { return mSelectionProperties; }
|
||||||
MOZ_CAN_RUN_SCRIPT void SetSelectionProperties(SelectionProperties& aProps);
|
MOZ_CAN_RUN_SCRIPT void SetSelectionProperties(SelectionProperties& aProps);
|
||||||
void WillInitEagerly() { mSelectionRestoreEagerInit = true; }
|
|
||||||
bool HasNeverInitializedBefore() const { return !mEverInited; }
|
bool HasNeverInitializedBefore() const { return !mEverInited; }
|
||||||
// Sync up our selection properties with our editor prior to being destroyed.
|
// Sync up our selection properties with our editor prior to being destroyed.
|
||||||
// This will invoke UnbindFromFrame() to ensure that we grab whatever
|
// 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
|
bool mValueTransferInProgress; // Whether a value is being transferred to the
|
||||||
// frame
|
// frame
|
||||||
bool mSelectionCached; // Whether mSelectionProperties is valid
|
bool mSelectionCached; // Whether mSelectionProperties is valid
|
||||||
mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing
|
|
||||||
// because of selection restore
|
|
||||||
bool mPlaceholderVisibility;
|
bool mPlaceholderVisibility;
|
||||||
bool mPreviewVisibility;
|
bool mPreviewVisibility;
|
||||||
|
|
||||||
|
|||||||
@@ -285,12 +285,6 @@ nsresult EditorBase::Init(Document& aDocument, Element* aRoot,
|
|||||||
NS_WARNING_ASSERTION(
|
NS_WARNING_ASSERTION(
|
||||||
NS_SUCCEEDED(rvIgnored),
|
NS_SUCCEEDED(rvIgnored),
|
||||||
"nsISelectionController::SetCaretReadOnly(false) failed, but ignored");
|
"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.
|
// Show all the selection reflected to user.
|
||||||
rvIgnored =
|
rvIgnored =
|
||||||
selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
|
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
|
// Make sure that the editor will be destroyed properly
|
||||||
mDidPreDestroy = false;
|
mDidPreDestroy = false;
|
||||||
// Make sure that the ediotr will be created properly
|
// Make sure that the editor will be created properly
|
||||||
mDidPostCreate = false;
|
mDidPostCreate = false;
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@@ -2505,7 +2499,7 @@ nsresult EditorBase::GetPreferredIMEState(IMEState* aState) {
|
|||||||
aState->mEnabled = IMEState::ENABLED;
|
aState->mEnabled = IMEState::ENABLED;
|
||||||
aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
|
aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
|
||||||
|
|
||||||
if (IsReadonly() || IsDisabled()) {
|
if (IsReadonly()) {
|
||||||
aState->mEnabled = IMEState::DISABLED;
|
aState->mEnabled = IMEState::DISABLED;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@@ -5039,7 +5033,7 @@ nsresult EditorBase::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
|
|||||||
"HandleKeyPressEvent gets non-keypress event");
|
"HandleKeyPressEvent gets non-keypress event");
|
||||||
|
|
||||||
// if we are readonly or disabled, then do nothing.
|
// if we are readonly or disabled, then do nothing.
|
||||||
if (IsReadonly() || IsDisabled()) {
|
if (IsReadonly()) {
|
||||||
// consume backspace for disabled and readonly textfields, to prevent
|
// consume backspace for disabled and readonly textfields, to prevent
|
||||||
// back in history, which could be confusing to users
|
// back in history, which could be confusing to users
|
||||||
if (aKeyboardEvent->mKeyCode == NS_VK_BACK) {
|
if (aKeyboardEvent->mKeyCode == NS_VK_BACK) {
|
||||||
@@ -5134,21 +5128,13 @@ nsresult EditorBase::InitializeSelection(EventTarget* aFocusEventTarget) {
|
|||||||
caret->SetIgnoreUserModify(targetNode->OwnerDoc()->HasFlag(NODE_IS_EDITABLE));
|
caret->SetIgnoreUserModify(targetNode->OwnerDoc()->HasFlag(NODE_IS_EDITABLE));
|
||||||
|
|
||||||
// Init selection
|
// Init selection
|
||||||
rvIgnored = selectionController->SetDisplaySelection(
|
|
||||||
nsISelectionController::SELECTION_ON);
|
|
||||||
NS_WARNING_ASSERTION(
|
|
||||||
NS_SUCCEEDED(rvIgnored),
|
|
||||||
"nsISelectionController::SetDisplaySelection() failed, but ignored");
|
|
||||||
rvIgnored =
|
rvIgnored =
|
||||||
selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
|
selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
|
||||||
NS_WARNING_ASSERTION(
|
NS_WARNING_ASSERTION(
|
||||||
NS_SUCCEEDED(rvIgnored),
|
NS_SUCCEEDED(rvIgnored),
|
||||||
"nsISelectionController::SetSelectionFlags() failed, but ignored");
|
"nsISelectionController::SetSelectionFlags() failed, but ignored");
|
||||||
rvIgnored = selectionController->RepaintSelection(
|
|
||||||
nsISelectionController::SELECTION_NORMAL);
|
selectionController->SelectionWillTakeFocus();
|
||||||
NS_WARNING_ASSERTION(
|
|
||||||
NS_SUCCEEDED(rvIgnored),
|
|
||||||
"nsISelectionController::RepaintSelection() failed, but ignored");
|
|
||||||
|
|
||||||
// If the computed selection root isn't root content, we should set it
|
// 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
|
// as selection ancestor limit. However, if that is root element, it means
|
||||||
@@ -5192,26 +5178,6 @@ nsresult EditorBase::InitializeSelection(EventTarget* aFocusEventTarget) {
|
|||||||
return NS_OK;
|
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() {
|
nsresult EditorBase::FinalizeSelection() {
|
||||||
nsCOMPtr<nsISelectionController> selectionController =
|
nsCOMPtr<nsISelectionController> selectionController =
|
||||||
GetSelectionController();
|
GetSelectionController();
|
||||||
@@ -5243,58 +5209,11 @@ nsresult EditorBase::FinalizeSelection() {
|
|||||||
return NS_ERROR_NOT_INITIALIZED;
|
return NS_ERROR_NOT_INITIALIZED;
|
||||||
}
|
}
|
||||||
focusManager->UpdateCaretForCaretBrowsingMode();
|
focusManager->UpdateCaretForCaretBrowsingMode();
|
||||||
|
if (nsCOMPtr<nsINode> node = do_QueryInterface(GetDOMEventTarget())) {
|
||||||
if (!HasIndependentSelection()) {
|
if (node->OwnerDoc()->GetUnretargetedFocusedContent() != node) {
|
||||||
// If this editor doesn't have an independent selection, i.e., it must
|
selectionController->SelectionWillLoseFocus();
|
||||||
// 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");
|
|
||||||
}
|
}
|
||||||
} 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;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -491,10 +491,6 @@ class EditorBase : public nsIEditor,
|
|||||||
return (mFlags & nsIEditor::eEditorReadonlyMask) != 0;
|
return (mFlags & nsIEditor::eEditorReadonlyMask) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsDisabled() const {
|
|
||||||
return (mFlags & nsIEditor::eEditorDisabledMask) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsInputFiltered() const {
|
bool IsInputFiltered() const {
|
||||||
return (mFlags & nsIEditor::eEditorFilterInputMask) != 0;
|
return (mFlags & nsIEditor::eEditorFilterInputMask) != 0;
|
||||||
}
|
}
|
||||||
@@ -2565,8 +2561,7 @@ class EditorBase : public nsIEditor,
|
|||||||
// Check for password/readonly/disabled, which are not spellchecked
|
// Check for password/readonly/disabled, which are not spellchecked
|
||||||
// regardless of DOM. Also, check to see if spell check should be skipped
|
// regardless of DOM. Also, check to see if spell check should be skipped
|
||||||
// or not.
|
// or not.
|
||||||
return !IsPasswordEditor() && !IsReadonly() && !IsDisabled() &&
|
return !IsPasswordEditor() && !IsReadonly() && !ShouldSkipSpellCheck();
|
||||||
!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.
|
// nothing to do if editor isn't editable or clicked on out of the editor.
|
||||||
RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
|
RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
|
||||||
if (textEditor->IsReadonly() || textEditor->IsDisabled() ||
|
if (textEditor->IsReadonly() ||
|
||||||
!textEditor->IsAcceptableInputEvent(aMouseClickEvent)) {
|
!textEditor->IsAcceptableInputEvent(aMouseClickEvent)) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@@ -840,8 +840,8 @@ nsresult EditorEventListener::DragOverOrDrop(DragEvent* aDragEvent) {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notEditable = !dropParentContent->IsEditable() ||
|
bool notEditable =
|
||||||
mEditorBase->IsReadonly() || mEditorBase->IsDisabled();
|
!dropParentContent->IsEditable() || mEditorBase->IsReadonly();
|
||||||
|
|
||||||
// First of all, hide caret if we won't insert the drop data into the editor
|
// First of all, hide caret if we won't insert the drop data into the editor
|
||||||
// obviously.
|
// obviously.
|
||||||
@@ -977,7 +977,7 @@ bool EditorEventListener::DragEventHasSupportingData(
|
|||||||
bool EditorEventListener::CanInsertAtDropPosition(DragEvent* aDragEvent) {
|
bool EditorEventListener::CanInsertAtDropPosition(DragEvent* aDragEvent) {
|
||||||
MOZ_ASSERT(
|
MOZ_ASSERT(
|
||||||
!DetachedFromEditorOrDefaultPrevented(aDragEvent->WidgetEventPtr()));
|
!DetachedFromEditorOrDefaultPrevented(aDragEvent->WidgetEventPtr()));
|
||||||
MOZ_ASSERT(!mEditorBase->IsReadonly() && !mEditorBase->IsDisabled());
|
MOZ_ASSERT(!mEditorBase->IsReadonly());
|
||||||
MOZ_ASSERT(DragEventHasSupportingData(aDragEvent));
|
MOZ_ASSERT(DragEventHasSupportingData(aDragEvent));
|
||||||
|
|
||||||
// If there is no source node, this is probably an external drag and the
|
// If there is no source node, this is probably an external drag and the
|
||||||
@@ -1086,8 +1086,8 @@ nsresult EditorEventListener::HandleChangeComposition(
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we are readonly or disabled, then do nothing.
|
// if we are readonly, then do nothing.
|
||||||
if (textEditor->IsReadonly() || textEditor->IsDisabled()) {
|
if (textEditor->IsReadonly()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1117,11 +1117,7 @@ nsresult EditorEventListener::Focus(InternalFocusEvent* aFocusEvent) {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't turn on selection and caret when the editor is disabled.
|
|
||||||
RefPtr<EditorBase> editorBase(mEditorBase);
|
RefPtr<EditorBase> editorBase(mEditorBase);
|
||||||
if (editorBase->IsDisabled()) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spell check a textarea the first time that it is focused.
|
// Spell check a textarea the first time that it is focused.
|
||||||
SpellCheckIfNeeded();
|
SpellCheckIfNeeded();
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ class nsAtom;
|
|||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
class HTMLEditUtils final {
|
class HTMLEditUtils final {
|
||||||
|
using Element = dom::Element;
|
||||||
|
using Selection = dom::Selection;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool IsInlineStyle(nsINode* aNode);
|
static bool IsInlineStyle(nsINode* aNode);
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -662,7 +662,7 @@ nsresult HTMLEditor::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
|
|||||||
// NOTE: When you change this method, you should also change:
|
// NOTE: When you change this method, you should also change:
|
||||||
// * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
|
// * 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
|
// When we're not editable, the events are handled on EditorBase, so, we can
|
||||||
// bypass TextEditor.
|
// bypass TextEditor.
|
||||||
nsresult rv = EditorBase::HandleKeyPressEvent(aKeyboardEvent);
|
nsresult rv = EditorBase::HandleKeyPressEvent(aKeyboardEvent);
|
||||||
@@ -1675,7 +1675,7 @@ nsresult HTMLEditor::InsertElementAtSelectionAsAction(
|
|||||||
"EditorBase::CommitComposition() failed, but ignored");
|
"EditorBase::CommitComposition() failed, but ignored");
|
||||||
|
|
||||||
// XXX Oh, this should be done before dispatching `beforeinput` event.
|
// XXX Oh, this should be done before dispatching `beforeinput` event.
|
||||||
if (IsReadonly() || IsDisabled()) {
|
if (IsReadonly()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5349,7 +5349,7 @@ bool HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) {
|
|||||||
nsresult HTMLEditor::GetPreferredIMEState(IMEState* aState) {
|
nsresult HTMLEditor::GetPreferredIMEState(IMEState* aState) {
|
||||||
// HTML editor don't prefer the CSS ime-mode because IE didn't do so too.
|
// HTML editor don't prefer the CSS ime-mode because IE didn't do so too.
|
||||||
aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
|
aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
|
||||||
if (IsReadonly() || IsDisabled()) {
|
if (IsReadonly()) {
|
||||||
aState->mEnabled = IMEState::DISABLED;
|
aState->mEnabled = IMEState::DISABLED;
|
||||||
} else {
|
} else {
|
||||||
aState->mEnabled = IMEState::ENABLED;
|
aState->mEnabled = IMEState::ENABLED;
|
||||||
|
|||||||
@@ -340,8 +340,8 @@ nsresult HTMLEditor::DoInsertHTMLWithContext(
|
|||||||
"Selection::Collapse() failed, but ignored");
|
"Selection::Collapse() failed, but ignored");
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX Why don't we test these first?
|
// XXX Why don't we test this first?
|
||||||
if (IsReadonly() || IsDisabled()) {
|
if (IsReadonly()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1979,7 +1979,7 @@ nsresult HTMLEditor::PasteAsQuotationAsAction(int32_t aClipboardType,
|
|||||||
MOZ_ASSERT(aClipboardType == nsIClipboard::kGlobalClipboard ||
|
MOZ_ASSERT(aClipboardType == nsIClipboard::kGlobalClipboard ||
|
||||||
aClipboardType == nsIClipboard::kSelectionClipboard);
|
aClipboardType == nsIClipboard::kSelectionClipboard);
|
||||||
|
|
||||||
if (IsReadonly() || IsDisabled()) {
|
if (IsReadonly()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2162,7 +2162,7 @@ nsresult HTMLEditor::InsertWithQuotationsAsSubAction(
|
|||||||
const nsAString& aQuotedText) {
|
const nsAString& aQuotedText) {
|
||||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||||
|
|
||||||
if (IsReadonly() || IsDisabled()) {
|
if (IsReadonly()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2405,7 +2405,7 @@ nsresult HTMLEditor::InsertAsPlaintextQuotation(const nsAString& aQuotedText,
|
|||||||
*aNodeInserted = nullptr;
|
*aNodeInserted = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsReadonly() || IsDisabled()) {
|
if (IsReadonly()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2656,7 +2656,7 @@ nsresult HTMLEditor::InsertAsCitedQuotationInternal(
|
|||||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||||
MOZ_ASSERT(!IsPlaintextEditor());
|
MOZ_ASSERT(!IsPlaintextEditor());
|
||||||
|
|
||||||
if (IsReadonly() || IsDisabled()) {
|
if (IsReadonly()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ namespace mozilla {
|
|||||||
|
|
||||||
using namespace dom;
|
using namespace dom;
|
||||||
|
|
||||||
#define CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED \
|
#define CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY \
|
||||||
if (IsReadonly() || IsDisabled()) { \
|
if (IsReadonly()) { \
|
||||||
return EditActionCanceled(NS_OK); \
|
return EditActionCanceled(NS_OK); \
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult TextEditor::InitEditorContentAndSelection() {
|
nsresult TextEditor::InitEditorContentAndSelection() {
|
||||||
@@ -189,7 +189,7 @@ EditActionResult TextEditor::InsertLineFeedCharacterAtSelection() {
|
|||||||
|
|
||||||
UndefineCaretBidiLevel();
|
UndefineCaretBidiLevel();
|
||||||
|
|
||||||
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
|
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY
|
||||||
|
|
||||||
if (mMaxTextLength >= 0) {
|
if (mMaxTextLength >= 0) {
|
||||||
nsAutoString insertionString(NS_LITERAL_STRING("\n"));
|
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?
|
// 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();
|
MaybeDoAutoPasswordMasking();
|
||||||
|
|
||||||
@@ -630,7 +630,7 @@ EditActionResult TextEditor::SetTextWithoutTransaction(
|
|||||||
UndefineCaretBidiLevel();
|
UndefineCaretBidiLevel();
|
||||||
|
|
||||||
// XXX If we're setting value, shouldn't we keep setting the new value here?
|
// 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();
|
MaybeDoAutoPasswordMasking();
|
||||||
|
|
||||||
@@ -748,7 +748,7 @@ EditActionResult TextEditor::HandleDeleteSelection(
|
|||||||
|
|
||||||
UndefineCaretBidiLevel();
|
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
|
// if there is only padding <br> element for empty editor, cancel the
|
||||||
// operation.
|
// 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
|
// And also when you add new key handling, you need to change the subclass's
|
||||||
// HandleKeyPressEvent()'s switch statement.
|
// HandleKeyPressEvent()'s switch statement.
|
||||||
|
|
||||||
if (IsReadonly() || IsDisabled()) {
|
if (IsReadonly()) {
|
||||||
// When we're not editable, the events handled on EditorBase.
|
// When we're not editable, the events handled on EditorBase.
|
||||||
return EditorBase::HandleKeyPressEvent(aKeyboardEvent);
|
return EditorBase::HandleKeyPressEvent(aKeyboardEvent);
|
||||||
}
|
}
|
||||||
@@ -1494,7 +1494,7 @@ NS_IMETHODIMP TextEditor::GetTextLength(int32_t* aCount) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
|
nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
|
||||||
if (aCount == 0 || IsReadonly() || IsDisabled()) {
|
if (aCount == 0 || IsReadonly()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1575,7 +1575,7 @@ nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsresult TextEditor::RedoAsAction(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;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2016,7 +2016,7 @@ nsresult TextEditor::InsertWithQuotationsAsSubAction(
|
|||||||
const nsAString& aQuotedText) {
|
const nsAString& aQuotedText) {
|
||||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||||
|
|
||||||
if (IsReadonly() || IsDisabled()) {
|
if (IsReadonly()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,33 +58,31 @@ interface nsIEditor : nsISupports
|
|||||||
const long eEditorPasswordMask = 0x0004;
|
const long eEditorPasswordMask = 0x0004;
|
||||||
// editing events are disabled. Editor may still accept focus.
|
// editing events are disabled. Editor may still accept focus.
|
||||||
const long eEditorReadonlyMask = 0x0008;
|
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
|
// text input is limited to certain character types, use mFilter
|
||||||
const long eEditorFilterInputMask = 0x0020;
|
const long eEditorFilterInputMask = 0x0010;
|
||||||
// use mail-compose editing rules
|
// use mail-compose editing rules
|
||||||
const long eEditorMailMask = 0x0040;
|
const long eEditorMailMask = 0x0020;
|
||||||
// allow the editor to set font: monospace on the root node
|
// allow the editor to set font: monospace on the root node
|
||||||
const long eEditorEnableWrapHackMask = 0x0080;
|
const long eEditorEnableWrapHackMask = 0x0040;
|
||||||
// bit for widgets (form elements)
|
// bit for widgets (form elements)
|
||||||
const long eEditorWidgetMask = 0x0100;
|
const long eEditorWidgetMask = 0x0080;
|
||||||
// this HTML editor should not create css styles
|
// 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.
|
// whether HTML document specific actions are executed or not.
|
||||||
// e.g., if this flag is set, the editor doesn't handle Tab key.
|
// e.g., if this flag is set, the editor doesn't handle Tab key.
|
||||||
// besides, anchors of HTML are not clickable.
|
// 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.
|
// when this is set, the characters in password editor are always masked.
|
||||||
// see bug 530367 for the detail.
|
// 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.
|
// 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
|
// if neither of the direction flags are set, the direction is determined
|
||||||
// from the text control's content node.
|
// 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.
|
// 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.
|
// 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.
|
* 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
|
== 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(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
|
fuzzy-if(asyncPan&&!layersGPUAccelerated,0-102,0-2824) == 642800.html 642800-ref.html
|
||||||
== selection_visibility_after_reframe.html selection_visibility_after_reframe-ref.html
|
needs-focus == selection_visibility_after_reframe.html selection_visibility_after_reframe-ref.html
|
||||||
!= selection_visibility_after_reframe-2.html selection_visibility_after_reframe-ref.html
|
needs-focus != 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-3.html selection_visibility_after_reframe-ref.html
|
||||||
== 672709.html 672709-ref.html
|
== 672709.html 672709-ref.html
|
||||||
== 338427-1.html 338427-1-ref.html
|
== 338427-1.html 338427-1-ref.html
|
||||||
needs-focus == 674212-spellcheck.html 674212-spellcheck-ref.html
|
needs-focus == 674212-spellcheck.html 674212-spellcheck-ref.html
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<input value="foo">
|
<input value="foo">
|
||||||
<script>
|
<script>
|
||||||
var i = document.querySelector("input");
|
var i = document.querySelector("input");
|
||||||
|
i.focus();
|
||||||
i.selectionStart = 1;
|
i.selectionStart = 1;
|
||||||
i.selectionEnd = 2;
|
i.selectionEnd = 2;
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<input value="foo">
|
<input value="foo">
|
||||||
<script>
|
<script>
|
||||||
var i = document.querySelector("input");
|
var i = document.querySelector("input");
|
||||||
|
i.focus();
|
||||||
i.selectionStart = 1;
|
i.selectionStart = 1;
|
||||||
i.selectionEnd = 2;
|
i.selectionEnd = 2;
|
||||||
i.style.display = "none";
|
i.style.display = "none";
|
||||||
|
|||||||
@@ -1281,6 +1281,8 @@ void PresShell::Destroy() {
|
|||||||
mCaret = nullptr;
|
mCaret = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mFocusedFrameSelection = nullptr;
|
||||||
|
|
||||||
if (mSelection) {
|
if (mSelection) {
|
||||||
RefPtr<nsFrameSelection> frameSelection = mSelection;
|
RefPtr<nsFrameSelection> frameSelection = mSelection;
|
||||||
frameSelection->DisconnectFromPresShell();
|
frameSelection->DisconnectFromPresShell();
|
||||||
@@ -1542,6 +1544,67 @@ void PresShell::AddAuthorSheet(StyleSheet* aSheet) {
|
|||||||
mDocument->ApplicableStylesChanged();
|
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
|
NS_IMETHODIMP
|
||||||
PresShell::SetDisplaySelection(int16_t aToggle) {
|
PresShell::SetDisplaySelection(int16_t aToggle) {
|
||||||
RefPtr<nsFrameSelection> frameSelection = mSelection;
|
RefPtr<nsFrameSelection> frameSelection = mSelection;
|
||||||
@@ -1583,41 +1646,16 @@ Selection* PresShell::GetSelection(RawSelectionType aRawSelectionType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Selection* PresShell::GetCurrentSelection(SelectionType aSelectionType) {
|
Selection* PresShell::GetCurrentSelection(SelectionType aSelectionType) {
|
||||||
if (!mSelection) return nullptr;
|
if (!mSelection) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<nsFrameSelection> frameSelection = mSelection;
|
RefPtr<nsFrameSelection> frameSelection = mSelection;
|
||||||
return frameSelection->GetSelection(aSelectionType);
|
return frameSelection->GetSelection(aSelectionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<nsISelectionController>
|
nsFrameSelection* PresShell::GetLastFocusedFrameSelection() {
|
||||||
PresShell::GetSelectionControllerForFocusedContent(
|
return mFocusedFrameSelection ? mFocusedFrameSelection : mSelection;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
|||||||
@@ -558,6 +558,12 @@ class PresShell final : public nsStubDocumentObserver,
|
|||||||
|
|
||||||
void ClearFrameRefs(nsIFrame* aFrame);
|
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
|
* Get a reference rendering context. This is a context that should not
|
||||||
* be rendered to, but is suitable for measuring text and performing
|
* 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);
|
dom::Selection* GetCurrentSelection(SelectionType aSelectionType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a selection controller for the focused content in the DOM window
|
* Gets the last selection that took focus in this document. This is basically
|
||||||
* for mDocument.
|
* the frame selection that's visible to the user.
|
||||||
*
|
|
||||||
* @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.
|
|
||||||
*/
|
*/
|
||||||
already_AddRefed<nsISelectionController>
|
nsFrameSelection* GetLastFocusedFrameSelection();
|
||||||
GetSelectionControllerForFocusedContent(
|
|
||||||
nsIContent** aFocusedContent = nullptr);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to dispatch events via the presshell
|
* Interface to dispatch events via the presshell
|
||||||
@@ -1272,6 +1266,8 @@ class PresShell final : public nsStubDocumentObserver,
|
|||||||
SelectionRegion aRegion,
|
SelectionRegion aRegion,
|
||||||
int16_t aFlags) override;
|
int16_t aFlags) override;
|
||||||
NS_IMETHOD RepaintSelection(RawSelectionType aRawSelectionType) 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
|
* Set a "resolution" for the document, which if not 1.0 will
|
||||||
@@ -2832,6 +2828,10 @@ class PresShell final : public nsStubDocumentObserver,
|
|||||||
UniquePtr<nsCSSFrameConstructor> mFrameConstructor;
|
UniquePtr<nsCSSFrameConstructor> mFrameConstructor;
|
||||||
nsViewManager* mViewManager; // [WEAK] docViewer owns it so I don't have to
|
nsViewManager* mViewManager; // [WEAK] docViewer owns it so I don't have to
|
||||||
RefPtr<nsFrameSelection> mSelection;
|
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> mCaret;
|
||||||
RefPtr<nsCaret> mOriginalCaret;
|
RefPtr<nsCaret> mOriginalCaret;
|
||||||
RefPtr<AccessibleCaretEventHub> mAccessibleCaretEventHub;
|
RefPtr<AccessibleCaretEventHub> mAccessibleCaretEventHub;
|
||||||
|
|||||||
@@ -2629,7 +2629,7 @@ NS_IMETHODIMP nsDocumentViewer::GetContents(const char* mimeType,
|
|||||||
// Now we have the selection. Make sure it's nonzero:
|
// Now we have the selection. Make sure it's nonzero:
|
||||||
RefPtr<Selection> sel;
|
RefPtr<Selection> sel;
|
||||||
if (selectionOnly) {
|
if (selectionOnly) {
|
||||||
nsCopySupport::GetSelectionForCopy(mDocument, getter_AddRefs(sel));
|
sel = nsCopySupport::GetSelectionForCopy(mDocument);
|
||||||
NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
if (sel->IsCollapsed()) {
|
if (sel->IsCollapsed()) {
|
||||||
|
|||||||
@@ -1053,42 +1053,21 @@ nsresult nsTextControlFrame::AttributeChanged(int32_t aNameSpaceID,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nsGkAtoms::readonly == aAttribute) {
|
if (nsGkAtoms::readonly == aAttribute || nsGkAtoms::disabled == aAttribute) {
|
||||||
if (AttributeExists(nsGkAtoms::readonly)) { // set readonly
|
if (AttributeExists(aAttribute)) {
|
||||||
if (nsContentUtils::IsFocusedContent(mContent)) {
|
if (nsContentUtils::IsFocusedContent(mContent)) {
|
||||||
selCon->SetCaretEnabled(false);
|
selCon->SetCaretEnabled(false);
|
||||||
}
|
}
|
||||||
textEditor->AddFlags(nsIEditor::eEditorReadonlyMask);
|
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 {
|
} 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;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1296,6 +1296,8 @@ nsresult nsFrameSelection::TakeFocus(nsIContent* aNewFocus,
|
|||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mPresShell->FrameSelectionWillTakeFocus(*this);
|
||||||
|
|
||||||
// Clear all table selection data
|
// Clear all table selection data
|
||||||
mTableSelection.mMode = TableSelectionMode::None;
|
mTableSelection.mMode = TableSelectionMode::None;
|
||||||
mTableSelection.mDragSelectingCells = false;
|
mTableSelection.mDragSelectingCells = false;
|
||||||
|
|||||||
@@ -1207,9 +1207,7 @@ function runEditorFlagChangeTests() {
|
|||||||
description + "The editor doesn't get focus");
|
description + "The editor doesn't get focus");
|
||||||
is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
|
is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
|
||||||
description + "IME isn't enabled on HTML editor");
|
description + "IME isn't enabled on HTML editor");
|
||||||
const kIMEStateChangeFlags =
|
const kIMEStateChangeFlags = Ci.nsIEditor.eEditorReadonlyMask;
|
||||||
Ci.nsIEditor.eEditorReadonlyMask |
|
|
||||||
Ci.nsIEditor.eEditorDisabledMask;
|
|
||||||
var editor = window.docShell.editor;
|
var editor = window.docShell.editor;
|
||||||
var flags = editor.flags;
|
var flags = editor.flags;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user