/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"nsCOMPtr.h"#include"nsTextControlFrame.h"#include"nsIDocument.h"#include"nsIFormControl.h"#include"nsIServiceManager.h"#include"nsFrameSelection.h"#include"nsIPlaintextEditor.h"#include"nsEditorCID.h"#include"nsLayoutCID.h"#include"nsIDocumentEncoder.h"#include"nsCaret.h"#include"nsISelectionListener.h"#include"nsIController.h"#include"nsIControllers.h"#include"nsIControllerContext.h"#include"nsGenericHTMLElement.h"#include"nsIEditorIMESupport.h"#include"nsIPhonetic.h"#include"nsTextFragment.h"#include"nsIEditorObserver.h"#include"nsEditProperty.h"#include"nsIDOMHTMLTextAreaElement.h"#include"nsINameSpaceManager.h"#include"nsINodeInfo.h"#include"nsFormControlFrame.h" //for registering accesskeys#include"nsIContent.h"#include"nsIAtom.h"#include"nsPresContext.h"#include"nsRenderingContext.h"#include"nsGkAtoms.h"#include"nsLayoutUtils.h"#include"nsIComponentManager.h"#include"nsIView.h"#include"nsIViewManager.h"#include"nsIDOMHTMLInputElement.h"#include"nsIDOMElement.h"#include"nsIDOMHTMLElement.h"#include"nsIPresShell.h"#include"nsBoxLayoutState.h"//for keylistener for "return" check#include"nsIDOMEventTarget.h"#include"nsIDocument.h" //observe documents to send onchangenotifications#include"nsIStyleSheet.h"//observe documents to send onchangenotifications#include"nsIStyleRule.h"//observe documents to send onchangenotifications#include"nsIDOMEventListener.h"//observe documents to send onchangenotifications#include"nsGUIEvent.h"#include"nsIDOMNSEvent.h"#include"nsIDOMCharacterData.h" //for selection setting helper func#include"nsIDOMNodeList.h" //for selection setting helper func#include"nsIDOMRange.h" //for selection setting helper func#include"nsPIDOMWindow.h" //needed for notify selection changed to update the menus ect.#ifdef ACCESSIBILITY#include"nsAccessibilityService.h"#endif#include"nsIDOMNode.h"#include"nsITransactionManager.h"#include"nsIDOMText.h" //for multiline getselection#include"nsNodeInfoManager.h"#include"nsContentCreatorFunctions.h"#include"nsINativeKeyBindings.h"#include"nsIJSContextStack.h"#include"nsFocusManager.h"#include"nsTextEditRules.h"#include"nsPresState.h"#include"mozilla/FunctionTimer.h"#include"mozilla/Selection.h"#define DEFAULT_COLUMN_WIDTH 20usingnamespacemozilla;nsIFrame*NS_NewTextControlFrame(nsIPresShell*aPresShell,nsStyleContext*aContext){returnnew(aPresShell)nsTextControlFrame(aPresShell,aContext);}NS_IMPL_FRAMEARENA_HELPERS(nsTextControlFrame)NS_QUERYFRAME_HEAD(nsTextControlFrame)NS_QUERYFRAME_ENTRY(nsIFormControlFrame)NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)NS_QUERYFRAME_ENTRY(nsITextControlFrame)NS_QUERYFRAME_ENTRY(nsIStatefulFrame)NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)#ifdef ACCESSIBILITYalready_AddRefed<Accessible>nsTextControlFrame::CreateAccessible(){nsAccessibilityService*accService=nsIPresShell::AccService();if(accService){returnaccService->CreateHTMLTextFieldAccessible(mContent,PresContext()->PresShell());}returnnsnull;}#endif#ifdef DEBUGclassEditorInitializerEntryTracker{public:explicitEditorInitializerEntryTracker(nsTextControlFrame&frame):mFrame(frame),mFirstEntry(false){if(!mFrame.mInEditorInitialization){mFrame.mInEditorInitialization=true;mFirstEntry=true;}}~EditorInitializerEntryTracker(){if(mFirstEntry){mFrame.mInEditorInitialization=false;}}boolEnteredMoreThanOnce()const{return!mFirstEntry;}private:nsTextControlFrame&mFrame;boolmFirstEntry;};#endifnsTextControlFrame::nsTextControlFrame(nsIPresShell*aShell,nsStyleContext*aContext):nsContainerFrame(aContext),mUseEditor(false),mIsProcessing(false)#ifdef DEBUG,mInEditorInitialization(false)#endif{}nsTextControlFrame::~nsTextControlFrame(){}voidnsTextControlFrame::DestroyFrom(nsIFrame*aDestructRoot){mScrollEvent.Revoke();EditorInitializer*initializer=(EditorInitializer*)Properties().Get(TextControlInitializer());if(initializer){initializer->Revoke();Properties().Delete(TextControlInitializer());}// Unbind the text editor state object from the frame. The editor will live// on, but things like controllers will be released.nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");txtCtrl->UnbindFromFrame(this);nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this),false);nsContainerFrame::DestroyFrom(aDestructRoot);}nsIAtom*nsTextControlFrame::GetType()const{returnnsGkAtoms::textInputFrame;}nsresultnsTextControlFrame::CalcIntrinsicSize(nsRenderingContext*aRenderingContext,nsSize&aIntrinsicSize,floataFontSizeInflation){// Get leading and the Average/MaxAdvance char width nscoordlineHeight=0;nscoordcharWidth=0;nscoordcharMaxAdvance=0;nsRefPtr<nsFontMetrics>fontMet;nsresultrv=nsLayoutUtils::GetFontMetricsForFrame(this,getter_AddRefs(fontMet),aFontSizeInflation);NS_ENSURE_SUCCESS(rv,rv);aRenderingContext->SetFont(fontMet);lineHeight=nsHTMLReflowState::CalcLineHeight(GetStyleContext(),NS_AUTOHEIGHT,aFontSizeInflation);charWidth=fontMet->AveCharWidth();charMaxAdvance=fontMet->MaxAdvance();// Set the width equal to the width in charactersPRInt32cols=GetCols();aIntrinsicSize.width=cols*charWidth;// To better match IE, take the maximum character width(in twips) and remove// 4 pixels add this on as additional padding(internalPadding). But only do// this if charMaxAdvance != charWidth; if they are equal, this is almost// certainly a fixed-width font.if(charWidth!=charMaxAdvance){nscoordinternalPadding=NS_MAX(0,charMaxAdvance-nsPresContext::CSSPixelsToAppUnits(4));nscoordt=nsPresContext::CSSPixelsToAppUnits(1);// Round to a multiple of tnscoordrest=internalPadding%t;if(rest<t-rest){internalPadding-=rest;}else{internalPadding+=t-rest;}// Now add the extra padding on (so that small input sizes work well)aIntrinsicSize.width+=internalPadding;}else{// This is to account for the anonymous <br> having a 1 twip width// in Full Standards mode, see BRFrame::Reflow and bug 228752.if(PresContext()->CompatibilityMode()==eCompatibility_FullStandards){aIntrinsicSize.width+=1;}// Also add in the padding of our value div child. Note that it hasn't// been reflowed yet, so we can't get its used padding, but it shouldn't be// using percentage padding anyway.nsMarginchildPadding;nsIFrame*firstChild=GetFirstPrincipalChild();if(firstChild&&firstChild->GetStylePadding()->GetPadding(childPadding)){aIntrinsicSize.width+=childPadding.LeftRight();}else{NS_ERROR("Percentage padding on value div?");}}// Increment width with cols * letter-spacing.{constnsStyleCoord&lsCoord=GetStyleText()->mLetterSpacing;if(eStyleUnit_Coord==lsCoord.GetUnit()){nscoordletterSpacing=lsCoord.GetCoordValue();if(letterSpacing!=0){aIntrinsicSize.width+=cols*letterSpacing;}}}// Set the height equal to total number of rows (times the height of each// line, of course)aIntrinsicSize.height=lineHeight*GetRows();// Add in the size of the scrollbars for textareaif(IsTextArea()){nsIFrame*first=GetFirstPrincipalChild();nsIScrollableFrame*scrollableFrame=do_QueryFrame(first);NS_ASSERTION(scrollableFrame,"Child must be scrollable");if(scrollableFrame){nsMarginscrollbarSizes=scrollableFrame->GetDesiredScrollbarSizes(PresContext(),aRenderingContext);aIntrinsicSize.width+=scrollbarSizes.LeftRight();aIntrinsicSize.height+=scrollbarSizes.TopBottom();;}}returnNS_OK;}nsresultnsTextControlFrame::EnsureEditorInitialized(){// This method initializes our editor, if needed.// This code used to be called from CreateAnonymousContent(), but// when the editor set the initial string, it would trigger a// PresShell listener which called FlushPendingNotifications()// during frame construction. This was causing other form controls// to display wrong values. Additionally, calling this every time// a text frame control is instantiated means that we're effectively// instantiating the editor for all text fields, even if they// never get used. So, now this method is being called lazily only// when we actually need an editor.// Check if this method has been called already.// If so, just return early.if(mUseEditor)returnNS_OK;NS_TIME_FUNCTION;nsIDocument*doc=mContent->GetCurrentDoc();NS_ENSURE_TRUE(doc,NS_ERROR_FAILURE);nsWeakFrameweakFrame(this);// Flush out content on our document. Have to do this, because script// blockers don't prevent the sink flushing out content and notifying in the// process, which can destroy frames.doc->FlushPendingNotifications(Flush_ContentAndNotify);NS_ENSURE_TRUE(weakFrame.IsAlive(),NS_ERROR_FAILURE);// Make sure that editor init doesn't do things that would kill us off// (especially off the script blockers it'll create for its DOM mutations).nsAutoScriptBlockerscriptBlocker;// Time to mess with our security context... See comments in GetValue()// for why this is needed.nsCxPusherpusher;pusher.PushNull();// Make sure that we try to focus the content even if the method failsclassEnsureSetFocus{public:explicitEnsureSetFocus(nsTextControlFrame*aFrame):mFrame(aFrame){}~EnsureSetFocus(){if(nsContentUtils::IsFocusedContent(mFrame->GetContent()))mFrame->SetFocus(true,false);}private:nsTextControlFrame*mFrame;};EnsureSetFocusmakeSureSetFocusHappens(this);#ifdef DEBUG// Make sure we are not being called again until we're finished.// If reentrancy happens, just pretend that we don't have an editor.constEditorInitializerEntryTrackertracker(*this);NS_ASSERTION(!tracker.EnteredMoreThanOnce(),"EnsureEditorInitialized has been called while a previous call was in progress");#endif// Create an editor for the frame, if one doesn't already existnsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");nsresultrv=txtCtrl->CreateEditor();NS_ENSURE_SUCCESS(rv,rv);// Turn on mUseEditor so that subsequent calls will use the// editor.mUseEditor=true;// Set the selection to the beginning of the text field.if(weakFrame.IsAlive()){SetSelectionEndPoints(0,0);}returnNS_OK;}nsresultnsTextControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>&aElements){NS_ASSERTION(mContent,"We should have a content!");mState|=NS_FRAME_INDEPENDENT_SELECTION;nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");// Bind the frame to its text controlnsresultrv=txtCtrl->BindToFrame(this);NS_ENSURE_SUCCESS(rv,rv);nsIContent*rootNode=txtCtrl->GetRootEditorNode();NS_ENSURE_TRUE(rootNode,NS_ERROR_OUT_OF_MEMORY);if(!aElements.AppendElement(rootNode))returnNS_ERROR_OUT_OF_MEMORY;// Do we need a placeholder node?nsAutoStringplaceholderTxt;mContent->GetAttr(kNameSpaceID_None,nsGkAtoms::placeholder,placeholderTxt);nsContentUtils::RemoveNewlines(placeholderTxt);mUsePlaceholder=!placeholderTxt.IsEmpty();// Create the placeholder anonymous content if needed.if(mUsePlaceholder){nsIContent*placeholderNode=txtCtrl->CreatePlaceholderNode();NS_ENSURE_TRUE(placeholderNode,NS_ERROR_OUT_OF_MEMORY);if(!aElements.AppendElement(placeholderNode))returnNS_ERROR_OUT_OF_MEMORY;}rv=UpdateValueDisplay(false);NS_ENSURE_SUCCESS(rv,rv);// textareas are eagerly initializedboolinitEagerly=!IsSingleLineTextControl();if(!initEagerly){// Also, input elements which have a cached selection should get eager// editor initialization.nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");initEagerly=txtCtrl->HasCachedSelection();}if(!initEagerly){nsCOMPtr<nsIDOMHTMLElement>element=do_QueryInterface(txtCtrl);if(element){// so are input text controls with spellcheck=trueelement->GetSpellcheck(&initEagerly);}}if(initEagerly){NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),"Someone forgot a script blocker?");EditorInitializer*initializer=(EditorInitializer*)Properties().Get(TextControlInitializer());if(initializer){initializer->Revoke();}initializer=newEditorInitializer(this);Properties().Set(TextControlInitializer(),initializer);if(!nsContentUtils::AddScriptRunner(initializer)){initializer->Revoke();// paranoiaProperties().Delete(TextControlInitializer());deleteinitializer;returnNS_ERROR_OUT_OF_MEMORY;}}returnNS_OK;}voidnsTextControlFrame::AppendAnonymousContentTo(nsBaseContentList&aElements,PRUint32aFilter){nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");aElements.MaybeAppendElement(txtCtrl->GetRootEditorNode());if(!(aFilter&nsIContent::eSkipPlaceholderContent))aElements.MaybeAppendElement(txtCtrl->GetPlaceholderNode());}nscoordnsTextControlFrame::GetPrefWidth(nsRenderingContext*aRenderingContext){nscoordresult=0;DISPLAY_PREF_WIDTH(this,result);floatinflation=nsLayoutUtils::FontSizeInflationFor(this);nsSizeautoSize;CalcIntrinsicSize(aRenderingContext,autoSize,inflation);returnautoSize.width;}nscoordnsTextControlFrame::GetMinWidth(nsRenderingContext*aRenderingContext){// Our min width is just our preferred width if we have auto width.nscoordresult;DISPLAY_MIN_WIDTH(this,result);result=GetPrefWidth(aRenderingContext);returnresult;}nsSizensTextControlFrame::ComputeAutoSize(nsRenderingContext*aRenderingContext,nsSizeaCBSize,nscoordaAvailableWidth,nsSizeaMargin,nsSizeaBorder,nsSizeaPadding,boolaShrinkWrap){floatinflation=nsLayoutUtils::FontSizeInflationFor(this);nsSizeautoSize;nsresultrv=CalcIntrinsicSize(aRenderingContext,autoSize,inflation);if(NS_FAILED(rv)){// What now?autoSize.SizeTo(0,0);}#ifdef DEBUG// Note: Ancestor ComputeAutoSize only computes a width if we're auto-widthelseif(GetStylePosition()->mWidth.GetUnit()==eStyleUnit_Auto){nsSizeancestorAutoSize=nsContainerFrame::ComputeAutoSize(aRenderingContext,aCBSize,aAvailableWidth,aMargin,aBorder,aPadding,aShrinkWrap);// Disabled when there's inflation; see comment in GetPrefSize.NS_ASSERTION(inflation!=1.0f||ancestorAutoSize.width==autoSize.width,"Incorrect size computed by ComputeAutoSize?");}#endifreturnautoSize;}NS_IMETHODIMPnsTextControlFrame::Reflow(nsPresContext*aPresContext,nsHTMLReflowMetrics&aDesiredSize,constnsHTMLReflowState&aReflowState,nsReflowStatus&aStatus){DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame");DISPLAY_REFLOW(aPresContext,this,aReflowState,aDesiredSize,aStatus);// make sure that the form registers itself on the initial/first reflowif(mState&NS_FRAME_FIRST_REFLOW){nsFormControlFrame::RegUnRegAccessKey(this,true);}// set values of reflow's out parametersaDesiredSize.width=aReflowState.ComputedWidth()+aReflowState.mComputedBorderPadding.LeftRight();aDesiredSize.height=NS_CSS_MINMAX(aReflowState.ComputedHeight(),aReflowState.mComputedMinHeight,aReflowState.mComputedMaxHeight);nscoordlineHeight=aDesiredSize.height;aDesiredSize.height+=aReflowState.mComputedBorderPadding.TopBottom();// computation of the ascent wrt the input heightfloatinflation=nsLayoutUtils::FontSizeInflationFor(this);if(!IsSingleLineTextControl()){lineHeight=nsHTMLReflowState::CalcLineHeight(GetStyleContext(),NS_AUTOHEIGHT,inflation);}nsRefPtr<nsFontMetrics>fontMet;nsresultrv=nsLayoutUtils::GetFontMetricsForFrame(this,getter_AddRefs(fontMet),inflation);NS_ENSURE_SUCCESS(rv,rv);// now adjust for our borders and paddingaDesiredSize.ascent=nsLayoutUtils::GetCenteredFontBaseline(fontMet,lineHeight)+aReflowState.mComputedBorderPadding.top;// overflow handlingaDesiredSize.SetOverflowAreasToDesiredBounds();// perform reflow on all kidsnsIFrame*kid=mFrames.FirstChild();while(kid){ReflowTextControlChild(kid,aPresContext,aReflowState,aStatus,aDesiredSize);kid=kid->GetNextSibling();}// take into account css properties that affect overflow handlingFinishAndStoreOverflow(&aDesiredSize);aStatus=NS_FRAME_COMPLETE;NS_FRAME_SET_TRUNCATION(aStatus,aReflowState,aDesiredSize);returnNS_OK;}voidnsTextControlFrame::ReflowTextControlChild(nsIFrame*aKid,nsPresContext*aPresContext,constnsHTMLReflowState&aReflowState,nsReflowStatus&aStatus,nsHTMLReflowMetrics&aParentDesiredSize){// compute available size and frame offsets for childnsSizeavailSize(aReflowState.ComputedWidth(),aReflowState.ComputedHeight());availSize.width=NS_MAX(availSize.width,0);availSize.height=NS_MAX(availSize.height,0);nsHTMLReflowStatekidReflowState(aPresContext,aReflowState,aKid,availSize);// Set computed width and computed height for the childnscoordwidth=availSize.width;width-=kidReflowState.mComputedMargin.LeftRight()+kidReflowState.mComputedBorderPadding.LeftRight();width=NS_MAX(width,0);kidReflowState.SetComputedWidth(width);nscoordheight=availSize.height;height-=kidReflowState.mComputedMargin.TopBottom()+kidReflowState.mComputedBorderPadding.TopBottom();height=NS_MAX(height,0);kidReflowState.SetComputedHeight(height);// compute the offsetsnscoordxOffset=aReflowState.mComputedBorderPadding.left+kidReflowState.mComputedMargin.left;nscoordyOffset=aReflowState.mComputedBorderPadding.top+kidReflowState.mComputedMargin.top;// reflow the childnsHTMLReflowMetricsdesiredSize;ReflowChild(aKid,aPresContext,desiredSize,kidReflowState,xOffset,yOffset,0,aStatus);// place the childFinishReflowChild(aKid,aPresContext,&kidReflowState,desiredSize,xOffset,yOffset,0);// consider the overflowaParentDesiredSize.mOverflowAreas.UnionWith(desiredSize.mOverflowAreas);}nsSizensTextControlFrame::GetMinSize(nsBoxLayoutState&aState){// XXXbz why? Why not the nsBoxFrame sizes?returnnsBox::GetMinSize(aState);}boolnsTextControlFrame::IsCollapsed(){// We're never collapsed in the box sense.returnfalse;}boolnsTextControlFrame::IsLeaf()const{returntrue;}NS_IMETHODIMPnsTextControlFrame::ScrollOnFocusEvent::Run(){if(mFrame){nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(mFrame->GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");nsISelectionController*selCon=txtCtrl->GetSelectionController();if(selCon){mFrame->mScrollEvent.Forget();selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,nsISelectionController::SELECTION_FOCUS_REGION,nsISelectionController::SCROLL_SYNCHRONOUS);}}returnNS_OK;}//IMPLEMENTING NS_IFORMCONTROLFRAMEvoidnsTextControlFrame::SetFocus(boolaOn,boolaRepaint){nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");// Revoke the previous scroll event if one existsmScrollEvent.Revoke();if(!aOn){return;}nsISelectionController*selCon=txtCtrl->GetSelectionController();if(!selCon)return;nsCOMPtr<nsISelection>ourSel;selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,getter_AddRefs(ourSel));if(!ourSel)return;nsIPresShell*presShell=PresContext()->GetPresShell();nsRefPtr<nsCaret>caret=presShell->GetCaret();if(!caret)return;// Scroll the current selection into viewnsISelection*caretSelection=caret->GetCaretDOMSelection();constboolisFocusedRightNow=ourSel==caretSelection;if(!isFocusedRightNow){// Don't scroll the current selection if we've been focused using the mouse.PRUint32lastFocusMethod=0;nsIDocument*doc=GetContent()->GetCurrentDoc();if(doc){nsIFocusManager*fm=nsFocusManager::GetFocusManager();if(fm){fm->GetLastFocusMethod(doc->GetWindow(),&lastFocusMethod);}}if(!(lastFocusMethod&nsIFocusManager::FLAG_BYMOUSE)){nsRefPtr<ScrollOnFocusEvent>event=newScrollOnFocusEvent(this);nsresultrv=NS_DispatchToCurrentThread(event);if(NS_SUCCEEDED(rv)){mScrollEvent=event;}}}// tell the caret to use our selectioncaret->SetCaretDOMSelection(ourSel);// mutual-exclusion: the selection is either controlled by the// document or by the text input/area. Clear any selection in the// document since the focus is now on our independent selection.nsCOMPtr<nsISelectionController>selcon=do_QueryInterface(presShell);nsCOMPtr<nsISelection>docSel;selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,getter_AddRefs(docSel));if(!docSel)return;boolisCollapsed=false;docSel->GetIsCollapsed(&isCollapsed);if(!isCollapsed)docSel->RemoveAllRanges();}nsresultnsTextControlFrame::SetFormProperty(nsIAtom*aName,constnsAString&aValue){if(!mIsProcessing)//some kind of lock.{mIsProcessing=true;if(nsGkAtoms::select==aName){// Select all the text.//// XXX: This is lame, we can't call editor's SelectAll method// because that triggers AutoCopies in unix builds.// Instead, we have to call our own homegrown version// of select all which merely builds a range that selects// all of the content and adds that to the selection.nsWeakFrameweakThis=this;SelectAllOrCollapseToEndOfText(true);// NOTE: can destroy the worldif(!weakThis.IsAlive()){returnNS_OK;}}mIsProcessing=false;}returnNS_OK;}nsresultnsTextControlFrame::GetFormProperty(nsIAtom*aName,nsAString&aValue)const{NS_ASSERTION(nsGkAtoms::value!=aName,"Should get the value from the content node instead");returnNS_OK;}NS_IMETHODIMPnsTextControlFrame::GetEditor(nsIEditor**aEditor){NS_ENSURE_ARG_POINTER(aEditor);nsresultrv=EnsureEditorInitialized();NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");*aEditor=txtCtrl->GetTextEditor();NS_IF_ADDREF(*aEditor);returnNS_OK;}NS_IMETHODIMPnsTextControlFrame::GetTextLength(PRInt32*aTextLength){NS_ENSURE_ARG_POINTER(aTextLength);nsAutoStringtextContents;nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");txtCtrl->GetTextEditorValue(textContents,false);// this is expensive!*aTextLength=textContents.Length();returnNS_OK;}nsresultnsTextControlFrame::SetSelectionInternal(nsIDOMNode*aStartNode,PRInt32aStartOffset,nsIDOMNode*aEndNode,PRInt32aEndOffset,nsITextControlFrame::SelectionDirectionaDirection){// Create a new range to represent the new selection.// Note that we use a new range to avoid having to do// isIncreasing checks to avoid possible errors.nsRefPtr<nsRange>range=newnsRange();nsresultrv=range->SetStart(aStartNode,aStartOffset);NS_ENSURE_SUCCESS(rv,rv);rv=range->SetEnd(aEndNode,aEndOffset);NS_ENSURE_SUCCESS(rv,rv);// Get the selection, clear it and add the new range to it!nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");nsISelectionController*selCon=txtCtrl->GetSelectionController();NS_ENSURE_TRUE(selCon,NS_ERROR_FAILURE);nsCOMPtr<nsISelection>selection;selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,getter_AddRefs(selection));NS_ENSURE_TRUE(selection,NS_ERROR_FAILURE);nsCOMPtr<nsISelectionPrivate>selPriv=do_QueryInterface(selection,&rv);NS_ENSURE_SUCCESS(rv,rv);nsDirectiondirection;if(aDirection==eNone){// Preserve the directiondirection=selPriv->GetSelectionDirection();}else{direction=(aDirection==eBackward)?eDirPrevious:eDirNext;}rv=selection->RemoveAllRanges();NS_ENSURE_SUCCESS(rv,rv);rv=selection->AddRange(range);// NOTE: can destroy the worldNS_ENSURE_SUCCESS(rv,rv);selPriv->SetSelectionDirection(direction);returnrv;}nsresultnsTextControlFrame::ScrollSelectionIntoView(){nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");nsISelectionController*selCon=txtCtrl->GetSelectionController();if(selCon){// Scroll the selection into view (see bug 231389).returnselCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,nsISelectionController::SELECTION_FOCUS_REGION,nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY);}returnNS_ERROR_FAILURE;}mozilla::dom::Element*nsTextControlFrame::GetRootNodeAndInitializeEditor(){nsCOMPtr<nsIDOMElement>root;GetRootNodeAndInitializeEditor(getter_AddRefs(root));nsCOMPtr<mozilla::dom::Element>rootElem=do_QueryInterface(root);returnrootElem;}nsresultnsTextControlFrame::GetRootNodeAndInitializeEditor(nsIDOMElement**aRootElement){NS_ENSURE_ARG_POINTER(aRootElement);nsCOMPtr<nsIEditor>editor;GetEditor(getter_AddRefs(editor));if(!editor)returnNS_OK;returneditor->GetRootElement(aRootElement);}nsresultnsTextControlFrame::SelectAllOrCollapseToEndOfText(boolaSelect){nsCOMPtr<nsIDOMElement>rootElement;nsresultrv=GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIContent>rootContent=do_QueryInterface(rootElement);nsCOMPtr<nsIDOMNode>rootNode(do_QueryInterface(rootElement));NS_ENSURE_TRUE(rootNode&&rootContent,NS_ERROR_FAILURE);PRInt32numChildren=rootContent->GetChildCount();if(numChildren>0){// We never want to place the selection after the last// br under the root node!nsIContent*child=rootContent->GetChildAt(numChildren-1);if(child){if(child->Tag()==nsGkAtoms::br)--numChildren;}if(!aSelect&&numChildren){child=rootContent->GetChildAt(numChildren-1);if(child&&child->IsNodeOfType(nsINode::eTEXT)){rootNode=do_QueryInterface(child);constnsTextFragment*fragment=child->GetText();numChildren=fragment?fragment->GetLength():0;}}}rv=SetSelectionInternal(rootNode,aSelect?0:numChildren,rootNode,numChildren);NS_ENSURE_SUCCESS(rv,rv);returnScrollSelectionIntoView();}nsresultnsTextControlFrame::SetSelectionEndPoints(PRInt32aSelStart,PRInt32aSelEnd,nsITextControlFrame::SelectionDirectionaDirection){NS_ASSERTION(aSelStart<=aSelEnd,"Invalid selection offsets!");if(aSelStart>aSelEnd)returnNS_ERROR_FAILURE;nsCOMPtr<nsIDOMNode>startNode,endNode;PRInt32startOffset,endOffset;// Calculate the selection start point.nsresultrv=OffsetToDOMPoint(aSelStart,getter_AddRefs(startNode),&startOffset);NS_ENSURE_SUCCESS(rv,rv);if(aSelStart==aSelEnd){// Collapsed selection, so start and end are the same!endNode=startNode;endOffset=startOffset;}else{// Selection isn't collapsed so we have to calculate// the end point too.rv=OffsetToDOMPoint(aSelEnd,getter_AddRefs(endNode),&endOffset);NS_ENSURE_SUCCESS(rv,rv);}returnSetSelectionInternal(startNode,startOffset,endNode,endOffset,aDirection);}NS_IMETHODIMPnsTextControlFrame::SetSelectionRange(PRInt32aSelStart,PRInt32aSelEnd,nsITextControlFrame::SelectionDirectionaDirection){nsresultrv=EnsureEditorInitialized();NS_ENSURE_SUCCESS(rv,rv);if(aSelStart>aSelEnd){// Simulate what we'd see SetSelectionStart() was called, followed// by a SetSelectionEnd().aSelStart=aSelEnd;}returnSetSelectionEndPoints(aSelStart,aSelEnd,aDirection);}NS_IMETHODIMPnsTextControlFrame::SetSelectionStart(PRInt32aSelectionStart){nsresultrv=EnsureEditorInitialized();NS_ENSURE_SUCCESS(rv,rv);PRInt32selStart=0,selEnd=0;rv=GetSelectionRange(&selStart,&selEnd);NS_ENSURE_SUCCESS(rv,rv);if(aSelectionStart>selEnd){// Collapse to the new start point.selEnd=aSelectionStart;}selStart=aSelectionStart;returnSetSelectionEndPoints(selStart,selEnd);}NS_IMETHODIMPnsTextControlFrame::SetSelectionEnd(PRInt32aSelectionEnd){nsresultrv=EnsureEditorInitialized();NS_ENSURE_SUCCESS(rv,rv);PRInt32selStart=0,selEnd=0;rv=GetSelectionRange(&selStart,&selEnd);NS_ENSURE_SUCCESS(rv,rv);if(aSelectionEnd<selStart){// Collapse to the new end point.selStart=aSelectionEnd;}selEnd=aSelectionEnd;returnSetSelectionEndPoints(selStart,selEnd);}nsresultnsTextControlFrame::OffsetToDOMPoint(PRInt32aOffset,nsIDOMNode**aResult,PRInt32*aPosition){NS_ENSURE_ARG_POINTER(aResult&&aPosition);*aResult=nsnull;*aPosition=0;nsCOMPtr<nsIDOMElement>rootElement;nsresultrv=GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIDOMNode>rootNode(do_QueryInterface(rootElement));NS_ENSURE_TRUE(rootNode,NS_ERROR_FAILURE);nsCOMPtr<nsIDOMNodeList>nodeList;rv=rootNode->GetChildNodes(getter_AddRefs(nodeList));NS_ENSURE_SUCCESS(rv,rv);NS_ENSURE_TRUE(nodeList,NS_ERROR_FAILURE);PRUint32length=0;rv=nodeList->GetLength(&length);NS_ENSURE_SUCCESS(rv,rv);NS_ASSERTION(length<=2,"We should have one text node and one mozBR at most");nsCOMPtr<nsIDOMNode>firstNode;rv=nodeList->Item(0,getter_AddRefs(firstNode));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIDOMText>textNode=do_QueryInterface(firstNode);if(length==0||aOffset<0){NS_IF_ADDREF(*aResult=rootNode);*aPosition=0;}elseif(textNode){PRUint32textLength=0;textNode->GetLength(&textLength);if(length==2&&PRUint32(aOffset)==textLength){// If we're at the end of the text node and we have a trailing BR node,// set the selection on the BR node.NS_IF_ADDREF(*aResult=rootNode);*aPosition=1;}else{// Otherwise, set the selection on the textnode itself.NS_IF_ADDREF(*aResult=firstNode);*aPosition=NS_MIN(aOffset,PRInt32(textLength));}}else{NS_IF_ADDREF(*aResult=rootNode);*aPosition=0;}returnNS_OK;}NS_IMETHODIMPnsTextControlFrame::GetSelectionRange(PRInt32*aSelectionStart,PRInt32*aSelectionEnd,SelectionDirection*aDirection){// make sure we have an editornsresultrv=EnsureEditorInitialized();NS_ENSURE_SUCCESS(rv,rv);if(aSelectionStart){*aSelectionStart=0;}if(aSelectionEnd){*aSelectionEnd=0;}if(aDirection){*aDirection=eNone;}nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");nsISelectionController*selCon=txtCtrl->GetSelectionController();NS_ENSURE_TRUE(selCon,NS_ERROR_FAILURE);nsCOMPtr<nsISelection>selection;rv=selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,getter_AddRefs(selection));NS_ENSURE_SUCCESS(rv,rv);NS_ENSURE_TRUE(selection,NS_ERROR_FAILURE);nsCOMPtr<nsISelectionPrivate>selPriv=do_QueryInterface(selection);NS_ENSURE_TRUE(selPriv,NS_ERROR_FAILURE);nsRefPtr<nsFrameSelection>frameSel;rv=selPriv->GetFrameSelection(getter_AddRefs(frameSel));NS_ENSURE_SUCCESS(rv,rv);NS_ENSURE_TRUE(frameSel,NS_ERROR_FAILURE);nsRefPtr<Selection>typedSel=frameSel->GetSelection(nsISelectionController::SELECTION_NORMAL);NS_ENSURE_TRUE(typedSel,NS_ERROR_FAILURE);if(aDirection){nsDirectiondirection=typedSel->GetSelectionDirection();if(direction==eDirNext){*aDirection=eForward;}elseif(direction==eDirPrevious){*aDirection=eBackward;}else{NS_NOTREACHED("Invalid nsDirection enum value");}}if(!aSelectionStart||!aSelectionEnd){returnNS_OK;}nsContentUtils::GetSelectionInTextControl(typedSel,GetRootNodeAndInitializeEditor(),*aSelectionStart,*aSelectionEnd);returnNS_OK;}/////END INTERFACE IMPLEMENTATIONS////NSIFRAMENS_IMETHODIMPnsTextControlFrame::AttributeChanged(PRInt32aNameSpaceID,nsIAtom*aAttribute,PRInt32aModType){nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");nsISelectionController*selCon=txtCtrl->GetSelectionController();constboolneedEditor=nsGkAtoms::maxlength==aAttribute||nsGkAtoms::readonly==aAttribute||nsGkAtoms::disabled==aAttribute||nsGkAtoms::spellcheck==aAttribute;nsCOMPtr<nsIEditor>editor;if(needEditor){GetEditor(getter_AddRefs(editor));}if((needEditor&&!editor)||!selCon)returnnsContainerFrame::AttributeChanged(aNameSpaceID,aAttribute,aModType);nsresultrv=NS_OK;if(nsGkAtoms::maxlength==aAttribute){PRInt32maxLength;boolmaxDefined=GetMaxLength(&maxLength);nsCOMPtr<nsIPlaintextEditor>textEditor=do_QueryInterface(editor);if(textEditor){if(maxDefined){// set the maxLength attributetextEditor->SetMaxTextLength(maxLength);// if maxLength>docLength, we need to truncate the doc content}else{// unset the maxLength attributetextEditor->SetMaxTextLength(-1);}}rv=NS_OK;// don't propagate the error}elseif(nsGkAtoms::readonly==aAttribute){PRUint32flags;editor->GetFlags(&flags);if(AttributeExists(nsGkAtoms::readonly)){// set readonlyflags|=nsIPlaintextEditor::eEditorReadonlyMask;if(nsContentUtils::IsFocusedContent(mContent))selCon->SetCaretEnabled(false);}else{// unset readonlyflags&=~(nsIPlaintextEditor::eEditorReadonlyMask);if(!(flags&nsIPlaintextEditor::eEditorDisabledMask)&&nsContentUtils::IsFocusedContent(mContent))selCon->SetCaretEnabled(true);}editor->SetFlags(flags);}elseif(nsGkAtoms::disabled==aAttribute){PRUint32flags;editor->GetFlags(&flags);if(AttributeExists(nsGkAtoms::disabled)){// set disabledflags|=nsIPlaintextEditor::eEditorDisabledMask;selCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);if(nsContentUtils::IsFocusedContent(mContent))selCon->SetCaretEnabled(false);}else{// unset disabledflags&=~(nsIPlaintextEditor::eEditorDisabledMask);selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);if(nsContentUtils::IsFocusedContent(mContent)){selCon->SetCaretEnabled(true);}}editor->SetFlags(flags);}elseif(!mUseEditor&&nsGkAtoms::value==aAttribute){UpdateValueDisplay(true);}// Allow the base class to handle common attributes supported// by all form elements... else{rv=nsContainerFrame::AttributeChanged(aNameSpaceID,aAttribute,aModType);}returnrv;}nsresultnsTextControlFrame::GetText(nsString&aText){nsresultrv=NS_OK;nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");if(IsSingleLineTextControl()){// There will be no line breaks so we can ignore the wrap property.txtCtrl->GetTextEditorValue(aText,true);}else{nsCOMPtr<nsIDOMHTMLTextAreaElement>textArea=do_QueryInterface(mContent);if(textArea){rv=textArea->GetValue(aText);}}returnrv;}nsresultnsTextControlFrame::GetPhonetic(nsAString&aPhonetic){aPhonetic.Truncate(0);nsCOMPtr<nsIEditor>editor;nsresultrv=GetEditor(getter_AddRefs(editor));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIEditorIMESupport>imeSupport=do_QueryInterface(editor);if(imeSupport){nsCOMPtr<nsIPhonetic>phonetic=do_QueryInterface(imeSupport);if(phonetic)phonetic->GetPhonetic(aPhonetic);}returnNS_OK;}///END NSIFRAME OVERLOADS/////BEGIN PROTECTED METHODSboolnsTextControlFrame::GetMaxLength(PRInt32*aSize){*aSize=-1;nsGenericHTMLElement*content=nsGenericHTMLElement::FromContent(mContent);if(content){constnsAttrValue*attr=content->GetParsedAttr(nsGkAtoms::maxlength);if(attr&&attr->Type()==nsAttrValue::eInteger){*aSize=attr->GetIntegerValue();returntrue;}}returnfalse;}// END IMPLEMENTING NS_IFORMCONTROLFRAMENS_IMETHODIMPnsTextControlFrame::SetInitialChildList(ChildListIDaListID,nsFrameList&aChildList){nsresultrv=nsContainerFrame::SetInitialChildList(aListID,aChildList);nsIFrame*first=GetFirstPrincipalChild();// Mark the scroll frame as being a reflow root. This will allow// incremental reflows to be initiated at the scroll frame, rather// than descending from the root frame of the frame hierarchy.if(first){first->AddStateBits(NS_FRAME_REFLOW_ROOT);nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");txtCtrl->InitializeKeyboardEventListeners();nsPoint*contentScrollPos=static_cast<nsPoint*>(Properties().Get(ContentScrollPos()));if(contentScrollPos){// If we have a scroll pos stored to be passed to our anonymous// div, do it here!nsIStatefulFrame*statefulFrame=do_QueryFrame(first);NS_ASSERTION(statefulFrame,"unexpected type of frame for the anonymous div");nsPresStatefakePresState;fakePresState.SetScrollState(*contentScrollPos);statefulFrame->RestoreState(&fakePresState);Properties().Remove(ContentScrollPos());deletecontentScrollPos;}}returnrv;}boolnsTextControlFrame::IsScrollable()const{return!IsSingleLineTextControl();}voidnsTextControlFrame::SetValueChanged(boolaValueChanged){nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");if(mUsePlaceholder){PRInt32textLength;GetTextLength(&textLength);nsWeakFrameweakFrame(this);txtCtrl->SetPlaceholderClass(!textLength,true);if(!weakFrame.IsAlive()){return;}}txtCtrl->SetValueChanged(aValueChanged);}nsresultnsTextControlFrame::UpdateValueDisplay(boolaNotify,boolaBeforeEditorInit,constnsAString*aValue){if(!IsSingleLineTextControl())// textareas don't use thisreturnNS_OK;nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");nsIContent*rootNode=txtCtrl->GetRootEditorNode();NS_PRECONDITION(rootNode,"Must have a div content\n");NS_PRECONDITION(!mUseEditor,"Do not call this after editor has been initialized");NS_ASSERTION(!mUsePlaceholder||txtCtrl->GetPlaceholderNode(),"A placeholder div must exist");nsIContent*textContent=rootNode->GetChildAt(0);if(!textContent){// Set up a textnode with our valuensCOMPtr<nsIContent>textNode;nsresultrv=NS_NewTextNode(getter_AddRefs(textNode),mContent->NodeInfo()->NodeInfoManager());NS_ENSURE_SUCCESS(rv,rv);NS_ASSERTION(textNode,"Must have textcontent!\n");rootNode->AppendChildTo(textNode,aNotify);textContent=textNode;}NS_ENSURE_TRUE(textContent,NS_ERROR_UNEXPECTED);// Get the current value of the textfield from the content.nsAutoStringvalue;if(aValue){value=*aValue;}else{txtCtrl->GetTextEditorValue(value,true);}// Update the display of the placeholder value if needed.// We don't need to do this if we're about to initialize the// editor, since EnsureEditorInitialized takes care of this.if(mUsePlaceholder&&!aBeforeEditorInit){nsWeakFrameweakFrame(this);txtCtrl->SetPlaceholderClass(value.IsEmpty(),aNotify);NS_ENSURE_STATE(weakFrame.IsAlive());}if(aBeforeEditorInit&&value.IsEmpty()){rootNode->RemoveChildAt(0,true);returnNS_OK;}if(!value.IsEmpty()&&IsPasswordTextControl()){nsTextEditRules::FillBufWithPWChars(&value,value.Length());}returntextContent->SetText(value,aNotify);}NS_IMETHODIMPnsTextControlFrame::GetOwnedSelectionController(nsISelectionController**aSelCon){NS_ENSURE_ARG_POINTER(aSelCon);nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");*aSelCon=txtCtrl->GetSelectionController();NS_IF_ADDREF(*aSelCon);returnNS_OK;}nsFrameSelection*nsTextControlFrame::GetOwnedFrameSelection(){nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");returntxtCtrl->GetConstFrameSelection();}NS_IMETHODIMPnsTextControlFrame::SaveState(nsIStatefulFrame::SpecialStateIDaStateID,nsPresState**aState){NS_ENSURE_ARG_POINTER(aState);*aState=nsnull;nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");nsIContent*rootNode=txtCtrl->GetRootEditorNode();if(rootNode){// Query the nsIStatefulFrame from the HTMLScrollFramensIStatefulFrame*scrollStateFrame=do_QueryFrame(rootNode->GetPrimaryFrame());if(scrollStateFrame){returnscrollStateFrame->SaveState(aStateID,aState);}}returnNS_OK;}NS_IMETHODIMPnsTextControlFrame::RestoreState(nsPresState*aState){NS_ENSURE_ARG_POINTER(aState);nsCOMPtr<nsITextControlElement>txtCtrl=do_QueryInterface(GetContent());NS_ASSERTION(txtCtrl,"Content not a text control element");nsIContent*rootNode=txtCtrl->GetRootEditorNode();if(rootNode){// Query the nsIStatefulFrame from the HTMLScrollFramensIStatefulFrame*scrollStateFrame=do_QueryFrame(rootNode->GetPrimaryFrame());if(scrollStateFrame){returnscrollStateFrame->RestoreState(aState);}}// Most likely, we don't have our anonymous content constructed yet, which// would cause us to end up here. In this case, we'll just store the scroll// pos ourselves, and forward it to the scroll frame later when it's created.Properties().Set(ContentScrollPos(),newnsPoint(aState->GetScrollState()));returnNS_OK;}NS_IMETHODIMPnsTextControlFrame::PeekOffset(nsPeekOffsetStruct*aPos){returnNS_ERROR_FAILURE;}