1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsIsIndexFrame.h"
40 #include "nsIContent.h"
43 #include "nsPresContext.h"
44 #include "nsGkAtoms.h"
45 #include "nsPresState.h"
46 #include "nsWidgetsCID.h"
47 #include "nsIComponentManager.h"
48 #include "nsHTMLParts.h"
49 #include "nsIDOMHTMLInputElement.h"
50 #include "nsINameSpaceManager.h"
52 #include "nsIDOMElement.h"
53 #include "nsIDOMDocument.h"
54 #include "nsIDocument.h"
55 #include "nsIPresShell.h"
56 #include "nsIDOMHTMLInputElement.h"
57 #include "nsIStatefulFrame.h"
59 #include "nsISupportsPrimitives.h"
60 #include "nsIComponentManager.h"
61 #include "nsHTMLParts.h"
62 #include "nsLinebreakConverter.h"
63 #include "nsILinkHandler.h"
64 #include "nsIHTMLDocument.h"
65 #include "nsXPIDLString.h"
66 #include "nsReadableUtils.h"
67 #include "nsNetUtil.h"
68 #include "nsICharsetConverterManager.h"
70 #include "nsIDOMKeyListener.h"
71 #include "nsIDOMKeyEvent.h"
72 #include "nsIFormControlFrame.h"
73 #include "nsINodeInfo.h"
74 #include "nsIDOMEventTarget.h"
75 #include "nsContentCID.h"
76 #include "nsNodeInfoManager.h"
77 #include "nsContentCreatorFunctions.h"
78 #include "nsContentUtils.h"
79 #include "nsLayoutErrors.h"
82 NS_NewIsIndexFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
84 return new (aPresShell
) nsIsIndexFrame(aContext
);
87 nsIsIndexFrame::nsIsIndexFrame(nsStyleContext
* aContext
) :
88 nsBlockFrame(aContext
)
90 SetFlags(NS_BLOCK_FLOAT_MGR
);
93 nsIsIndexFrame::~nsIsIndexFrame()
98 nsIsIndexFrame::Destroy()
100 // remove ourself as a listener of the text control (bug 40533)
102 mInputContent
->RemoveEventListenerByIID(this, NS_GET_IID(nsIDOMKeyListener
));
103 nsContentUtils::DestroyAnonymousContent(&mInputContent
);
105 nsContentUtils::DestroyAnonymousContent(&mTextContent
);
106 nsContentUtils::DestroyAnonymousContent(&mPreHr
);
107 nsContentUtils::DestroyAnonymousContent(&mPostHr
);
108 nsBlockFrame::Destroy();
111 // REVIEW: We don't need to override BuildDisplayList, nsBlockFrame will honour
112 // our visibility setting
115 nsIsIndexFrame::UpdatePromptLabel(PRBool aNotify
)
117 if (!mTextContent
) return NS_ERROR_UNEXPECTED
;
119 nsresult result
= NS_OK
;
121 // Get the text from the "prompt" attribute.
122 // If it is zero length, set it to a default value (localized)
123 nsXPIDLString prompt
;
125 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::prompt
, prompt
);
127 if (prompt
.IsEmpty()) {
128 // Generate localized label.
129 // We can't make any assumption as to what the default would be
130 // because the value is localized for non-english platforms, thus
131 // it might not be the string "This is a searchable index. Enter search keywords: "
133 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES
,
134 "IsIndexPrompt", prompt
);
137 mTextContent
->SetText(prompt
, aNotify
);
143 nsIsIndexFrame::GetInputFrame(nsIFormControlFrame
** oFrame
)
145 nsIPresShell
*presShell
= PresContext()->GetPresShell();
146 if (!mInputContent
) NS_WARNING("null content - cannot restore state");
147 if (presShell
&& mInputContent
) {
148 nsIFrame
*frame
= presShell
->GetPrimaryFrameFor(mInputContent
);
150 *oFrame
= do_QueryFrame(frame
);
151 return *oFrame
? NS_OK
: NS_NOINTERFACE
;
158 nsIsIndexFrame::GetInputValue(nsString
& oString
)
160 nsIFormControlFrame
* frame
= nsnull
;
161 GetInputFrame(&frame
);
163 ((nsNewFrame
*)frame
)->GetValue(oString
, PR_FALSE
);
168 nsIsIndexFrame::SetInputValue(const nsString
& aString
)
170 nsIFormControlFrame
* frame
= nsnull
;
171 GetInputFrame(&frame
);
173 ((nsNewFrame
*)frame
)->SetValue(aString
);
178 nsIsIndexFrame::SetFocus(PRBool aOn
, PRBool aRepaint
)
180 nsIFormControlFrame
* frame
= nsnull
;
181 GetInputFrame(&frame
);
183 frame
->SetFocus(aOn
, aRepaint
);
188 nsIsIndexFrame::CreateAnonymousContent(nsTArray
<nsIContent
*>& aElements
)
190 // Get the node info manager (used to create hr's and input's)
191 nsCOMPtr
<nsIDocument
> doc
= mContent
->GetDocument();
192 nsNodeInfoManager
*nimgr
= doc
->NodeInfoManager();
195 nsCOMPtr
<nsINodeInfo
> hrInfo
;
196 hrInfo
= nimgr
->GetNodeInfo(nsGkAtoms::hr
, nsnull
, kNameSpaceID_XHTML
);
198 NS_NewHTMLElement(getter_AddRefs(mPreHr
), hrInfo
, PR_FALSE
);
199 if (!mPreHr
|| !aElements
.AppendElement(mPreHr
))
200 return NS_ERROR_OUT_OF_MEMORY
;
202 // Add a child text content node for the label
203 NS_NewTextNode(getter_AddRefs(mTextContent
), nimgr
);
205 return NS_ERROR_OUT_OF_MEMORY
;
207 // set the value of the text node and add it to the child list
208 UpdatePromptLabel(PR_FALSE
);
209 if (!aElements
.AppendElement(mTextContent
))
210 return NS_ERROR_OUT_OF_MEMORY
;
212 // Create text input field
213 nsCOMPtr
<nsINodeInfo
> inputInfo
;
214 inputInfo
= nimgr
->GetNodeInfo(nsGkAtoms::input
, nsnull
, kNameSpaceID_XHTML
);
216 NS_NewHTMLElement(getter_AddRefs(mInputContent
), inputInfo
, PR_FALSE
);
218 return NS_ERROR_OUT_OF_MEMORY
;
220 mInputContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::type
,
221 NS_LITERAL_STRING("text"), PR_FALSE
);
223 if (!aElements
.AppendElement(mInputContent
))
224 return NS_ERROR_OUT_OF_MEMORY
;
226 // Register as an event listener to submit on Enter press
227 mInputContent
->AddEventListenerByIID(this, NS_GET_IID(nsIDOMKeyListener
));
230 NS_NewHTMLElement(getter_AddRefs(mPostHr
), hrInfo
, PR_FALSE
);
231 if (!mPostHr
|| !aElements
.AppendElement(mPostHr
))
232 return NS_ERROR_OUT_OF_MEMORY
;
237 NS_QUERYFRAME_HEAD(nsIsIndexFrame
)
238 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
239 NS_QUERYFRAME_ENTRY(nsIStatefulFrame
)
240 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame
)
242 // Frames are not refcounted, no need to AddRef
244 nsIsIndexFrame::QueryInterface(const nsIID
& aIID
, void** aInstancePtr
)
246 NS_PRECONDITION(aInstancePtr
, "null out param");
248 if (aIID
.Equals(NS_GET_IID(nsIDOMKeyListener
))) {
249 *aInstancePtr
= static_cast<nsIDOMKeyListener
*>(this);
252 if (aIID
.Equals(NS_GET_IID(nsIDOMEventListener
))) {
253 *aInstancePtr
= static_cast<nsIDOMEventListener
*>(this);
257 return NS_NOINTERFACE
;
261 nsIsIndexFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
264 DISPLAY_MIN_WIDTH(this, result
);
266 // Our min width is our pref width; the rest of our reflow is
267 // happily handled by nsBlockFrame
268 result
= GetPrefWidth(aRenderingContext
);
273 nsIsIndexFrame::IsLeaf() const
279 nsIsIndexFrame::AttributeChanged(PRInt32 aNameSpaceID
,
284 if (nsGkAtoms::prompt
== aAttribute
) {
285 rv
= UpdatePromptLabel(PR_TRUE
);
287 rv
= nsBlockFrame::AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
294 nsIsIndexFrame::KeyPress(nsIDOMEvent
* aEvent
)
296 nsCOMPtr
<nsIDOMKeyEvent
> keyEvent
= do_QueryInterface(aEvent
);
299 keyEvent
->GetKeyCode(&code
);
301 keyEvent
->GetCharCode(&code
);
303 if (nsIDOMKeyEvent::DOM_VK_RETURN
== code
) {
304 OnSubmit(PresContext());
305 aEvent
->PreventDefault(); // XXX Needed?
314 nsIsIndexFrame::GetFrameName(nsAString
& aResult
) const
316 return MakeFrameName(NS_LITERAL_STRING("IsIndex"), aResult
);
321 // much of this is cut and paste from nsFormFrame::OnSubmit
323 nsIsIndexFrame::OnSubmit(nsPresContext
* aPresContext
)
325 if (!mContent
|| !mInputContent
) {
326 return NS_ERROR_UNEXPECTED
;
329 if (mContent
->IsEditable()) {
333 nsresult result
= NS_OK
;
335 // Begin ProcessAsURLEncoded
338 nsCOMPtr
<nsIUnicodeEncoder
> encoder
;
339 if(NS_FAILED(GetEncoder(getter_AddRefs(encoder
)))) // Non-fatal error
343 GetInputValue(value
);
344 URLEncode(value
, encoder
, data
);
345 // End ProcessAsURLEncoded
347 // make the url string
348 nsILinkHandler
*handler
= aPresContext
->GetLinkHandler();
353 // We'll need it now to form the URL we're submitting to.
354 // We'll also need it later to get the DOM window when notifying form submit observers (bug 33203)
355 nsCOMPtr
<nsIDocument
> document
= mContent
->GetDocument();
356 if (!document
) return NS_OK
; // No doc means don't submit, see Bug 28988
358 // Resolve url to an absolute url
359 nsIURI
*baseURI
= document
->GetBaseURI();
361 NS_ERROR("No Base URL found in Form Submit!\n");
362 return NS_OK
; // No base URL -> exit early, see Bug 30721
365 // If an action is not specified and we are inside
366 // a HTML document then reload the URL. This makes us
367 // compatible with 4.x browsers.
368 // If we are in some other type of document such as XML or
369 // XUL, do nothing. This prevents undesirable reloading of
370 // a document inside XUL.
373 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
;
374 htmlDoc
= do_QueryInterface(document
, &rv
);
376 // Must be a XML, XUL or other non-HTML document type
381 // Necko's MakeAbsoluteURI doesn't reuse the baseURL's rel path if it is
382 // passed a zero length rel path.
383 nsCAutoString relPath
;
384 baseURI
->GetSpec(relPath
);
385 if (!relPath
.IsEmpty()) {
386 CopyUTF8toUTF16(relPath
, href
);
388 // If re-using the same URL, chop off old query string (bug 25330)
389 PRInt32 queryStart
= href
.FindChar('?');
390 if (kNotFound
!= queryStart
) {
391 href
.Truncate(queryStart
);
394 NS_ERROR("Rel path couldn't be formed in form submit!\n");
395 return NS_ERROR_OUT_OF_MEMORY
;
398 // Add the URI encoded form values to the URI
399 // Get the scheme of the URI.
400 nsCOMPtr
<nsIURI
> actionURL
;
401 nsXPIDLCString scheme
;
402 PRBool isJSURL
= PR_FALSE
;
403 const nsACString
&docCharset
= document
->GetDocumentCharacterSet();
404 const nsPromiseFlatCString
& flatDocCharset
= PromiseFlatCString(docCharset
);
406 if (NS_SUCCEEDED(result
= NS_NewURI(getter_AddRefs(actionURL
), href
,
407 flatDocCharset
.get(),
409 result
= actionURL
->SchemeIs("javascript", &isJSURL
);
411 // Append the URI encoded variable/value pairs for GET's
412 if (!isJSURL
) { // Not for JS URIs, see bug 26917
413 if (href
.FindChar('?') == kNotFound
) { // Add a ? if needed
414 href
.Append(PRUnichar('?'));
415 } else { // Adding to existing query string
416 if (href
.Last() != '&' && href
.Last() != '?') { // Add a & if needed
417 href
.Append(PRUnichar('&'));
422 nsCOMPtr
<nsIURI
> uri
;
423 result
= NS_NewURI(getter_AddRefs(uri
), href
,
424 flatDocCharset
.get(), baseURI
);
425 if (NS_FAILED(result
)) return result
;
427 // Now pass on absolute url to the click handler
429 handler
->OnLinkClick(mContent
, uri
, nsnull
);
434 void nsIsIndexFrame::GetSubmitCharset(nsCString
& oCharset
)
436 oCharset
.AssignLiteral("UTF-8"); // default to utf-8
438 // We may want to get it from the HTML 4 Accept-Charset attribute first
439 // see 17.3 The FORM element in HTML 4 for details
441 // Get the charset from document
442 nsIDocument
* doc
= mContent
->GetDocument();
444 oCharset
= doc
->GetDocumentCharacterSet();
448 NS_IMETHODIMP
nsIsIndexFrame::GetEncoder(nsIUnicodeEncoder
** encoder
)
451 nsCAutoString charset
;
453 GetSubmitCharset(charset
);
455 // Get Charset, get the encoder.
456 nsICharsetConverterManager
* ccm
= nsnull
;
457 rv
= CallGetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID
, &ccm
);
458 if(NS_SUCCEEDED(rv
) && (nsnull
!= ccm
)) {
459 rv
= ccm
->GetUnicodeEncoderRaw(charset
.get(), encoder
);
462 rv
= NS_ERROR_FAILURE
;
464 if (NS_SUCCEEDED(rv
)) {
465 rv
= (*encoder
)->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace
, nsnull
, (PRUnichar
)'?');
471 // XXX i18n helper routines
473 nsIsIndexFrame::UnicodeToNewBytes(const PRUnichar
* aSrc
, PRUint32 aLen
, nsIUnicodeEncoder
* encoder
)
476 if(NS_SUCCEEDED(encoder
->Reset()))
478 PRInt32 maxByteLen
= 0;
479 if(NS_SUCCEEDED(encoder
->GetMaxLength(aSrc
, (PRInt32
) aLen
, &maxByteLen
)))
481 res
= new char[maxByteLen
+1];
484 PRInt32 reslen
= maxByteLen
;
486 PRInt32 srclen
= aLen
;
487 encoder
->Convert(aSrc
, &srclen
, res
, &reslen
);
488 reslen2
= maxByteLen
-reslen
;
489 encoder
->Finish(res
+reslen
, &reslen2
);
490 res
[reslen
+reslen2
] = '\0';
498 // XXX i18n helper routines
500 nsIsIndexFrame::URLEncode(const nsString
& aString
, nsIUnicodeEncoder
* encoder
, nsString
& oString
)
502 char* inBuf
= nsnull
;
504 inBuf
= UnicodeToNewBytes(aString
.get(), aString
.Length(), encoder
);
507 inBuf
= ToNewCString(aString
);
509 // convert to CRLF breaks
510 char* convertedBuf
= nsLinebreakConverter::ConvertLineBreaks(inBuf
,
511 nsLinebreakConverter::eLinebreakAny
, nsLinebreakConverter::eLinebreakNet
);
514 char* outBuf
= nsEscape(convertedBuf
, url_XPAlphas
);
515 oString
.AssignASCII(outBuf
);
516 nsMemory::Free(outBuf
);
517 nsMemory::Free(convertedBuf
);
520 //----------------------------------------------------------------------
522 //----------------------------------------------------------------------
524 nsIsIndexFrame::SaveState(SpecialStateID aStateID
, nsPresState
** aState
)
526 NS_ENSURE_ARG_POINTER(aState
);
528 // Get the value string
529 nsAutoString stateString
;
530 GetInputValue(stateString
);
532 nsresult res
= NS_OK
;
533 if (! stateString
.IsEmpty()) {
535 // Construct a pres state and store value in it.
536 *aState
= new nsPresState();
538 return NS_ERROR_OUT_OF_MEMORY
;
540 nsCOMPtr
<nsISupportsString
> state
541 (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
));
544 return NS_ERROR_OUT_OF_MEMORY
;
546 state
->SetData(stateString
);
547 (*aState
)->SetStateProperty(state
);
554 nsIsIndexFrame::RestoreState(nsPresState
* aState
)
556 NS_ENSURE_ARG_POINTER(aState
);
558 // Set the value to the stored state.
559 nsCOMPtr
<nsISupportsString
> stateString
560 (do_QueryInterface(aState
->GetStateProperty()));
563 stateString
->GetData(data
);