michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsTextControlFrame.h" michael@0: #include "nsIPlaintextEditor.h" michael@0: #include "nsCaret.h" michael@0: #include "nsGenericHTMLElement.h" michael@0: #include "nsIEditor.h" michael@0: #include "nsIEditorIMESupport.h" michael@0: #include "nsIPhonetic.h" michael@0: #include "nsTextFragment.h" michael@0: #include "nsIDOMHTMLTextAreaElement.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsINodeInfo.h" michael@0: #include "nsFormControlFrame.h" //for registering accesskeys michael@0: michael@0: #include "nsIContent.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMHTMLElement.h" michael@0: #include "nsIPresShell.h" michael@0: michael@0: #include michael@0: #include "nsIDOMNodeList.h" //for selection setting helper func michael@0: #include "nsIDOMRange.h" //for selection setting helper func michael@0: #include "nsPIDOMWindow.h" //needed for notify selection changed to update the menus ect. michael@0: #include "nsIDOMNode.h" michael@0: michael@0: #include "nsIDOMText.h" //for multiline getselection michael@0: #include "nsFocusManager.h" michael@0: #include "nsTextEditRules.h" michael@0: #include "nsPresState.h" michael@0: #include "nsContentList.h" michael@0: #include "nsAttrValueInlines.h" michael@0: #include "mozilla/dom/Selection.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsTextNode.h" michael@0: #include "nsStyleSet.h" michael@0: #include "mozilla/dom/ScriptSettings.h" michael@0: #include "mozilla/MathAlgorithms.h" michael@0: michael@0: #define DEFAULT_COLUMN_WIDTH 20 michael@0: michael@0: using namespace mozilla; michael@0: michael@0: nsIFrame* michael@0: NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsTextControlFrame(aPresShell, aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsTextControlFrame) michael@0: michael@0: NS_QUERYFRAME_HEAD(nsTextControlFrame) michael@0: NS_QUERYFRAME_ENTRY(nsIFormControlFrame) michael@0: NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) michael@0: NS_QUERYFRAME_ENTRY(nsITextControlFrame) michael@0: NS_QUERYFRAME_ENTRY(nsIStatefulFrame) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: a11y::AccType michael@0: nsTextControlFrame::AccessibleType() michael@0: { michael@0: return a11y::eHTMLTextFieldType; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: class EditorInitializerEntryTracker { michael@0: public: michael@0: explicit EditorInitializerEntryTracker(nsTextControlFrame &frame) michael@0: : mFrame(frame) michael@0: , mFirstEntry(false) michael@0: { michael@0: if (!mFrame.mInEditorInitialization) { michael@0: mFrame.mInEditorInitialization = true; michael@0: mFirstEntry = true; michael@0: } michael@0: } michael@0: ~EditorInitializerEntryTracker() michael@0: { michael@0: if (mFirstEntry) { michael@0: mFrame.mInEditorInitialization = false; michael@0: } michael@0: } michael@0: bool EnteredMoreThanOnce() const { return !mFirstEntry; } michael@0: private: michael@0: nsTextControlFrame &mFrame; michael@0: bool mFirstEntry; michael@0: }; michael@0: #endif michael@0: michael@0: nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext) michael@0: : nsContainerFrame(aContext) michael@0: , mEditorHasBeenInitialized(false) michael@0: , mIsProcessing(false) michael@0: #ifdef DEBUG michael@0: , mInEditorInitialization(false) michael@0: #endif michael@0: { michael@0: } michael@0: michael@0: nsTextControlFrame::~nsTextControlFrame() michael@0: { michael@0: } michael@0: michael@0: void michael@0: nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: mScrollEvent.Revoke(); michael@0: michael@0: EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer()); michael@0: if (initializer) { michael@0: initializer->Revoke(); michael@0: Properties().Delete(TextControlInitializer()); michael@0: } michael@0: michael@0: // Unbind the text editor state object from the frame. The editor will live michael@0: // on, but things like controllers will be released. michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: txtCtrl->UnbindFromFrame(this); michael@0: michael@0: nsFormControlFrame::RegUnRegAccessKey(static_cast(this), false); michael@0: michael@0: nsContainerFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsTextControlFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::textInputFrame; michael@0: } michael@0: michael@0: nsresult michael@0: nsTextControlFrame::CalcIntrinsicSize(nsRenderingContext* aRenderingContext, michael@0: nsSize& aIntrinsicSize, michael@0: float aFontSizeInflation) michael@0: { michael@0: // Get leading and the Average/MaxAdvance char width michael@0: nscoord lineHeight = 0; michael@0: nscoord charWidth = 0; michael@0: nscoord charMaxAdvance = 0; michael@0: michael@0: nsRefPtr fontMet; michael@0: nsresult rv = michael@0: nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet), michael@0: aFontSizeInflation); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: aRenderingContext->SetFont(fontMet); michael@0: michael@0: lineHeight = michael@0: nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(), michael@0: NS_AUTOHEIGHT, aFontSizeInflation); michael@0: charWidth = fontMet->AveCharWidth(); michael@0: charMaxAdvance = fontMet->MaxAdvance(); michael@0: michael@0: // Set the width equal to the width in characters michael@0: int32_t cols = GetCols(); michael@0: aIntrinsicSize.width = cols * charWidth; michael@0: michael@0: // To better match IE, take the maximum character width(in twips) and remove michael@0: // 4 pixels add this on as additional padding(internalPadding). But only do michael@0: // this if we think we have a fixed-width font. michael@0: if (mozilla::Abs(charWidth - charMaxAdvance) > (unsigned)nsPresContext::CSSPixelsToAppUnits(1)) { michael@0: nscoord internalPadding = michael@0: std::max(0, charMaxAdvance - nsPresContext::CSSPixelsToAppUnits(4)); michael@0: nscoord t = nsPresContext::CSSPixelsToAppUnits(1); michael@0: // Round to a multiple of t michael@0: nscoord rest = internalPadding % t; michael@0: if (rest < t - rest) { michael@0: internalPadding -= rest; michael@0: } else { michael@0: internalPadding += t - rest; michael@0: } michael@0: // Now add the extra padding on (so that small input sizes work well) michael@0: aIntrinsicSize.width += internalPadding; michael@0: } else { michael@0: // This is to account for the anonymous
having a 1 twip width michael@0: // in Full Standards mode, see BRFrame::Reflow and bug 228752. michael@0: if (PresContext()->CompatibilityMode() == eCompatibility_FullStandards) { michael@0: aIntrinsicSize.width += 1; michael@0: } michael@0: } michael@0: michael@0: // Increment width with cols * letter-spacing. michael@0: { michael@0: const nsStyleCoord& lsCoord = StyleText()->mLetterSpacing; michael@0: if (eStyleUnit_Coord == lsCoord.GetUnit()) { michael@0: nscoord letterSpacing = lsCoord.GetCoordValue(); michael@0: if (letterSpacing != 0) { michael@0: aIntrinsicSize.width += cols * letterSpacing; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Set the height equal to total number of rows (times the height of each michael@0: // line, of course) michael@0: aIntrinsicSize.height = lineHeight * GetRows(); michael@0: michael@0: // Add in the size of the scrollbars for textarea michael@0: if (IsTextArea()) { michael@0: nsIFrame* first = GetFirstPrincipalChild(); michael@0: michael@0: nsIScrollableFrame *scrollableFrame = do_QueryFrame(first); michael@0: NS_ASSERTION(scrollableFrame, "Child must be scrollable"); michael@0: michael@0: if (scrollableFrame) { michael@0: nsMargin scrollbarSizes = michael@0: scrollableFrame->GetDesiredScrollbarSizes(PresContext(), aRenderingContext); michael@0: michael@0: aIntrinsicSize.width += scrollbarSizes.LeftRight(); michael@0: michael@0: aIntrinsicSize.height += scrollbarSizes.TopBottom();; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsTextControlFrame::EnsureEditorInitialized() michael@0: { michael@0: // This method initializes our editor, if needed. michael@0: michael@0: // This code used to be called from CreateAnonymousContent(), but michael@0: // when the editor set the initial string, it would trigger a michael@0: // PresShell listener which called FlushPendingNotifications() michael@0: // during frame construction. This was causing other form controls michael@0: // to display wrong values. Additionally, calling this every time michael@0: // a text frame control is instantiated means that we're effectively michael@0: // instantiating the editor for all text fields, even if they michael@0: // never get used. So, now this method is being called lazily only michael@0: // when we actually need an editor. michael@0: michael@0: if (mEditorHasBeenInitialized) michael@0: return NS_OK; michael@0: michael@0: nsIDocument* doc = mContent->GetCurrentDoc(); michael@0: NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); michael@0: michael@0: nsWeakFrame weakFrame(this); michael@0: michael@0: // Flush out content on our document. Have to do this, because script michael@0: // blockers don't prevent the sink flushing out content and notifying in the michael@0: // process, which can destroy frames. michael@0: doc->FlushPendingNotifications(Flush_ContentAndNotify); michael@0: NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_ERROR_FAILURE); michael@0: michael@0: // Make sure that editor init doesn't do things that would kill us off michael@0: // (especially off the script blockers it'll create for its DOM mutations). michael@0: { michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: michael@0: // Time to mess with our security context... See comments in GetValue() michael@0: // for why this is needed. michael@0: mozilla::dom::AutoNoJSAPI nojsapi; michael@0: michael@0: // Make sure that we try to focus the content even if the method fails michael@0: class EnsureSetFocus { michael@0: public: michael@0: explicit EnsureSetFocus(nsTextControlFrame* aFrame) michael@0: : mFrame(aFrame) {} michael@0: ~EnsureSetFocus() { michael@0: if (nsContentUtils::IsFocusedContent(mFrame->GetContent())) michael@0: mFrame->SetFocus(true, false); michael@0: } michael@0: private: michael@0: nsTextControlFrame *mFrame; michael@0: }; michael@0: EnsureSetFocus makeSureSetFocusHappens(this); michael@0: michael@0: #ifdef DEBUG michael@0: // Make sure we are not being called again until we're finished. michael@0: // If reentrancy happens, just pretend that we don't have an editor. michael@0: const EditorInitializerEntryTracker tracker(*this); michael@0: NS_ASSERTION(!tracker.EnteredMoreThanOnce(), michael@0: "EnsureEditorInitialized has been called while a previous call was in progress"); michael@0: #endif michael@0: michael@0: // Create an editor for the frame, if one doesn't already exist michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: nsresult rv = txtCtrl->CreateEditor(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: NS_ENSURE_STATE(weakFrame.IsAlive()); michael@0: michael@0: // Set mEditorHasBeenInitialized so that subsequent calls will use the michael@0: // editor. michael@0: mEditorHasBeenInitialized = true; michael@0: michael@0: // Set the selection to the beginning of the text field. michael@0: if (weakFrame.IsAlive()) { michael@0: SetSelectionEndPoints(0, 0); michael@0: } michael@0: } michael@0: NS_ENSURE_STATE(weakFrame.IsAlive()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsTextControlFrame::CreateAnonymousContent(nsTArray& aElements) michael@0: { michael@0: NS_ASSERTION(mContent, "We should have a content!"); michael@0: michael@0: mState |= NS_FRAME_INDEPENDENT_SELECTION; michael@0: michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: michael@0: // Bind the frame to its text control michael@0: nsresult rv = txtCtrl->BindToFrame(this); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsIContent* rootNode = txtCtrl->GetRootEditorNode(); michael@0: NS_ENSURE_TRUE(rootNode, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: if (!aElements.AppendElement(rootNode)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // Do we need a placeholder node? michael@0: nsAutoString placeholderTxt; michael@0: mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, michael@0: placeholderTxt); michael@0: nsContentUtils::RemoveNewlines(placeholderTxt); michael@0: mUsePlaceholder = !placeholderTxt.IsEmpty(); michael@0: michael@0: // Create the placeholder anonymous content if needed. michael@0: if (mUsePlaceholder) { michael@0: nsIContent* placeholderNode = txtCtrl->CreatePlaceholderNode(); michael@0: NS_ENSURE_TRUE(placeholderNode, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: // Associate ::-moz-placeholder pseudo-element with the placeholder node. michael@0: nsCSSPseudoElements::Type pseudoType = michael@0: nsCSSPseudoElements::ePseudo_mozPlaceholder; michael@0: michael@0: nsRefPtr placeholderStyleContext = michael@0: PresContext()->StyleSet()->ResolvePseudoElementStyle( michael@0: mContent->AsElement(), pseudoType, StyleContext(), michael@0: placeholderNode->AsElement()); michael@0: michael@0: if (!aElements.AppendElement(ContentInfo(placeholderNode, michael@0: placeholderStyleContext))) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: michael@0: rv = UpdateValueDisplay(false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // textareas are eagerly initialized michael@0: bool initEagerly = !IsSingleLineTextControl(); michael@0: if (!initEagerly) { michael@0: // Also, input elements which have a cached selection should get eager michael@0: // editor initialization. michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: initEagerly = txtCtrl->HasCachedSelection(); michael@0: } michael@0: if (!initEagerly) { michael@0: nsCOMPtr element = do_QueryInterface(txtCtrl); michael@0: if (element) { michael@0: // so are input text controls with spellcheck=true michael@0: element->GetSpellcheck(&initEagerly); michael@0: } michael@0: } michael@0: michael@0: if (initEagerly) { michael@0: NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), michael@0: "Someone forgot a script blocker?"); michael@0: EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer()); michael@0: if (initializer) { michael@0: initializer->Revoke(); michael@0: } michael@0: initializer = new EditorInitializer(this); michael@0: Properties().Set(TextControlInitializer(),initializer); michael@0: if (!nsContentUtils::AddScriptRunner(initializer)) { michael@0: initializer->Revoke(); // paranoia michael@0: Properties().Delete(TextControlInitializer()); michael@0: delete initializer; michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsTextControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, michael@0: uint32_t aFilter) michael@0: { michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: michael@0: aElements.MaybeAppendElement(txtCtrl->GetRootEditorNode()); michael@0: if (!(aFilter & nsIContent::eSkipPlaceholderContent)) michael@0: aElements.MaybeAppendElement(txtCtrl->GetPlaceholderNode()); michael@0: michael@0: } michael@0: michael@0: nscoord michael@0: nsTextControlFrame::GetPrefWidth(nsRenderingContext* aRenderingContext) michael@0: { michael@0: DebugOnly result = 0; michael@0: DISPLAY_PREF_WIDTH(this, result); michael@0: michael@0: float inflation = nsLayoutUtils::FontSizeInflationFor(this); michael@0: nsSize autoSize; michael@0: CalcIntrinsicSize(aRenderingContext, autoSize, inflation); michael@0: michael@0: return autoSize.width; michael@0: } michael@0: michael@0: nscoord michael@0: nsTextControlFrame::GetMinWidth(nsRenderingContext* aRenderingContext) michael@0: { michael@0: // Our min width is just our preferred width if we have auto width. michael@0: nscoord result; michael@0: DISPLAY_MIN_WIDTH(this, result); michael@0: michael@0: result = GetPrefWidth(aRenderingContext); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: nsSize michael@0: nsTextControlFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, michael@0: nsSize aPadding, bool aShrinkWrap) michael@0: { michael@0: float inflation = nsLayoutUtils::FontSizeInflationFor(this); michael@0: nsSize autoSize; michael@0: nsresult rv = CalcIntrinsicSize(aRenderingContext, autoSize, inflation); michael@0: if (NS_FAILED(rv)) { michael@0: // What now? michael@0: autoSize.SizeTo(0, 0); michael@0: } michael@0: #ifdef DEBUG michael@0: // Note: Ancestor ComputeAutoSize only computes a width if we're auto-width michael@0: else if (StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) { michael@0: nsSize ancestorAutoSize = michael@0: nsContainerFrame::ComputeAutoSize(aRenderingContext, michael@0: aCBSize, aAvailableWidth, michael@0: aMargin, aBorder, michael@0: aPadding, aShrinkWrap); michael@0: // Disabled when there's inflation; see comment in GetPrefSize. michael@0: NS_ASSERTION(inflation != 1.0f || ancestorAutoSize.width == autoSize.width, michael@0: "Incorrect size computed by ComputeAutoSize?"); michael@0: } michael@0: #endif michael@0: michael@0: return autoSize; michael@0: } michael@0: michael@0: nsresult michael@0: nsTextControlFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); michael@0: michael@0: // make sure that the form registers itself on the initial/first reflow michael@0: if (mState & NS_FRAME_FIRST_REFLOW) { michael@0: nsFormControlFrame::RegUnRegAccessKey(this, true); michael@0: } michael@0: michael@0: // set values of reflow's out parameters michael@0: aDesiredSize.Width() = aReflowState.ComputedWidth() + michael@0: aReflowState.ComputedPhysicalBorderPadding().LeftRight(); michael@0: aDesiredSize.Height() = aReflowState.ComputedHeight() + michael@0: aReflowState.ComputedPhysicalBorderPadding().TopBottom(); michael@0: michael@0: // computation of the ascent wrt the input height michael@0: nscoord lineHeight = aReflowState.ComputedHeight(); michael@0: float inflation = nsLayoutUtils::FontSizeInflationFor(this); michael@0: if (!IsSingleLineTextControl()) { michael@0: lineHeight = nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(), michael@0: NS_AUTOHEIGHT, inflation); michael@0: } michael@0: nsRefPtr fontMet; michael@0: nsresult rv = nsLayoutUtils::GetFontMetricsForFrame(this, michael@0: getter_AddRefs(fontMet), michael@0: inflation); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: // now adjust for our borders and padding michael@0: aDesiredSize.SetTopAscent( michael@0: nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight) michael@0: + aReflowState.ComputedPhysicalBorderPadding().top); michael@0: michael@0: // overflow handling michael@0: aDesiredSize.SetOverflowAreasToDesiredBounds(); michael@0: // perform reflow on all kids michael@0: nsIFrame* kid = mFrames.FirstChild(); michael@0: while (kid) { michael@0: ReflowTextControlChild(kid, aPresContext, aReflowState, aStatus, aDesiredSize); michael@0: kid = kid->GetNextSibling(); michael@0: } michael@0: michael@0: // take into account css properties that affect overflow handling michael@0: FinishAndStoreOverflow(&aDesiredSize); michael@0: michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsTextControlFrame::ReflowTextControlChild(nsIFrame* aKid, michael@0: nsPresContext* aPresContext, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus, michael@0: nsHTMLReflowMetrics& aParentDesiredSize) michael@0: { michael@0: // compute available size and frame offsets for child michael@0: nsSize availSize(aReflowState.ComputedWidth() + michael@0: aReflowState.ComputedPhysicalPadding().LeftRight(), michael@0: NS_UNCONSTRAINEDSIZE); michael@0: michael@0: nsHTMLReflowState kidReflowState(aPresContext, aReflowState, michael@0: aKid, availSize, -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); michael@0: // Override padding with our computed padding in case we got it from theming or percentage michael@0: kidReflowState.Init(aPresContext, -1, -1, nullptr, &aReflowState.ComputedPhysicalPadding()); michael@0: michael@0: // Set computed width and computed height for the child michael@0: kidReflowState.SetComputedWidth(aReflowState.ComputedWidth()); michael@0: kidReflowState.SetComputedHeight(aReflowState.ComputedHeight()); michael@0: michael@0: // Offset the frame by the size of the parent's border michael@0: nscoord xOffset = aReflowState.ComputedPhysicalBorderPadding().left - michael@0: aReflowState.ComputedPhysicalPadding().left; michael@0: nscoord yOffset = aReflowState.ComputedPhysicalBorderPadding().top - michael@0: aReflowState.ComputedPhysicalPadding().top; michael@0: michael@0: // reflow the child michael@0: nsHTMLReflowMetrics desiredSize(aReflowState); michael@0: ReflowChild(aKid, aPresContext, desiredSize, kidReflowState, michael@0: xOffset, yOffset, 0, aStatus); michael@0: michael@0: // place the child michael@0: FinishReflowChild(aKid, aPresContext, desiredSize, michael@0: &kidReflowState, xOffset, yOffset, 0); michael@0: michael@0: // consider the overflow michael@0: aParentDesiredSize.mOverflowAreas.UnionWith(desiredSize.mOverflowAreas); michael@0: } michael@0: michael@0: nsSize michael@0: nsTextControlFrame::GetMinSize(nsBoxLayoutState& aState) michael@0: { michael@0: // XXXbz why? Why not the nsBoxFrame sizes? michael@0: return nsBox::GetMinSize(aState); michael@0: } michael@0: michael@0: bool michael@0: nsTextControlFrame::IsCollapsed() michael@0: { michael@0: // We're never collapsed in the box sense. michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsTextControlFrame::IsLeaf() const michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTextControlFrame::ScrollOnFocusEvent::Run() michael@0: { michael@0: if (mFrame) { michael@0: nsCOMPtr txtCtrl = do_QueryInterface(mFrame->GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: nsISelectionController* selCon = txtCtrl->GetSelectionController(); michael@0: if (selCon) { michael@0: mFrame->mScrollEvent.Forget(); michael@0: selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, michael@0: nsISelectionController::SELECTION_FOCUS_REGION, michael@0: nsISelectionController::SCROLL_SYNCHRONOUS); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: //IMPLEMENTING NS_IFORMCONTROLFRAME michael@0: void nsTextControlFrame::SetFocus(bool aOn, bool aRepaint) michael@0: { michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: michael@0: // Revoke the previous scroll event if one exists michael@0: mScrollEvent.Revoke(); michael@0: michael@0: // If 'dom.placeholeder.show_on_focus' preference is 'false', focusing or michael@0: // blurring the frame can have an impact on the placeholder visibility. michael@0: if (mUsePlaceholder) { michael@0: txtCtrl->UpdatePlaceholderVisibility(true); michael@0: } michael@0: michael@0: if (!aOn) { michael@0: return; michael@0: } michael@0: michael@0: nsISelectionController* selCon = txtCtrl->GetSelectionController(); michael@0: if (!selCon) michael@0: return; michael@0: michael@0: nsCOMPtr ourSel; michael@0: selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, michael@0: getter_AddRefs(ourSel)); michael@0: if (!ourSel) return; michael@0: michael@0: nsIPresShell* presShell = PresContext()->GetPresShell(); michael@0: nsRefPtr caret = presShell->GetCaret(); michael@0: if (!caret) return; michael@0: michael@0: // Scroll the current selection into view michael@0: nsISelection *caretSelection = caret->GetCaretDOMSelection(); michael@0: const bool isFocusedRightNow = ourSel == caretSelection; michael@0: if (!isFocusedRightNow) { michael@0: // Don't scroll the current selection if we've been focused using the mouse. michael@0: uint32_t lastFocusMethod = 0; michael@0: nsIDocument* doc = GetContent()->GetCurrentDoc(); michael@0: if (doc) { michael@0: nsIFocusManager* fm = nsFocusManager::GetFocusManager(); michael@0: if (fm) { michael@0: fm->GetLastFocusMethod(doc->GetWindow(), &lastFocusMethod); michael@0: } michael@0: } michael@0: if (!(lastFocusMethod & nsIFocusManager::FLAG_BYMOUSE)) { michael@0: nsRefPtr event = new ScrollOnFocusEvent(this); michael@0: nsresult rv = NS_DispatchToCurrentThread(event); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mScrollEvent = event; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // tell the caret to use our selection michael@0: caret->SetCaretDOMSelection(ourSel); michael@0: michael@0: // mutual-exclusion: the selection is either controlled by the michael@0: // document or by the text input/area. Clear any selection in the michael@0: // document since the focus is now on our independent selection. michael@0: michael@0: nsCOMPtr selcon = do_QueryInterface(presShell); michael@0: nsCOMPtr docSel; michael@0: selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, michael@0: getter_AddRefs(docSel)); michael@0: if (!docSel) return; michael@0: michael@0: bool isCollapsed = false; michael@0: docSel->GetIsCollapsed(&isCollapsed); michael@0: if (!isCollapsed) michael@0: docSel->RemoveAllRanges(); michael@0: } michael@0: michael@0: nsresult nsTextControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue) michael@0: { michael@0: if (!mIsProcessing)//some kind of lock. michael@0: { michael@0: mIsProcessing = true; michael@0: if (nsGkAtoms::select == aName) michael@0: { michael@0: // Select all the text. michael@0: // michael@0: // XXX: This is lame, we can't call editor's SelectAll method michael@0: // because that triggers AutoCopies in unix builds. michael@0: // Instead, we have to call our own homegrown version michael@0: // of select all which merely builds a range that selects michael@0: // all of the content and adds that to the selection. michael@0: michael@0: nsWeakFrame weakThis = this; michael@0: SelectAllOrCollapseToEndOfText(true); // NOTE: can destroy the world michael@0: if (!weakThis.IsAlive()) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: mIsProcessing = false; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTextControlFrame::GetEditor(nsIEditor **aEditor) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aEditor); michael@0: michael@0: nsresult rv = EnsureEditorInitialized(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: *aEditor = txtCtrl->GetTextEditor(); michael@0: NS_IF_ADDREF(*aEditor); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsTextControlFrame::SetSelectionInternal(nsIDOMNode *aStartNode, michael@0: int32_t aStartOffset, michael@0: nsIDOMNode *aEndNode, michael@0: int32_t aEndOffset, michael@0: nsITextControlFrame::SelectionDirection aDirection) michael@0: { michael@0: // Create a new range to represent the new selection. michael@0: // Note that we use a new range to avoid having to do michael@0: // isIncreasing checks to avoid possible errors. michael@0: michael@0: nsRefPtr range = new nsRange(mContent); michael@0: nsresult rv = range->SetStart(aStartNode, aStartOffset); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = range->SetEnd(aEndNode, aEndOffset); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Get the selection, clear it and add the new range to it! michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: nsISelectionController* selCon = txtCtrl->GetSelectionController(); michael@0: NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr selection; michael@0: selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr selPriv = do_QueryInterface(selection, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsDirection direction; michael@0: if (aDirection == eNone) { michael@0: // Preserve the direction michael@0: direction = selPriv->GetSelectionDirection(); michael@0: } else { michael@0: direction = (aDirection == eBackward) ? eDirPrevious : eDirNext; michael@0: } michael@0: michael@0: rv = selection->RemoveAllRanges(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = selection->AddRange(range); // NOTE: can destroy the world michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: selPriv->SetSelectionDirection(direction); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsTextControlFrame::ScrollSelectionIntoView() michael@0: { michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: nsISelectionController* selCon = txtCtrl->GetSelectionController(); michael@0: if (selCon) { michael@0: // Scroll the selection into view (see bug 231389). michael@0: return selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, michael@0: nsISelectionController::SELECTION_FOCUS_REGION, michael@0: nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY); michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mozilla::dom::Element* michael@0: nsTextControlFrame::GetRootNodeAndInitializeEditor() michael@0: { michael@0: nsCOMPtr root; michael@0: GetRootNodeAndInitializeEditor(getter_AddRefs(root)); michael@0: nsCOMPtr rootElem = do_QueryInterface(root); michael@0: return rootElem; michael@0: } michael@0: michael@0: nsresult michael@0: nsTextControlFrame::GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aRootElement); michael@0: michael@0: nsCOMPtr editor; michael@0: GetEditor(getter_AddRefs(editor)); michael@0: if (!editor) michael@0: return NS_OK; michael@0: michael@0: return editor->GetRootElement(aRootElement); michael@0: } michael@0: michael@0: nsresult michael@0: nsTextControlFrame::SelectAllOrCollapseToEndOfText(bool aSelect) michael@0: { michael@0: nsCOMPtr rootElement; michael@0: nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr rootContent = do_QueryInterface(rootElement); michael@0: nsCOMPtr rootNode(do_QueryInterface(rootElement)); michael@0: michael@0: NS_ENSURE_TRUE(rootNode && rootContent, NS_ERROR_FAILURE); michael@0: michael@0: int32_t numChildren = rootContent->GetChildCount(); michael@0: michael@0: if (numChildren > 0) { michael@0: // We never want to place the selection after the last michael@0: // br under the root node! michael@0: nsIContent *child = rootContent->GetChildAt(numChildren - 1); michael@0: if (child) { michael@0: if (child->Tag() == nsGkAtoms::br) michael@0: --numChildren; michael@0: } michael@0: if (!aSelect && numChildren) { michael@0: child = rootContent->GetChildAt(numChildren - 1); michael@0: if (child && child->IsNodeOfType(nsINode::eTEXT)) { michael@0: rootNode = do_QueryInterface(child); michael@0: const nsTextFragment* fragment = child->GetText(); michael@0: numChildren = fragment ? fragment->GetLength() : 0; michael@0: } michael@0: } michael@0: } michael@0: michael@0: rv = SetSelectionInternal(rootNode, aSelect ? 0 : numChildren, michael@0: rootNode, numChildren); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return ScrollSelectionIntoView(); michael@0: } michael@0: michael@0: nsresult michael@0: nsTextControlFrame::SetSelectionEndPoints(int32_t aSelStart, int32_t aSelEnd, michael@0: nsITextControlFrame::SelectionDirection aDirection) michael@0: { michael@0: NS_ASSERTION(aSelStart <= aSelEnd, "Invalid selection offsets!"); michael@0: michael@0: if (aSelStart > aSelEnd) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr startNode, endNode; michael@0: int32_t startOffset, endOffset; michael@0: michael@0: // Calculate the selection start point. michael@0: michael@0: nsresult rv = OffsetToDOMPoint(aSelStart, getter_AddRefs(startNode), &startOffset); michael@0: michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aSelStart == aSelEnd) { michael@0: // Collapsed selection, so start and end are the same! michael@0: endNode = startNode; michael@0: endOffset = startOffset; michael@0: } michael@0: else { michael@0: // Selection isn't collapsed so we have to calculate michael@0: // the end point too. michael@0: michael@0: rv = OffsetToDOMPoint(aSelEnd, getter_AddRefs(endNode), &endOffset); michael@0: michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return SetSelectionInternal(startNode, startOffset, endNode, endOffset, aDirection); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTextControlFrame::SetSelectionRange(int32_t aSelStart, int32_t aSelEnd, michael@0: nsITextControlFrame::SelectionDirection aDirection) michael@0: { michael@0: nsresult rv = EnsureEditorInitialized(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aSelStart > aSelEnd) { michael@0: // Simulate what we'd see SetSelectionStart() was called, followed michael@0: // by a SetSelectionEnd(). michael@0: michael@0: aSelStart = aSelEnd; michael@0: } michael@0: michael@0: return SetSelectionEndPoints(aSelStart, aSelEnd, aDirection); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsTextControlFrame::SetSelectionStart(int32_t aSelectionStart) michael@0: { michael@0: nsresult rv = EnsureEditorInitialized(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: int32_t selStart = 0, selEnd = 0; michael@0: michael@0: rv = GetSelectionRange(&selStart, &selEnd); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aSelectionStart > selEnd) { michael@0: // Collapse to the new start point. michael@0: selEnd = aSelectionStart; michael@0: } michael@0: michael@0: selStart = aSelectionStart; michael@0: michael@0: return SetSelectionEndPoints(selStart, selEnd); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTextControlFrame::SetSelectionEnd(int32_t aSelectionEnd) michael@0: { michael@0: nsresult rv = EnsureEditorInitialized(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: int32_t selStart = 0, selEnd = 0; michael@0: michael@0: rv = GetSelectionRange(&selStart, &selEnd); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aSelectionEnd < selStart) { michael@0: // Collapse to the new end point. michael@0: selStart = aSelectionEnd; michael@0: } michael@0: michael@0: selEnd = aSelectionEnd; michael@0: michael@0: return SetSelectionEndPoints(selStart, selEnd); michael@0: } michael@0: michael@0: nsresult michael@0: nsTextControlFrame::OffsetToDOMPoint(int32_t aOffset, michael@0: nsIDOMNode** aResult, michael@0: int32_t* aPosition) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aResult && aPosition); michael@0: michael@0: *aResult = nullptr; michael@0: *aPosition = 0; michael@0: michael@0: nsCOMPtr rootElement; michael@0: nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsCOMPtr rootNode(do_QueryInterface(rootElement)); michael@0: michael@0: NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr nodeList; michael@0: michael@0: rv = rootNode->GetChildNodes(getter_AddRefs(nodeList)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); michael@0: michael@0: uint32_t length = 0; michael@0: michael@0: rv = nodeList->GetLength(&length); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ASSERTION(length <= 2, "We should have one text node and one mozBR at most"); michael@0: michael@0: nsCOMPtr firstNode; michael@0: rv = nodeList->Item(0, getter_AddRefs(firstNode)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsCOMPtr textNode = do_QueryInterface(firstNode); michael@0: michael@0: if (length == 0 || aOffset < 0) { michael@0: NS_IF_ADDREF(*aResult = rootNode); michael@0: *aPosition = 0; michael@0: } else if (textNode) { michael@0: uint32_t textLength = 0; michael@0: textNode->GetLength(&textLength); michael@0: if (length == 2 && uint32_t(aOffset) == textLength) { michael@0: // If we're at the end of the text node and we have a trailing BR node, michael@0: // set the selection on the BR node. michael@0: NS_IF_ADDREF(*aResult = rootNode); michael@0: *aPosition = 1; michael@0: } else { michael@0: // Otherwise, set the selection on the textnode itself. michael@0: NS_IF_ADDREF(*aResult = firstNode); michael@0: *aPosition = std::min(aOffset, int32_t(textLength)); michael@0: } michael@0: } else { michael@0: NS_IF_ADDREF(*aResult = rootNode); michael@0: *aPosition = 0; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTextControlFrame::GetSelectionRange(int32_t* aSelectionStart, michael@0: int32_t* aSelectionEnd, michael@0: SelectionDirection* aDirection) michael@0: { michael@0: // make sure we have an editor michael@0: nsresult rv = EnsureEditorInitialized(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aSelectionStart) { michael@0: *aSelectionStart = 0; michael@0: } michael@0: if (aSelectionEnd) { michael@0: *aSelectionEnd = 0; michael@0: } michael@0: if (aDirection) { michael@0: *aDirection = eNone; michael@0: } michael@0: michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: nsISelectionController* selCon = txtCtrl->GetSelectionController(); michael@0: NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE); michael@0: nsCOMPtr selection; michael@0: rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); michael@0: michael@0: dom::Selection* sel = static_cast(selection.get()); michael@0: if (aDirection) { michael@0: nsDirection direction = sel->GetSelectionDirection(); michael@0: if (direction == eDirNext) { michael@0: *aDirection = eForward; michael@0: } else if (direction == eDirPrevious) { michael@0: *aDirection = eBackward; michael@0: } else { michael@0: NS_NOTREACHED("Invalid nsDirection enum value"); michael@0: } michael@0: } michael@0: michael@0: if (!aSelectionStart || !aSelectionEnd) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: mozilla::dom::Element* root = GetRootNodeAndInitializeEditor(); michael@0: NS_ENSURE_STATE(root); michael@0: nsContentUtils::GetSelectionInTextControl(sel, root, michael@0: *aSelectionStart, *aSelectionEnd); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /////END INTERFACE IMPLEMENTATIONS michael@0: michael@0: ////NSIFRAME michael@0: nsresult michael@0: nsTextControlFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: nsISelectionController* selCon = txtCtrl->GetSelectionController(); michael@0: const bool needEditor = nsGkAtoms::maxlength == aAttribute || michael@0: nsGkAtoms::readonly == aAttribute || michael@0: nsGkAtoms::disabled == aAttribute || michael@0: nsGkAtoms::spellcheck == aAttribute; michael@0: nsCOMPtr editor; michael@0: if (needEditor) { michael@0: GetEditor(getter_AddRefs(editor)); michael@0: } michael@0: if ((needEditor && !editor) || !selCon) { michael@0: return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); michael@0: } michael@0: michael@0: if (nsGkAtoms::maxlength == aAttribute) { michael@0: int32_t maxLength; michael@0: bool maxDefined = GetMaxLength(&maxLength); michael@0: nsCOMPtr textEditor = do_QueryInterface(editor); michael@0: if (textEditor) { michael@0: if (maxDefined) { // set the maxLength attribute michael@0: textEditor->SetMaxTextLength(maxLength); michael@0: // if maxLength>docLength, we need to truncate the doc content michael@0: } else { // unset the maxLength attribute michael@0: textEditor->SetMaxTextLength(-1); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (nsGkAtoms::readonly == aAttribute) { michael@0: uint32_t flags; michael@0: editor->GetFlags(&flags); michael@0: if (AttributeExists(nsGkAtoms::readonly)) { // set readonly michael@0: flags |= nsIPlaintextEditor::eEditorReadonlyMask; michael@0: if (nsContentUtils::IsFocusedContent(mContent)) { michael@0: selCon->SetCaretEnabled(false); michael@0: } michael@0: } else { // unset readonly michael@0: flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask); michael@0: if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) && michael@0: nsContentUtils::IsFocusedContent(mContent)) { michael@0: selCon->SetCaretEnabled(true); michael@0: } michael@0: } michael@0: editor->SetFlags(flags); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (nsGkAtoms::disabled == aAttribute) { michael@0: uint32_t flags; michael@0: editor->GetFlags(&flags); michael@0: int16_t displaySelection = nsISelectionController::SELECTION_OFF; michael@0: const bool focused = nsContentUtils::IsFocusedContent(mContent); michael@0: const bool hasAttr = AttributeExists(nsGkAtoms::disabled); michael@0: if (hasAttr) { // set disabled michael@0: flags |= nsIPlaintextEditor::eEditorDisabledMask; michael@0: } else { // unset disabled michael@0: flags &= ~(nsIPlaintextEditor::eEditorDisabledMask); michael@0: displaySelection = focused ? nsISelectionController::SELECTION_ON michael@0: : nsISelectionController::SELECTION_HIDDEN; michael@0: } michael@0: selCon->SetDisplaySelection(displaySelection); michael@0: if (focused) { michael@0: selCon->SetCaretEnabled(!hasAttr); michael@0: } michael@0: editor->SetFlags(flags); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!mEditorHasBeenInitialized && nsGkAtoms::value == aAttribute) { michael@0: UpdateValueDisplay(true); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Allow the base class to handle common attributes supported by all form michael@0: // elements... michael@0: return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsTextControlFrame::GetText(nsString& aText) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: if (IsSingleLineTextControl()) { michael@0: // There will be no line breaks so we can ignore the wrap property. michael@0: txtCtrl->GetTextEditorValue(aText, true); michael@0: } else { michael@0: nsCOMPtr textArea = do_QueryInterface(mContent); michael@0: if (textArea) { michael@0: rv = textArea->GetValue(aText); michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsTextControlFrame::GetPhonetic(nsAString& aPhonetic) michael@0: { michael@0: aPhonetic.Truncate(0); michael@0: michael@0: nsCOMPtr editor; michael@0: nsresult rv = GetEditor(getter_AddRefs(editor)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr imeSupport = do_QueryInterface(editor); michael@0: if (imeSupport) { michael@0: nsCOMPtr phonetic = do_QueryInterface(imeSupport); michael@0: if (phonetic) michael@0: phonetic->GetPhonetic(aPhonetic); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: ///END NSIFRAME OVERLOADS michael@0: /////BEGIN PROTECTED METHODS michael@0: michael@0: bool michael@0: nsTextControlFrame::GetMaxLength(int32_t* aSize) michael@0: { michael@0: *aSize = -1; michael@0: michael@0: nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent); michael@0: if (content) { michael@0: const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::maxlength); michael@0: if (attr && attr->Type() == nsAttrValue::eInteger) { michael@0: *aSize = attr->GetIntegerValue(); michael@0: michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // END IMPLEMENTING NS_IFORMCONTROLFRAME michael@0: michael@0: nsresult michael@0: nsTextControlFrame::SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) michael@0: { michael@0: nsresult rv = nsContainerFrame::SetInitialChildList(aListID, aChildList); michael@0: michael@0: nsIFrame* first = GetFirstPrincipalChild(); michael@0: michael@0: // Mark the scroll frame as being a reflow root. This will allow michael@0: // incremental reflows to be initiated at the scroll frame, rather michael@0: // than descending from the root frame of the frame hierarchy. michael@0: if (first) { michael@0: first->AddStateBits(NS_FRAME_REFLOW_ROOT); michael@0: michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: txtCtrl->InitializeKeyboardEventListeners(); michael@0: michael@0: nsPoint* contentScrollPos = static_cast michael@0: (Properties().Get(ContentScrollPos())); michael@0: if (contentScrollPos) { michael@0: // If we have a scroll pos stored to be passed to our anonymous michael@0: // div, do it here! michael@0: nsIStatefulFrame* statefulFrame = do_QueryFrame(first); michael@0: NS_ASSERTION(statefulFrame, "unexpected type of frame for the anonymous div"); michael@0: nsPresState fakePresState; michael@0: fakePresState.SetScrollState(*contentScrollPos); michael@0: statefulFrame->RestoreState(&fakePresState); michael@0: Properties().Remove(ContentScrollPos()); michael@0: delete contentScrollPos; michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsTextControlFrame::SetValueChanged(bool aValueChanged) michael@0: { michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: michael@0: if (mUsePlaceholder) { michael@0: nsWeakFrame weakFrame(this); michael@0: txtCtrl->UpdatePlaceholderVisibility(true); michael@0: if (!weakFrame.IsAlive()) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: txtCtrl->SetValueChanged(aValueChanged); michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsTextControlFrame::UpdateValueDisplay(bool aNotify, michael@0: bool aBeforeEditorInit, michael@0: const nsAString *aValue) michael@0: { michael@0: if (!IsSingleLineTextControl()) // textareas don't use this michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: nsIContent* rootNode = txtCtrl->GetRootEditorNode(); michael@0: michael@0: NS_PRECONDITION(rootNode, "Must have a div content\n"); michael@0: NS_PRECONDITION(!mEditorHasBeenInitialized, michael@0: "Do not call this after editor has been initialized"); michael@0: NS_ASSERTION(!mUsePlaceholder || txtCtrl->GetPlaceholderNode(), michael@0: "A placeholder div must exist"); michael@0: michael@0: nsIContent *textContent = rootNode->GetChildAt(0); michael@0: if (!textContent) { michael@0: // Set up a textnode with our value michael@0: nsRefPtr textNode = michael@0: new nsTextNode(mContent->NodeInfo()->NodeInfoManager()); michael@0: michael@0: NS_ASSERTION(textNode, "Must have textcontent!\n"); michael@0: michael@0: rootNode->AppendChildTo(textNode, aNotify); michael@0: textContent = textNode; michael@0: } michael@0: michael@0: NS_ENSURE_TRUE(textContent, NS_ERROR_UNEXPECTED); michael@0: michael@0: // Get the current value of the textfield from the content. michael@0: nsAutoString value; michael@0: if (aValue) { michael@0: value = *aValue; michael@0: } else { michael@0: txtCtrl->GetTextEditorValue(value, true); michael@0: } michael@0: michael@0: // Update the display of the placeholder value if needed. michael@0: // We don't need to do this if we're about to initialize the michael@0: // editor, since EnsureEditorInitialized takes care of this. michael@0: if (mUsePlaceholder && !aBeforeEditorInit) michael@0: { michael@0: nsWeakFrame weakFrame(this); michael@0: txtCtrl->UpdatePlaceholderVisibility(aNotify); michael@0: NS_ENSURE_STATE(weakFrame.IsAlive()); michael@0: } michael@0: michael@0: if (aBeforeEditorInit && value.IsEmpty()) { michael@0: rootNode->RemoveChildAt(0, true); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!value.IsEmpty() && IsPasswordTextControl()) { michael@0: nsTextEditRules::FillBufWithPWChars(&value, value.Length()); michael@0: } michael@0: return textContent->SetText(value, aNotify); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTextControlFrame::GetOwnedSelectionController(nsISelectionController** aSelCon) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aSelCon); michael@0: michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: michael@0: *aSelCon = txtCtrl->GetSelectionController(); michael@0: NS_IF_ADDREF(*aSelCon); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsFrameSelection* michael@0: nsTextControlFrame::GetOwnedFrameSelection() michael@0: { michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: michael@0: return txtCtrl->GetConstFrameSelection(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTextControlFrame::SaveState(nsPresState** aState) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aState); michael@0: michael@0: *aState = nullptr; michael@0: michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: michael@0: nsIContent* rootNode = txtCtrl->GetRootEditorNode(); michael@0: if (rootNode) { michael@0: // Query the nsIStatefulFrame from the HTMLScrollFrame michael@0: nsIStatefulFrame* scrollStateFrame = do_QueryFrame(rootNode->GetPrimaryFrame()); michael@0: if (scrollStateFrame) { michael@0: return scrollStateFrame->SaveState(aState); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTextControlFrame::RestoreState(nsPresState* aState) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aState); michael@0: michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element"); michael@0: michael@0: nsIContent* rootNode = txtCtrl->GetRootEditorNode(); michael@0: if (rootNode) { michael@0: // Query the nsIStatefulFrame from the HTMLScrollFrame michael@0: nsIStatefulFrame* scrollStateFrame = do_QueryFrame(rootNode->GetPrimaryFrame()); michael@0: if (scrollStateFrame) { michael@0: return scrollStateFrame->RestoreState(aState); michael@0: } michael@0: } michael@0: michael@0: // Most likely, we don't have our anonymous content constructed yet, which michael@0: // would cause us to end up here. In this case, we'll just store the scroll michael@0: // pos ourselves, and forward it to the scroll frame later when it's created. michael@0: Properties().Set(ContentScrollPos(), new nsPoint(aState->GetScrollState())); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsTextControlFrame::PeekOffset(nsPeekOffsetStruct *aPos) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: void michael@0: nsTextControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: /* michael@0: * The implementation of this method is equivalent as: michael@0: * nsContainerFrame::BuildDisplayList() michael@0: * with the difference that we filter-out the placeholder frame when it michael@0: * should not be visible. michael@0: */ michael@0: DO_GLOBAL_REFLOW_COUNT_DSP("nsTextControlFrame"); michael@0: michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: NS_ASSERTION(txtCtrl, "Content not a text control element!"); michael@0: michael@0: DisplayBorderBackgroundOutline(aBuilder, aLists); michael@0: michael@0: nsIFrame* kid = mFrames.FirstChild(); michael@0: // Redirect all lists to the Content list so that nothing can escape, ie michael@0: // opacity creating stacking contexts that then get sorted with stacking michael@0: // contexts external to us. michael@0: nsDisplayList* content = aLists.Content(); michael@0: nsDisplayListSet set(content, content, content, content, content, content); michael@0: michael@0: while (kid) { michael@0: // If the frame is the placeholder frame, we should only show it if the michael@0: // placeholder has to be visible. michael@0: if (kid->GetContent() != txtCtrl->GetPlaceholderNode() || michael@0: txtCtrl->GetPlaceholderVisibility()) { michael@0: BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, 0); michael@0: } michael@0: kid = kid->GetNextSibling(); michael@0: } michael@0: } michael@0: michael@0: mozilla::dom::Element* michael@0: nsTextControlFrame::GetPseudoElement(nsCSSPseudoElements::Type aType) michael@0: { michael@0: if (aType == nsCSSPseudoElements::ePseudo_mozPlaceholder) { michael@0: nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); michael@0: return txtCtrl->GetPlaceholderNode(); michael@0: } michael@0: michael@0: return nsContainerFrame::GetPseudoElement(aType); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsTextControlFrame::EditorInitializer::Run() michael@0: { michael@0: if (!mFrame) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Need to block script to avoid bug 669767. michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: michael@0: nsCOMPtr shell = michael@0: mFrame->PresContext()->GetPresShell(); michael@0: bool observes = shell->ObservesNativeAnonMutationsForPrint(); michael@0: shell->ObserveNativeAnonMutationsForPrint(true); michael@0: // This can cause the frame to be destroyed (and call Revoke()). michael@0: mFrame->EnsureEditorInitialized(); michael@0: shell->ObserveNativeAnonMutationsForPrint(observes); michael@0: michael@0: // The frame can *still* be destroyed even though we have a scriptblocker, michael@0: // bug 682684. michael@0: if (!mFrame) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mFrame->FinishedInitializer(); michael@0: return NS_OK; michael@0: }