1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/forms/nsTextControlFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1452 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/DebugOnly.h" 1.10 + 1.11 +#include "nsCOMPtr.h" 1.12 +#include "nsTextControlFrame.h" 1.13 +#include "nsIPlaintextEditor.h" 1.14 +#include "nsCaret.h" 1.15 +#include "nsGenericHTMLElement.h" 1.16 +#include "nsIEditor.h" 1.17 +#include "nsIEditorIMESupport.h" 1.18 +#include "nsIPhonetic.h" 1.19 +#include "nsTextFragment.h" 1.20 +#include "nsIDOMHTMLTextAreaElement.h" 1.21 +#include "nsNameSpaceManager.h" 1.22 +#include "nsINodeInfo.h" 1.23 +#include "nsFormControlFrame.h" //for registering accesskeys 1.24 + 1.25 +#include "nsIContent.h" 1.26 +#include "nsPresContext.h" 1.27 +#include "nsRenderingContext.h" 1.28 +#include "nsGkAtoms.h" 1.29 +#include "nsLayoutUtils.h" 1.30 +#include "nsIDOMElement.h" 1.31 +#include "nsIDOMHTMLElement.h" 1.32 +#include "nsIPresShell.h" 1.33 + 1.34 +#include <algorithm> 1.35 +#include "nsIDOMNodeList.h" //for selection setting helper func 1.36 +#include "nsIDOMRange.h" //for selection setting helper func 1.37 +#include "nsPIDOMWindow.h" //needed for notify selection changed to update the menus ect. 1.38 +#include "nsIDOMNode.h" 1.39 + 1.40 +#include "nsIDOMText.h" //for multiline getselection 1.41 +#include "nsFocusManager.h" 1.42 +#include "nsTextEditRules.h" 1.43 +#include "nsPresState.h" 1.44 +#include "nsContentList.h" 1.45 +#include "nsAttrValueInlines.h" 1.46 +#include "mozilla/dom/Selection.h" 1.47 +#include "nsContentUtils.h" 1.48 +#include "nsTextNode.h" 1.49 +#include "nsStyleSet.h" 1.50 +#include "mozilla/dom/ScriptSettings.h" 1.51 +#include "mozilla/MathAlgorithms.h" 1.52 + 1.53 +#define DEFAULT_COLUMN_WIDTH 20 1.54 + 1.55 +using namespace mozilla; 1.56 + 1.57 +nsIFrame* 1.58 +NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.59 +{ 1.60 + return new (aPresShell) nsTextControlFrame(aPresShell, aContext); 1.61 +} 1.62 + 1.63 +NS_IMPL_FRAMEARENA_HELPERS(nsTextControlFrame) 1.64 + 1.65 +NS_QUERYFRAME_HEAD(nsTextControlFrame) 1.66 + NS_QUERYFRAME_ENTRY(nsIFormControlFrame) 1.67 + NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) 1.68 + NS_QUERYFRAME_ENTRY(nsITextControlFrame) 1.69 + NS_QUERYFRAME_ENTRY(nsIStatefulFrame) 1.70 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 1.71 + 1.72 +#ifdef ACCESSIBILITY 1.73 +a11y::AccType 1.74 +nsTextControlFrame::AccessibleType() 1.75 +{ 1.76 + return a11y::eHTMLTextFieldType; 1.77 +} 1.78 +#endif 1.79 + 1.80 +#ifdef DEBUG 1.81 +class EditorInitializerEntryTracker { 1.82 +public: 1.83 + explicit EditorInitializerEntryTracker(nsTextControlFrame &frame) 1.84 + : mFrame(frame) 1.85 + , mFirstEntry(false) 1.86 + { 1.87 + if (!mFrame.mInEditorInitialization) { 1.88 + mFrame.mInEditorInitialization = true; 1.89 + mFirstEntry = true; 1.90 + } 1.91 + } 1.92 + ~EditorInitializerEntryTracker() 1.93 + { 1.94 + if (mFirstEntry) { 1.95 + mFrame.mInEditorInitialization = false; 1.96 + } 1.97 + } 1.98 + bool EnteredMoreThanOnce() const { return !mFirstEntry; } 1.99 +private: 1.100 + nsTextControlFrame &mFrame; 1.101 + bool mFirstEntry; 1.102 +}; 1.103 +#endif 1.104 + 1.105 +nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext) 1.106 + : nsContainerFrame(aContext) 1.107 + , mEditorHasBeenInitialized(false) 1.108 + , mIsProcessing(false) 1.109 +#ifdef DEBUG 1.110 + , mInEditorInitialization(false) 1.111 +#endif 1.112 +{ 1.113 +} 1.114 + 1.115 +nsTextControlFrame::~nsTextControlFrame() 1.116 +{ 1.117 +} 1.118 + 1.119 +void 1.120 +nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.121 +{ 1.122 + mScrollEvent.Revoke(); 1.123 + 1.124 + EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer()); 1.125 + if (initializer) { 1.126 + initializer->Revoke(); 1.127 + Properties().Delete(TextControlInitializer()); 1.128 + } 1.129 + 1.130 + // Unbind the text editor state object from the frame. The editor will live 1.131 + // on, but things like controllers will be released. 1.132 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.133 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.134 + txtCtrl->UnbindFromFrame(this); 1.135 + 1.136 + nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false); 1.137 + 1.138 + nsContainerFrame::DestroyFrom(aDestructRoot); 1.139 +} 1.140 + 1.141 +nsIAtom* 1.142 +nsTextControlFrame::GetType() const 1.143 +{ 1.144 + return nsGkAtoms::textInputFrame; 1.145 +} 1.146 + 1.147 +nsresult 1.148 +nsTextControlFrame::CalcIntrinsicSize(nsRenderingContext* aRenderingContext, 1.149 + nsSize& aIntrinsicSize, 1.150 + float aFontSizeInflation) 1.151 +{ 1.152 + // Get leading and the Average/MaxAdvance char width 1.153 + nscoord lineHeight = 0; 1.154 + nscoord charWidth = 0; 1.155 + nscoord charMaxAdvance = 0; 1.156 + 1.157 + nsRefPtr<nsFontMetrics> fontMet; 1.158 + nsresult rv = 1.159 + nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet), 1.160 + aFontSizeInflation); 1.161 + NS_ENSURE_SUCCESS(rv, rv); 1.162 + aRenderingContext->SetFont(fontMet); 1.163 + 1.164 + lineHeight = 1.165 + nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(), 1.166 + NS_AUTOHEIGHT, aFontSizeInflation); 1.167 + charWidth = fontMet->AveCharWidth(); 1.168 + charMaxAdvance = fontMet->MaxAdvance(); 1.169 + 1.170 + // Set the width equal to the width in characters 1.171 + int32_t cols = GetCols(); 1.172 + aIntrinsicSize.width = cols * charWidth; 1.173 + 1.174 + // To better match IE, take the maximum character width(in twips) and remove 1.175 + // 4 pixels add this on as additional padding(internalPadding). But only do 1.176 + // this if we think we have a fixed-width font. 1.177 + if (mozilla::Abs(charWidth - charMaxAdvance) > (unsigned)nsPresContext::CSSPixelsToAppUnits(1)) { 1.178 + nscoord internalPadding = 1.179 + std::max(0, charMaxAdvance - nsPresContext::CSSPixelsToAppUnits(4)); 1.180 + nscoord t = nsPresContext::CSSPixelsToAppUnits(1); 1.181 + // Round to a multiple of t 1.182 + nscoord rest = internalPadding % t; 1.183 + if (rest < t - rest) { 1.184 + internalPadding -= rest; 1.185 + } else { 1.186 + internalPadding += t - rest; 1.187 + } 1.188 + // Now add the extra padding on (so that small input sizes work well) 1.189 + aIntrinsicSize.width += internalPadding; 1.190 + } else { 1.191 + // This is to account for the anonymous <br> having a 1 twip width 1.192 + // in Full Standards mode, see BRFrame::Reflow and bug 228752. 1.193 + if (PresContext()->CompatibilityMode() == eCompatibility_FullStandards) { 1.194 + aIntrinsicSize.width += 1; 1.195 + } 1.196 + } 1.197 + 1.198 + // Increment width with cols * letter-spacing. 1.199 + { 1.200 + const nsStyleCoord& lsCoord = StyleText()->mLetterSpacing; 1.201 + if (eStyleUnit_Coord == lsCoord.GetUnit()) { 1.202 + nscoord letterSpacing = lsCoord.GetCoordValue(); 1.203 + if (letterSpacing != 0) { 1.204 + aIntrinsicSize.width += cols * letterSpacing; 1.205 + } 1.206 + } 1.207 + } 1.208 + 1.209 + // Set the height equal to total number of rows (times the height of each 1.210 + // line, of course) 1.211 + aIntrinsicSize.height = lineHeight * GetRows(); 1.212 + 1.213 + // Add in the size of the scrollbars for textarea 1.214 + if (IsTextArea()) { 1.215 + nsIFrame* first = GetFirstPrincipalChild(); 1.216 + 1.217 + nsIScrollableFrame *scrollableFrame = do_QueryFrame(first); 1.218 + NS_ASSERTION(scrollableFrame, "Child must be scrollable"); 1.219 + 1.220 + if (scrollableFrame) { 1.221 + nsMargin scrollbarSizes = 1.222 + scrollableFrame->GetDesiredScrollbarSizes(PresContext(), aRenderingContext); 1.223 + 1.224 + aIntrinsicSize.width += scrollbarSizes.LeftRight(); 1.225 + 1.226 + aIntrinsicSize.height += scrollbarSizes.TopBottom();; 1.227 + } 1.228 + } 1.229 + 1.230 + return NS_OK; 1.231 +} 1.232 + 1.233 +nsresult 1.234 +nsTextControlFrame::EnsureEditorInitialized() 1.235 +{ 1.236 + // This method initializes our editor, if needed. 1.237 + 1.238 + // This code used to be called from CreateAnonymousContent(), but 1.239 + // when the editor set the initial string, it would trigger a 1.240 + // PresShell listener which called FlushPendingNotifications() 1.241 + // during frame construction. This was causing other form controls 1.242 + // to display wrong values. Additionally, calling this every time 1.243 + // a text frame control is instantiated means that we're effectively 1.244 + // instantiating the editor for all text fields, even if they 1.245 + // never get used. So, now this method is being called lazily only 1.246 + // when we actually need an editor. 1.247 + 1.248 + if (mEditorHasBeenInitialized) 1.249 + return NS_OK; 1.250 + 1.251 + nsIDocument* doc = mContent->GetCurrentDoc(); 1.252 + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); 1.253 + 1.254 + nsWeakFrame weakFrame(this); 1.255 + 1.256 + // Flush out content on our document. Have to do this, because script 1.257 + // blockers don't prevent the sink flushing out content and notifying in the 1.258 + // process, which can destroy frames. 1.259 + doc->FlushPendingNotifications(Flush_ContentAndNotify); 1.260 + NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_ERROR_FAILURE); 1.261 + 1.262 + // Make sure that editor init doesn't do things that would kill us off 1.263 + // (especially off the script blockers it'll create for its DOM mutations). 1.264 + { 1.265 + nsAutoScriptBlocker scriptBlocker; 1.266 + 1.267 + // Time to mess with our security context... See comments in GetValue() 1.268 + // for why this is needed. 1.269 + mozilla::dom::AutoNoJSAPI nojsapi; 1.270 + 1.271 + // Make sure that we try to focus the content even if the method fails 1.272 + class EnsureSetFocus { 1.273 + public: 1.274 + explicit EnsureSetFocus(nsTextControlFrame* aFrame) 1.275 + : mFrame(aFrame) {} 1.276 + ~EnsureSetFocus() { 1.277 + if (nsContentUtils::IsFocusedContent(mFrame->GetContent())) 1.278 + mFrame->SetFocus(true, false); 1.279 + } 1.280 + private: 1.281 + nsTextControlFrame *mFrame; 1.282 + }; 1.283 + EnsureSetFocus makeSureSetFocusHappens(this); 1.284 + 1.285 +#ifdef DEBUG 1.286 + // Make sure we are not being called again until we're finished. 1.287 + // If reentrancy happens, just pretend that we don't have an editor. 1.288 + const EditorInitializerEntryTracker tracker(*this); 1.289 + NS_ASSERTION(!tracker.EnteredMoreThanOnce(), 1.290 + "EnsureEditorInitialized has been called while a previous call was in progress"); 1.291 +#endif 1.292 + 1.293 + // Create an editor for the frame, if one doesn't already exist 1.294 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.295 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.296 + nsresult rv = txtCtrl->CreateEditor(); 1.297 + NS_ENSURE_SUCCESS(rv, rv); 1.298 + NS_ENSURE_STATE(weakFrame.IsAlive()); 1.299 + 1.300 + // Set mEditorHasBeenInitialized so that subsequent calls will use the 1.301 + // editor. 1.302 + mEditorHasBeenInitialized = true; 1.303 + 1.304 + // Set the selection to the beginning of the text field. 1.305 + if (weakFrame.IsAlive()) { 1.306 + SetSelectionEndPoints(0, 0); 1.307 + } 1.308 + } 1.309 + NS_ENSURE_STATE(weakFrame.IsAlive()); 1.310 + return NS_OK; 1.311 +} 1.312 + 1.313 +nsresult 1.314 +nsTextControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) 1.315 +{ 1.316 + NS_ASSERTION(mContent, "We should have a content!"); 1.317 + 1.318 + mState |= NS_FRAME_INDEPENDENT_SELECTION; 1.319 + 1.320 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.321 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.322 + 1.323 + // Bind the frame to its text control 1.324 + nsresult rv = txtCtrl->BindToFrame(this); 1.325 + NS_ENSURE_SUCCESS(rv, rv); 1.326 + 1.327 + nsIContent* rootNode = txtCtrl->GetRootEditorNode(); 1.328 + NS_ENSURE_TRUE(rootNode, NS_ERROR_OUT_OF_MEMORY); 1.329 + 1.330 + if (!aElements.AppendElement(rootNode)) 1.331 + return NS_ERROR_OUT_OF_MEMORY; 1.332 + 1.333 + // Do we need a placeholder node? 1.334 + nsAutoString placeholderTxt; 1.335 + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, 1.336 + placeholderTxt); 1.337 + nsContentUtils::RemoveNewlines(placeholderTxt); 1.338 + mUsePlaceholder = !placeholderTxt.IsEmpty(); 1.339 + 1.340 + // Create the placeholder anonymous content if needed. 1.341 + if (mUsePlaceholder) { 1.342 + nsIContent* placeholderNode = txtCtrl->CreatePlaceholderNode(); 1.343 + NS_ENSURE_TRUE(placeholderNode, NS_ERROR_OUT_OF_MEMORY); 1.344 + 1.345 + // Associate ::-moz-placeholder pseudo-element with the placeholder node. 1.346 + nsCSSPseudoElements::Type pseudoType = 1.347 + nsCSSPseudoElements::ePseudo_mozPlaceholder; 1.348 + 1.349 + nsRefPtr<nsStyleContext> placeholderStyleContext = 1.350 + PresContext()->StyleSet()->ResolvePseudoElementStyle( 1.351 + mContent->AsElement(), pseudoType, StyleContext(), 1.352 + placeholderNode->AsElement()); 1.353 + 1.354 + if (!aElements.AppendElement(ContentInfo(placeholderNode, 1.355 + placeholderStyleContext))) { 1.356 + return NS_ERROR_OUT_OF_MEMORY; 1.357 + } 1.358 + } 1.359 + 1.360 + rv = UpdateValueDisplay(false); 1.361 + NS_ENSURE_SUCCESS(rv, rv); 1.362 + 1.363 + // textareas are eagerly initialized 1.364 + bool initEagerly = !IsSingleLineTextControl(); 1.365 + if (!initEagerly) { 1.366 + // Also, input elements which have a cached selection should get eager 1.367 + // editor initialization. 1.368 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.369 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.370 + initEagerly = txtCtrl->HasCachedSelection(); 1.371 + } 1.372 + if (!initEagerly) { 1.373 + nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(txtCtrl); 1.374 + if (element) { 1.375 + // so are input text controls with spellcheck=true 1.376 + element->GetSpellcheck(&initEagerly); 1.377 + } 1.378 + } 1.379 + 1.380 + if (initEagerly) { 1.381 + NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), 1.382 + "Someone forgot a script blocker?"); 1.383 + EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer()); 1.384 + if (initializer) { 1.385 + initializer->Revoke(); 1.386 + } 1.387 + initializer = new EditorInitializer(this); 1.388 + Properties().Set(TextControlInitializer(),initializer); 1.389 + if (!nsContentUtils::AddScriptRunner(initializer)) { 1.390 + initializer->Revoke(); // paranoia 1.391 + Properties().Delete(TextControlInitializer()); 1.392 + delete initializer; 1.393 + return NS_ERROR_OUT_OF_MEMORY; 1.394 + } 1.395 + } 1.396 + 1.397 + return NS_OK; 1.398 +} 1.399 + 1.400 +void 1.401 +nsTextControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, 1.402 + uint32_t aFilter) 1.403 +{ 1.404 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.405 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.406 + 1.407 + aElements.MaybeAppendElement(txtCtrl->GetRootEditorNode()); 1.408 + if (!(aFilter & nsIContent::eSkipPlaceholderContent)) 1.409 + aElements.MaybeAppendElement(txtCtrl->GetPlaceholderNode()); 1.410 + 1.411 +} 1.412 + 1.413 +nscoord 1.414 +nsTextControlFrame::GetPrefWidth(nsRenderingContext* aRenderingContext) 1.415 +{ 1.416 + DebugOnly<nscoord> result = 0; 1.417 + DISPLAY_PREF_WIDTH(this, result); 1.418 + 1.419 + float inflation = nsLayoutUtils::FontSizeInflationFor(this); 1.420 + nsSize autoSize; 1.421 + CalcIntrinsicSize(aRenderingContext, autoSize, inflation); 1.422 + 1.423 + return autoSize.width; 1.424 +} 1.425 + 1.426 +nscoord 1.427 +nsTextControlFrame::GetMinWidth(nsRenderingContext* aRenderingContext) 1.428 +{ 1.429 + // Our min width is just our preferred width if we have auto width. 1.430 + nscoord result; 1.431 + DISPLAY_MIN_WIDTH(this, result); 1.432 + 1.433 + result = GetPrefWidth(aRenderingContext); 1.434 + 1.435 + return result; 1.436 +} 1.437 + 1.438 +nsSize 1.439 +nsTextControlFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, 1.440 + nsSize aCBSize, nscoord aAvailableWidth, 1.441 + nsSize aMargin, nsSize aBorder, 1.442 + nsSize aPadding, bool aShrinkWrap) 1.443 +{ 1.444 + float inflation = nsLayoutUtils::FontSizeInflationFor(this); 1.445 + nsSize autoSize; 1.446 + nsresult rv = CalcIntrinsicSize(aRenderingContext, autoSize, inflation); 1.447 + if (NS_FAILED(rv)) { 1.448 + // What now? 1.449 + autoSize.SizeTo(0, 0); 1.450 + } 1.451 +#ifdef DEBUG 1.452 + // Note: Ancestor ComputeAutoSize only computes a width if we're auto-width 1.453 + else if (StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) { 1.454 + nsSize ancestorAutoSize = 1.455 + nsContainerFrame::ComputeAutoSize(aRenderingContext, 1.456 + aCBSize, aAvailableWidth, 1.457 + aMargin, aBorder, 1.458 + aPadding, aShrinkWrap); 1.459 + // Disabled when there's inflation; see comment in GetPrefSize. 1.460 + NS_ASSERTION(inflation != 1.0f || ancestorAutoSize.width == autoSize.width, 1.461 + "Incorrect size computed by ComputeAutoSize?"); 1.462 + } 1.463 +#endif 1.464 + 1.465 + return autoSize; 1.466 +} 1.467 + 1.468 +nsresult 1.469 +nsTextControlFrame::Reflow(nsPresContext* aPresContext, 1.470 + nsHTMLReflowMetrics& aDesiredSize, 1.471 + const nsHTMLReflowState& aReflowState, 1.472 + nsReflowStatus& aStatus) 1.473 +{ 1.474 + DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame"); 1.475 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); 1.476 + 1.477 + // make sure that the form registers itself on the initial/first reflow 1.478 + if (mState & NS_FRAME_FIRST_REFLOW) { 1.479 + nsFormControlFrame::RegUnRegAccessKey(this, true); 1.480 + } 1.481 + 1.482 + // set values of reflow's out parameters 1.483 + aDesiredSize.Width() = aReflowState.ComputedWidth() + 1.484 + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.485 + aDesiredSize.Height() = aReflowState.ComputedHeight() + 1.486 + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); 1.487 + 1.488 + // computation of the ascent wrt the input height 1.489 + nscoord lineHeight = aReflowState.ComputedHeight(); 1.490 + float inflation = nsLayoutUtils::FontSizeInflationFor(this); 1.491 + if (!IsSingleLineTextControl()) { 1.492 + lineHeight = nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(), 1.493 + NS_AUTOHEIGHT, inflation); 1.494 + } 1.495 + nsRefPtr<nsFontMetrics> fontMet; 1.496 + nsresult rv = nsLayoutUtils::GetFontMetricsForFrame(this, 1.497 + getter_AddRefs(fontMet), 1.498 + inflation); 1.499 + NS_ENSURE_SUCCESS(rv, rv); 1.500 + // now adjust for our borders and padding 1.501 + aDesiredSize.SetTopAscent( 1.502 + nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight) 1.503 + + aReflowState.ComputedPhysicalBorderPadding().top); 1.504 + 1.505 + // overflow handling 1.506 + aDesiredSize.SetOverflowAreasToDesiredBounds(); 1.507 + // perform reflow on all kids 1.508 + nsIFrame* kid = mFrames.FirstChild(); 1.509 + while (kid) { 1.510 + ReflowTextControlChild(kid, aPresContext, aReflowState, aStatus, aDesiredSize); 1.511 + kid = kid->GetNextSibling(); 1.512 + } 1.513 + 1.514 + // take into account css properties that affect overflow handling 1.515 + FinishAndStoreOverflow(&aDesiredSize); 1.516 + 1.517 + aStatus = NS_FRAME_COMPLETE; 1.518 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.519 + return NS_OK; 1.520 +} 1.521 + 1.522 +void 1.523 +nsTextControlFrame::ReflowTextControlChild(nsIFrame* aKid, 1.524 + nsPresContext* aPresContext, 1.525 + const nsHTMLReflowState& aReflowState, 1.526 + nsReflowStatus& aStatus, 1.527 + nsHTMLReflowMetrics& aParentDesiredSize) 1.528 +{ 1.529 + // compute available size and frame offsets for child 1.530 + nsSize availSize(aReflowState.ComputedWidth() + 1.531 + aReflowState.ComputedPhysicalPadding().LeftRight(), 1.532 + NS_UNCONSTRAINEDSIZE); 1.533 + 1.534 + nsHTMLReflowState kidReflowState(aPresContext, aReflowState, 1.535 + aKid, availSize, -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); 1.536 + // Override padding with our computed padding in case we got it from theming or percentage 1.537 + kidReflowState.Init(aPresContext, -1, -1, nullptr, &aReflowState.ComputedPhysicalPadding()); 1.538 + 1.539 + // Set computed width and computed height for the child 1.540 + kidReflowState.SetComputedWidth(aReflowState.ComputedWidth()); 1.541 + kidReflowState.SetComputedHeight(aReflowState.ComputedHeight()); 1.542 + 1.543 + // Offset the frame by the size of the parent's border 1.544 + nscoord xOffset = aReflowState.ComputedPhysicalBorderPadding().left - 1.545 + aReflowState.ComputedPhysicalPadding().left; 1.546 + nscoord yOffset = aReflowState.ComputedPhysicalBorderPadding().top - 1.547 + aReflowState.ComputedPhysicalPadding().top; 1.548 + 1.549 + // reflow the child 1.550 + nsHTMLReflowMetrics desiredSize(aReflowState); 1.551 + ReflowChild(aKid, aPresContext, desiredSize, kidReflowState, 1.552 + xOffset, yOffset, 0, aStatus); 1.553 + 1.554 + // place the child 1.555 + FinishReflowChild(aKid, aPresContext, desiredSize, 1.556 + &kidReflowState, xOffset, yOffset, 0); 1.557 + 1.558 + // consider the overflow 1.559 + aParentDesiredSize.mOverflowAreas.UnionWith(desiredSize.mOverflowAreas); 1.560 +} 1.561 + 1.562 +nsSize 1.563 +nsTextControlFrame::GetMinSize(nsBoxLayoutState& aState) 1.564 +{ 1.565 + // XXXbz why? Why not the nsBoxFrame sizes? 1.566 + return nsBox::GetMinSize(aState); 1.567 +} 1.568 + 1.569 +bool 1.570 +nsTextControlFrame::IsCollapsed() 1.571 +{ 1.572 + // We're never collapsed in the box sense. 1.573 + return false; 1.574 +} 1.575 + 1.576 +bool 1.577 +nsTextControlFrame::IsLeaf() const 1.578 +{ 1.579 + return true; 1.580 +} 1.581 + 1.582 +NS_IMETHODIMP 1.583 +nsTextControlFrame::ScrollOnFocusEvent::Run() 1.584 +{ 1.585 + if (mFrame) { 1.586 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(mFrame->GetContent()); 1.587 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.588 + nsISelectionController* selCon = txtCtrl->GetSelectionController(); 1.589 + if (selCon) { 1.590 + mFrame->mScrollEvent.Forget(); 1.591 + selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, 1.592 + nsISelectionController::SELECTION_FOCUS_REGION, 1.593 + nsISelectionController::SCROLL_SYNCHRONOUS); 1.594 + } 1.595 + } 1.596 + return NS_OK; 1.597 +} 1.598 + 1.599 +//IMPLEMENTING NS_IFORMCONTROLFRAME 1.600 +void nsTextControlFrame::SetFocus(bool aOn, bool aRepaint) 1.601 +{ 1.602 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.603 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.604 + 1.605 + // Revoke the previous scroll event if one exists 1.606 + mScrollEvent.Revoke(); 1.607 + 1.608 + // If 'dom.placeholeder.show_on_focus' preference is 'false', focusing or 1.609 + // blurring the frame can have an impact on the placeholder visibility. 1.610 + if (mUsePlaceholder) { 1.611 + txtCtrl->UpdatePlaceholderVisibility(true); 1.612 + } 1.613 + 1.614 + if (!aOn) { 1.615 + return; 1.616 + } 1.617 + 1.618 + nsISelectionController* selCon = txtCtrl->GetSelectionController(); 1.619 + if (!selCon) 1.620 + return; 1.621 + 1.622 + nsCOMPtr<nsISelection> ourSel; 1.623 + selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, 1.624 + getter_AddRefs(ourSel)); 1.625 + if (!ourSel) return; 1.626 + 1.627 + nsIPresShell* presShell = PresContext()->GetPresShell(); 1.628 + nsRefPtr<nsCaret> caret = presShell->GetCaret(); 1.629 + if (!caret) return; 1.630 + 1.631 + // Scroll the current selection into view 1.632 + nsISelection *caretSelection = caret->GetCaretDOMSelection(); 1.633 + const bool isFocusedRightNow = ourSel == caretSelection; 1.634 + if (!isFocusedRightNow) { 1.635 + // Don't scroll the current selection if we've been focused using the mouse. 1.636 + uint32_t lastFocusMethod = 0; 1.637 + nsIDocument* doc = GetContent()->GetCurrentDoc(); 1.638 + if (doc) { 1.639 + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); 1.640 + if (fm) { 1.641 + fm->GetLastFocusMethod(doc->GetWindow(), &lastFocusMethod); 1.642 + } 1.643 + } 1.644 + if (!(lastFocusMethod & nsIFocusManager::FLAG_BYMOUSE)) { 1.645 + nsRefPtr<ScrollOnFocusEvent> event = new ScrollOnFocusEvent(this); 1.646 + nsresult rv = NS_DispatchToCurrentThread(event); 1.647 + if (NS_SUCCEEDED(rv)) { 1.648 + mScrollEvent = event; 1.649 + } 1.650 + } 1.651 + } 1.652 + 1.653 + // tell the caret to use our selection 1.654 + caret->SetCaretDOMSelection(ourSel); 1.655 + 1.656 + // mutual-exclusion: the selection is either controlled by the 1.657 + // document or by the text input/area. Clear any selection in the 1.658 + // document since the focus is now on our independent selection. 1.659 + 1.660 + nsCOMPtr<nsISelectionController> selcon = do_QueryInterface(presShell); 1.661 + nsCOMPtr<nsISelection> docSel; 1.662 + selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, 1.663 + getter_AddRefs(docSel)); 1.664 + if (!docSel) return; 1.665 + 1.666 + bool isCollapsed = false; 1.667 + docSel->GetIsCollapsed(&isCollapsed); 1.668 + if (!isCollapsed) 1.669 + docSel->RemoveAllRanges(); 1.670 +} 1.671 + 1.672 +nsresult nsTextControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue) 1.673 +{ 1.674 + if (!mIsProcessing)//some kind of lock. 1.675 + { 1.676 + mIsProcessing = true; 1.677 + if (nsGkAtoms::select == aName) 1.678 + { 1.679 + // Select all the text. 1.680 + // 1.681 + // XXX: This is lame, we can't call editor's SelectAll method 1.682 + // because that triggers AutoCopies in unix builds. 1.683 + // Instead, we have to call our own homegrown version 1.684 + // of select all which merely builds a range that selects 1.685 + // all of the content and adds that to the selection. 1.686 + 1.687 + nsWeakFrame weakThis = this; 1.688 + SelectAllOrCollapseToEndOfText(true); // NOTE: can destroy the world 1.689 + if (!weakThis.IsAlive()) { 1.690 + return NS_OK; 1.691 + } 1.692 + } 1.693 + mIsProcessing = false; 1.694 + } 1.695 + return NS_OK; 1.696 +} 1.697 + 1.698 +NS_IMETHODIMP 1.699 +nsTextControlFrame::GetEditor(nsIEditor **aEditor) 1.700 +{ 1.701 + NS_ENSURE_ARG_POINTER(aEditor); 1.702 + 1.703 + nsresult rv = EnsureEditorInitialized(); 1.704 + NS_ENSURE_SUCCESS(rv, rv); 1.705 + 1.706 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.707 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.708 + *aEditor = txtCtrl->GetTextEditor(); 1.709 + NS_IF_ADDREF(*aEditor); 1.710 + return NS_OK; 1.711 +} 1.712 + 1.713 +nsresult 1.714 +nsTextControlFrame::SetSelectionInternal(nsIDOMNode *aStartNode, 1.715 + int32_t aStartOffset, 1.716 + nsIDOMNode *aEndNode, 1.717 + int32_t aEndOffset, 1.718 + nsITextControlFrame::SelectionDirection aDirection) 1.719 +{ 1.720 + // Create a new range to represent the new selection. 1.721 + // Note that we use a new range to avoid having to do 1.722 + // isIncreasing checks to avoid possible errors. 1.723 + 1.724 + nsRefPtr<nsRange> range = new nsRange(mContent); 1.725 + nsresult rv = range->SetStart(aStartNode, aStartOffset); 1.726 + NS_ENSURE_SUCCESS(rv, rv); 1.727 + 1.728 + rv = range->SetEnd(aEndNode, aEndOffset); 1.729 + NS_ENSURE_SUCCESS(rv, rv); 1.730 + 1.731 + // Get the selection, clear it and add the new range to it! 1.732 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.733 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.734 + nsISelectionController* selCon = txtCtrl->GetSelectionController(); 1.735 + NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE); 1.736 + 1.737 + nsCOMPtr<nsISelection> selection; 1.738 + selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); 1.739 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.740 + 1.741 + nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection, &rv); 1.742 + NS_ENSURE_SUCCESS(rv, rv); 1.743 + 1.744 + nsDirection direction; 1.745 + if (aDirection == eNone) { 1.746 + // Preserve the direction 1.747 + direction = selPriv->GetSelectionDirection(); 1.748 + } else { 1.749 + direction = (aDirection == eBackward) ? eDirPrevious : eDirNext; 1.750 + } 1.751 + 1.752 + rv = selection->RemoveAllRanges(); 1.753 + NS_ENSURE_SUCCESS(rv, rv); 1.754 + 1.755 + rv = selection->AddRange(range); // NOTE: can destroy the world 1.756 + NS_ENSURE_SUCCESS(rv, rv); 1.757 + 1.758 + selPriv->SetSelectionDirection(direction); 1.759 + return rv; 1.760 +} 1.761 + 1.762 +nsresult 1.763 +nsTextControlFrame::ScrollSelectionIntoView() 1.764 +{ 1.765 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.766 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.767 + nsISelectionController* selCon = txtCtrl->GetSelectionController(); 1.768 + if (selCon) { 1.769 + // Scroll the selection into view (see bug 231389). 1.770 + return selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, 1.771 + nsISelectionController::SELECTION_FOCUS_REGION, 1.772 + nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY); 1.773 + } 1.774 + 1.775 + return NS_ERROR_FAILURE; 1.776 +} 1.777 + 1.778 +mozilla::dom::Element* 1.779 +nsTextControlFrame::GetRootNodeAndInitializeEditor() 1.780 +{ 1.781 + nsCOMPtr<nsIDOMElement> root; 1.782 + GetRootNodeAndInitializeEditor(getter_AddRefs(root)); 1.783 + nsCOMPtr<mozilla::dom::Element> rootElem = do_QueryInterface(root); 1.784 + return rootElem; 1.785 +} 1.786 + 1.787 +nsresult 1.788 +nsTextControlFrame::GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement) 1.789 +{ 1.790 + NS_ENSURE_ARG_POINTER(aRootElement); 1.791 + 1.792 + nsCOMPtr<nsIEditor> editor; 1.793 + GetEditor(getter_AddRefs(editor)); 1.794 + if (!editor) 1.795 + return NS_OK; 1.796 + 1.797 + return editor->GetRootElement(aRootElement); 1.798 +} 1.799 + 1.800 +nsresult 1.801 +nsTextControlFrame::SelectAllOrCollapseToEndOfText(bool aSelect) 1.802 +{ 1.803 + nsCOMPtr<nsIDOMElement> rootElement; 1.804 + nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement)); 1.805 + NS_ENSURE_SUCCESS(rv, rv); 1.806 + 1.807 + nsCOMPtr<nsIContent> rootContent = do_QueryInterface(rootElement); 1.808 + nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement)); 1.809 + 1.810 + NS_ENSURE_TRUE(rootNode && rootContent, NS_ERROR_FAILURE); 1.811 + 1.812 + int32_t numChildren = rootContent->GetChildCount(); 1.813 + 1.814 + if (numChildren > 0) { 1.815 + // We never want to place the selection after the last 1.816 + // br under the root node! 1.817 + nsIContent *child = rootContent->GetChildAt(numChildren - 1); 1.818 + if (child) { 1.819 + if (child->Tag() == nsGkAtoms::br) 1.820 + --numChildren; 1.821 + } 1.822 + if (!aSelect && numChildren) { 1.823 + child = rootContent->GetChildAt(numChildren - 1); 1.824 + if (child && child->IsNodeOfType(nsINode::eTEXT)) { 1.825 + rootNode = do_QueryInterface(child); 1.826 + const nsTextFragment* fragment = child->GetText(); 1.827 + numChildren = fragment ? fragment->GetLength() : 0; 1.828 + } 1.829 + } 1.830 + } 1.831 + 1.832 + rv = SetSelectionInternal(rootNode, aSelect ? 0 : numChildren, 1.833 + rootNode, numChildren); 1.834 + NS_ENSURE_SUCCESS(rv, rv); 1.835 + 1.836 + return ScrollSelectionIntoView(); 1.837 +} 1.838 + 1.839 +nsresult 1.840 +nsTextControlFrame::SetSelectionEndPoints(int32_t aSelStart, int32_t aSelEnd, 1.841 + nsITextControlFrame::SelectionDirection aDirection) 1.842 +{ 1.843 + NS_ASSERTION(aSelStart <= aSelEnd, "Invalid selection offsets!"); 1.844 + 1.845 + if (aSelStart > aSelEnd) 1.846 + return NS_ERROR_FAILURE; 1.847 + 1.848 + nsCOMPtr<nsIDOMNode> startNode, endNode; 1.849 + int32_t startOffset, endOffset; 1.850 + 1.851 + // Calculate the selection start point. 1.852 + 1.853 + nsresult rv = OffsetToDOMPoint(aSelStart, getter_AddRefs(startNode), &startOffset); 1.854 + 1.855 + NS_ENSURE_SUCCESS(rv, rv); 1.856 + 1.857 + if (aSelStart == aSelEnd) { 1.858 + // Collapsed selection, so start and end are the same! 1.859 + endNode = startNode; 1.860 + endOffset = startOffset; 1.861 + } 1.862 + else { 1.863 + // Selection isn't collapsed so we have to calculate 1.864 + // the end point too. 1.865 + 1.866 + rv = OffsetToDOMPoint(aSelEnd, getter_AddRefs(endNode), &endOffset); 1.867 + 1.868 + NS_ENSURE_SUCCESS(rv, rv); 1.869 + } 1.870 + 1.871 + return SetSelectionInternal(startNode, startOffset, endNode, endOffset, aDirection); 1.872 +} 1.873 + 1.874 +NS_IMETHODIMP 1.875 +nsTextControlFrame::SetSelectionRange(int32_t aSelStart, int32_t aSelEnd, 1.876 + nsITextControlFrame::SelectionDirection aDirection) 1.877 +{ 1.878 + nsresult rv = EnsureEditorInitialized(); 1.879 + NS_ENSURE_SUCCESS(rv, rv); 1.880 + 1.881 + if (aSelStart > aSelEnd) { 1.882 + // Simulate what we'd see SetSelectionStart() was called, followed 1.883 + // by a SetSelectionEnd(). 1.884 + 1.885 + aSelStart = aSelEnd; 1.886 + } 1.887 + 1.888 + return SetSelectionEndPoints(aSelStart, aSelEnd, aDirection); 1.889 +} 1.890 + 1.891 + 1.892 +NS_IMETHODIMP 1.893 +nsTextControlFrame::SetSelectionStart(int32_t aSelectionStart) 1.894 +{ 1.895 + nsresult rv = EnsureEditorInitialized(); 1.896 + NS_ENSURE_SUCCESS(rv, rv); 1.897 + 1.898 + int32_t selStart = 0, selEnd = 0; 1.899 + 1.900 + rv = GetSelectionRange(&selStart, &selEnd); 1.901 + NS_ENSURE_SUCCESS(rv, rv); 1.902 + 1.903 + if (aSelectionStart > selEnd) { 1.904 + // Collapse to the new start point. 1.905 + selEnd = aSelectionStart; 1.906 + } 1.907 + 1.908 + selStart = aSelectionStart; 1.909 + 1.910 + return SetSelectionEndPoints(selStart, selEnd); 1.911 +} 1.912 + 1.913 +NS_IMETHODIMP 1.914 +nsTextControlFrame::SetSelectionEnd(int32_t aSelectionEnd) 1.915 +{ 1.916 + nsresult rv = EnsureEditorInitialized(); 1.917 + NS_ENSURE_SUCCESS(rv, rv); 1.918 + 1.919 + int32_t selStart = 0, selEnd = 0; 1.920 + 1.921 + rv = GetSelectionRange(&selStart, &selEnd); 1.922 + NS_ENSURE_SUCCESS(rv, rv); 1.923 + 1.924 + if (aSelectionEnd < selStart) { 1.925 + // Collapse to the new end point. 1.926 + selStart = aSelectionEnd; 1.927 + } 1.928 + 1.929 + selEnd = aSelectionEnd; 1.930 + 1.931 + return SetSelectionEndPoints(selStart, selEnd); 1.932 +} 1.933 + 1.934 +nsresult 1.935 +nsTextControlFrame::OffsetToDOMPoint(int32_t aOffset, 1.936 + nsIDOMNode** aResult, 1.937 + int32_t* aPosition) 1.938 +{ 1.939 + NS_ENSURE_ARG_POINTER(aResult && aPosition); 1.940 + 1.941 + *aResult = nullptr; 1.942 + *aPosition = 0; 1.943 + 1.944 + nsCOMPtr<nsIDOMElement> rootElement; 1.945 + nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement)); 1.946 + NS_ENSURE_SUCCESS(rv, rv); 1.947 + nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement)); 1.948 + 1.949 + NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE); 1.950 + 1.951 + nsCOMPtr<nsIDOMNodeList> nodeList; 1.952 + 1.953 + rv = rootNode->GetChildNodes(getter_AddRefs(nodeList)); 1.954 + NS_ENSURE_SUCCESS(rv, rv); 1.955 + NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); 1.956 + 1.957 + uint32_t length = 0; 1.958 + 1.959 + rv = nodeList->GetLength(&length); 1.960 + NS_ENSURE_SUCCESS(rv, rv); 1.961 + 1.962 + NS_ASSERTION(length <= 2, "We should have one text node and one mozBR at most"); 1.963 + 1.964 + nsCOMPtr<nsIDOMNode> firstNode; 1.965 + rv = nodeList->Item(0, getter_AddRefs(firstNode)); 1.966 + NS_ENSURE_SUCCESS(rv, rv); 1.967 + nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(firstNode); 1.968 + 1.969 + if (length == 0 || aOffset < 0) { 1.970 + NS_IF_ADDREF(*aResult = rootNode); 1.971 + *aPosition = 0; 1.972 + } else if (textNode) { 1.973 + uint32_t textLength = 0; 1.974 + textNode->GetLength(&textLength); 1.975 + if (length == 2 && uint32_t(aOffset) == textLength) { 1.976 + // If we're at the end of the text node and we have a trailing BR node, 1.977 + // set the selection on the BR node. 1.978 + NS_IF_ADDREF(*aResult = rootNode); 1.979 + *aPosition = 1; 1.980 + } else { 1.981 + // Otherwise, set the selection on the textnode itself. 1.982 + NS_IF_ADDREF(*aResult = firstNode); 1.983 + *aPosition = std::min(aOffset, int32_t(textLength)); 1.984 + } 1.985 + } else { 1.986 + NS_IF_ADDREF(*aResult = rootNode); 1.987 + *aPosition = 0; 1.988 + } 1.989 + 1.990 + return NS_OK; 1.991 +} 1.992 + 1.993 +NS_IMETHODIMP 1.994 +nsTextControlFrame::GetSelectionRange(int32_t* aSelectionStart, 1.995 + int32_t* aSelectionEnd, 1.996 + SelectionDirection* aDirection) 1.997 +{ 1.998 + // make sure we have an editor 1.999 + nsresult rv = EnsureEditorInitialized(); 1.1000 + NS_ENSURE_SUCCESS(rv, rv); 1.1001 + 1.1002 + if (aSelectionStart) { 1.1003 + *aSelectionStart = 0; 1.1004 + } 1.1005 + if (aSelectionEnd) { 1.1006 + *aSelectionEnd = 0; 1.1007 + } 1.1008 + if (aDirection) { 1.1009 + *aDirection = eNone; 1.1010 + } 1.1011 + 1.1012 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1013 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.1014 + nsISelectionController* selCon = txtCtrl->GetSelectionController(); 1.1015 + NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE); 1.1016 + nsCOMPtr<nsISelection> selection; 1.1017 + rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); 1.1018 + NS_ENSURE_SUCCESS(rv, rv); 1.1019 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.1020 + 1.1021 + dom::Selection* sel = static_cast<dom::Selection*>(selection.get()); 1.1022 + if (aDirection) { 1.1023 + nsDirection direction = sel->GetSelectionDirection(); 1.1024 + if (direction == eDirNext) { 1.1025 + *aDirection = eForward; 1.1026 + } else if (direction == eDirPrevious) { 1.1027 + *aDirection = eBackward; 1.1028 + } else { 1.1029 + NS_NOTREACHED("Invalid nsDirection enum value"); 1.1030 + } 1.1031 + } 1.1032 + 1.1033 + if (!aSelectionStart || !aSelectionEnd) { 1.1034 + return NS_OK; 1.1035 + } 1.1036 + 1.1037 + mozilla::dom::Element* root = GetRootNodeAndInitializeEditor(); 1.1038 + NS_ENSURE_STATE(root); 1.1039 + nsContentUtils::GetSelectionInTextControl(sel, root, 1.1040 + *aSelectionStart, *aSelectionEnd); 1.1041 + 1.1042 + return NS_OK; 1.1043 +} 1.1044 + 1.1045 +/////END INTERFACE IMPLEMENTATIONS 1.1046 + 1.1047 +////NSIFRAME 1.1048 +nsresult 1.1049 +nsTextControlFrame::AttributeChanged(int32_t aNameSpaceID, 1.1050 + nsIAtom* aAttribute, 1.1051 + int32_t aModType) 1.1052 +{ 1.1053 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1054 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.1055 + nsISelectionController* selCon = txtCtrl->GetSelectionController(); 1.1056 + const bool needEditor = nsGkAtoms::maxlength == aAttribute || 1.1057 + nsGkAtoms::readonly == aAttribute || 1.1058 + nsGkAtoms::disabled == aAttribute || 1.1059 + nsGkAtoms::spellcheck == aAttribute; 1.1060 + nsCOMPtr<nsIEditor> editor; 1.1061 + if (needEditor) { 1.1062 + GetEditor(getter_AddRefs(editor)); 1.1063 + } 1.1064 + if ((needEditor && !editor) || !selCon) { 1.1065 + return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); 1.1066 + } 1.1067 + 1.1068 + if (nsGkAtoms::maxlength == aAttribute) { 1.1069 + int32_t maxLength; 1.1070 + bool maxDefined = GetMaxLength(&maxLength); 1.1071 + nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(editor); 1.1072 + if (textEditor) { 1.1073 + if (maxDefined) { // set the maxLength attribute 1.1074 + textEditor->SetMaxTextLength(maxLength); 1.1075 + // if maxLength>docLength, we need to truncate the doc content 1.1076 + } else { // unset the maxLength attribute 1.1077 + textEditor->SetMaxTextLength(-1); 1.1078 + } 1.1079 + } 1.1080 + return NS_OK; 1.1081 + } 1.1082 + 1.1083 + if (nsGkAtoms::readonly == aAttribute) { 1.1084 + uint32_t flags; 1.1085 + editor->GetFlags(&flags); 1.1086 + if (AttributeExists(nsGkAtoms::readonly)) { // set readonly 1.1087 + flags |= nsIPlaintextEditor::eEditorReadonlyMask; 1.1088 + if (nsContentUtils::IsFocusedContent(mContent)) { 1.1089 + selCon->SetCaretEnabled(false); 1.1090 + } 1.1091 + } else { // unset readonly 1.1092 + flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask); 1.1093 + if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) && 1.1094 + nsContentUtils::IsFocusedContent(mContent)) { 1.1095 + selCon->SetCaretEnabled(true); 1.1096 + } 1.1097 + } 1.1098 + editor->SetFlags(flags); 1.1099 + return NS_OK; 1.1100 + } 1.1101 + 1.1102 + if (nsGkAtoms::disabled == aAttribute) { 1.1103 + uint32_t flags; 1.1104 + editor->GetFlags(&flags); 1.1105 + int16_t displaySelection = nsISelectionController::SELECTION_OFF; 1.1106 + const bool focused = nsContentUtils::IsFocusedContent(mContent); 1.1107 + const bool hasAttr = AttributeExists(nsGkAtoms::disabled); 1.1108 + if (hasAttr) { // set disabled 1.1109 + flags |= nsIPlaintextEditor::eEditorDisabledMask; 1.1110 + } else { // unset disabled 1.1111 + flags &= ~(nsIPlaintextEditor::eEditorDisabledMask); 1.1112 + displaySelection = focused ? nsISelectionController::SELECTION_ON 1.1113 + : nsISelectionController::SELECTION_HIDDEN; 1.1114 + } 1.1115 + selCon->SetDisplaySelection(displaySelection); 1.1116 + if (focused) { 1.1117 + selCon->SetCaretEnabled(!hasAttr); 1.1118 + } 1.1119 + editor->SetFlags(flags); 1.1120 + return NS_OK; 1.1121 + } 1.1122 + 1.1123 + if (!mEditorHasBeenInitialized && nsGkAtoms::value == aAttribute) { 1.1124 + UpdateValueDisplay(true); 1.1125 + return NS_OK; 1.1126 + } 1.1127 + 1.1128 + // Allow the base class to handle common attributes supported by all form 1.1129 + // elements... 1.1130 + return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); 1.1131 +} 1.1132 + 1.1133 + 1.1134 +nsresult 1.1135 +nsTextControlFrame::GetText(nsString& aText) 1.1136 +{ 1.1137 + nsresult rv = NS_OK; 1.1138 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1139 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.1140 + if (IsSingleLineTextControl()) { 1.1141 + // There will be no line breaks so we can ignore the wrap property. 1.1142 + txtCtrl->GetTextEditorValue(aText, true); 1.1143 + } else { 1.1144 + nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(mContent); 1.1145 + if (textArea) { 1.1146 + rv = textArea->GetValue(aText); 1.1147 + } 1.1148 + } 1.1149 + return rv; 1.1150 +} 1.1151 + 1.1152 + 1.1153 +nsresult 1.1154 +nsTextControlFrame::GetPhonetic(nsAString& aPhonetic) 1.1155 +{ 1.1156 + aPhonetic.Truncate(0); 1.1157 + 1.1158 + nsCOMPtr<nsIEditor> editor; 1.1159 + nsresult rv = GetEditor(getter_AddRefs(editor)); 1.1160 + NS_ENSURE_SUCCESS(rv, rv); 1.1161 + 1.1162 + nsCOMPtr<nsIEditorIMESupport> imeSupport = do_QueryInterface(editor); 1.1163 + if (imeSupport) { 1.1164 + nsCOMPtr<nsIPhonetic> phonetic = do_QueryInterface(imeSupport); 1.1165 + if (phonetic) 1.1166 + phonetic->GetPhonetic(aPhonetic); 1.1167 + } 1.1168 + return NS_OK; 1.1169 +} 1.1170 + 1.1171 +///END NSIFRAME OVERLOADS 1.1172 +/////BEGIN PROTECTED METHODS 1.1173 + 1.1174 +bool 1.1175 +nsTextControlFrame::GetMaxLength(int32_t* aSize) 1.1176 +{ 1.1177 + *aSize = -1; 1.1178 + 1.1179 + nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent); 1.1180 + if (content) { 1.1181 + const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::maxlength); 1.1182 + if (attr && attr->Type() == nsAttrValue::eInteger) { 1.1183 + *aSize = attr->GetIntegerValue(); 1.1184 + 1.1185 + return true; 1.1186 + } 1.1187 + } 1.1188 + return false; 1.1189 +} 1.1190 + 1.1191 +// END IMPLEMENTING NS_IFORMCONTROLFRAME 1.1192 + 1.1193 +nsresult 1.1194 +nsTextControlFrame::SetInitialChildList(ChildListID aListID, 1.1195 + nsFrameList& aChildList) 1.1196 +{ 1.1197 + nsresult rv = nsContainerFrame::SetInitialChildList(aListID, aChildList); 1.1198 + 1.1199 + nsIFrame* first = GetFirstPrincipalChild(); 1.1200 + 1.1201 + // Mark the scroll frame as being a reflow root. This will allow 1.1202 + // incremental reflows to be initiated at the scroll frame, rather 1.1203 + // than descending from the root frame of the frame hierarchy. 1.1204 + if (first) { 1.1205 + first->AddStateBits(NS_FRAME_REFLOW_ROOT); 1.1206 + 1.1207 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1208 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.1209 + txtCtrl->InitializeKeyboardEventListeners(); 1.1210 + 1.1211 + nsPoint* contentScrollPos = static_cast<nsPoint*> 1.1212 + (Properties().Get(ContentScrollPos())); 1.1213 + if (contentScrollPos) { 1.1214 + // If we have a scroll pos stored to be passed to our anonymous 1.1215 + // div, do it here! 1.1216 + nsIStatefulFrame* statefulFrame = do_QueryFrame(first); 1.1217 + NS_ASSERTION(statefulFrame, "unexpected type of frame for the anonymous div"); 1.1218 + nsPresState fakePresState; 1.1219 + fakePresState.SetScrollState(*contentScrollPos); 1.1220 + statefulFrame->RestoreState(&fakePresState); 1.1221 + Properties().Remove(ContentScrollPos()); 1.1222 + delete contentScrollPos; 1.1223 + } 1.1224 + } 1.1225 + return rv; 1.1226 +} 1.1227 + 1.1228 +void 1.1229 +nsTextControlFrame::SetValueChanged(bool aValueChanged) 1.1230 +{ 1.1231 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1232 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.1233 + 1.1234 + if (mUsePlaceholder) { 1.1235 + nsWeakFrame weakFrame(this); 1.1236 + txtCtrl->UpdatePlaceholderVisibility(true); 1.1237 + if (!weakFrame.IsAlive()) { 1.1238 + return; 1.1239 + } 1.1240 + } 1.1241 + 1.1242 + txtCtrl->SetValueChanged(aValueChanged); 1.1243 +} 1.1244 + 1.1245 + 1.1246 +nsresult 1.1247 +nsTextControlFrame::UpdateValueDisplay(bool aNotify, 1.1248 + bool aBeforeEditorInit, 1.1249 + const nsAString *aValue) 1.1250 +{ 1.1251 + if (!IsSingleLineTextControl()) // textareas don't use this 1.1252 + return NS_OK; 1.1253 + 1.1254 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1255 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.1256 + nsIContent* rootNode = txtCtrl->GetRootEditorNode(); 1.1257 + 1.1258 + NS_PRECONDITION(rootNode, "Must have a div content\n"); 1.1259 + NS_PRECONDITION(!mEditorHasBeenInitialized, 1.1260 + "Do not call this after editor has been initialized"); 1.1261 + NS_ASSERTION(!mUsePlaceholder || txtCtrl->GetPlaceholderNode(), 1.1262 + "A placeholder div must exist"); 1.1263 + 1.1264 + nsIContent *textContent = rootNode->GetChildAt(0); 1.1265 + if (!textContent) { 1.1266 + // Set up a textnode with our value 1.1267 + nsRefPtr<nsTextNode> textNode = 1.1268 + new nsTextNode(mContent->NodeInfo()->NodeInfoManager()); 1.1269 + 1.1270 + NS_ASSERTION(textNode, "Must have textcontent!\n"); 1.1271 + 1.1272 + rootNode->AppendChildTo(textNode, aNotify); 1.1273 + textContent = textNode; 1.1274 + } 1.1275 + 1.1276 + NS_ENSURE_TRUE(textContent, NS_ERROR_UNEXPECTED); 1.1277 + 1.1278 + // Get the current value of the textfield from the content. 1.1279 + nsAutoString value; 1.1280 + if (aValue) { 1.1281 + value = *aValue; 1.1282 + } else { 1.1283 + txtCtrl->GetTextEditorValue(value, true); 1.1284 + } 1.1285 + 1.1286 + // Update the display of the placeholder value if needed. 1.1287 + // We don't need to do this if we're about to initialize the 1.1288 + // editor, since EnsureEditorInitialized takes care of this. 1.1289 + if (mUsePlaceholder && !aBeforeEditorInit) 1.1290 + { 1.1291 + nsWeakFrame weakFrame(this); 1.1292 + txtCtrl->UpdatePlaceholderVisibility(aNotify); 1.1293 + NS_ENSURE_STATE(weakFrame.IsAlive()); 1.1294 + } 1.1295 + 1.1296 + if (aBeforeEditorInit && value.IsEmpty()) { 1.1297 + rootNode->RemoveChildAt(0, true); 1.1298 + return NS_OK; 1.1299 + } 1.1300 + 1.1301 + if (!value.IsEmpty() && IsPasswordTextControl()) { 1.1302 + nsTextEditRules::FillBufWithPWChars(&value, value.Length()); 1.1303 + } 1.1304 + return textContent->SetText(value, aNotify); 1.1305 +} 1.1306 + 1.1307 +NS_IMETHODIMP 1.1308 +nsTextControlFrame::GetOwnedSelectionController(nsISelectionController** aSelCon) 1.1309 +{ 1.1310 + NS_ENSURE_ARG_POINTER(aSelCon); 1.1311 + 1.1312 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1313 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.1314 + 1.1315 + *aSelCon = txtCtrl->GetSelectionController(); 1.1316 + NS_IF_ADDREF(*aSelCon); 1.1317 + 1.1318 + return NS_OK; 1.1319 +} 1.1320 + 1.1321 +nsFrameSelection* 1.1322 +nsTextControlFrame::GetOwnedFrameSelection() 1.1323 +{ 1.1324 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1325 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.1326 + 1.1327 + return txtCtrl->GetConstFrameSelection(); 1.1328 +} 1.1329 + 1.1330 +NS_IMETHODIMP 1.1331 +nsTextControlFrame::SaveState(nsPresState** aState) 1.1332 +{ 1.1333 + NS_ENSURE_ARG_POINTER(aState); 1.1334 + 1.1335 + *aState = nullptr; 1.1336 + 1.1337 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1338 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.1339 + 1.1340 + nsIContent* rootNode = txtCtrl->GetRootEditorNode(); 1.1341 + if (rootNode) { 1.1342 + // Query the nsIStatefulFrame from the HTMLScrollFrame 1.1343 + nsIStatefulFrame* scrollStateFrame = do_QueryFrame(rootNode->GetPrimaryFrame()); 1.1344 + if (scrollStateFrame) { 1.1345 + return scrollStateFrame->SaveState(aState); 1.1346 + } 1.1347 + } 1.1348 + 1.1349 + return NS_OK; 1.1350 +} 1.1351 + 1.1352 +NS_IMETHODIMP 1.1353 +nsTextControlFrame::RestoreState(nsPresState* aState) 1.1354 +{ 1.1355 + NS_ENSURE_ARG_POINTER(aState); 1.1356 + 1.1357 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1358 + NS_ASSERTION(txtCtrl, "Content not a text control element"); 1.1359 + 1.1360 + nsIContent* rootNode = txtCtrl->GetRootEditorNode(); 1.1361 + if (rootNode) { 1.1362 + // Query the nsIStatefulFrame from the HTMLScrollFrame 1.1363 + nsIStatefulFrame* scrollStateFrame = do_QueryFrame(rootNode->GetPrimaryFrame()); 1.1364 + if (scrollStateFrame) { 1.1365 + return scrollStateFrame->RestoreState(aState); 1.1366 + } 1.1367 + } 1.1368 + 1.1369 + // Most likely, we don't have our anonymous content constructed yet, which 1.1370 + // would cause us to end up here. In this case, we'll just store the scroll 1.1371 + // pos ourselves, and forward it to the scroll frame later when it's created. 1.1372 + Properties().Set(ContentScrollPos(), new nsPoint(aState->GetScrollState())); 1.1373 + return NS_OK; 1.1374 +} 1.1375 + 1.1376 +nsresult 1.1377 +nsTextControlFrame::PeekOffset(nsPeekOffsetStruct *aPos) 1.1378 +{ 1.1379 + return NS_ERROR_FAILURE; 1.1380 +} 1.1381 + 1.1382 +void 1.1383 +nsTextControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.1384 + const nsRect& aDirtyRect, 1.1385 + const nsDisplayListSet& aLists) 1.1386 +{ 1.1387 + /* 1.1388 + * The implementation of this method is equivalent as: 1.1389 + * nsContainerFrame::BuildDisplayList() 1.1390 + * with the difference that we filter-out the placeholder frame when it 1.1391 + * should not be visible. 1.1392 + */ 1.1393 + DO_GLOBAL_REFLOW_COUNT_DSP("nsTextControlFrame"); 1.1394 + 1.1395 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1396 + NS_ASSERTION(txtCtrl, "Content not a text control element!"); 1.1397 + 1.1398 + DisplayBorderBackgroundOutline(aBuilder, aLists); 1.1399 + 1.1400 + nsIFrame* kid = mFrames.FirstChild(); 1.1401 + // Redirect all lists to the Content list so that nothing can escape, ie 1.1402 + // opacity creating stacking contexts that then get sorted with stacking 1.1403 + // contexts external to us. 1.1404 + nsDisplayList* content = aLists.Content(); 1.1405 + nsDisplayListSet set(content, content, content, content, content, content); 1.1406 + 1.1407 + while (kid) { 1.1408 + // If the frame is the placeholder frame, we should only show it if the 1.1409 + // placeholder has to be visible. 1.1410 + if (kid->GetContent() != txtCtrl->GetPlaceholderNode() || 1.1411 + txtCtrl->GetPlaceholderVisibility()) { 1.1412 + BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, 0); 1.1413 + } 1.1414 + kid = kid->GetNextSibling(); 1.1415 + } 1.1416 +} 1.1417 + 1.1418 +mozilla::dom::Element* 1.1419 +nsTextControlFrame::GetPseudoElement(nsCSSPseudoElements::Type aType) 1.1420 +{ 1.1421 + if (aType == nsCSSPseudoElements::ePseudo_mozPlaceholder) { 1.1422 + nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); 1.1423 + return txtCtrl->GetPlaceholderNode(); 1.1424 + } 1.1425 + 1.1426 + return nsContainerFrame::GetPseudoElement(aType); 1.1427 +} 1.1428 + 1.1429 +NS_IMETHODIMP 1.1430 +nsTextControlFrame::EditorInitializer::Run() 1.1431 +{ 1.1432 + if (!mFrame) { 1.1433 + return NS_OK; 1.1434 + } 1.1435 + 1.1436 + // Need to block script to avoid bug 669767. 1.1437 + nsAutoScriptBlocker scriptBlocker; 1.1438 + 1.1439 + nsCOMPtr<nsIPresShell> shell = 1.1440 + mFrame->PresContext()->GetPresShell(); 1.1441 + bool observes = shell->ObservesNativeAnonMutationsForPrint(); 1.1442 + shell->ObserveNativeAnonMutationsForPrint(true); 1.1443 + // This can cause the frame to be destroyed (and call Revoke()). 1.1444 + mFrame->EnsureEditorInitialized(); 1.1445 + shell->ObserveNativeAnonMutationsForPrint(observes); 1.1446 + 1.1447 + // The frame can *still* be destroyed even though we have a scriptblocker, 1.1448 + // bug 682684. 1.1449 + if (!mFrame) { 1.1450 + return NS_ERROR_FAILURE; 1.1451 + } 1.1452 + 1.1453 + mFrame->FinishedInitializer(); 1.1454 + return NS_OK; 1.1455 +}