Backed out changeset 8366e5cc9f57 (bug 125282) because of four windows unit test...
[mozilla-central.git] / layout / forms / nsIsIndexFrame.cpp
blob4cad3acfc4fd8207ea89b649c0ecfdfa1224f45e
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
13 * License.
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.
22 * Contributor(s):
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"
41 #include "prtypes.h"
42 #include "nsIAtom.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"
51 #include "nsCOMPtr.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"
58 #include "nsXPCOM.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"
69 #include "nsEscape.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"
81 nsIFrame*
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()
97 void
98 nsIsIndexFrame::Destroy()
100 // remove ourself as a listener of the text control (bug 40533)
101 if (mInputContent) {
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
114 nsresult
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;
124 if (mContent)
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: "
132 result =
133 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
134 "IsIndexPrompt", prompt);
137 mTextContent->SetText(prompt, aNotify);
139 return NS_OK;
142 nsresult
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);
149 if (frame) {
150 *oFrame = do_QueryFrame(frame);
151 return *oFrame ? NS_OK : NS_NOINTERFACE;
154 return NS_OK;
157 void
158 nsIsIndexFrame::GetInputValue(nsString& oString)
160 nsIFormControlFrame* frame = nsnull;
161 GetInputFrame(&frame);
162 if (frame) {
163 ((nsNewFrame*)frame)->GetValue(oString, PR_FALSE);
167 void
168 nsIsIndexFrame::SetInputValue(const nsString& aString)
170 nsIFormControlFrame* frame = nsnull;
171 GetInputFrame(&frame);
172 if (frame) {
173 ((nsNewFrame*)frame)->SetValue(aString);
177 void
178 nsIsIndexFrame::SetFocus(PRBool aOn, PRBool aRepaint)
180 nsIFormControlFrame* frame = nsnull;
181 GetInputFrame(&frame);
182 if (frame) {
183 frame->SetFocus(aOn, aRepaint);
187 nsresult
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();
194 // Create an hr
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);
204 if (!mTextContent)
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);
217 if (!mInputContent)
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));
229 // Create an hr
230 NS_NewHTMLElement(getter_AddRefs(mPostHr), hrInfo, PR_FALSE);
231 if (!mPostHr || !aElements.AppendElement(mPostHr))
232 return NS_ERROR_OUT_OF_MEMORY;
234 return NS_OK;
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
243 NS_IMETHODIMP
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);
250 return NS_OK;
252 if (aIID.Equals(NS_GET_IID(nsIDOMEventListener))) {
253 *aInstancePtr = static_cast<nsIDOMEventListener*>(this);
254 return NS_OK;
257 return NS_NOINTERFACE;
260 nscoord
261 nsIsIndexFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
263 nscoord result;
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);
269 return result;
272 PRBool
273 nsIsIndexFrame::IsLeaf() const
275 return PR_TRUE;
278 NS_IMETHODIMP
279 nsIsIndexFrame::AttributeChanged(PRInt32 aNameSpaceID,
280 nsIAtom* aAttribute,
281 PRInt32 aModType)
283 nsresult rv = NS_OK;
284 if (nsGkAtoms::prompt == aAttribute) {
285 rv = UpdatePromptLabel(PR_TRUE);
286 } else {
287 rv = nsBlockFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
289 return rv;
293 nsresult
294 nsIsIndexFrame::KeyPress(nsIDOMEvent* aEvent)
296 nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
297 if (keyEvent) {
298 PRUint32 code;
299 keyEvent->GetKeyCode(&code);
300 if (code == 0) {
301 keyEvent->GetCharCode(&code);
303 if (nsIDOMKeyEvent::DOM_VK_RETURN == code) {
304 OnSubmit(PresContext());
305 aEvent->PreventDefault(); // XXX Needed?
309 return NS_OK;
312 #ifdef NS_DEBUG
313 NS_IMETHODIMP
314 nsIsIndexFrame::GetFrameName(nsAString& aResult) const
316 return MakeFrameName(NS_LITERAL_STRING("IsIndex"), aResult);
318 #endif
320 // submission
321 // much of this is cut and paste from nsFormFrame::OnSubmit
322 NS_IMETHODIMP
323 nsIsIndexFrame::OnSubmit(nsPresContext* aPresContext)
325 if (!mContent || !mInputContent) {
326 return NS_ERROR_UNEXPECTED;
329 if (mContent->IsEditable()) {
330 return NS_OK;
333 nsresult result = NS_OK;
335 // Begin ProcessAsURLEncoded
336 nsAutoString data;
338 nsCOMPtr<nsIUnicodeEncoder> encoder;
339 if(NS_FAILED(GetEncoder(getter_AddRefs(encoder)))) // Non-fatal error
340 encoder = nsnull;
342 nsAutoString value;
343 GetInputValue(value);
344 URLEncode(value, encoder, data);
345 // End ProcessAsURLEncoded
347 // make the url string
348 nsILinkHandler *handler = aPresContext->GetLinkHandler();
350 nsAutoString href;
352 // Get the document.
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();
360 if (!baseURI) {
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.
372 nsresult rv;
373 nsCOMPtr<nsIHTMLDocument> htmlDoc;
374 htmlDoc = do_QueryInterface(document, &rv);
375 if (NS_FAILED(rv)) {
376 // Must be a XML, XUL or other non-HTML document type
377 // so do nothing.
378 return NS_OK;
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);
393 } else {
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(),
408 baseURI))) {
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('&'));
420 href.Append(data);
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
428 if (handler) {
429 handler->OnLinkClick(mContent, uri, nsnull);
431 return result;
434 void nsIsIndexFrame::GetSubmitCharset(nsCString& oCharset)
436 oCharset.AssignLiteral("UTF-8"); // default to utf-8
437 // XXX
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();
443 if (doc) {
444 oCharset = doc->GetDocumentCharacterSet();
448 NS_IMETHODIMP nsIsIndexFrame::GetEncoder(nsIUnicodeEncoder** encoder)
450 *encoder = nsnull;
451 nsCAutoString charset;
452 nsresult rv = NS_OK;
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);
460 NS_RELEASE(ccm);
461 if (!*encoder) {
462 rv = NS_ERROR_FAILURE;
464 if (NS_SUCCEEDED(rv)) {
465 rv = (*encoder)->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nsnull, (PRUnichar)'?');
468 return rv;
471 // XXX i18n helper routines
472 char*
473 nsIsIndexFrame::UnicodeToNewBytes(const PRUnichar* aSrc, PRUint32 aLen, nsIUnicodeEncoder* encoder)
475 char* res = nsnull;
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];
482 if(nsnull != res)
484 PRInt32 reslen = maxByteLen;
485 PRInt32 reslen2 ;
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';
495 return res;
498 // XXX i18n helper routines
499 void
500 nsIsIndexFrame::URLEncode(const nsString& aString, nsIUnicodeEncoder* encoder, nsString& oString)
502 char* inBuf = nsnull;
503 if(encoder)
504 inBuf = UnicodeToNewBytes(aString.get(), aString.Length(), encoder);
506 if(nsnull == inBuf)
507 inBuf = ToNewCString(aString);
509 // convert to CRLF breaks
510 char* convertedBuf = nsLinebreakConverter::ConvertLineBreaks(inBuf,
511 nsLinebreakConverter::eLinebreakAny, nsLinebreakConverter::eLinebreakNet);
512 delete [] inBuf;
514 char* outBuf = nsEscape(convertedBuf, url_XPAlphas);
515 oString.AssignASCII(outBuf);
516 nsMemory::Free(outBuf);
517 nsMemory::Free(convertedBuf);
520 //----------------------------------------------------------------------
521 // nsIStatefulFrame
522 //----------------------------------------------------------------------
523 NS_IMETHODIMP
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();
537 if (!*aState)
538 return NS_ERROR_OUT_OF_MEMORY;
540 nsCOMPtr<nsISupportsString> state
541 (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
543 if (!state)
544 return NS_ERROR_OUT_OF_MEMORY;
546 state->SetData(stateString);
547 (*aState)->SetStateProperty(state);
550 return res;
553 NS_IMETHODIMP
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()));
562 nsAutoString data;
563 stateString->GetData(data);
564 SetInputValue(data);
566 return NS_OK;